Merge "Revert "Turn on V3 cow"" into main
diff --git a/ci/build_test_suites b/ci/build_test_suites
index 89ecefe..03f6731 100755
--- a/ci/build_test_suites
+++ b/ci/build_test_suites
@@ -1,4 +1,4 @@
-#!prebuilts/build-tools/linux-x86/bin/py3-cmd
+#!prebuilts/build-tools/linux-x86/bin/py3-cmd -B
 # Copyright 2024, The Android Open Source Project
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,10 +14,6 @@
 # limitations under the License.
 
 import sys
-
 import build_test_suites
 
-if __name__ == '__main__':
-  sys.dont_write_bytecode = True
-
-  build_test_suites.main(sys.argv)
+build_test_suites.main(sys.argv)
diff --git a/ci/build_test_suites.py b/ci/build_test_suites.py
index 5798f2b..1d5b377 100644
--- a/ci/build_test_suites.py
+++ b/ci/build_test_suites.py
@@ -39,13 +39,12 @@
 def build_test_suites(argv):
   args = parse_args(argv)
 
-  if not os.environ.get('BUILD_NUMBER')[0] == 'P':
+  if is_optimization_enabled():
+    # Call the class to map changed files to modules to build.
+    # TODO(lucafarsi): Move this into a replaceable class.
+    build_affected_modules(args)
+  else:
     build_everything(args)
-    return
-
-  # Call the class to map changed files to modules to build.
-  # TODO(lucafarsi): Move this into a replaceable class.
-  build_affected_modules(args)
 
 
 def parse_args(argv):
@@ -58,15 +57,21 @@
   argparser.add_argument(
       '--with_dexpreopt_boot_img_and_system_server_only', action='store_true'
   )
-  argparser.add_argument('--dist_dir')
   argparser.add_argument('--change_info', nargs='?')
-  argparser.add_argument('--extra_required_modules', nargs='*')
 
   return argparser.parse_args()
 
 
+def is_optimization_enabled() -> bool:
+  # TODO(lucafarsi): switch back to building only affected general-tests modules
+  # in presubmit once ready.
+  # if os.environ.get('BUILD_NUMBER')[0] == 'P':
+  #   return True
+  return False
+
+
 def build_everything(args: argparse.Namespace):
-  build_command = base_build_command(args)
+  build_command = base_build_command(args, args.extra_targets)
   build_command.append('general-tests')
 
   run_command(build_command, print_output=True)
@@ -78,7 +83,7 @@
   )
 
   # Call the build command with everything.
-  build_command = base_build_command(args)
+  build_command = base_build_command(args, args.extra_targets)
   build_command.extend(modules_to_build)
   # When not building general-tests we also have to build the general tests
   # shared libs.
@@ -86,21 +91,22 @@
 
   run_command(build_command, print_output=True)
 
-  zip_build_outputs(modules_to_build, args.dist_dir, args.target_release)
+  zip_build_outputs(modules_to_build, args.target_release)
 
 
-def base_build_command(args: argparse.Namespace) -> list:
+def base_build_command(
+    args: argparse.Namespace, extra_targets: set[str]
+) -> list:
   build_command = []
   build_command.append('time')
   build_command.append('./build/soong/soong_ui.bash')
   build_command.append('--make-mode')
   build_command.append('dist')
-  build_command.append('DIST_DIR=' + args.dist_dir)
   build_command.append('TARGET_PRODUCT=' + args.target_product)
   build_command.append('TARGET_RELEASE=' + args.target_release)
   if args.with_dexpreopt_boot_img_and_system_server_only:
     build_command.append('WITH_DEXPREOPT_BOOT_IMG_AND_SYSTEM_SERVER_ONLY=true')
-  build_command.extend(args.extra_targets)
+  build_command.extend(extra_targets)
 
   return build_command
 
@@ -214,7 +220,7 @@
 
 
 def zip_build_outputs(
-    modules_to_build: set[str], dist_dir: str, target_release: str
+    modules_to_build: set[str], target_release: str
 ):
   src_top = os.environ.get('TOP', os.getcwd())
 
@@ -230,6 +236,7 @@
   product_out = pathlib.Path(get_soong_var('PRODUCT_OUT', target_release))
   soong_host_out = pathlib.Path(get_soong_var('SOONG_HOST_OUT', target_release))
   host_out = pathlib.Path(get_soong_var('HOST_OUT', target_release))
+  dist_dir = pathlib.Path(get_soong_var('DIST_DIR', target_release))
 
   # Call the class to package the outputs.
   # TODO(lucafarsi): Move this code into a replaceable class.
@@ -405,4 +412,4 @@
 
 
 def main(argv):
-  build_test_suites(sys.argv)
+  build_test_suites(argv)
diff --git a/cogsetup.sh b/cogsetup.sh
index 6439af0..44538f2 100644
--- a/cogsetup.sh
+++ b/cogsetup.sh
@@ -21,18 +21,21 @@
     OUT_DIR="out"
   fi
 
-  if [[ -L "${OUT_DIR}" ]]; then
+  # getoutdir ensures paths are absolute. envsetup could be called from a
+  # directory other than the root of the source tree
+  local outdir=$(getoutdir)
+  if [[ -L "${outdir}" ]]; then
     return
   fi
-  if [ -d "${OUT_DIR}" ]; then
-    echo -e "\tOutput directory ${OUT_DIR} cannot be present in a Cog workspace."
-    echo -e "\tDelete \"${OUT_DIR}\" or create a symlink from \"${OUT_DIR}\" to a directory outside your workspace."
+  if [ -d "${outdir}" ]; then
+    echo -e "\tOutput directory ${outdir} cannot be present in a Cog workspace."
+    echo -e "\tDelete \"${outdir}\" or create a symlink from \"${outdir}\" to a directory outside your workspace."
     return 1
   fi
 
   DEFAULT_OUTPUT_DIR="${HOME}/.cog/android-build-out"
   mkdir -p ${DEFAULT_OUTPUT_DIR}
-  ln -s ${DEFAULT_OUTPUT_DIR} `pwd`/out
+  ln -s ${DEFAULT_OUTPUT_DIR} ${outdir}
 }
 
 # This function sets up the build environment to be appropriate for Cog.
@@ -63,4 +66,4 @@
   echo -e "\e[01;31mERROR:\e[0m This script must be run from a Cog workspace."
 fi
 
-_setup_cog_env
\ No newline at end of file
+_setup_cog_env
diff --git a/core/Makefile b/core/Makefile
index d4e241e..9d77ec1 100644
--- a/core/Makefile
+++ b/core/Makefile
@@ -465,9 +465,7 @@
 	)
 	$(if $(1),\
 	  cp $$(PRIVATE_MODULES) $$(PRIVATE_MODULE_DIR)/; \
-	  for MODULE in $$(PRIVATE_LOAD_MODULES); do \
-	    basename $$$$MODULE >> $$(PRIVATE_LOAD_FILE); \
-	  done; \
+	  if [ -n "$$(PRIVATE_LOAD_MODULES)" ]; then basename -a $$(PRIVATE_LOAD_MODULES); fi > $$(PRIVATE_LOAD_FILE); \
 	)
 	# The ln -sf + find -delete sequence is to remove any modules in
 	# PRIVATE_EXTRA_MODULES which have same basename as MODULES in PRIVATE_MODULES
@@ -2727,6 +2725,8 @@
 	$(if $(strip $(recovery_wipe)), \
 	  cp -f $(recovery_wipe) $(TARGET_RECOVERY_ROOT_OUT)/system/etc/recovery.wipe)
 	ln -sf prop.default $(TARGET_RECOVERY_ROOT_OUT)/default.prop
+	# Silence warnings in first_stage_console.
+	touch $(TARGET_RECOVERY_ROOT_OUT)/linkerconfig/ld.config.txt
 	$(BOARD_RECOVERY_IMAGE_PREPARE)
 	$(hide) touch $@
 
@@ -3395,6 +3395,14 @@
 
 FULL_SYSTEMIMAGE_DEPS += $(INTERNAL_ROOT_FILES) $(INSTALLED_FILES_FILE_ROOT)
 
+define write-file-lines
+$(1):
+	@echo Writing $$@
+	rm -f $$@
+	echo -n > $$@
+	$$(foreach f,$(2),echo "$$(f)" >> $$@$$(newline))
+endef
+
 # -----------------------------------------------------------------
 ifdef BUILDING_SYSTEM_IMAGE
 
@@ -3440,22 +3448,9 @@
 .PHONY: installed-file-list
 installed-file-list: $(INSTALLED_FILES_FILE)
 
-systemimage_intermediates :=$= $(call intermediates-dir-for,PACKAGING,systemimage)
+systemimage_intermediates :=$= $(call intermediates-dir-for,PACKAGING,system)
 BUILT_SYSTEMIMAGE :=$= $(systemimage_intermediates)/system.img
 
-
-# Used by the bazel sandwich to request the staging dir be built
-$(systemimage_intermediates)/staging_dir.stamp: $(FULL_SYSTEMIMAGE_DEPS)
-	touch $@
-
-define write-file-lines
-$(1):
-	@echo Writing $$@
-	rm -f $$@
-	echo -n > $$@
-	$$(foreach f,$(2),echo "$$(f)" >> $$@$$(newline))
-endef
-
 # $(1): output file
 define build-systemimage-target
   @echo "Target system fs image: $(1)"
@@ -3472,6 +3467,9 @@
 endef
 
 $(eval $(call write-file-lines,$(systemimage_intermediates)/file_list.txt,$(subst $(TARGET_OUT)/,,$(filter $(TARGET_OUT)/%,$(FULL_SYSTEMIMAGE_DEPS)))))
+# Used by soong sandwich to request the staging dir be built
+$(systemimage_intermediates)/staging_dir.stamp: $(filter $(TARGET_OUT)/%,$(FULL_SYSTEMIMAGE_DEPS))
+	touch $@
 
 ifeq ($(BOARD_AVB_ENABLE),true)
 $(BUILT_SYSTEMIMAGE): $(BOARD_AVB_SYSTEM_KEY_PATH)
@@ -3586,6 +3584,9 @@
     $(INTERNAL_USERDATAIMAGE_FILES)
 
 $(eval $(call write-file-lines,$(userdataimage_intermediates)/file_list.txt,$(subst $(TARGET_OUT_DATA)/,,$(filter $(TARGET_OUT_DATA)/%,$(INSTALLED_USERDATAIMAGE_TARGET_DEPS)))))
+# Used by soong sandwich to request the staging dir be built
+$(userdataimage_intermediates)/staging_dir.stamp: $(filter $(TARGET_OUT_DATA)/%,$(INSTALLED_USERDATAIMAGE_TARGET_DEPS))
+	touch $@
 
 $(INSTALLED_USERDATAIMAGE_TARGET): $(INSTALLED_USERDATAIMAGE_TARGET_DEPS) $(userdataimage_intermediates)/file_list.txt
 	$(build-userdataimage-target)
@@ -3639,6 +3640,9 @@
 endef
 
 $(eval $(call write-file-lines,$(cacheimage_intermediates)/file_list.txt,$(subst $(TARGET_OUT_CACHE)/,,$(filter $(TARGET_OUT_CACHE)/%,$(INTERNAL_CACHEIMAGE_FILES)))))
+# Used by soong sandwich to request the staging dir be built
+$(cacheimage_intermediates)/staging_dir.stamp: $(filter $(TARGET_OUT_CACHE)/%,$(INTERNAL_CACHEIMAGE_FILES))
+	touch $@
 
 # We just build this directly to the install location.
 INSTALLED_CACHEIMAGE_TARGET := $(BUILT_CACHEIMAGE_TARGET)
@@ -3723,6 +3727,9 @@
 endef
 
 $(eval $(call write-file-lines,$(systemotherimage_intermediates)/file_list.txt,$(subst $(TARGET_OUT_SYSTEM_OTHER)/,,$(filter $(TARGET_OUT_SYSTEM_OTHER)/%,$(INTERNAL_SYSTEMOTHERIMAGE_FILES)))))
+# Used by soong sandwich to request the staging dir be built
+$(systemotherimage_intermediates)/staging_dir.stamp: $(filter $(TARGET_OUT_SYSTEM_OTHER)/%,$(INTERNAL_SYSTEMOTHERIMAGE_FILES))
+	touch $@
 
 # We just build this directly to the install location.
 INSTALLED_SYSTEMOTHERIMAGE_TARGET := $(BUILT_SYSTEMOTHERIMAGE_TARGET)
@@ -3826,6 +3833,9 @@
 endef
 
 $(eval $(call write-file-lines,$(vendorimage_intermediates)/file_list.txt,$(subst $(TARGET_OUT_VENDOR)/,,$(filter $(TARGET_OUT_VENDOR)/%,$(INTERNAL_VENDORIMAGE_FILES)))))
+# Used by soong sandwich to request the staging dir be built
+$(vendorimage_intermediates)/staging_dir.stamp: $(filter $(TARGET_OUT_VENDOR)/%,$(INTERNAL_VENDORIMAGE_FILES))
+	touch $@
 
 # We just build this directly to the install location.
 INSTALLED_VENDORIMAGE_TARGET := $(BUILT_VENDORIMAGE_TARGET)
@@ -3896,6 +3906,9 @@
 endef
 
 $(eval $(call write-file-lines,$(productimage_intermediates)/file_list.txt,$(subst $(TARGET_OUT_PRODUCT)/,,$(filter $(TARGET_OUT_PRODUCT)/%,$(INTERNAL_PRODUCTIMAGE_FILES)))))
+# Used by soong sandwich to request the staging dir be built
+$(productimage_intermediates)/staging_dir.stamp: $(filter $(TARGET_OUT_PRODUCT)/%,$(INTERNAL_PRODUCTIMAGE_FILES))
+	touch $@
 
 # We just build this directly to the install location.
 INSTALLED_PRODUCTIMAGE_TARGET := $(BUILT_PRODUCTIMAGE_TARGET)
@@ -3963,6 +3976,9 @@
 endef
 
 $(eval $(call write-file-lines,$(system_extimage_intermediates)/file_list.txt,$(subst $(TARGET_OUT_SYSTEM_EXT)/,,$(filter $(TARGET_OUT_SYSTEM_EXT)/%,$(INTERNAL_SYSTEM_EXTIMAGE_FILES)))))
+# Used by soong sandwich to request the staging dir be built
+$(system_extimage_intermediates)/staging_dir.stamp: $(filter $(TARGET_OUT_SYSTEM_EXT)/%,$(INTERNAL_SYSTEM_EXTIMAGE_FILES))
+	touch $@
 
 # We just build this directly to the install location.
 INSTALLED_SYSTEM_EXTIMAGE_TARGET := $(BUILT_SYSTEM_EXTIMAGE_TARGET)
@@ -4049,6 +4065,9 @@
 endef
 
 $(eval $(call write-file-lines,$(odmimage_intermediates)/file_list.txt,$(subst $(TARGET_OUT_ODM)/,,$(filter $(TARGET_OUT_ODM)/%,$(INTERNAL_ODMIMAGE_FILES)))))
+# Used by soong sandwich to request the staging dir be built
+$(odmimage_intermediates)/staging_dir.stamp: $(filter $(TARGET_OUT_ODM)/%,$(INTERNAL_ODMIMAGE_FILES))
+	touch $@
 
 # We just build this directly to the install location.
 INSTALLED_ODMIMAGE_TARGET := $(BUILT_ODMIMAGE_TARGET)
@@ -4115,6 +4134,9 @@
 endef
 
 $(eval $(call write-file-lines,$(vendor_dlkmimage_intermediates)/file_list.txt,$(subst $(TARGET_OUT_VENDOR_DLKM)/,,$(filter $(TARGET_OUT_VENDOR_DLKM)/%,$(INTERNAL_VENDOR_DLKMIMAGE_FILES)))))
+# Used by soong sandwich to request the staging dir be built
+$(vendor_dlkmimage_intermediates)/staging_dir.stamp: $(filter $(TARGET_OUT_VENDOR_DLKM)/%,$(INTERNAL_VENDOR_DLKMIMAGE_FILES))
+	touch $@
 
 # We just build this directly to the install location.
 INSTALLED_VENDOR_DLKMIMAGE_TARGET := $(BUILT_VENDOR_DLKMIMAGE_TARGET)
@@ -4181,6 +4203,9 @@
 endef
 
 $(eval $(call write-file-lines,$(odm_dlkmimage_intermediates)/file_list.txt,$(subst $(TARGET_OUT_ODM_DLKM)/,,$(filter $(TARGET_OUT_ODM_DLKM)/%,$(INTERNAL_ODM_DLKMIMAGE_FILES)))))
+# Used by soong sandwich to request the staging dir be built
+$(odm_dlkmimage_intermediates)/staging_dir.stamp: $(filter $(TARGET_OUT_ODM_DLKM)/%,$(INTERNAL_ODM_DLKMIMAGE_FILES))
+	touch $@
 
 # We just build this directly to the install location.
 INSTALLED_ODM_DLKMIMAGE_TARGET := $(BUILT_ODM_DLKMIMAGE_TARGET)
@@ -4249,6 +4274,9 @@
 endef
 
 $(eval $(call write-file-lines,$(system_dlkmimage_intermediates)/file_list.txt,$(subst $(TARGET_OUT_SYSTEM_DLKM)/,,$(filter $(TARGET_OUT_SYSTEM_DLKM)/%,$(INTERNAL_SYSTEM_DLKMIMAGE_FILES)))))
+# Used by soong sandwich to request the staging dir be built
+$(system_dlkmimage_intermediates)/staging_dir.stamp: $(filter $(TARGET_OUT_SYSTEM_DLKM)/%,$(INTERNAL_SYSTEM_DLKMIMAGE_FILES))
+	touch $@
 
 # We just build this directly to the install location.
 INSTALLED_SYSTEM_DLKMIMAGE_TARGET := $(BUILT_SYSTEM_DLKMIMAGE_TARGET)
@@ -5061,9 +5089,12 @@
 check_vintf_all_deps :=
 
 # -----------------------------------------------------------------
-# Activate vendor APEXes for checkvintf
+# Activate APEXes for checkvintf
 
 apex_dirs := \
+  $(TARGET_OUT)/apex/% \
+  $(TARGET_OUT_PRODUCT)/apex/% \
+  $(TARGET_OUT_SYSTEM_EXT)/apex/% \
   $(TARGET_OUT_VENDOR)/apex/% \
 
 apex_files := $(sort $(filter $(apex_dirs), $(INTERNAL_ALLIMAGES_FILES)))
@@ -5079,7 +5110,10 @@
 	@echo $(PRIVATE_APEX_FILES) > /dev/null
 	@rm -rf $(APEX_OUT)
 	@mkdir -p $(APEX_OUT)
-	$< --vendor_path $(TARGET_OUT_VENDOR) \
+	$< --system_path $(TARGET_OUT) \
+	   --system_ext_path $(TARGET_OUT_SYSTEM_EXT) \
+	   --product_path $(TARGET_OUT_PRODUCT) \
+	   --vendor_path $(TARGET_OUT_VENDOR) \
 	   --apex_path $(APEX_OUT)
 
 apex_files :=
@@ -5087,14 +5121,14 @@
 
 # The build system only writes VINTF metadata to */etc/vintf paths. Legacy paths aren't needed here
 # because they are only used for prebuilt images.
-# APEX files in /vendor/apex can have VINTF fragments as well.
+# APEX files in /$partition/apex can have VINTF fragments as well.
 check_vintf_common_srcs_patterns := \
   $(TARGET_OUT)/etc/vintf/% \
   $(TARGET_OUT_VENDOR)/etc/vintf/% \
   $(TARGET_OUT_ODM)/etc/vintf/% \
   $(TARGET_OUT_PRODUCT)/etc/vintf/% \
   $(TARGET_OUT_SYSTEM_EXT)/etc/vintf/% \
-  $(TARGET_OUT_VENDOR)/apex/% \
+  $(apex_dirs)
 
 check_vintf_common_srcs := $(sort $(filter $(check_vintf_common_srcs_patterns),$(INTERNAL_ALLIMAGES_FILES)))
 check_vintf_common_srcs_patterns :=
@@ -5110,16 +5144,19 @@
 endif
 
 # -- Check system and system_ext manifests / matrices including fragments (excluding other framework manifests / matrices, e.g. product);
+ifdef BUILDING_SYSTEM_IMAGE
 check_vintf_system_deps := $(filter $(TARGET_OUT)/etc/vintf/% \
-                                    $(TARGET_OUT_SYSTEM_EXT)/etc/vintf/%, \
+                                    $(TARGET_OUT_SYSTEM_EXT)/etc/vintf/% \
+                                    $(TARGET_OUT)/apex/% \
+                                    $(TARGET_OUT_SYSTEM_EXT)/apex/%, \
                                     $(check_vintf_common_srcs))
 ifneq ($(check_vintf_system_deps),)
 check_vintf_has_system := true
 
 check_vintf_system_log := $(intermediates)/check_vintf_system.log
 check_vintf_all_deps += $(check_vintf_system_log)
-$(check_vintf_system_log): $(HOST_OUT_EXECUTABLES)/checkvintf $(check_vintf_system_deps)
-	@( $< --check-one --dirmap /system:$(TARGET_OUT) > $@ 2>&1 ) || ( cat $@ && exit 1 )
+$(check_vintf_system_log): $(HOST_OUT_EXECUTABLES)/checkvintf $(check_vintf_system_deps) $(APEX_INFO_FILE)
+	@( $< --check-one --dirmap /system:$(TARGET_OUT) --dirmap /apex:$(APEX_OUT) > $@ 2>&1 ) || ( cat $@ && exit 1 )
 $(call declare-1p-target,$(check_vintf_system_log))
 check_vintf_system_log :=
 
@@ -5128,10 +5165,11 @@
     vintffm_log := $(intermediates)/vintffm.log
 endif
 check_vintf_all_deps += $(vintffm_log)
-$(vintffm_log): $(HOST_OUT_EXECUTABLES)/vintffm $(check_vintf_system_deps)
+$(vintffm_log): $(HOST_OUT_EXECUTABLES)/vintffm $(check_vintf_system_deps) $(APEX_INFO_FILE)
 	@( $< --check --dirmap /system:$(TARGET_OUT) \
 	  --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 )
 
 $(call declare-1p-target,$(vintffm_log))
@@ -5139,6 +5177,8 @@
 endif # check_vintf_system_deps
 check_vintf_system_deps :=
 
+endif # BUILDING_SYSTEM_IMAGE
+
 # -- Check vendor manifest / matrix including fragments (excluding other device manifests / matrices)
 check_vintf_vendor_deps := $(filter $(TARGET_OUT_VENDOR)/etc/vintf/% \
                                     $(TARGET_OUT_VENDOR)/apex/%, \
@@ -5714,6 +5754,10 @@
 	  echo "flash vbmeta_$(partition)" >> $@;)
 endif
 endif # BOARD_AVB_ENABLE
+ifneq (,$(strip $(BOARD_CUSTOMIMAGES_PARTITION_LIST)))
+	$(hide) $(foreach partition,$(BOARD_CUSTOMIMAGES_PARTITION_LIST), \
+	  echo "flash $(partition)" >> $@;)
+endif
 	$(hide) echo "reboot fastboot" >> $@
 	$(hide) echo "update-super" >> $@
 	$(hide) $(foreach partition,$(BOARD_SUPER_PARTITION_PARTITION_LIST), \
@@ -5915,6 +5959,9 @@
 ifeq ($(TARGET_OTA_ALLOW_NON_AB),true)
 	$(hide) echo "allow_non_ab=true" >> $@
 endif
+ifeq ($(BOARD_NON_AB_OTA_DISABLE_COMPRESSION),true)
+	$(hide) echo "board_non_ab_ota_disable_compression=true" >> $@
+endif
 ifdef BOARD_PREBUILT_DTBOIMAGE
 	$(hide) echo "has_dtbo=true" >> $@
 ifeq ($(BOARD_AVB_ENABLE),true)
@@ -6075,7 +6122,7 @@
 # $1: root directory
 # $2: add prefix
 define fs_config
-(cd $(1); find . -type d | sed 's,$$,/,'; find . \! -type d) | cut -c 3- | sort | sed 's,^,$(2),' | $(HOST_OUT_EXECUTABLES)/fs_config -C -D $(TARGET_OUT) -S $(SELINUX_FC) -R "$(2)"
+(cd $(1); find . -type d | sed 's,$$,/,'; find . \! -type d) | cut -c 3- | sort | sed 's,^,$(2),' | $(HOST_OUT_EXECUTABLES)/fs_config -C -D $(TARGET_OUT) -R "$(2)"
 endef
 
 define filter-out-missing-vendor
@@ -7555,6 +7602,24 @@
 sdk_atree_files += $(atree_dir)/sdk.atree
 endif
 
+SDK_METADATA_DIR :=$= $(call intermediates-dir-for,PACKAGING,framework-doc-stubs-metadata,,COMMON)
+SDK_METADATA_FILES :=$= $(addprefix $(SDK_METADATA_DIR)/,\
+    activity_actions.txt \
+    broadcast_actions.txt \
+    categories.txt \
+    features.txt \
+    service_actions.txt \
+    widgets.txt)
+SDK_METADATA :=$= $(firstword $(SDK_METADATA_FILES))
+$(SDK_METADATA): .KATI_IMPLICIT_OUTPUTS := $(filter-out $(SDK_METADATA),$(SDK_METADATA_FILES))
+$(SDK_METADATA): $(TARGET_OUT_COMMON_INTERMEDIATES)/PACKAGING/framework-doc-stubs-metadata.zip
+	rm -rf $(SDK_METADATA_DIR)
+	mkdir -p $(SDK_METADATA_DIR)
+	unzip -DDqo $< -d $(SDK_METADATA_DIR)
+
+.PHONY: framework-doc-stubs
+framework-doc-stubs: $(SDK_METADATA)
+
 deps := \
 	$(OUT_DOCS)/offline-sdk-timestamp \
 	$(SDK_METADATA_FILES) \
diff --git a/core/OWNERS b/core/OWNERS
index 36951a9..1c3d017 100644
--- a/core/OWNERS
+++ b/core/OWNERS
@@ -10,3 +10,6 @@
 
 # For Ravenwood test configs
 per-file ravenwood_test_config_template.xml = jsharkey@google.com,omakoto@google.com
+
+# For binary_translation
+per-file berberis_test.mk = levarum@google.com,khim@google.com,dimitry@google.com
diff --git a/core/android_soong_config_vars.mk b/core/android_soong_config_vars.mk
index 9f43a3e..ca87417 100644
--- a/core/android_soong_config_vars.mk
+++ b/core/android_soong_config_vars.mk
@@ -37,7 +37,12 @@
 # Default behavior for the tree wrt building modules or using prebuilts. This
 # can always be overridden by setting the environment variable
 # MODULE_BUILD_FROM_SOURCE.
-BRANCH_DEFAULT_MODULE_BUILD_FROM_SOURCE := true
+BRANCH_DEFAULT_MODULE_BUILD_FROM_SOURCE := $(RELEASE_DEFAULT_MODULE_BUILD_FROM_SOURCE)
+# TODO(b/301454934): The value from build flag is set to empty when use `False`
+# The condition below can be removed after the issue get sorted.
+ifeq (,$(BRANCH_DEFAULT_MODULE_BUILD_FROM_SOURCE))
+  BRANCH_DEFAULT_MODULE_BUILD_FROM_SOURCE := false
+endif
 
 ifneq ($(SANITIZE_TARGET)$(EMMA_INSTRUMENT_FRAMEWORK),)
   # Always use sources when building the framework with Java coverage or
@@ -46,6 +51,18 @@
   BRANCH_DEFAULT_MODULE_BUILD_FROM_SOURCE := true
 endif
 
+ifneq ($(CLANG_COVERAGE)$(NATIVE_COVERAGE_PATHS),)
+  # Always use sources when building with clang coverage and native coverage.
+  # It is possible that there are certain situations when building with coverage
+  # would work with prebuilts, e.g. when the coverage is not being applied to
+  # modules for which we provide prebuilts. Unfortunately, determining that
+  # would require embedding knowledge of which coverage paths affect which
+  # modules here. That would duplicate a lot of information, add yet another
+  # location  module authors have to update and complicate the logic here.
+  # For nowe we will just always build from sources when doing coverage builds.
+  BRANCH_DEFAULT_MODULE_BUILD_FROM_SOURCE := true
+endif
+
 # ART does not provide linux_bionic variants needed for products that
 # set HOST_CROSS_OS=linux_bionic.
 ifeq (linux_bionic,${HOST_CROSS_OS})
@@ -111,10 +128,23 @@
 # are controlled by the MODULE_BUILD_FROM_SOURCE environment variable by
 # default.
 INDIVIDUALLY_TOGGLEABLE_PREBUILT_MODULES := \
+  adservices \
+  appsearch \
   btservices \
   devicelock \
+  configinfrastructure \
+  conscrypt \
+  healthfitness \
+  ipsec \
+  media \
+  mediaprovider \
+  ondevicepersonalization \
   permission \
   rkpd \
+  scheduling \
+  sdkext \
+  statsd \
+  tethering \
   uwb \
   wifi \
 
@@ -144,8 +174,8 @@
 SYSTEMUI_OPTIMIZE_JAVA ?= true
 $(call add_soong_config_var,ANDROID,SYSTEMUI_OPTIMIZE_JAVA)
 
-# Disable Compose in SystemUI by default.
-SYSTEMUI_USE_COMPOSE ?= false
+# Enable Compose in SystemUI by default.
+SYSTEMUI_USE_COMPOSE ?= true
 $(call add_soong_config_var,ANDROID,SYSTEMUI_USE_COMPOSE)
 
 ifdef PRODUCT_AVF_ENABLED
@@ -164,6 +194,8 @@
 
 $(call add_soong_config_var_value,ANDROID,release_binder_death_recipient_weak_from_jni,$(RELEASE_BINDER_DEATH_RECIPIENT_WEAK_FROM_JNI))
 
+$(call add_soong_config_var_value,ANDROID,release_selinux_data_data_ignore,$(RELEASE_SELINUX_DATA_DATA_IGNORE))
+
 # Enable system_server optimizations by default unless explicitly set or if
 # there may be dependent runtime jars.
 # TODO(b/240588226): Remove the off-by-default exceptions after handling
diff --git a/core/art_config.mk b/core/art_config.mk
index 54bfd6b..47b4bcf 100644
--- a/core/art_config.mk
+++ b/core/art_config.mk
@@ -27,8 +27,11 @@
 # soong variables indicate whether the prebuilt is enabled:
 # - $(m)_module/source_build for art and TOGGLEABLE_PREBUILT_MODULES
 # - ANDROID/module_build_from_source for other mainline modules
+# Note that RELEASE_APEX_BOOT_JARS_PREBUILT_EXCLUDED_LIST is the list of module names
+# and library names of jars that need to be removed. We have to keep separated list per
+# release config due to possibility of different prebuilt content.
 APEX_BOOT_JARS_EXCLUDED :=
-$(foreach pair, $(PRODUCT_APEX_BOOT_JARS_FOR_SOURCE_BUILD_ONLY),\
+$(foreach pair, $(RELEASE_APEX_BOOT_JARS_PREBUILT_EXCLUDED_LIST),\
   $(eval m := $(subst com.android.,,$(call word-colon,1,$(pair)))) \
   $(if $(call soong_config_get,$(m)_module,source_build), \
     $(if $(filter true,$(call soong_config_get,$(m)_module,source_build)),, \
diff --git a/core/binary.mk b/core/binary.mk
index 6dab49c..b17ab00 100644
--- a/core/binary.mk
+++ b/core/binary.mk
@@ -481,6 +481,34 @@
     my_cflags += $(CLANG_EXTERNAL_CFLAGS)
 endif
 
+# Extra cflags for projects under hardware/ directory.
+# This should match the definition of `thirdPartyDirPrefixExceptions`
+# in build/soong/android/paths.go.
+# Get the second element of LOCAL_PATH
+ifneq ($(filter hardware/%,$(LOCAL_PATH)),)
+  my_subdir := $(word 2,$(subst /,$(space),$(LOCAL_PATH)))
+  must_compile_hardware_subdirs := \
+                  hardware/google/% \
+                  hardware/interfaces/% \
+                  hardware/libhardware/% \
+                  hardware/libhardware_legacy/% \
+                  hardware/ril/%
+  ifeq ($(filter $(must_compile_hardware_subdirs),$(my_subdir)),)
+    my_cflags += $(CLANG_EXTERNAL_CFLAGS)
+  endif
+endif
+
+# Extra cflags for projects under vendor/ directory.
+# This should match the definition of `thirdPartyDirPrefixExceptions`
+# in build/soong/android/paths.go.
+ifneq ($(filter vendor/%,$(LOCAL_PATH)),)
+  my_subdir := $(word 2,$(subst /,$(space),$(LOCAL_PATH)))
+  # Do not add the flags for any subdir that contains the string "google".
+  ifneq ($(findstring google,$(my_subdir)),)
+    my_cflags += $(CLANG_EXTERNAL_CFLAGS)
+  endif
+endif
+
 # arch-specific static libraries go first so that generic ones can depend on them
 my_static_libraries := $(LOCAL_STATIC_LIBRARIES_$($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH)) $(LOCAL_STATIC_LIBRARIES_$(my_32_64_bit_suffix)) $(my_static_libraries)
 my_whole_static_libraries := $(LOCAL_WHOLE_STATIC_LIBRARIES_$($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH)) $(LOCAL_WHOLE_STATIC_LIBRARIES_$(my_32_64_bit_suffix)) $(my_whole_static_libraries)
diff --git a/core/board_config.mk b/core/board_config.mk
index 8c23f93..633303f 100644
--- a/core/board_config.mk
+++ b/core/board_config.mk
@@ -287,6 +287,9 @@
 
 include $(BUILD_SYSTEM)/board_config_wifi.mk
 
+# Set up soong config for "soong_config_value_variable".
+-include vendor/google/build/soong/soong_config_namespace/camera.mk
+
 # Default *_CPU_VARIANT_RUNTIME to CPU_VARIANT if unspecified.
 TARGET_CPU_VARIANT_RUNTIME := $(or $(TARGET_CPU_VARIANT_RUNTIME),$(TARGET_CPU_VARIANT))
 TARGET_2ND_CPU_VARIANT_RUNTIME := $(or $(TARGET_2ND_CPU_VARIANT_RUNTIME),$(TARGET_2ND_CPU_VARIANT))
@@ -946,6 +949,11 @@
   endif
 endif
 
+# For Non A/B full OTA, disable brotli compression.
+ifeq ($(TARGET_OTA_ALLOW_NON_AB),true)
+  BOARD_NON_AB_OTA_DISABLE_COMPRESSION := true
+endif
+
 # Quick check for building generic OTA packages. Currently it only supports A/B OTAs.
 ifeq ($(PRODUCT_BUILD_GENERIC_OTA_PACKAGE),true)
   ifneq ($(AB_OTA_UPDATER),true)
@@ -965,15 +973,6 @@
   $(if $(wildcard $(vndk_path)/*/Android.bp),,$(error VNDK version $(1) not found))
 endef
 
-ifeq ($(KEEP_VNDK),true)
-ifeq ($(BOARD_VNDK_VERSION),$(PLATFORM_VNDK_VERSION))
-  $(error BOARD_VNDK_VERSION is equal to PLATFORM_VNDK_VERSION; use BOARD_VNDK_VERSION := current)
-endif
-ifneq ($(BOARD_VNDK_VERSION),current)
-  $(call check_vndk_version,$(BOARD_VNDK_VERSION))
-endif
-endif
-
 TARGET_VENDOR_TEST_SUFFIX := /vendor
 
 ifeq (,$(TARGET_BUILD_UNBUNDLED))
@@ -993,7 +992,7 @@
 # BOARD_API_LEVEL for vendor API surface
 ifdef RELEASE_BOARD_API_LEVEL
   ifdef BOARD_API_LEVEL
-    $(error BOARD_API_LEVEL must not set manully. The build system automatically sets this value.)
+    $(error BOARD_API_LEVEL must not be set manually. The build system automatically sets this value.)
   endif
   BOARD_API_LEVEL := $(RELEASE_BOARD_API_LEVEL)
   .KATI_READONLY := BOARD_API_LEVEL
diff --git a/core/config.mk b/core/config.mk
index dbee0a0..daefa70 100644
--- a/core/config.mk
+++ b/core/config.mk
@@ -172,6 +172,7 @@
 $(KATI_obsolete_var BOARD_PREBUILT_PVMFWIMAGE,pvmfw.bin is now built in AOSP and custom versions are no longer supported)
 $(KATI_obsolete_var BUILDING_PVMFW_IMAGE,BUILDING_PVMFW_IMAGE is no longer used)
 $(KATI_obsolete_var BOARD_BUILD_SYSTEM_ROOT_IMAGE)
+$(KATI_obsolete_var FS_GET_STATS)
 
 # Used to force goals to build.  Only use for conditionally defined goals.
 .PHONY: FORCE
@@ -406,35 +407,22 @@
 else ifeq ($(strip $(call is-low-mem-device)),true)
   # Low memory device will have 4096 binary alignment.
   TARGET_MAX_PAGE_SIZE_SUPPORTED := 4096
-else
-  # The default binary alignment for userspace is 4096.
+else ifeq ($(call math_lt,$(VSR_VENDOR_API_LEVEL),34),true)
   TARGET_MAX_PAGE_SIZE_SUPPORTED := 4096
-  # When VSR vendor API level >= 34, binary alignment will be 65536.
-  ifeq ($(call math_gt_or_eq,$(VSR_VENDOR_API_LEVEL),34),true)
-    ifeq ($(TARGET_ARCH),arm64)
-      TARGET_MAX_PAGE_SIZE_SUPPORTED := 65536
-    endif
-  endif
+else ifeq (,$(filter arm64 x86_64,$(TARGET_ARCH)))
+  # TARGET_MAX_PAGE_SIZE_SUPPORTED > 4096 is only supported in arm64 and
+  # x86_64 targets.
+  TARGET_MAX_PAGE_SIZE_SUPPORTED := 4096
+else
+  # The default binary alignment for userspace is 16384.
+  TARGET_MAX_PAGE_SIZE_SUPPORTED := 16384
 endif
 .KATI_READONLY := TARGET_MAX_PAGE_SIZE_SUPPORTED
 
-# Only arm64 and x86_64 archs supports TARGET_MAX_PAGE_SIZE_SUPPORTED greater than 4096.
-ifneq ($(TARGET_MAX_PAGE_SIZE_SUPPORTED),4096)
-  ifeq (,$(filter arm64 x86_64,$(TARGET_ARCH)))
-    $(error TARGET_MAX_PAGE_SIZE_SUPPORTED=$(TARGET_MAX_PAGE_SIZE_SUPPORTED) is greater than 4096. Only supported in arm64 and x86_64 archs)
-  endif
-endif
-
-# Boolean variable determining if AOSP is page size agnostic. This means
-# that AOSP can use a kernel configured with 4k/16k/64k PAGE SIZES.
+# Boolean variable determining if AOSP relies on bionic's PAGE_SIZE macro.
 TARGET_NO_BIONIC_PAGE_SIZE_MACRO := false
 ifdef PRODUCT_NO_BIONIC_PAGE_SIZE_MACRO
   TARGET_NO_BIONIC_PAGE_SIZE_MACRO := $(PRODUCT_NO_BIONIC_PAGE_SIZE_MACRO)
-  ifeq ($(TARGET_NO_BIONIC_PAGE_SIZE_MACRO),true)
-      ifneq ($(TARGET_MAX_PAGE_SIZE_SUPPORTED),65536)
-          $(error TARGET_MAX_PAGE_SIZE_SUPPORTED has to be 65536 to support page size agnostic)
-      endif
-  endif
 endif
 .KATI_READONLY := TARGET_NO_BIONIC_PAGE_SIZE_MACRO
 
@@ -679,18 +667,12 @@
 else
 MKBOOTIMG := $(BOARD_CUSTOM_MKBOOTIMG)
 endif
-ifeq (,$(strip $(BOARD_CUSTOM_BPTTOOL)))
-BPTTOOL := $(HOST_OUT_EXECUTABLES)/bpttool$(HOST_EXECUTABLE_SUFFIX)
-else
-BPTTOOL := $(BOARD_CUSTOM_BPTTOOL)
-endif
 ifeq (,$(strip $(BOARD_CUSTOM_AVBTOOL)))
 AVBTOOL := $(HOST_OUT_EXECUTABLES)/avbtool$(HOST_EXECUTABLE_SUFFIX)
 else
 AVBTOOL := $(BOARD_CUSTOM_AVBTOOL)
 endif
 APICHECK := $(HOST_OUT_JAVA_LIBRARIES)/metalava$(COMMON_JAVA_PACKAGE_SUFFIX)
-FS_GET_STATS := $(HOST_OUT_EXECUTABLES)/fs_get_stats$(HOST_EXECUTABLE_SUFFIX)
 MKEXTUSERIMG := $(HOST_OUT_EXECUTABLES)/mkuserimg_mke2fs
 MKE2FS_CONF := system/extras/ext4_utils/mke2fs.conf
 MKEROFS := $(HOST_OUT_EXECUTABLES)/mkfs.erofs
@@ -885,6 +867,7 @@
     32.0 \
     33.0 \
     34.0 \
+    202404 \
     )
 
 .KATI_READONLY := \
@@ -1273,7 +1256,7 @@
 endif
 
 ifeq (true,$(FULL_SYSTEM_OPTIMIZE_JAVA))
-ifeq (,$(SYSTEM_OPTIMIZE_JAVA))
+ifeq (false,$(SYSTEM_OPTIMIZE_JAVA))
 $(error SYSTEM_OPTIMIZE_JAVA must be enabled when FULL_SYSTEM_OPTIMIZE_JAVA is enabled)
 endif
 endif
diff --git a/core/envsetup.mk b/core/envsetup.mk
index 30a6c06..7f9cbad 100644
--- a/core/envsetup.mk
+++ b/core/envsetup.mk
@@ -789,6 +789,7 @@
 TARGET_OUT_ODM_APPS := $(target_out_odm_app_base)/app
 TARGET_OUT_ODM_APPS_PRIVILEGED := $(target_out_odm_app_base)/priv-app
 TARGET_OUT_ODM_ETC := $(TARGET_OUT_ODM)/etc
+TARGET_OUT_ODM_FAKE := $(PRODUCT_OUT)/odm_fake_packages
 .KATI_READONLY := \
   TARGET_OUT_ODM \
   TARGET_OUT_ODM_EXECUTABLES \
@@ -798,7 +799,8 @@
   TARGET_OUT_ODM_JAVA_LIBRARIES \
   TARGET_OUT_ODM_APPS \
   TARGET_OUT_ODM_APPS_PRIVILEGED \
-  TARGET_OUT_ODM_ETC
+  TARGET_OUT_ODM_ETC \
+  TARGET_OUT_ODM_FAKE
 
 $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_ODM_EXECUTABLES := $(TARGET_OUT_ODM_EXECUTABLES)
 $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_ODM_SHARED_LIBRARIES := $(target_out_odm_shared_libraries_base)/lib
@@ -936,13 +938,15 @@
 TARGET_OUT_PRODUCT_APPS := $(target_out_product_app_base)/app
 TARGET_OUT_PRODUCT_APPS_PRIVILEGED := $(target_out_product_app_base)/priv-app
 TARGET_OUT_PRODUCT_ETC := $(TARGET_OUT_PRODUCT)/etc
+TARGET_OUT_PRODUCT_FAKE := $(TARGET_OUT_PRODUCT)/product_fake_packages
 .KATI_READONLY := \
   TARGET_OUT_PRODUCT_EXECUTABLES \
   TARGET_OUT_PRODUCT_SHARED_LIBRARIES \
   TARGET_OUT_PRODUCT_JAVA_LIBRARIES \
   TARGET_OUT_PRODUCT_APPS \
   TARGET_OUT_PRODUCT_APPS_PRIVILEGED \
-  TARGET_OUT_PRODUCT_ETC
+  TARGET_OUT_PRODUCT_ETC \
+  TARGET_OUT_PRODUCT_FAKE
 
 $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_PRODUCT_EXECUTABLES := $(TARGET_OUT_PRODUCT_EXECUTABLES)
 $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_PRODUCT_SHARED_LIBRARIES := $(target_out_product_shared_libraries_base)/lib
@@ -979,13 +983,15 @@
 TARGET_OUT_SYSTEM_EXT_APPS_PRIVILEGED := $(target_out_system_ext_app_base)/priv-app
 TARGET_OUT_SYSTEM_EXT_ETC := $(TARGET_OUT_SYSTEM_EXT)/etc
 TARGET_OUT_SYSTEM_EXT_EXECUTABLES := $(TARGET_OUT_SYSTEM_EXT)/bin
+TARGET_OUT_SYSTEM_EXT_FAKE := $(PRODUCT_OUT)/system_ext_fake_packages
 .KATI_READONLY := \
   TARGET_OUT_SYSTEM_EXT_EXECUTABLES \
   TARGET_OUT_SYSTEM_EXT_SHARED_LIBRARIES \
   TARGET_OUT_SYSTEM_EXT_JAVA_LIBRARIES \
   TARGET_OUT_SYSTEM_EXT_APPS \
   TARGET_OUT_SYSTEM_EXT_APPS_PRIVILEGED \
-  TARGET_OUT_SYSTEM_EXT_ETC
+  TARGET_OUT_SYSTEM_EXT_ETC \
+  TARGET_OUT_SYSTEM_EXT_FAKE
 
 $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_SYSTEM_EXT_EXECUTABLES := $(TARGET_OUT_SYSTEM_EXT_EXECUTABLES)
 $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_SYSTEM_EXT_SHARED_LIBRARIES := $(target_out_system_ext_shared_libraries_base)/lib
diff --git a/core/main.mk b/core/main.mk
index 9b7382b..bc8adde 100644
--- a/core/main.mk
+++ b/core/main.mk
@@ -221,20 +221,6 @@
 # mount system_other partition.
 ADDITIONAL_SYSTEM_PROPERTIES += ro.postinstall.fstab.prefix=/system
 
-# -----------------------------------------------------------------
-# ADDITIONAL_VENDOR_PROPERTIES will be installed in vendor/build.prop if
-# property_overrides_split_enabled is true. Otherwise it will be installed in
-# /system/build.prop
-ifeq ($(KEEP_VNDK),true)
-ifdef BOARD_VNDK_VERSION
-  ifeq ($(BOARD_VNDK_VERSION),current)
-    ADDITIONAL_VENDOR_PROPERTIES := ro.vndk.version=$(PLATFORM_VNDK_VERSION)
-  else
-    ADDITIONAL_VENDOR_PROPERTIES := ro.vndk.version=$(BOARD_VNDK_VERSION)
-  endif
-endif
-endif
-
 # Add cpu properties for bionic and ART.
 ADDITIONAL_VENDOR_PROPERTIES += ro.bionic.arch=$(TARGET_ARCH)
 ADDITIONAL_VENDOR_PROPERTIES += ro.bionic.cpu_variant=$(TARGET_CPU_VARIANT_RUNTIME)
@@ -284,6 +270,11 @@
     ro.product.first_api_level=$(PRODUCT_SHIPPING_API_LEVEL)
 endif
 
+ifdef PRODUCT_SHIPPING_VENDOR_API_LEVEL
+ADDITIONAL_VENDOR_PROPERTIES += \
+    ro.vendor.api_level=$(PRODUCT_SHIPPING_VENDOR_API_LEVEL)
+endif
+
 ifneq ($(TARGET_BUILD_VARIANT),user)
   ifdef PRODUCT_SET_DEBUGFS_RESTRICTIONS
     ADDITIONAL_VENDOR_PROPERTIES += \
@@ -341,18 +332,6 @@
     ro.build.ab_update=$(AB_OTA_UPDATER)
 endif
 
-# Set ro.product.vndk.version to PLATFORM_VNDK_VERSION only if
-# KEEP_VNDK is true, PRODUCT_PRODUCT_VNDK_VERSION is current and
-# PLATFORM_VNDK_VERSION is less than or equal to 35.
-# ro.product.vndk.version must be removed for the other future builds.
-ifeq ($(KEEP_VNDK)|$(PRODUCT_PRODUCT_VNDK_VERSION),true|current)
-ifeq ($(call math_is_number,$(PLATFORM_VNDK_VERSION)),true)
-ifeq ($(call math_lt_or_eq,$(PLATFORM_VNDK_VERSION),35),true)
-ADDITIONAL_PRODUCT_PROPERTIES += ro.product.vndk.version=$(PLATFORM_VNDK_VERSION)
-endif
-endif
-endif
-
 ADDITIONAL_PRODUCT_PROPERTIES += ro.build.characteristics=$(TARGET_AAPT_CHARACTERISTICS)
 
 ifeq ($(AB_OTA_UPDATER),true)
@@ -1672,6 +1651,7 @@
     $(INSTALLED_SYSTEM_DLKMIMAGE_TARGET) \
     $(INSTALLED_SUPERIMAGE_EMPTY_TARGET) \
     $(INSTALLED_PRODUCTIMAGE_TARGET) \
+    $(INSTALLED_SYSTEM_EXTIMAGE_TARGET) \
     $(INSTALLED_SYSTEMOTHERIMAGE_TARGET) \
     $(INSTALLED_TEST_HARNESS_RAMDISK_TARGET) \
     $(INSTALLED_TEST_HARNESS_BOOTIMAGE_TARGET) \
@@ -1853,12 +1833,12 @@
     $(INSTALLED_FILES_JSON_SYSTEMOTHER) \
     $(INSTALLED_FILES_FILE_RECOVERY) \
     $(INSTALLED_FILES_JSON_RECOVERY) \
-    $(INSTALLED_BUILD_PROP_TARGET):build.prop \
-    $(INSTALLED_VENDOR_BUILD_PROP_TARGET):build.prop-vendor \
-    $(INSTALLED_PRODUCT_BUILD_PROP_TARGET):build.prop-product \
-    $(INSTALLED_ODM_BUILD_PROP_TARGET):build.prop-odm \
-    $(INSTALLED_SYSTEM_EXT_BUILD_PROP_TARGET):build.prop-system_ext \
-    $(INSTALLED_RAMDISK_BUILD_PROP_TARGET):build.prop-ramdisk \
+    $(if $(BUILDING_SYSTEM_IMAGE), $(INSTALLED_BUILD_PROP_TARGET):build.prop) \
+    $(if $(BUILDING_VENDOR_IMAGE), $(INSTALLED_VENDOR_BUILD_PROP_TARGET):build.prop-vendor) \
+    $(if $(BUILDING_PRODUCT_IMAGE), $(INSTALLED_PRODUCT_BUILD_PROP_TARGET):build.prop-product) \
+    $(if $(BUILDING_ODM_IMAGE), $(INSTALLED_ODM_BUILD_PROP_TARGET):build.prop-odm) \
+    $(if $(BUILDING_SYSTEM_EXT_IMAGE), $(INSTALLED_SYSTEM_EXT_BUILD_PROP_TARGET):build.prop-system_ext) \
+    $(if $(BUILDING_RAMDISK_IMAGE), $(INSTALLED_RAMDISK_BUILD_PROP_TARGET):build.prop-ramdisk) \
     $(INSTALLED_ANDROID_INFO_TXT_TARGET) \
     $(INSTALLED_MISC_INFO_TARGET) \
     $(INSTALLED_RAMDISK_TARGET) \
diff --git a/core/packaging/flags.mk b/core/packaging/flags.mk
index 62ef3df..e715fd1 100644
--- a/core/packaging/flags.mk
+++ b/core/packaging/flags.mk
@@ -119,13 +119,14 @@
 # $(5): installed aconfig flags storage flag map file (out)
 # $(6): installed aconfig flags storage flag value file (out)
 # $(7): input aconfig files for the partition (in)
+# $(8): partition name
 define generate-partition-aconfig-storage-file
 $(eval $(strip $(1)): PRIVATE_OUT := $(strip $(1)))
 $(eval $(strip $(1)): PRIVATE_IN := $(strip $(7)))
 $(strip $(1)): $(ACONFIG) $(strip $(7))
 	mkdir -p $$(dir $$(PRIVATE_OUT))
 	$$(if $$(PRIVATE_IN), \
-		$$(ACONFIG) create-storage --container "" --file package_map --out $$(PRIVATE_OUT) \
+		$$(ACONFIG) create-storage --container $(8) --file package_map --out $$(PRIVATE_OUT) \
 			$$(addprefix --cache ,$$(PRIVATE_IN)), \
 	)
 	touch $$(PRIVATE_OUT)
@@ -134,7 +135,7 @@
 $(strip $(2)): $(ACONFIG) $(strip $(7))
 	mkdir -p $$(dir $$(PRIVATE_OUT))
 	$$(if $$(PRIVATE_IN), \
-		$$(ACONFIG) create-storage --container "" --file flag_map --out $$(PRIVATE_OUT) \
+		$$(ACONFIG) create-storage --container $(8) --file flag_map --out $$(PRIVATE_OUT) \
 			$$(addprefix --cache ,$$(PRIVATE_IN)), \
 	)
 	touch $$(PRIVATE_OUT)
@@ -143,7 +144,7 @@
 $(strip $(3)): $(ACONFIG) $(strip $(7))
 	mkdir -p $$(dir $$(PRIVATE_OUT))
 	$$(if $$(PRIVATE_IN), \
-		$$(ACONFIG) create-storage --container "" --file flag_val --out $$(PRIVATE_OUT) \
+		$$(ACONFIG) create-storage --container $(8) --file flag_val --out $$(PRIVATE_OUT) \
 		$$(addprefix --cache ,$$(PRIVATE_IN)), \
 	)
 	touch $$(PRIVATE_OUT)
@@ -154,9 +155,9 @@
 
 ifeq ($(RELEASE_CREATE_ACONFIG_STORAGE_FILE),true)
 $(foreach partition, $(_FLAG_PARTITIONS), \
-	$(eval aconfig_storage_package_map.$(partition) := $(PRODUCT_OUT)/$(partition)/etc/package.map) \
-	$(eval aconfig_storage_flag_map.$(partition) := $(PRODUCT_OUT)/$(partition)/etc/flag.map) \
-	$(eval aconfig_storage_flag_val.$(partition) := $(PRODUCT_OUT)/$(partition)/etc/flag.val) \
+	$(eval aconfig_storage_package_map.$(partition) := $(PRODUCT_OUT)/$(partition)/etc/aconfig/package.map) \
+	$(eval aconfig_storage_flag_map.$(partition) := $(PRODUCT_OUT)/$(partition)/etc/aconfig/flag.map) \
+	$(eval aconfig_storage_flag_val.$(partition) := $(PRODUCT_OUT)/$(partition)/etc/aconfig/flag.val) \
 	$(eval $(call generate-partition-aconfig-storage-file, \
 				$(TARGET_OUT_FLAGS)/$(partition)/package.map, \
 				$(TARGET_OUT_FLAGS)/$(partition)/flag.map, \
@@ -167,6 +168,7 @@
 				$(sort $(foreach m,$(call register-names-for-partition, $(partition)), \
 					$(ALL_MODULES.$(m).ACONFIG_FILES) \
 				)), \
+				$(partition), \
 	)) \
 )
 endif
diff --git a/core/product.mk b/core/product.mk
index 4250253..01b5ead 100644
--- a/core/product.mk
+++ b/core/product.mk
@@ -33,8 +33,7 @@
 # 4096, 16384 and 65536.
 _product_single_value_vars += PRODUCT_MAX_PAGE_SIZE_SUPPORTED
 
-# Indicates that AOSP can use a kernel configured with 4k/16k/64k page sizes.
-# The possible values are true or false.
+# Boolean variable determining if AOSP relies on bionic's PAGE_SIZE macro.
 _product_single_value_vars += PRODUCT_NO_BIONIC_PAGE_SIZE_MACRO
 
 # The resource configuration options to use for this product.
@@ -230,6 +229,9 @@
 # The first API level this product shipped with
 _product_single_value_vars += PRODUCT_SHIPPING_API_LEVEL
 
+# The first vendor API level this product shipped with
+_product_single_value_vars += PRODUCT_SHIPPING_VENDOR_API_LEVEL
+
 _product_list_vars += VENDOR_PRODUCT_RESTRICT_VENDOR_FILES
 _product_list_vars += VENDOR_EXCEPTION_MODULES
 _product_list_vars += VENDOR_EXCEPTION_PATHS
@@ -456,6 +458,10 @@
 
 _product_list_vars += PRODUCT_BUILD_IGNORE_APEX_CONTRIBUTION_CONTENTS
 
+_product_single_value_vars += PRODUCT_HIDDEN_API_EXPORTABLE_STUBS
+
+_product_single_value_vars += PRODUCT_EXPORT_RUNTIME_APIS
+
 .KATI_READONLY := _product_single_value_vars _product_list_vars
 _product_var_list :=$= $(_product_single_value_vars) $(_product_list_vars)
 
diff --git a/core/product_config.mk b/core/product_config.mk
index 500735e..b9952b0 100644
--- a/core/product_config.mk
+++ b/core/product_config.mk
@@ -552,20 +552,27 @@
       $(PRODUCT_ENFORCE_RRO_EXEMPTED_TARGETS))
 endif
 
-# Get the board API level.
-board_api_level := $(PLATFORM_SDK_VERSION)
-ifdef BOARD_API_LEVEL
-  board_api_level := $(BOARD_API_LEVEL)
-else ifdef BOARD_SHIPPING_API_LEVEL
-  # Vendors with GRF must define BOARD_SHIPPING_API_LEVEL for the vendor API level.
-  board_api_level := $(BOARD_SHIPPING_API_LEVEL)
-endif
+# This table maps sdk version 35 to vendor api level 202404 and assumes yearly
+# release for the same month.
+define sdk-to-vendor-api-level
+  $(if $(call math_lt_or_eq,$(1),34),$(1),20$(call int_subtract,$(1),11)04)
+endef
 
-# Calculate the VSR vendor API level.
-VSR_VENDOR_API_LEVEL := $(board_api_level)
-
-ifdef PRODUCT_SHIPPING_API_LEVEL
-  VSR_VENDOR_API_LEVEL := $(call math_min,$(PRODUCT_SHIPPING_API_LEVEL),$(board_api_level))
+ifdef PRODUCT_SHIPPING_VENDOR_API_LEVEL
+# Follow the version that is set manually.
+  VSR_VENDOR_API_LEVEL := $(PRODUCT_SHIPPING_VENDOR_API_LEVEL)
+else
+  # VSR API level is the vendor api level of the product shipping API level.
+  VSR_VENDOR_API_LEVEL := $(call sdk-to-vendor-api-level,$(PLATFORM_SDK_VERSION))
+  ifdef PRODUCT_SHIPPING_API_LEVEL
+    VSR_VENDOR_API_LEVEL := $(call sdk-to-vendor-api-level,$(PRODUCT_SHIPPING_API_LEVEL))
+  endif
+  ifdef BOARD_SHIPPING_API_LEVEL
+    # 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))
+  endif
 endif
 .KATI_READONLY := VSR_VENDOR_API_LEVEL
 
@@ -580,7 +587,7 @@
 
 # Boolean variable determining if selinux labels of /dev are enforced
 CHECK_DEV_TYPE_VIOLATIONS := false
-ifneq ($(call math_gt,$(VSR_VENDOR_API_LEVEL),35),)
+ifneq ($(call math_gt,$(VSR_VENDOR_API_LEVEL),202404),)
   CHECK_DEV_TYPE_VIOLATIONS := true
 else ifneq ($(PRODUCT_CHECK_DEV_TYPE_VIOLATIONS),)
   CHECK_DEV_TYPE_VIOLATIONS := $(PRODUCT_CHECK_DEV_TYPE_VIOLATIONS)
diff --git a/core/release_config.mk b/core/release_config.mk
index 8d19bc7..3e51af5 100644
--- a/core/release_config.mk
+++ b/core/release_config.mk
@@ -42,6 +42,7 @@
 # TODO: Remove wildcard for build/release one when all branch manifests
 # have updated.
 config_map_files := $(wildcard build/release/release_config_map.mk) \
+    $(wildcard vendor/google_shared/build/release/release_config_map.mk) \
     $(if $(wildcard vendor/google/release/release_config_map.mk), \
         vendor/google/release/release_config_map.mk, \
         $(sort \
@@ -117,15 +118,32 @@
 endef
 
 # Include the config map files and populate _flag_declaration_files.
+# If the file is found more than once, only include it the first time.
 _flag_declaration_files :=
+_included_config_map_files :=
 $(foreach f, $(config_map_files), \
     $(eval FLAG_DECLARATION_FILES:= ) \
-    $(eval _included := $(f)) \
-    $(eval include $(f)) \
-    $(eval _flag_declaration_files += $(FLAG_DECLARATION_FILES)) \
+    $(if $(filter $(_included_config_map_files),$(f)),,\
+        $(eval _included := $(f)) \
+        $(eval include $(f)) \
+        $(eval _flag_declaration_files += $(FLAG_DECLARATION_FILES)) \
+        $(eval _included_config_map_files += $(f)) \
+    ) \
 )
 FLAG_DECLARATION_FILES :=
 
+# Verify that all inherited/overridden release configs are declared.
+$(foreach config,$(_all_release_configs),\
+  $(foreach r,$(all_release_configs.$(r).OVERRIDES),\
+    $(if $(strip $(_all_release_configs.$(r).FILES)$(_all_release_configs.$(r).OVERRIDES)),,\
+    $(error Release config $(config) [declared in: $(_all_release_configs.$(r).DECLARED_IN)] inherits from non-existent $(r).)\
+)))
+# Verify that alias configs do not have config files.
+$(foreach r,$(_all_release_configs),\
+  $(if $(_all_release_configs.$(r).ALIAS),$(if $(_all_release_configs.$(r).FILES),\
+    $(error Alias release config "$(r)" may not specify release config files $(_all_release_configs.$(r).FILES))\
+)))
+
 ifeq ($(TARGET_RELEASE),)
     # We allow some internal paths to explicitly set TARGET_RELEASE to the
     # empty string.  For the most part, 'make' treats unset and empty string as
@@ -141,8 +159,12 @@
     TARGET_RELEASE = trunk_staging
 endif
 
-ifeq ($(filter $(_all_release_configs), $(TARGET_RELEASE)),)
-    $(error No release config found for TARGET_RELEASE: $(TARGET_RELEASE). Available releases are: $(_all_release_configs))
+# During pass 1 of product config, using a non-existent release config is not an error.
+# We can safely assume that we are doing pass 1 if DUMP_MANY_VARS=="PRODUCT_RELEASE_CONFIG_MAPS".
+ifneq (PRODUCT_RELEASE_CONFIG_MAPS,$(DUMP_MANY_VARS))
+    ifeq ($(filter $(_all_release_configs), $(TARGET_RELEASE)),)
+        $(error No release config found for TARGET_RELEASE: $(TARGET_RELEASE). Available releases are: $(_all_release_configs))
+    endif
 endif
 
 # Choose flag files
@@ -170,7 +192,7 @@
 define declare-release-config
 $(error declare-release-config can only be called from inside release_config_map.mk files)
 endef
-define apply-release-config-overrides
+define _apply-release-config-overrides
 $(error invalid use of apply-release-config-overrides)
 endef
 
@@ -185,12 +207,6 @@
 endif
 .KATI_READONLY := TARGET_RELEASE
 
-# Verify that alias configs do not have config files.
-$(foreach r,$(_all_release_configs),\
-  $(if $(_all_release_configs.$(r).ALIAS),$(if $(_all_release_configs.$(r).FILES),\
-    $(error Alias release config "$(r)" may not specify release config files $(_all_release_configs.$(r).FILES))\
-)))
-
 $(foreach config, $(_all_release_configs), \
     $(eval _all_release_configs.$(config).DECLARED_IN:= ) \
     $(eval _all_release_configs.$(config).FILES:= ) \
diff --git a/core/release_config.scl b/core/release_config.scl
index 662d155..c5815df 100644
--- a/core/release_config.scl
+++ b/core/release_config.scl
@@ -53,6 +53,7 @@
                     for t in _valid_types
                 ],
             },
+            "origin": {"type": "string"},
             "declared_in": {"type": "string"},
         },
         "optional_keys": {
@@ -80,13 +81,14 @@
     },
 }
 
-def flag(name, partitions, default, *, appends = False):
+def flag(name, partitions, default, *, origin = "Unknown", appends = False):
     """Declare a flag.
 
     Args:
       name: name of the flag
       partitions: the partitions where this should be recorded.
       default: the default value of the flag.
+      origin: The origin of this flag.
       appends: Whether new values should be append (not replace) the old.
 
     Returns:
@@ -112,6 +114,7 @@
         "partitions": partitions,
         "default": default,
         "appends": appends,
+        "origin": origin,
     }
 
 def value(name, value):
@@ -145,6 +148,24 @@
     else:
         return val
 
+def equal_flag_declaration(flag, other):
+    """Return true if the flag declarations are equal.
+
+    Args:
+      flag: This flag declaration.
+      other: Another flag declaration.
+
+    Returns:
+      Whether the declarations are the same.
+    """
+    for key in "name", "partitions", "default", "appends":
+        if flag[key] != other[key]:
+            return False
+    # For now, allow Unknown to match any other origin.
+    if flag["origin"] == "Unknown" or other["origin"] == "Unknown":
+        return True
+    return flag["origin"] == other["origin"]
+
 def release_config(all_flags, all_values):
     """Return the make variables that should be set for this release config.
 
@@ -158,14 +179,23 @@
     validate(all_flags, _all_flags_schema)
     validate(all_values, _all_values_schema)
 
+    # Final values.
+    values = {}
     # Validate flags
     flag_names = []
     flags_dict = {}
     for flag in all_flags:
-        if flag["name"] in flag_names:
-            fail(flag["declared_in"] + ": Duplicate declaration of flag " + flag["name"])
-        flag_names.append(flag["name"])
-        flags_dict[flag["name"]] = flag
+        name = flag["name"]
+        if name in flag_names:
+            if equal_flag_declaration(flag, flags_dict[name]):
+                continue
+            else:
+                fail(flag["declared_in"] + ": Duplicate declaration of flag " + name +
+                     " (declared first in " + flags_dict[name]["declared_in"] + ")")
+        flag_names.append(name)
+        flags_dict[name] = flag
+        # Set the flag value to the default value.
+        values[name] = {"name": name, "value": _format_value(flag["default"]), "set_in": flag["declared_in"]}
 
     # Record which flags go on which partition
     partitions = {}
@@ -181,7 +211,6 @@
 
     # Generate final values.
     # Only declared flags may have a value.
-    values = {}
     for value in all_values:
         name = value["name"]
         if name not in flag_names:
@@ -202,18 +231,13 @@
     for partition, names in partitions.items():
         result["_ALL_RELEASE_FLAGS.PARTITIONS." + partition] = names
     for flag in all_flags:
-        if flag["name"] in values:
-            val = values[flag["name"]]["value"]
-            set_in = values[flag["name"]]["set_in"]
-        else:
-            val = flag["default"]
-            set_in = flag["declared_in"]
-        val = _format_value(val)
+        val = _format_value(values[flag["name"]]["value"])
         result[flag["name"]] = val
         result["_ALL_RELEASE_FLAGS." + flag["name"] + ".PARTITIONS"] = flag["partitions"]
         result["_ALL_RELEASE_FLAGS." + flag["name"] + ".DEFAULT"] = _format_value(flag["default"])
         result["_ALL_RELEASE_FLAGS." + flag["name"] + ".VALUE"] = val
         result["_ALL_RELEASE_FLAGS." + flag["name"] + ".DECLARED_IN"] = flag["declared_in"]
-        result["_ALL_RELEASE_FLAGS." + flag["name"] + ".SET_IN"] = set_in
+        result["_ALL_RELEASE_FLAGS." + flag["name"] + ".SET_IN"] = values[flag["name"]]["set_in"]
+        result["_ALL_RELEASE_FLAGS." + flag["name"] + ".ORIGIN"] = flag["origin"]
 
     return result
diff --git a/core/soong_cc_rust_prebuilt.mk b/core/soong_cc_rust_prebuilt.mk
index 943ed30..a1c6478 100644
--- a/core/soong_cc_rust_prebuilt.mk
+++ b/core/soong_cc_rust_prebuilt.mk
@@ -38,14 +38,6 @@
   endif
 endif
 
-# Don't install modules of current VNDK when it is told so
-ifeq ($(TARGET_SKIP_CURRENT_VNDK),true)
-  ifeq ($(LOCAL_SOONG_VNDK_VERSION),$(PLATFORM_VNDK_VERSION))
-    LOCAL_UNINSTALLABLE_MODULE := true
-  endif
-endif
-
-
 # Use the Soong output as the checkbuild target instead of LOCAL_BUILT_MODULE
 # to avoid checkbuilds making an extra copy of every module.
 LOCAL_CHECKED_MODULE := $(LOCAL_PREBUILT_MODULE_FILE)
diff --git a/core/soong_config.mk b/core/soong_config.mk
index ec0c70e..1e419f3 100644
--- a/core/soong_config.mk
+++ b/core/soong_config.mk
@@ -31,7 +31,11 @@
 
 $(call add_json_str,  BuildId,                           $(BUILD_ID))
 $(call add_json_str,  BuildNumberFile,                   build_number.txt)
+$(call add_json_str,  BuildHostnameFile,                 build_hostname.txt)
+$(call add_json_str,  BuildThumbprintFile,               build_thumbprint.txt)
+$(call add_json_bool, DisplayBuildNumber,                $(filter true,$(DISPLAY_BUILD_NUMBER)))
 
+$(call add_json_str,  Platform_display_version_name,     $(PLATFORM_DISPLAY_VERSION))
 $(call add_json_str,  Platform_version_name,             $(PLATFORM_VERSION))
 $(call add_json_val,  Platform_sdk_version,              $(PLATFORM_SDK_VERSION))
 $(call add_json_str,  Platform_sdk_codename,             $(PLATFORM_VERSION_CODENAME))
@@ -58,6 +62,7 @@
 
 $(call add_json_bool, Debuggable,                        $(filter userdebug eng,$(TARGET_BUILD_VARIANT)))
 $(call add_json_bool, Eng,                               $(filter eng,$(TARGET_BUILD_VARIANT)))
+$(call add_json_str,  BuildType,                         $(TARGET_BUILD_TYPE))
 
 $(call add_json_str,  DeviceName,                        $(TARGET_DEVICE))
 $(call add_json_str,  DeviceProduct,                     $(TARGET_PRODUCT))
@@ -323,7 +328,6 @@
 
 $(call add_json_str,  ProductManufacturer, $(PRODUCT_MANUFACTURER))
 $(call add_json_str,  ProductBrand,        $(PRODUCT_BRAND))
-$(call add_json_list, BuildVersionTags,    $(BUILD_VERSION_TAGS))
 
 $(call add_json_str, ReleaseVersion,    $(_RELEASE_VERSION))
 $(call add_json_list, ReleaseAconfigValueSets,    $(RELEASE_ACONFIG_VALUE_SETS))
@@ -400,6 +404,20 @@
 
 $(call add_json_bool, BuildFromSourceStub, $(findstring true,$(PRODUCT_BUILD_FROM_SOURCE_STUB) $(BUILD_FROM_SOURCE_STUB)))
 
+$(call add_json_bool, HiddenapiExportableStubs, $(filter true,$(PRODUCT_HIDDEN_API_EXPORTABLE_STUBS)))
+
+$(call add_json_bool, ExportRuntimeApis, $(filter true,$(PRODUCT_EXPORT_RUNTIME_APIS)))
+
+$(call add_json_str, AconfigContainerValidation, $(ACONFIG_CONTAINER_VALIDATION))
+
+$(call add_json_list, ProductLocales, $(subst _,-,$(PRODUCT_LOCALES)))
+
+$(call add_json_list, ProductDefaultWifiChannels, $(PRODUCT_DEFAULT_WIFI_CHANNELS))
+
+$(call add_json_bool, BoardUseVbmetaDigestInFingerprint, $(filter true,$(BOARD_USE_VBMETA_DIGTEST_IN_FINGERPRINT)))
+
+$(call add_json_list, OemProperties, $(PRODUCT_OEM_PROPERTIES))
+
 $(call json_end)
 
 $(file >$(SOONG_VARIABLES).tmp,$(json_contents))
diff --git a/core/sysprop.mk b/core/sysprop.mk
index 652ca97..47d8a41 100644
--- a/core/sysprop.mk
+++ b/core/sysprop.mk
@@ -23,7 +23,6 @@
   property_overrides_split_enabled := true
 endif
 
-BUILDINFO_SH := build/make/tools/buildinfo.sh
 POST_PROCESS_PROPS := $(HOST_OUT_EXECUTABLES)/post_process_props$(HOST_EXECUTABLE_SUFFIX)
 
 # Emits a set of sysprops common to all partitions to a file.
@@ -212,44 +211,10 @@
 ifneq (,$(shell mkdir -p $(PRODUCT_OUT) && echo $(BUILD_THUMBPRINT) >$(BUILD_THUMBPRINT_FILE) && grep " " $(BUILD_THUMBPRINT_FILE)))
   $(error BUILD_THUMBPRINT cannot contain spaces: "$(file <$(BUILD_THUMBPRINT_FILE))")
 endif
-BUILD_THUMBPRINT_FROM_FILE := $$(cat $(BUILD_THUMBPRINT_FILE))
 # unset it for safety.
+BUILD_THUMBPRINT_FILE :=
 BUILD_THUMBPRINT :=
 
-# -----------------------------------------------------------------
-# Define human readable strings that describe this build
-#
-
-# BUILD_ID: detail info; has the same info as the build fingerprint
-BUILD_DESC := $(TARGET_PRODUCT)-$(TARGET_BUILD_VARIANT) $(PLATFORM_VERSION) $(BUILD_ID) $(BUILD_NUMBER_FROM_FILE) $(BUILD_VERSION_TAGS)
-
-# BUILD_DISPLAY_ID is shown under Settings -> About Phone
-ifeq ($(TARGET_BUILD_VARIANT),user)
-  # User builds should show:
-  # release build number or branch.buld_number non-release builds
-
-  # Dev. branches should have DISPLAY_BUILD_NUMBER set
-  ifeq (true,$(DISPLAY_BUILD_NUMBER))
-    BUILD_DISPLAY_ID := $(BUILD_ID).$(BUILD_NUMBER_FROM_FILE) $(BUILD_KEYS)
-  else
-    BUILD_DISPLAY_ID := $(BUILD_ID) $(BUILD_KEYS)
-  endif
-else
-  # Non-user builds should show detailed build information
-  BUILD_DISPLAY_ID := $(BUILD_DESC)
-endif
-
-# TARGET_BUILD_FLAVOR and ro.build.flavor are used only by the test
-# harness to distinguish builds. Only add _asan for a sanitized build
-# if it isn't already a part of the flavor (via a dedicated lunch
-# config for example).
-TARGET_BUILD_FLAVOR := $(TARGET_PRODUCT)-$(TARGET_BUILD_VARIANT)
-ifneq (, $(filter address, $(SANITIZE_TARGET)))
-ifeq (,$(findstring _asan,$(TARGET_BUILD_FLAVOR)))
-TARGET_BUILD_FLAVOR := $(TARGET_BUILD_FLAVOR)_asan
-endif
-endif
-
 KNOWN_OEM_THUMBPRINT_PROPERTIES := \
     ro.product.brand \
     ro.product.name \
@@ -264,54 +229,7 @@
 # Note: parts of this file that can't be generated by the build-properties
 # macro are manually created as separate files and then fed into the macro
 
-# Accepts a whitespace separated list of product locales such as
-# (en_US en_AU en_GB...) and returns the first locale in the list with
-# underscores replaced with hyphens. In the example above, this will
-# return "en-US".
-define get-default-product-locale
-$(strip $(subst _,-, $(firstword $(1))))
-endef
-
-gen_from_buildinfo_sh := $(call intermediates-dir-for,PACKAGING,system_build_prop)/buildinfo.prop
-
-ifeq ($(strip $(HAS_BUILD_NUMBER)),true)
-$(gen_from_buildinfo_sh): $(BUILD_NUMBER_FILE)
-endif
-$(gen_from_buildinfo_sh): $(INTERNAL_BUILD_ID_MAKEFILE) $(API_FINGERPRINT) $(BUILD_HOSTNAME_FILE) | $(BUILD_DATETIME_FILE)
-	$(hide) TARGET_BUILD_TYPE="$(TARGET_BUILD_VARIANT)" \
-	        TARGET_BUILD_FLAVOR="$(TARGET_BUILD_FLAVOR)" \
-	        TARGET_DEVICE="$(TARGET_DEVICE)" \
-	        PRODUCT_DEFAULT_LOCALE="$(call get-default-product-locale,$(PRODUCT_LOCALES))" \
-	        PRODUCT_DEFAULT_WIFI_CHANNELS="$(PRODUCT_DEFAULT_WIFI_CHANNELS)" \
-	        PRIVATE_BUILD_DESC="$(BUILD_DESC)" \
-	        BUILD_ID="$(BUILD_ID)" \
-	        BUILD_DISPLAY_ID="$(BUILD_DISPLAY_ID)" \
-	        DATE="$(DATE_FROM_FILE)" \
-	        BUILD_USERNAME="$(BUILD_USERNAME)" \
-	        BUILD_HOSTNAME="$(BUILD_HOSTNAME_FROM_FILE)" \
-	        BUILD_NUMBER="$(BUILD_NUMBER_FROM_FILE)" \
-	        BOARD_USE_VBMETA_DIGTEST_IN_FINGERPRINT="$(BOARD_USE_VBMETA_DIGTEST_IN_FINGERPRINT)" \
-	        PLATFORM_VERSION="$(PLATFORM_VERSION)" \
-	        PLATFORM_DISPLAY_VERSION="$(PLATFORM_DISPLAY_VERSION)" \
-	        PLATFORM_VERSION_LAST_STABLE="$(PLATFORM_VERSION_LAST_STABLE)" \
-	        PLATFORM_SECURITY_PATCH="$(PLATFORM_SECURITY_PATCH)" \
-	        PLATFORM_BASE_OS="$(PLATFORM_BASE_OS)" \
-	        PLATFORM_SDK_VERSION="$(PLATFORM_SDK_VERSION)" \
-	        PLATFORM_PREVIEW_SDK_VERSION="$(PLATFORM_PREVIEW_SDK_VERSION)" \
-	        PLATFORM_PREVIEW_SDK_FINGERPRINT="$$(cat $(API_FINGERPRINT))" \
-	        PLATFORM_VERSION_CODENAME="$(PLATFORM_VERSION_CODENAME)" \
-	        PLATFORM_VERSION_ALL_CODENAMES="$(PLATFORM_VERSION_ALL_CODENAMES)" \
-	        PLATFORM_VERSION_KNOWN_CODENAMES="$(PLATFORM_VERSION_KNOWN_CODENAMES)" \
-	        PLATFORM_MIN_SUPPORTED_TARGET_SDK_VERSION="$(PLATFORM_MIN_SUPPORTED_TARGET_SDK_VERSION)" \
-	        BUILD_VERSION_TAGS="$(BUILD_VERSION_TAGS)" \
-	        $(if $(OEM_THUMBPRINT_PROPERTIES),BUILD_THUMBPRINT="$(BUILD_THUMBPRINT_FROM_FILE)") \
-	        TARGET_CPU_ABI_LIST="$(TARGET_CPU_ABI_LIST)" \
-	        TARGET_CPU_ABI_LIST_32_BIT="$(TARGET_CPU_ABI_LIST_32_BIT)" \
-	        TARGET_CPU_ABI_LIST_64_BIT="$(TARGET_CPU_ABI_LIST_64_BIT)" \
-	        TARGET_CPU_ABI="$(TARGET_CPU_ABI)" \
-	        TARGET_CPU_ABI2="$(TARGET_CPU_ABI2)" \
-	        ZYGOTE_FORCE_64_BIT="$(ZYGOTE_FORCE_64_BIT)" \
-	        bash $(BUILDINFO_SH) > $@
+buildinfo_prop := $(call intermediates-dir-for,ETC,buildinfo.prop)/buildinfo.prop
 
 ifdef TARGET_SYSTEM_PROP
 system_prop_file := $(TARGET_SYSTEM_PROP)
@@ -320,7 +238,7 @@
 endif
 
 _prop_files_ := \
-  $(gen_from_buildinfo_sh) \
+  $(buildinfo_prop) \
   $(system_prop_file)
 
 # Order matters here. When there are duplicates, the last one wins.
diff --git a/core/tasks/berberis_test.mk b/core/tasks/berberis_test.mk
new file mode 100644
index 0000000..8604709
--- /dev/null
+++ b/core/tasks/berberis_test.mk
@@ -0,0 +1,25 @@
+#
+# Copyright (C) 2023 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.
+#
+
+BERBERIS_DIR := frameworks/libs/binary_translation
+
+# Berberis includes some components which may conflict with other packages.
+# Only build it when requested explicitly.
+ifeq ($(BUILD_BERBERIS),true)
+
+include $(BERBERIS_DIR)/tests/run_host_tests.mk
+
+endif  # BUILD_BERBERIS
diff --git a/core/tasks/cts.mk b/core/tasks/cts.mk
index 91cb2c9..b9f0988 100644
--- a/core/tasks/cts.mk
+++ b/core/tasks/cts.mk
@@ -144,30 +144,30 @@
 	$(call generate-coverage-report-cts,"CTS System API Coverage Report - XML",\
 			$(PRIVATE_TEST_CASES),xml)
 
-$(cts-verifier-coverage-report): PRIVATE_TEST_CASES := $(cts_verifier_apk)
+$(cts-verifier-coverage-report): PRIVATE_TEST_CASES := $(foreach c, $(cts_verifier_apk) $(verifier-dir), $(c))
 $(cts-verifier-coverage-report): PRIVATE_CTS_API_COVERAGE_EXE := $(cts_api_coverage_exe)
 $(cts-verifier-coverage-report): PRIVATE_DEXDEPS_EXE := $(dexdeps_exe)
 $(cts-verifier-coverage-report): PRIVATE_API_XML_DESC := $(api_xml_description)
 $(cts-verifier-coverage-report): PRIVATE_NAPI_XML_DESC := $(napi_xml_description)
-$(cts-verifier-coverage-report) : $(cts_verifier_apk) $(cts_api_coverage_dependencies) | $(ACP)
+$(cts-verifier-coverage-report) : $(cts_verifier_apk) $(verifier-zip) $(cts_api_coverage_dependencies) | $(ACP)
 	$(call generate-coverage-report-cts,"CTS Verifier API Coverage Report",\
 			$(PRIVATE_TEST_CASES),html)
 
-$(cts-combined-coverage-report): PRIVATE_TEST_CASES := $(foreach c, $(cts_verifier_apk) $(COMPATIBILITY_TESTCASES_OUT_cts), $(c))
+$(cts-combined-coverage-report): PRIVATE_TEST_CASES := $(foreach c, $(cts_verifier_apk) $(COMPATIBILITY_TESTCASES_OUT_cts) $(verifier-dir), $(c))
 $(cts-combined-coverage-report): PRIVATE_CTS_API_COVERAGE_EXE := $(cts_api_coverage_exe)
 $(cts-combined-coverage-report): PRIVATE_DEXDEPS_EXE := $(dexdeps_exe)
 $(cts-combined-coverage-report): PRIVATE_API_XML_DESC := $(api_xml_description)
 $(cts-combined-coverage-report): PRIVATE_NAPI_XML_DESC := $(napi_xml_description)
-$(cts-combined-coverage-report) : $(android_cts_zip) $(cts_verifier_apk) $(cts_api_coverage_dependencies) | $(ACP)
+$(cts-combined-coverage-report) : $(android_cts_zip) $(cts_verifier_apk) $(verifier-zip) $(cts_api_coverage_dependencies) | $(ACP)
 	$(call generate-coverage-report-cts,"CTS Combined API Coverage Report",\
 			$(PRIVATE_TEST_CASES),html)
 
-$(cts-combined-xml-coverage-report): PRIVATE_TEST_CASES := $(foreach c, $(cts_verifier_apk) $(COMPATIBILITY_TESTCASES_OUT_cts), $(c))
+$(cts-combined-xml-coverage-report): PRIVATE_TEST_CASES := $(foreach c, $(cts_verifier_apk) $(COMPATIBILITY_TESTCASES_OUT_cts) $(verifier-dir), $(c))
 $(cts-combined-xml-coverage-report): PRIVATE_CTS_API_COVERAGE_EXE := $(cts_api_coverage_exe)
 $(cts-combined-xml-coverage-report): PRIVATE_DEXDEPS_EXE := $(dexdeps_exe)
 $(cts-combined-xml-coverage-report): PRIVATE_API_XML_DESC := $(api_xml_description)
 $(cts-combined-xml-coverage-report): PRIVATE_NAPI_XML_DESC := $(napi_xml_description)
-$(cts-combined-xml-coverage-report) : $(android_cts_zip) $(cts_verifier_apk) $(cts_api_coverage_dependencies) | $(ACP)
+$(cts-combined-xml-coverage-report) : $(android_cts_zip) $(cts_verifier_apk) $(verifier-zip) $(cts_api_coverage_dependencies) | $(ACP)
 	$(call generate-coverage-report-cts,"CTS Combined API Coverage Report - XML",\
 			$(PRIVATE_TEST_CASES),xml)
 
@@ -236,3 +236,8 @@
 cts_api_coverage_exe :=
 cts_verifier_apk :=
 android_cts_zip :=
+cts-dir :=
+verifier-dir-name :=
+verifier-dir :=
+verifier-zip-name :=
+verifier-zip :=
diff --git a/core/tasks/meta-lic.mk b/core/tasks/meta-lic.mk
new file mode 100644
index 0000000..0348844
--- /dev/null
+++ b/core/tasks/meta-lic.mk
@@ -0,0 +1,35 @@
+# 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.
+
+# Declare license metadata for non-module files released with products.
+
+# Moved here from device/sample/Android.mk
+$(eval $(call declare-1p-copy-files,device/sample,))
+
+# Moved here from frameworks/av/media/Android.mk
+$(eval $(call declare-1p-copy-files,frameworks/av/media/libeffects,audio_effects.conf))
+$(eval $(call declare-1p-copy-files,frameworks/av/media/libeffects,audio_effects.xml))
+$(eval $(call declare-1p-copy-files,frameworks/av/media/libstagefright,))
+
+# Moved here from frameworks/av/services/Android.mk
+$(eval $(call declare-1p-copy-files,frameworks/av/services/audiopolicy,))
+
+# Moved here from frameworks/base/Android.mk
+$(eval $(call declare-1p-copy-files,frameworks/base,.ogg))
+$(eval $(call declare-1p-copy-files,frameworks/base,.kl))
+$(eval $(call declare-1p-copy-files,frameworks/base,.kcm))
+$(eval $(call declare-1p-copy-files,frameworks/base,.idc))
+$(eval $(call declare-1p-copy-files,frameworks/base,dirty-image-objects))
+$(eval $(call declare-1p-copy-files,frameworks/base/config,))
+$(eval $(call declare-1p-copy-files,frameworks/native/data,))
diff --git a/core/tasks/offline-sdk-docs.mk b/core/tasks/offline-sdk-docs.mk
new file mode 100644
index 0000000..d9e8006
--- /dev/null
+++ b/core/tasks/offline-sdk-docs.mk
@@ -0,0 +1,26 @@
+#
+# 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.
+#
+
+# sdk.atree needs to copy the whole dir: $(OUT_DOCS)/offline-sdk to the final zip.
+# So keep offline-sdk-timestamp target here, and unzip offline-sdk-docs.zip to
+# $(OUT_DOCS)/offline-sdk.
+$(OUT_DOCS)/offline-sdk-timestamp: $(OUT_DOCS)/offline-sdk-docs-docs.zip
+	$(hide) rm -rf $(OUT_DOCS)/offline-sdk
+	$(hide) mkdir -p $(OUT_DOCS)/offline-sdk
+	( unzip -qo $< -d $(OUT_DOCS)/offline-sdk && touch -f $@ ) || exit 1
+
+.PHONY: docs offline-sdk-docs
+docs offline-sdk-docs: $(OUT_DOCS)/offline-sdk-timestamp
diff --git a/core/tasks/tools/vts_package_utils.mk b/core/tasks/tools/vts_package_utils.mk
index 06161f0..1a819f2 100644
--- a/core/tasks/tools/vts_package_utils.mk
+++ b/core/tasks/tools/vts_package_utils.mk
@@ -21,7 +21,7 @@
 $(foreach m,$(1),\
   $(eval _built_files := $(strip $(ALL_MODULES.$(m).BUILT_INSTALLED)\
   $(ALL_MODULES.$(m)$(TARGET_2ND_ARCH_MODULE_SUFFIX).BUILT_INSTALLED)))\
-  $(foreach i, $(_built_files),\
+  $(foreach i, $(sort $(_built_files)),\
     $(eval bui_ins := $(subst :,$(space),$(i)))\
     $(eval ins := $(word 2,$(bui_ins)))\
     $(if $(filter $(TARGET_OUT_ROOT)/%,$(ins)),\
diff --git a/core/tasks/vndk.mk b/core/tasks/vndk.mk
deleted file mode 100644
index ebe9bd4..0000000
--- a/core/tasks/vndk.mk
+++ /dev/null
@@ -1,44 +0,0 @@
-# Copyright (C) 2017 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.
-
-current_makefile := $(lastword $(MAKEFILE_LIST))
-
-# BOARD_VNDK_VERSION must be set to 'current' in order to generate a VNDK snapshot.
-ifeq ($(BOARD_VNDK_VERSION),current)
-
-# PLATFORM_VNDK_VERSION must be set.
-ifneq (,$(PLATFORM_VNDK_VERSION))
-
-.PHONY: vndk
-vndk: $(SOONG_VNDK_SNAPSHOT_ZIP)
-
-$(call dist-for-goals, vndk, $(SOONG_VNDK_SNAPSHOT_ZIP))
-
-else # PLATFORM_VNDK_VERSION is NOT set
-error_msg := "CANNOT generate VNDK snapshot. PLATFORM_VNDK_VERSION must be set."
-endif # PLATFORM_VNDK_VERSION
-
-else # BOARD_VNDK_VERSION is NOT set to 'current'
-error_msg := "CANNOT generate VNDK snapshot. BOARD_VNDK_VERSION must be set to 'current'."
-endif # BOARD_VNDK_VERSION
-
-ifneq (,$(error_msg))
-
-.PHONY: vndk
-vndk: PRIVATE_MAKEFILE := $(current_makefile)
-vndk:
-	$(call echo-error,$(PRIVATE_MAKEFILE),$(error_msg))
-	exit 1
-
-endif
diff --git a/core/version_util.mk b/core/version_util.mk
index 6cda0fc..610cdaf 100644
--- a/core/version_util.mk
+++ b/core/version_util.mk
@@ -28,7 +28,6 @@
 #     BUILD_ID
 #     BUILD_NUMBER
 #     PLATFORM_SECURITY_PATCH
-#     PLATFORM_VNDK_VERSION
 #     PLATFORM_SYSTEMSDK_VERSIONS
 #     PLATFORM_VERSION_LAST_STABLE
 #     PLATFORM_VERSION_KNOWN_CODENAMES
@@ -151,24 +150,6 @@
 endif
 .KATI_READONLY := DEFAULT_APP_TARGET_SDK
 
-ifeq ($(KEEP_VNDK),true)
-  ifndef PLATFORM_VNDK_VERSION
-    # This is the definition of the VNDK version for the current VNDK libraries.
-    # With trunk stable, VNDK will not be frozen but deprecated.
-    # This version will be removed with the VNDK deprecation.
-    ifeq (REL,$(PLATFORM_VERSION_CODENAME))
-      ifdef RELEASE_PLATFORM_VNDK_VERSION
-        PLATFORM_VNDK_VERSION := $(RELEASE_PLATFORM_VNDK_VERSION)
-      else
-        PLATFORM_VNDK_VERSION := $(PLATFORM_SDK_VERSION)
-      endif
-    else
-      PLATFORM_VNDK_VERSION := $(PLATFORM_VERSION_CODENAME)
-    endif
-  endif
-  .KATI_READONLY := PLATFORM_VNDK_VERSION
-endif
-
 ifndef PLATFORM_SYSTEMSDK_MIN_VERSION
   # This is the oldest version of system SDK that the platform supports. Contrary
   # to the public SDK where platform essentially supports all previous SDK versions,
diff --git a/envsetup.sh b/envsetup.sh
index db21188..fbe522d 100644
--- a/envsetup.sh
+++ b/envsetup.sh
@@ -1084,8 +1084,23 @@
     echo "can't find Android.mk"
 }
 
+# Ensure that we're always using the adb in the tree. This works around the fact
+# that bash caches $PATH lookups, so if you use adb before lunching/building the
+# one in your tree, you'll continue to get /usr/bin/adb or whatever even after
+# you have the one from your current tree on your path. Historically this would
+# cause confusion because glinux had adb in /usr/bin/ by default, though that
+# doesn't appear to be the case on my rodete hosts; it is however still the case
+# that my Mac has /usr/local/bin/adb installed by default and on the default
+# path.
 function adb() {
-   command adb "${@}"
+    # We need `command which` because zsh has a built-in `which` that's more
+    # like `type`.
+    local ADB=$(command which adb)
+    if [ -z "$ADB" ]; then
+        echo "Command adb not found; try lunch (and building) first?"
+        return 1
+    fi
+    $ADB "${@}"
 }
 
 # simplified version of ps; output in the form
diff --git a/target/board/BoardConfigMainlineCommon.mk b/target/board/BoardConfigMainlineCommon.mk
index c3878b8..2b17349 100644
--- a/target/board/BoardConfigMainlineCommon.mk
+++ b/target/board/BoardConfigMainlineCommon.mk
@@ -2,6 +2,9 @@
 #
 # Common compile-time definitions for mainline images.
 
+# Ensure all trunk-stable flags are available.
+include build/make/target/product/build_variables.mk
+
 # The generic product target doesn't have any hardware-specific pieces.
 TARGET_NO_BOOTLOADER := true
 TARGET_NO_RECOVERY := true
diff --git a/target/board/mainline_sdk/BoardConfig.mk b/target/board/mainline_sdk/BoardConfig.mk
index 84f8b2d..e4c6a8c 100644
--- a/target/board/mainline_sdk/BoardConfig.mk
+++ b/target/board/mainline_sdk/BoardConfig.mk
@@ -13,6 +13,9 @@
 # limitations under the License.
 #
 
+# Ensure all trunk-stable flags are available.
+include build/make/target/product/build_variables.mk
+
 TARGET_ARCH_SUITE := mainline_sdk
 
 HOST_CROSS_OS := linux_bionic
diff --git a/target/product/AndroidProducts.mk b/target/product/AndroidProducts.mk
index 76b1c58..07eb96d 100644
--- a/target/product/AndroidProducts.mk
+++ b/target/product/AndroidProducts.mk
@@ -67,6 +67,7 @@
     $(LOCAL_DIR)/mainline_system_x86_arm.mk \
     $(LOCAL_DIR)/ndk.mk \
     $(LOCAL_DIR)/sdk.mk \
+    $(LOCAL_DIR)/sdk_with_runtime_apis.mk \
 
 endif
 
diff --git a/target/product/angle_default.mk b/target/product/angle_default.mk
index fdfc7f5..72846d3 100644
--- a/target/product/angle_default.mk
+++ b/target/product/angle_default.mk
@@ -17,7 +17,5 @@
 # To enable ANGLE as the default system GLES drivers, add
 # $(call inherit-product, $(SRC_TARGET_DIR)/product/angle_default.mk) to the Makefile.
 
-$(call inherit-product, $(SRC_TARGET_DIR)/product/angle_supported.mk)
-
 PRODUCT_SYSTEM_PROPERTIES += \
     persist.graphics.egl=angle
diff --git a/target/product/angle_supported.mk b/target/product/angle_supported.mk
deleted file mode 100644
index 59e6ea3..0000000
--- a/target/product/angle_supported.mk
+++ /dev/null
@@ -1,29 +0,0 @@
-#
-# Copyright 2023 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.
-#
-
-# To include ANGLE into the image build, add
-# $(call inherit-product, $(SRC_TARGET_DIR)/product/angle_supported.mk) to the Makefile.
-# By default, this will allow ANGLE binaries to coexist with native GLES drivers.
-
-ifneq ($(RELEASE_ANGLE_ON_SYSTEM),true)
-PRODUCT_PACKAGES += \
-    libEGL_angle \
-    libGLESv1_CM_angle \
-    libGLESv2_angle
-
-# Set ro.gfx.angle.supported based on if ANGLE is installed in vendor partition
-PRODUCT_VENDOR_PROPERTIES += ro.gfx.angle.supported=true
-endif
diff --git a/target/product/base_system.mk b/target/product/base_system.mk
index 3840e1f..884af4f 100644
--- a/target/product/base_system.mk
+++ b/target/product/base_system.mk
@@ -17,6 +17,7 @@
 # Base modules and settings for the system partition.
 PRODUCT_PACKAGES += \
     abx \
+    aconfigd \
     adbd_system_api \
     aflags \
     am \
@@ -93,15 +94,16 @@
     ExtShared \
     flags_health_check \
     framework-graphics \
+    framework-location \
     framework-minus-apex \
     framework-minus-apex-install-dependencies \
-    framework-res \
     framework-sysconfig.xml \
     fsck.erofs \
     fsck_msdos \
     fsverity-release-cert-der \
     fs_config_files_system \
     fs_config_dirs_system \
+    gpu_counter_producer \
     group_system \
     gsid \
     gsi_tool \
@@ -235,12 +237,15 @@
     PackageInstaller \
     passwd_system \
     perfetto \
+    perfetto-extras \
     ping \
     ping6 \
+    pintool \
     platform.xml \
     pm \
     preinstalled-packages-asl-files.xml \
     preinstalled-packages-platform.xml \
+    preinstalled-packages-strict-signature.xml \
     printflags \
     privapp-permissions-platform.xml \
     prng_seeder \
@@ -261,6 +266,7 @@
     services \
     settings \
     SettingsProvider \
+    sfdo \
     sgdisk \
     Shell \
     shell_and_utilities_system \
@@ -283,7 +289,6 @@
     uncrypt \
     usbd \
     vdc \
-    viewcompiler \
     voip-common \
     vold \
     watchdogd \
@@ -370,14 +375,10 @@
     WallpaperBackup
 endif
 
-# Moving angle from vendor to system
-ifeq ($(RELEASE_ANGLE_ON_SYSTEM),true)
 PRODUCT_PACKAGES += \
     libEGL_angle \
     libGLESv1_CM_angle \
     libGLESv2_angle
-$(call soong_config_set,angle,angle_on_system,true)
-endif
 
 # For testing purposes
 ifeq ($(FORCE_AUDIO_SILENT), true)
@@ -414,20 +415,18 @@
     unwind_info \
     unwind_reg_info \
     unwind_symbols \
-    viewcompiler \
     tzdata_host \
     tzdata_host_tzdata_apex \
     tzlookup.xml_host_tzdata_apex \
     tz_version_host \
     tz_version_host_tzdata_apex \
 
+PRODUCT_PACKAGES += init.usb.rc init.usb.configfs.rc
 
 PRODUCT_COPY_FILES += \
-    system/core/rootdir/init.usb.rc:system/etc/init/hw/init.usb.rc \
-    system/core/rootdir/init.usb.configfs.rc:system/etc/init/hw/init.usb.configfs.rc \
     system/core/rootdir/etc/hosts:system/etc/hosts
 
-PRODUCT_COPY_FILES += system/core/rootdir/init.zygote32.rc:system/etc/init/hw/init.zygote32.rc
+PRODUCT_PACKAGES += init.zygote32.rc
 PRODUCT_VENDOR_PROPERTIES += ro.zygote?=zygote32
 
 PRODUCT_SYSTEM_PROPERTIES += debug.atrace.tags.enableflags=0
@@ -439,6 +438,7 @@
     adevice_fingerprint \
     arping \
     dmuserd \
+    evemu-record \
     idlcli \
     init-debug.rc \
     iotop \
@@ -491,5 +491,8 @@
 
 $(call inherit-product, $(SRC_TARGET_DIR)/product/runtime_libart.mk)
 
+# Ensure all trunk-stable flags are available.
+$(call inherit-product, $(SRC_TARGET_DIR)/product/build_variables.mk)
+
 # Use "image" APEXes always.
 $(call inherit-product,$(SRC_TARGET_DIR)/product/updatable_apex.mk)
diff --git a/target/product/base_vendor.mk b/target/product/base_vendor.mk
index a0c5929..ec3de75 100644
--- a/target/product/base_vendor.mk
+++ b/target/product/base_vendor.mk
@@ -51,7 +51,6 @@
     dumpsys_vendor \
     fs_config_files_nonsystem \
     fs_config_dirs_nonsystem \
-    gpu_counter_producer \
     gralloc.default \
     group_odm \
     group_vendor \
diff --git a/target/product/build_variables.mk b/target/product/build_variables.mk
new file mode 100644
index 0000000..5fe5333
--- /dev/null
+++ b/target/product/build_variables.mk
@@ -0,0 +1,21 @@
+#
+# 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.
+#
+
+# This file contains the trunk-stable flags that should be exported to all
+# Android targets.
+
+# Use the configured release of sqlite
+$(call soong_config_set, libsqlite3, release_package_libsqlite3, $(RELEASE_PACKAGE_LIBSQLITE3))
diff --git a/target/product/core_64_bit.mk b/target/product/core_64_bit.mk
index e0c4d53..790f57b 100644
--- a/target/product/core_64_bit.mk
+++ b/target/product/core_64_bit.mk
@@ -23,9 +23,7 @@
 # for 32-bit only.
 
 # Copy the 64-bit primary, 32-bit secondary zygote startup script
-PRODUCT_COPY_FILES += \
-    system/core/rootdir/init.zygote64.rc:system/etc/init/hw/init.zygote64.rc \
-    system/core/rootdir/init.zygote64_32.rc:system/etc/init/hw/init.zygote64_32.rc \
+PRODUCT_PACKAGES += init.zygote64.rc init.zygote64_32.rc
 
 # Set the zygote property to select the 64-bit primary, 32-bit secondary script
 # This line must be parsed before the one in core_minimal.mk
diff --git a/target/product/core_64_bit_only.mk b/target/product/core_64_bit_only.mk
index fc2b8e5..ffa5567 100644
--- a/target/product/core_64_bit_only.mk
+++ b/target/product/core_64_bit_only.mk
@@ -20,7 +20,7 @@
 # to core_minimal.mk.
 
 # Copy the 64-bit zygote startup script
-PRODUCT_COPY_FILES += system/core/rootdir/init.zygote64.rc:system/etc/init/hw/init.zygote64.rc
+PRODUCT_PACKAGES += init.zygote64.rc
 
 # Set the zygote property to select the 64-bit script.
 # This line must be parsed before the one in core_minimal.mk
diff --git a/target/product/default_art_config.mk b/target/product/default_art_config.mk
index dca9baa..4a968d7 100644
--- a/target/product/default_art_config.mk
+++ b/target/product/default_art_config.mk
@@ -50,6 +50,7 @@
 PRODUCT_BOOT_JARS += \
     framework-minus-apex \
     framework-graphics \
+    framework-location \
     ext \
     telephony-common \
     voip-common \
@@ -104,13 +105,6 @@
         com.android.nfcservices:framework-nfc
 endif
 
-# TODO(b/308174306): Adjust this after multiple prebuilts version is supported.
-# APEX boot jars that are not in prebuilt apexes.
-# Keep the list sorted by module names and then library names.
-PRODUCT_APEX_BOOT_JARS_FOR_SOURCE_BUILD_ONLY := \
-    com.android.mediaprovider:framework-pdf \
-    com.android.mediaprovider:framework-pdf-v \
-
 # List of system_server classpath jars delivered via apex.
 # Keep the list sorted by module names and then library names.
 # Note: For modules available in Q, DO NOT add new entries here.
diff --git a/target/product/generic_system.mk b/target/product/generic_system.mk
index 19ec86d..fa31e04 100644
--- a/target/product/generic_system.mk
+++ b/target/product/generic_system.mk
@@ -111,10 +111,10 @@
     $(PRODUCT_PACKAGES_SHIPPING_API_LEVEL_34)
 
 # Include all zygote init scripts. "ro.zygote" will select one of them.
-PRODUCT_COPY_FILES += \
-    system/core/rootdir/init.zygote32.rc:system/etc/init/hw/init.zygote32.rc \
-    system/core/rootdir/init.zygote64.rc:system/etc/init/hw/init.zygote64.rc \
-    system/core/rootdir/init.zygote64_32.rc:system/etc/init/hw/init.zygote64_32.rc \
+PRODUCT_PACKAGES += \
+    init.zygote32.rc \
+    init.zygote64.rc \
+    init.zygote64_32.rc
 
 # Enable dynamic partition size
 PRODUCT_USE_DYNAMIC_PARTITION_SIZE := true
diff --git a/target/product/gsi/Android.mk b/target/product/gsi/Android.mk
index f348fbb..3bb65ac 100644
--- a/target/product/gsi/Android.mk
+++ b/target/product/gsi/Android.mk
@@ -5,23 +5,6 @@
 INTERNAL_VNDK_LIB_LIST := $(SOONG_VNDK_LIBRARIES_FILE)
 
 #####################################################################
-# This is the up-to-date list of vndk libs.
-LATEST_VNDK_LIB_LIST := $(LOCAL_PATH)/current.txt
-ifeq ($(KEEP_VNDK),true)
-UNFROZEN_VNDK := true
-ifeq (REL,$(PLATFORM_VERSION_CODENAME))
-    # Use frozen vndk lib list only if "34 >= PLATFORM_VNDK_VERSION"
-    ifeq ($(call math_gt_or_eq,34,$(PLATFORM_VNDK_VERSION)),true)
-        LATEST_VNDK_LIB_LIST := $(LOCAL_PATH)/$(PLATFORM_VNDK_VERSION).txt
-        ifeq ($(wildcard $(LATEST_VNDK_LIB_LIST)),)
-            $(error $(LATEST_VNDK_LIB_LIST) file not found. Please copy "$(LOCAL_PATH)/current.txt" to "$(LATEST_VNDK_LIB_LIST)" and commit a CL for release branch)
-        endif
-        UNFROZEN_VNDK :=
-    endif
-endif
-endif
-
-#####################################################################
 # Check the generate list against the latest list stored in the
 # source tree
 .PHONY: check-vndk-list
@@ -159,11 +142,7 @@
 $(notdir $(call filter-abi-dump-paths,$(1),$(2)))
 endef
 
-ifdef RELEASE_BOARD_API_LEVEL
-    VNDK_ABI_DUMP_DIR := prebuilts/abi-dumps/vndk/$(RELEASE_BOARD_API_LEVEL)
-else
-    VNDK_ABI_DUMP_DIR := prebuilts/abi-dumps/vndk/$(PLATFORM_VNDK_VERSION)
-endif
+
 ifeq (REL,$(PLATFORM_VERSION_CODENAME))
     NDK_ABI_DUMP_DIR := prebuilts/abi-dumps/ndk/$(PLATFORM_SDK_VERSION)
     PLATFORM_ABI_DUMP_DIR := prebuilts/abi-dumps/platform/$(PLATFORM_SDK_VERSION)
@@ -171,7 +150,6 @@
     NDK_ABI_DUMP_DIR := prebuilts/abi-dumps/ndk/current
     PLATFORM_ABI_DUMP_DIR := prebuilts/abi-dumps/platform/current
 endif
-VNDK_ABI_DUMPS := $(call find-abi-dump-paths,$(VNDK_ABI_DUMP_DIR))
 NDK_ABI_DUMPS := $(call find-abi-dump-paths,$(NDK_ABI_DUMP_DIR))
 PLATFORM_ABI_DUMPS := $(call find-abi-dump-paths,$(PLATFORM_ABI_DUMP_DIR))
 
@@ -224,13 +202,7 @@
     vndkcorevariant.libraries.txt \
     $(addsuffix .vendor,$(VNDK_CORE_LIBRARIES)) \
     $(addsuffix .vendor,$(VNDK_SAMEPROCESS_LIBRARIES)) \
-    $(VNDK_USING_CORE_VARIANT_LIBRARIES) \
-    com.android.vndk.current
-
-# Install VNDK apex on vendor partition if VNDK is unfrozen
-ifdef UNFROZEN_VNDK
-LOCAL_REQUIRED_MODULES += com.android.vndk.current.on_vendor
-endif
+    $(VNDK_USING_CORE_VARIANT_LIBRARIES)
 
 LOCAL_ADDITIONAL_DEPENDENCIES += $(call module-built-files,\
     $(addsuffix .vendor,$(VNDK_CORE_LIBRARIES) $(VNDK_SAMEPROCESS_LIBRARIES)))
diff --git a/target/product/gsi/current.txt b/target/product/gsi/current.txt
index 80aecb7..f771916 100644
--- a/target/product/gsi/current.txt
+++ b/target/product/gsi/current.txt
@@ -24,7 +24,7 @@
 VNDK-SP: android.hardware.common-V2-ndk.so
 VNDK-SP: android.hardware.common.fmq-V1-ndk.so
 VNDK-SP: android.hardware.graphics.allocator-V2-ndk.so
-VNDK-SP: android.hardware.graphics.common-V4-ndk.so
+VNDK-SP: android.hardware.graphics.common-V5-ndk.so
 VNDK-SP: android.hardware.graphics.common@1.0.so
 VNDK-SP: android.hardware.graphics.common@1.1.so
 VNDK-SP: android.hardware.graphics.common@1.2.so
@@ -60,8 +60,8 @@
 VNDK-SP: libutilscallstack.so
 VNDK-SP: libz.so
 VNDK-core: android.frameworks.cameraservice.common-V1-ndk.so
-VNDK-core: android.frameworks.cameraservice.device-V1-ndk.so
-VNDK-core: android.frameworks.cameraservice.service-V1-ndk.so
+VNDK-core: android.frameworks.cameraservice.device-V2-ndk.so
+VNDK-core: android.frameworks.cameraservice.service-V2-ndk.so
 VNDK-core: android.hardware.audio.common@2.0.so
 VNDK-core: android.hardware.configstore-utils.so
 VNDK-core: android.hardware.configstore@1.0.so
@@ -138,6 +138,7 @@
 VNDK-core: libxml2.so
 VNDK-core: libyuv.so
 VNDK-core: libziparchive.so
+VNDK-core: server_configurable_flags.so
 VNDK-private: libblas.so
 VNDK-private: libcompiler_rt.so
 VNDK-private: libft2.so
@@ -208,3 +209,4 @@
 VNDK-product: libyuv.so
 VNDK-product: libz.so
 VNDK-product: libziparchive.so
+VNDK-product: server_configurable_flags.so
diff --git a/target/product/gsi_release.mk b/target/product/gsi_release.mk
index a581324..2e37366 100644
--- a/target/product/gsi_release.mk
+++ b/target/product/gsi_release.mk
@@ -31,11 +31,7 @@
 PRODUCT_ARTIFACT_PATH_REQUIREMENT_ALLOWED_LIST += \
     system/etc/init/config \
     system/product/% \
-    system/system_ext/% \
-    system/lib/vndk-29 \
-    system/lib/vndk-sp-29 \
-    system/lib64/vndk-29 \
-    system/lib64/vndk-sp-29
+    system/system_ext/%
 
 # GSI should always support up-to-date platform features.
 # Keep this value at the latest API level to ensure latest build system
diff --git a/target/product/handheld_system.mk b/target/product/handheld_system.mk
index b5292d2..bf9aa41 100644
--- a/target/product/handheld_system.mk
+++ b/target/product/handheld_system.mk
@@ -74,6 +74,7 @@
     VpnDialogs \
     vr \
 
+PRODUCT_PACKAGES += $(RELEASE_PACKAGE_VIRTUAL_CAMERA)
 
 PRODUCT_SYSTEM_SERVER_APPS += \
     FusedLocation \
diff --git a/target/product/module_common.mk b/target/product/module_common.mk
index 53b2ca6..bf146a0 100644
--- a/target/product/module_common.mk
+++ b/target/product/module_common.mk
@@ -14,6 +14,7 @@
 # limitations under the License.
 #
 
+$(call inherit-product, $(SRC_TARGET_DIR)/product/build_variables.mk)
 $(call inherit-product, $(SRC_TARGET_DIR)/product/default_art_config.mk)
 $(call inherit-product, $(SRC_TARGET_DIR)/product/languages_default.mk)
 $(call inherit-product, $(SRC_TARGET_DIR)/product/cfi-common.mk)
diff --git a/target/product/sdk.mk b/target/product/sdk.mk
index 650f8e9..1a07363 100644
--- a/target/product/sdk.mk
+++ b/target/product/sdk.mk
@@ -17,6 +17,9 @@
 # This is a simple product that uses configures the minimum amount
 # needed to build the SDK (without the emulator).
 
+# Ensure all trunk-stable flags are available.
+$(call inherit-product, $(SRC_TARGET_DIR)/product/build_variables.mk)
+
 # In order to build the bootclasspath sources, the bootclasspath needs to
 # be setup via default_art_config.mk. The sources only really make sense
 # together with a device (e.g. the emulator). So if the SDK sources change
@@ -29,4 +32,11 @@
 PRODUCT_BRAND := Android
 PRODUCT_DEVICE := mainline_x86
 
-PRODUCT_BUILD_FROM_SOURCE_STUB := true
\ No newline at end of file
+PRODUCT_BUILD_FROM_SOURCE_STUB := true
+
+# Use sources of mainline modules
+PRODUCT_MODULE_BUILD_FROM_SOURCE := true
+
+ifeq ($(WITHOUT_CHECK_API),true)
+  $(error WITHOUT_CHECK_API cannot be set to true for SDK product builds)
+endif
diff --git a/target/product/sdk_with_runtime_apis.mk b/target/product/sdk_with_runtime_apis.mk
new file mode 100644
index 0000000..e80b4fb
--- /dev/null
+++ b/target/product/sdk_with_runtime_apis.mk
@@ -0,0 +1,22 @@
+#
+# 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.
+#
+
+$(call inherit-product, $(SRC_TARGET_DIR)/product/sdk.mk)
+
+PRODUCT_NAME := sdk_with_runtime_apis
+
+PRODUCT_HIDDEN_API_EXPORTABLE_STUBS := true
+PRODUCT_EXPORT_RUNTIME_APIS := true
\ No newline at end of file
diff --git a/target/product/sysconfig/preinstalled-packages-platform-aosp-product.xml b/target/product/sysconfig/preinstalled-packages-platform-aosp-product.xml
index 1295e1c..d3e2808 100644
--- a/target/product/sysconfig/preinstalled-packages-platform-aosp-product.xml
+++ b/target/product/sysconfig/preinstalled-packages-platform-aosp-product.xml
@@ -27,5 +27,6 @@
         <install-in user-type="FULL" />
         <install-in user-type="PROFILE" />
         <do-not-install-in user-type="android.os.usertype.profile.CLONE" />
+        <do-not-install-in user-type="android.os.usertype.profile.PRIVATE" />
     </install-in-user-type>
 </config>
diff --git a/target/product/sysconfig/preinstalled-packages-platform-generic-system.xml b/target/product/sysconfig/preinstalled-packages-platform-generic-system.xml
index e2482e1..ef8056f 100644
--- a/target/product/sysconfig/preinstalled-packages-platform-generic-system.xml
+++ b/target/product/sysconfig/preinstalled-packages-platform-generic-system.xml
@@ -24,6 +24,7 @@
         <install-in user-type="FULL" />
         <install-in user-type="PROFILE" />
         <do-not-install-in user-type="android.os.usertype.profile.CLONE" />
+        <do-not-install-in user-type="android.os.usertype.profile.PRIVATE" />
     </install-in-user-type>
 </config>
 
diff --git a/target/product/sysconfig/preinstalled-packages-platform-handheld-product.xml b/target/product/sysconfig/preinstalled-packages-platform-handheld-product.xml
index 54add22..536c35b 100644
--- a/target/product/sysconfig/preinstalled-packages-platform-handheld-product.xml
+++ b/target/product/sysconfig/preinstalled-packages-platform-handheld-product.xml
@@ -29,6 +29,7 @@
         <install-in user-type="FULL" />
         <install-in user-type="PROFILE" />
         <do-not-install-in user-type="android.os.usertype.profile.CLONE" />
+        <do-not-install-in user-type="android.os.usertype.profile.PRIVATE" />
     </install-in-user-type>
 
     <!--  Camera (Camera2) -->
@@ -42,6 +43,7 @@
     <install-in-user-type package="com.android.deskclock">
         <install-in user-type="FULL" />
         <do-not-install-in user-type="android.os.usertype.profile.CLONE" />
+        <do-not-install-in user-type="android.os.usertype.profile.PRIVATE" />
     </install-in-user-type>
 
     <!--  Contacts -->
@@ -56,6 +58,7 @@
         <install-in user-type="FULL" />
         <install-in user-type="PROFILE" />
         <do-not-install-in user-type="android.os.usertype.profile.CLONE" />
+        <do-not-install-in user-type="android.os.usertype.profile.PRIVATE" />
     </install-in-user-type>
 
     <!--  Search (QuickSearchBox) TODO(b/258055479) -->
@@ -64,6 +67,7 @@
         <install-in user-type="FULL" />
         <install-in user-type="PROFILE" />
         <do-not-install-in user-type="android.os.usertype.profile.CLONE" />
+        <do-not-install-in user-type="android.os.usertype.profile.PRIVATE" />
     </install-in-user-type>
 
     <!-- WallpaperCropper -->
diff --git a/target/product/sysconfig/preinstalled-packages-platform-telephony-product.xml b/target/product/sysconfig/preinstalled-packages-platform-telephony-product.xml
index cc1c135..67a2a01 100644
--- a/target/product/sysconfig/preinstalled-packages-platform-telephony-product.xml
+++ b/target/product/sysconfig/preinstalled-packages-platform-telephony-product.xml
@@ -24,6 +24,7 @@
         <install-in user-type="FULL" />
         <install-in user-type="PROFILE" />
         <do-not-install-in user-type="android.os.usertype.profile.CLONE" />
+        <do-not-install-in user-type="android.os.usertype.profile.PRIVATE" />
     </install-in-user-type>
 </config>
 
diff --git a/teams/Android.bp b/teams/Android.bp
index bae8e80..8f83e71 100644
--- a/teams/Android.bp
+++ b/teams/Android.bp
@@ -4364,3 +4364,10 @@
   // go/trendy/manage/engineers/4943966050844672
   trendy_team_id: "4943966050844672",
 }
+
+team {
+  name: "trendy_team_android_platform_performance_testing",
+
+  // go/trendy/manage/engineers/5810097836621824
+  trendy_team_id: "5810097836621824",
+}
diff --git a/tools/aconfig/Cargo.toml b/tools/aconfig/Cargo.toml
index 95f1215..6bd0d06 100644
--- a/tools/aconfig/Cargo.toml
+++ b/tools/aconfig/Cargo.toml
@@ -4,6 +4,8 @@
     "aconfig",
     "aconfig_protos",
     "aconfig_storage_file",
+    "aconfig_storage_read_api",
+    "aconfig_storage_write_api",
     "aflags",
     "printflags"
 ]
diff --git a/tools/aconfig/TEST_MAPPING b/tools/aconfig/TEST_MAPPING
index e42b5d3..638b92a 100644
--- a/tools/aconfig/TEST_MAPPING
+++ b/tools/aconfig/TEST_MAPPING
@@ -20,10 +20,11 @@
       // aconfig C++ integration tests (test mode auto-generated code)
       "name": "aconfig.test.cpp.test_mode"
     },
-    {
-      // aconfig C++ integration tests (exported mode auto-generated code)
-      "name": "aconfig.test.cpp.exported_mode"
-    },
+    // TODO(b/327420679): Enable export mode for native flag library
+    // {
+    //   // aconfig C++ integration tests (exported mode auto-generated code)
+    //   "name": "aconfig.test.cpp.exported_mode"
+    // },
     {
       // aconfig Rust integration tests (production mode auto-generated code)
       "name": "aconfig.prod_mode.test.rust"
@@ -32,9 +33,14 @@
       // aconfig Rust integration tests (test mode auto-generated code)
       "name": "aconfig.test_mode.test.rust"
     },
+    // TODO(b/327420679): Enable export mode for native flag library
+    // {
+    //   // aconfig Rust integration tests (exported mode auto-generated code)
+    //   "name": "aconfig.exported_mode.test.rust"
+    // },
     {
-      // aconfig Rust integration tests (exported mode auto-generated code)
-      "name": "aconfig.exported_mode.test.rust"
+      // aflags CLI unit tests
+      "name": "aflags.test"
     },
     {
       // printflags unit tests
@@ -64,21 +70,32 @@
       // test testing filtering logic. Breakage on this test means all tests
       // that using the flag macros to do filtering will get affected.
       "name": "FlagMacrosTests"
+    },
+    {
+      // aconfig_storage_write_api unit tests
+      "name": "aconfig_storage_write_api.test"
+    },
+    {
+      // aconfig_storage_read_api unit tests
+      "name": "aconfig_storage_read_api.test"
+    },
+    {
+      // aconfig_storage write api rust integration tests
+      "name": "aconfig_storage_write_api.test.rust"
+    },
+    {
+      // aconfig_storage read api rust integration tests
+      "name": "aconfig_storage_read_api.test.rust"
+    },
+    {
+      // aconfig_storage read api cpp integration tests
+      "name": "aconfig_storage_read_api.test.cpp"
     }
   ],
   "postsubmit": [
     {
-      // aconfig_storage read api rust integration tests
-      "name": "aconfig_storage.test.rust"
-    },
-    {
-      // aconfig_storage read api cpp integration tests
-      "name": "aconfig_storage.test.cpp"
-    },
-    {
-      // aflags CLI unit tests
-      // TODO(b/326062088): add to presubmit once proven in postsubmit.
-      "name": "aflags.test"
+      // aconfig_storage write api cpp integration tests
+      "name": "aconfig_storage_write_api.test.cpp"
     }
   ]
 }
diff --git a/tools/aconfig/aconfig/Android.bp b/tools/aconfig/aconfig/Android.bp
index 164bfe7..00a6fee 100644
--- a/tools/aconfig/aconfig/Android.bp
+++ b/tools/aconfig/aconfig/Android.bp
@@ -47,6 +47,7 @@
 aconfig_declarations {
     name: "aconfig.test.exported.flags",
     package: "com.android.aconfig.test.exported",
+    exportable: true,
     container: "system",
     srcs: ["tests/test_exported.aconfig"],
 }
@@ -143,12 +144,6 @@
 }
 
 cc_aconfig_library {
-    name: "aconfig_test_cpp_library_exported_variant",
-    aconfig_declarations: "aconfig.test.flags",
-    mode: "exported",
-}
-
-cc_aconfig_library {
     name: "aconfig_test_cpp_library_force_read_only_variant",
     aconfig_declarations: "aconfig.test.flags",
     mode: "force-read-only",
@@ -184,6 +179,14 @@
     test_suites: ["general-tests"],
 }
 
+// TODO(327420679): Enable export mode for native flag library
+/*
+cc_aconfig_library {
+    name: "aconfig_test_cpp_library_exported_variant",
+    aconfig_declarations: "aconfig.test.flags",
+    mode: "exported",
+}
+
 cc_test {
     name: "aconfig.test.cpp.exported_mode",
     srcs: [
@@ -198,6 +201,7 @@
     ],
     test_suites: ["general-tests"],
 }
+*/
 
 cc_test {
     name: "aconfig.test.cpp.force_read_only_mode",
@@ -249,6 +253,8 @@
     test_suites: ["general-tests"],
 }
 
+// TODO(327420679): Enable export mode for native flag library
+/*
 rust_aconfig_library {
     name: "libaconfig_test_rust_library_with_exported_mode",
     crate_name: "aconfig_test_rust_library",
@@ -266,6 +272,7 @@
     ],
     test_suites: ["general-tests"],
 }
+*/
 
 rust_aconfig_library {
     name: "libaconfig_test_rust_library_with_force_read_only_mode",
diff --git a/tools/aconfig/aconfig/src/codegen/java.rs b/tools/aconfig/aconfig/src/codegen/java.rs
index a18f9a8..920d5fa 100644
--- a/tools/aconfig/aconfig/src/codegen/java.rs
+++ b/tools/aconfig/aconfig/src/codegen/java.rs
@@ -180,27 +180,36 @@
     import android.compat.annotation.UnsupportedAppUsage;
     /** @hide */
     public interface FeatureFlags {
+        @com.android.aconfig.annotations.AconfigFlagAccessor
         @com.android.aconfig.annotations.AssumeFalseForR8
         @UnsupportedAppUsage
         boolean disabledRo();
+        @com.android.aconfig.annotations.AconfigFlagAccessor
         @UnsupportedAppUsage
         boolean disabledRw();
+        @com.android.aconfig.annotations.AconfigFlagAccessor
         @UnsupportedAppUsage
         boolean disabledRwExported();
+        @com.android.aconfig.annotations.AconfigFlagAccessor
         @UnsupportedAppUsage
         boolean disabledRwInOtherNamespace();
+        @com.android.aconfig.annotations.AconfigFlagAccessor
         @com.android.aconfig.annotations.AssumeTrueForR8
         @UnsupportedAppUsage
         boolean enabledFixedRo();
+        @com.android.aconfig.annotations.AconfigFlagAccessor
         @com.android.aconfig.annotations.AssumeTrueForR8
         @UnsupportedAppUsage
         boolean enabledFixedRoExported();
+        @com.android.aconfig.annotations.AconfigFlagAccessor
         @com.android.aconfig.annotations.AssumeTrueForR8
         @UnsupportedAppUsage
         boolean enabledRo();
+        @com.android.aconfig.annotations.AconfigFlagAccessor
         @com.android.aconfig.annotations.AssumeTrueForR8
         @UnsupportedAppUsage
         boolean enabledRoExported();
+        @com.android.aconfig.annotations.AconfigFlagAccessor
         @UnsupportedAppUsage
         boolean enabledRw();
     }
@@ -231,43 +240,52 @@
         /** @hide */
         public static final String FLAG_ENABLED_RW = "com.android.aconfig.test.enabled_rw";
 
+        @com.android.aconfig.annotations.AconfigFlagAccessor
         @com.android.aconfig.annotations.AssumeFalseForR8
         @UnsupportedAppUsage
         public static boolean disabledRo() {
             return FEATURE_FLAGS.disabledRo();
         }
+        @com.android.aconfig.annotations.AconfigFlagAccessor
         @UnsupportedAppUsage
         public static boolean disabledRw() {
             return FEATURE_FLAGS.disabledRw();
         }
+        @com.android.aconfig.annotations.AconfigFlagAccessor
         @UnsupportedAppUsage
         public static boolean disabledRwExported() {
             return FEATURE_FLAGS.disabledRwExported();
         }
+        @com.android.aconfig.annotations.AconfigFlagAccessor
         @UnsupportedAppUsage
         public static boolean disabledRwInOtherNamespace() {
             return FEATURE_FLAGS.disabledRwInOtherNamespace();
         }
+        @com.android.aconfig.annotations.AconfigFlagAccessor
         @com.android.aconfig.annotations.AssumeTrueForR8
         @UnsupportedAppUsage
         public static boolean enabledFixedRo() {
             return FEATURE_FLAGS.enabledFixedRo();
         }
+        @com.android.aconfig.annotations.AconfigFlagAccessor
         @com.android.aconfig.annotations.AssumeTrueForR8
         @UnsupportedAppUsage
         public static boolean enabledFixedRoExported() {
             return FEATURE_FLAGS.enabledFixedRoExported();
         }
+        @com.android.aconfig.annotations.AconfigFlagAccessor
         @com.android.aconfig.annotations.AssumeTrueForR8
         @UnsupportedAppUsage
         public static boolean enabledRo() {
             return FEATURE_FLAGS.enabledRo();
         }
+        @com.android.aconfig.annotations.AconfigFlagAccessor
         @com.android.aconfig.annotations.AssumeTrueForR8
         @UnsupportedAppUsage
         public static boolean enabledRoExported() {
             return FEATURE_FLAGS.enabledRoExported();
         }
+        @com.android.aconfig.annotations.AconfigFlagAccessor
         @UnsupportedAppUsage
         public static boolean enabledRw() {
             return FEATURE_FLAGS.enabledRw();
@@ -351,6 +369,10 @@
             }
             return false;
         }
+        @com.android.aconfig.annotations.AssumeTrueForR8
+        private boolean isOptimizationEnabled() {
+            return false;
+        }
         private boolean getValue(String flagName) {
             Boolean value = this.mFlagMap.get(flagName);
             if (value == null) {
@@ -358,10 +380,6 @@
             }
             return value;
         }
-        @com.android.aconfig.annotations.AssumeTrueForR8
-        private boolean isOptimizationEnabled() {
-            return false;
-        }
         private Map<String, Boolean> mFlagMap = new HashMap<>(
             Map.ofEntries(
                 Map.entry(Flags.FLAG_DISABLED_RO, false),
@@ -458,12 +476,13 @@
                 other_namespace_is_cached = true;
             }
 
-
+            @com.android.aconfig.annotations.AconfigFlagAccessor
             @Override
             @UnsupportedAppUsage
             public boolean disabledRo() {
                 return false;
             }
+            @com.android.aconfig.annotations.AconfigFlagAccessor
             @Override
             @UnsupportedAppUsage
             public boolean disabledRw() {
@@ -472,6 +491,7 @@
                 }
                 return disabledRw;
             }
+            @com.android.aconfig.annotations.AconfigFlagAccessor
             @Override
             @UnsupportedAppUsage
             public boolean disabledRwExported() {
@@ -480,6 +500,7 @@
                 }
                 return disabledRwExported;
             }
+            @com.android.aconfig.annotations.AconfigFlagAccessor
             @Override
             @UnsupportedAppUsage
             public boolean disabledRwInOtherNamespace() {
@@ -488,26 +509,31 @@
                 }
                 return disabledRwInOtherNamespace;
             }
+            @com.android.aconfig.annotations.AconfigFlagAccessor
             @Override
             @UnsupportedAppUsage
             public boolean enabledFixedRo() {
                 return true;
             }
+            @com.android.aconfig.annotations.AconfigFlagAccessor
             @Override
             @UnsupportedAppUsage
             public boolean enabledFixedRoExported() {
                 return true;
             }
+            @com.android.aconfig.annotations.AconfigFlagAccessor
             @Override
             @UnsupportedAppUsage
             public boolean enabledRo() {
                 return true;
             }
+            @com.android.aconfig.annotations.AconfigFlagAccessor
             @Override
             @UnsupportedAppUsage
             public boolean enabledRoExported() {
                 return true;
             }
+            @com.android.aconfig.annotations.AconfigFlagAccessor
             @Override
             @UnsupportedAppUsage
             public boolean enabledRw() {
@@ -558,8 +584,6 @@
 
         let expect_flags_content = r#"
         package com.android.aconfig.test;
-        // TODO(b/303773055): Remove the annotation after access issue is resolved.
-        import android.compat.annotation.UnsupportedAppUsage;
         /** @hide */
         public final class Flags {
             /** @hide */
@@ -568,16 +592,15 @@
             public static final String FLAG_ENABLED_FIXED_RO_EXPORTED = "com.android.aconfig.test.enabled_fixed_ro_exported";
             /** @hide */
             public static final String FLAG_ENABLED_RO_EXPORTED = "com.android.aconfig.test.enabled_ro_exported";
-
-            @UnsupportedAppUsage
+            @com.android.aconfig.annotations.AconfigFlagAccessor
             public static boolean disabledRwExported() {
                 return FEATURE_FLAGS.disabledRwExported();
             }
-            @UnsupportedAppUsage
+            @com.android.aconfig.annotations.AconfigFlagAccessor
             public static boolean enabledFixedRoExported() {
                 return FEATURE_FLAGS.enabledFixedRoExported();
             }
-            @UnsupportedAppUsage
+            @com.android.aconfig.annotations.AconfigFlagAccessor
             public static boolean enabledRoExported() {
                 return FEATURE_FLAGS.enabledRoExported();
             }
@@ -587,23 +610,19 @@
 
         let expect_feature_flags_content = r#"
         package com.android.aconfig.test;
-        // TODO(b/303773055): Remove the annotation after access issue is resolved.
-        import android.compat.annotation.UnsupportedAppUsage;
         /** @hide */
         public interface FeatureFlags {
-            @UnsupportedAppUsage
+            @com.android.aconfig.annotations.AconfigFlagAccessor
             boolean disabledRwExported();
-            @UnsupportedAppUsage
+            @com.android.aconfig.annotations.AconfigFlagAccessor
             boolean enabledFixedRoExported();
-            @UnsupportedAppUsage
+            @com.android.aconfig.annotations.AconfigFlagAccessor
             boolean enabledRoExported();
         }
         "#;
 
         let expect_feature_flags_impl_content = r#"
         package com.android.aconfig.test;
-        // TODO(b/303773055): Remove the annotation after access issue is resolved.
-        import android.compat.annotation.UnsupportedAppUsage;
         import android.provider.DeviceConfig;
         import android.provider.DeviceConfig.Properties;
         /** @hide */
@@ -635,27 +654,24 @@
                 }
                 aconfig_test_is_cached = true;
             }
-
+            @com.android.aconfig.annotations.AconfigFlagAccessor
             @Override
-            @UnsupportedAppUsage
             public boolean disabledRwExported() {
                 if (!aconfig_test_is_cached) {
                     load_overrides_aconfig_test();
                 }
                 return disabledRwExported;
             }
-
+            @com.android.aconfig.annotations.AconfigFlagAccessor
             @Override
-            @UnsupportedAppUsage
             public boolean enabledFixedRoExported() {
                 if (!aconfig_test_is_cached) {
                     load_overrides_aconfig_test();
                 }
                 return enabledFixedRoExported;
             }
-
+            @com.android.aconfig.annotations.AconfigFlagAccessor
             @Override
-            @UnsupportedAppUsage
             public boolean enabledRoExported() {
                 if (!aconfig_test_is_cached) {
                     load_overrides_aconfig_test();
@@ -666,8 +682,6 @@
 
         let expect_fake_feature_flags_impl_content = r#"
         package com.android.aconfig.test;
-        // TODO(b/303773055): Remove the annotation after access issue is resolved.
-        import android.compat.annotation.UnsupportedAppUsage;
         import java.util.Arrays;
         import java.util.HashMap;
         import java.util.HashSet;
@@ -679,17 +693,14 @@
                 resetAll();
             }
             @Override
-            @UnsupportedAppUsage
             public boolean disabledRwExported() {
                 return getValue(Flags.FLAG_DISABLED_RW_EXPORTED);
             }
             @Override
-            @UnsupportedAppUsage
             public boolean enabledFixedRoExported() {
                 return getValue(Flags.FLAG_ENABLED_FIXED_RO_EXPORTED);
             }
             @Override
-            @UnsupportedAppUsage
             public boolean enabledRoExported() {
                 return getValue(Flags.FLAG_ENABLED_RO_EXPORTED);
             }
@@ -704,13 +715,6 @@
                     entry.setValue(null);
                 }
             }
-            public boolean isFlagReadOnlyOptimized(String flagName) {
-                if (mReadOnlyFlagsSet.contains(flagName) &&
-                    isOptimizationEnabled()) {
-                        return true;
-                }
-                return false;
-            }
             private boolean getValue(String flagName) {
                 Boolean value = this.mFlagMap.get(flagName);
                 if (value == null) {
@@ -718,10 +722,6 @@
                 }
                 return value;
             }
-            @com.android.aconfig.annotations.AssumeTrueForR8
-            private boolean isOptimizationEnabled() {
-                return false;
-            }
             private Map<String, Boolean> mFlagMap = new HashMap<>(
                 Map.ofEntries(
                     Map.entry(Flags.FLAG_DISABLED_RW_EXPORTED, false),
@@ -792,54 +792,63 @@
         import android.compat.annotation.UnsupportedAppUsage;
         /** @hide */
         public final class FeatureFlagsImpl implements FeatureFlags {
+            @com.android.aconfig.annotations.AconfigFlagAccessor
             @Override
             @UnsupportedAppUsage
             public boolean disabledRo() {
                 throw new UnsupportedOperationException(
                     "Method is not implemented.");
             }
+            @com.android.aconfig.annotations.AconfigFlagAccessor
             @Override
             @UnsupportedAppUsage
             public boolean disabledRw() {
                 throw new UnsupportedOperationException(
                     "Method is not implemented.");
             }
+            @com.android.aconfig.annotations.AconfigFlagAccessor
             @Override
             @UnsupportedAppUsage
             public boolean disabledRwExported() {
                 throw new UnsupportedOperationException(
                     "Method is not implemented.");
             }
+            @com.android.aconfig.annotations.AconfigFlagAccessor
             @Override
             @UnsupportedAppUsage
             public boolean disabledRwInOtherNamespace() {
                 throw new UnsupportedOperationException(
                     "Method is not implemented.");
             }
+            @com.android.aconfig.annotations.AconfigFlagAccessor
             @Override
             @UnsupportedAppUsage
             public boolean enabledFixedRo() {
                 throw new UnsupportedOperationException(
                     "Method is not implemented.");
             }
+            @com.android.aconfig.annotations.AconfigFlagAccessor
             @Override
             @UnsupportedAppUsage
             public boolean enabledFixedRoExported() {
                 throw new UnsupportedOperationException(
                     "Method is not implemented.");
             }
+            @com.android.aconfig.annotations.AconfigFlagAccessor
             @Override
             @UnsupportedAppUsage
             public boolean enabledRo() {
                 throw new UnsupportedOperationException(
                     "Method is not implemented.");
             }
+            @com.android.aconfig.annotations.AconfigFlagAccessor
             @Override
             @UnsupportedAppUsage
             public boolean enabledRoExported() {
                 throw new UnsupportedOperationException(
                     "Method is not implemented.");
             }
+            @com.android.aconfig.annotations.AconfigFlagAccessor
             @Override
             @UnsupportedAppUsage
             public boolean enabledRw() {
@@ -892,21 +901,27 @@
         import android.compat.annotation.UnsupportedAppUsage;
         /** @hide */
         public interface FeatureFlags {
+            @com.android.aconfig.annotations.AconfigFlagAccessor
             @com.android.aconfig.annotations.AssumeFalseForR8
             @UnsupportedAppUsage
             boolean disabledRo();
+            @com.android.aconfig.annotations.AconfigFlagAccessor
             @com.android.aconfig.annotations.AssumeFalseForR8
             @UnsupportedAppUsage
             boolean disabledRw();
+            @com.android.aconfig.annotations.AconfigFlagAccessor
             @com.android.aconfig.annotations.AssumeFalseForR8
             @UnsupportedAppUsage
             boolean disabledRwInOtherNamespace();
+            @com.android.aconfig.annotations.AconfigFlagAccessor
             @com.android.aconfig.annotations.AssumeTrueForR8
             @UnsupportedAppUsage
             boolean enabledFixedRo();
+            @com.android.aconfig.annotations.AconfigFlagAccessor
             @com.android.aconfig.annotations.AssumeTrueForR8
             @UnsupportedAppUsage
             boolean enabledRo();
+            @com.android.aconfig.annotations.AconfigFlagAccessor
             @com.android.aconfig.annotations.AssumeTrueForR8
             @UnsupportedAppUsage
             boolean enabledRw();
@@ -918,31 +933,37 @@
         import android.compat.annotation.UnsupportedAppUsage;
         /** @hide */
         public final class FeatureFlagsImpl implements FeatureFlags {
+            @com.android.aconfig.annotations.AconfigFlagAccessor
             @Override
             @UnsupportedAppUsage
             public boolean disabledRo() {
                 return false;
             }
+            @com.android.aconfig.annotations.AconfigFlagAccessor
             @Override
             @UnsupportedAppUsage
             public boolean disabledRw() {
                 return false;
             }
+            @com.android.aconfig.annotations.AconfigFlagAccessor
             @Override
             @UnsupportedAppUsage
             public boolean disabledRwInOtherNamespace() {
                 return false;
             }
+            @com.android.aconfig.annotations.AconfigFlagAccessor
             @Override
             @UnsupportedAppUsage
             public boolean enabledFixedRo() {
                 return true;
             }
+            @com.android.aconfig.annotations.AconfigFlagAccessor
             @Override
             @UnsupportedAppUsage
             public boolean enabledRo() {
                 return true;
             }
+            @com.android.aconfig.annotations.AconfigFlagAccessor
             @Override
             @UnsupportedAppUsage
             public boolean enabledRw() {
@@ -969,32 +990,37 @@
             public static final String FLAG_ENABLED_RO = "com.android.aconfig.test.enabled_ro";
             /** @hide */
             public static final String FLAG_ENABLED_RW = "com.android.aconfig.test.enabled_rw";
-
+            @com.android.aconfig.annotations.AconfigFlagAccessor
             @com.android.aconfig.annotations.AssumeFalseForR8
             @UnsupportedAppUsage
             public static boolean disabledRo() {
                 return FEATURE_FLAGS.disabledRo();
             }
+            @com.android.aconfig.annotations.AconfigFlagAccessor
             @com.android.aconfig.annotations.AssumeFalseForR8
             @UnsupportedAppUsage
             public static boolean disabledRw() {
                 return FEATURE_FLAGS.disabledRw();
             }
+            @com.android.aconfig.annotations.AconfigFlagAccessor
             @com.android.aconfig.annotations.AssumeFalseForR8
             @UnsupportedAppUsage
             public static boolean disabledRwInOtherNamespace() {
                 return FEATURE_FLAGS.disabledRwInOtherNamespace();
             }
+            @com.android.aconfig.annotations.AconfigFlagAccessor
             @com.android.aconfig.annotations.AssumeTrueForR8
             @UnsupportedAppUsage
             public static boolean enabledFixedRo() {
                 return FEATURE_FLAGS.enabledFixedRo();
             }
+            @com.android.aconfig.annotations.AconfigFlagAccessor
             @com.android.aconfig.annotations.AssumeTrueForR8
             @UnsupportedAppUsage
             public static boolean enabledRo() {
                 return FEATURE_FLAGS.enabledRo();
             }
+            @com.android.aconfig.annotations.AconfigFlagAccessor
             @com.android.aconfig.annotations.AssumeTrueForR8
             @UnsupportedAppUsage
             public static boolean enabledRw() {
@@ -1065,6 +1091,10 @@
                 }
                 return false;
             }
+            @com.android.aconfig.annotations.AssumeTrueForR8
+            private boolean isOptimizationEnabled() {
+                return false;
+            }
             private boolean getValue(String flagName) {
                 Boolean value = this.mFlagMap.get(flagName);
                 if (value == null) {
@@ -1072,10 +1102,6 @@
                 }
                 return value;
             }
-            @com.android.aconfig.annotations.AssumeTrueForR8
-            private boolean isOptimizationEnabled() {
-                return false;
-            }
             private Map<String, Boolean> mFlagMap = new HashMap<>(
                 Map.ofEntries(
                     Map.entry(Flags.FLAG_DISABLED_RO, false),
diff --git a/tools/aconfig/aconfig/src/commands.rs b/tools/aconfig/aconfig/src/commands.rs
index 59f349b..7736ce7 100644
--- a/tools/aconfig/aconfig/src/commands.rs
+++ b/tools/aconfig/aconfig/src/commands.rs
@@ -31,7 +31,7 @@
     ParsedFlagExt, ProtoFlagMetadata, ProtoFlagPermission, ProtoFlagState, ProtoParsedFlag,
     ProtoParsedFlags, ProtoTracepoint,
 };
-use aconfig_storage_file::StorageFileSelection;
+use aconfig_storage_file::StorageFileType;
 
 pub struct Input {
     pub source: String,
@@ -203,6 +203,11 @@
 }
 
 pub fn create_cpp_lib(mut input: Input, codegen_mode: CodegenMode) -> Result<Vec<OutputFile>> {
+    // TODO(327420679): Enable export mode for native flag library
+    ensure!(
+        codegen_mode != CodegenMode::Exported,
+        "Exported mode for generated c/c++ flag library is disabled"
+    );
     let parsed_flags = input.try_parse_flags()?;
     let modified_parsed_flags = modify_parsed_flags_based_on_mode(parsed_flags, codegen_mode)?;
     let Some(package) = find_unique_package(&modified_parsed_flags) else {
@@ -214,6 +219,11 @@
 }
 
 pub fn create_rust_lib(mut input: Input, codegen_mode: CodegenMode) -> Result<OutputFile> {
+    // // TODO(327420679): Enable export mode for native flag library
+    ensure!(
+        codegen_mode != CodegenMode::Exported,
+        "Exported mode for generated rust flag library is disabled"
+    );
     let parsed_flags = input.try_parse_flags()?;
     let modified_parsed_flags = modify_parsed_flags_based_on_mode(parsed_flags, codegen_mode)?;
     let Some(package) = find_unique_package(&modified_parsed_flags) else {
@@ -227,7 +237,7 @@
 pub fn create_storage(
     caches: Vec<Input>,
     container: &str,
-    file: &StorageFileSelection,
+    file: &StorageFileType,
 ) -> Result<Vec<u8>> {
     let parsed_flags_vec: Vec<ProtoParsedFlags> = caches
         .into_iter()
@@ -307,9 +317,7 @@
 }
 
 fn find_unique_package(parsed_flags: &[ProtoParsedFlag]) -> Option<&str> {
-    let Some(package) = parsed_flags.first().map(|pf| pf.package()) else {
-        return None;
-    };
+    let package = parsed_flags.first().map(|pf| pf.package())?;
     if parsed_flags.iter().any(|pf| pf.package() != package) {
         return None;
     }
@@ -317,9 +325,7 @@
 }
 
 fn find_unique_container(parsed_flags: &ProtoParsedFlags) -> Option<&str> {
-    let Some(container) = parsed_flags.parsed_flag.first().map(|pf| pf.container()) else {
-        return None;
-    };
+    let container = parsed_flags.parsed_flag.first().map(|pf| pf.container())?;
     if parsed_flags.parsed_flag.iter().any(|pf| pf.container() != container) {
         return None;
     }
diff --git a/tools/aconfig/aconfig/src/main.rs b/tools/aconfig/aconfig/src/main.rs
index 5a4f23c..69f5458 100644
--- a/tools/aconfig/aconfig/src/main.rs
+++ b/tools/aconfig/aconfig/src/main.rs
@@ -29,7 +29,7 @@
 mod dump;
 mod storage;
 
-use aconfig_storage_file::StorageFileSelection;
+use aconfig_storage_file::StorageFileType;
 use codegen::CodegenMode;
 use dump::DumpFormat;
 
@@ -138,7 +138,7 @@
                 .arg(
                     Arg::new("file")
                         .long("file")
-                        .value_parser(|s: &str| StorageFileSelection::try_from(s)),
+                        .value_parser(|s: &str| StorageFileType::try_from(s)),
                 )
                 .arg(Arg::new("cache").long("cache").action(ArgAction::Append).required(true))
                 .arg(Arg::new("out").long("out").required(true)),
@@ -285,7 +285,7 @@
             write_output_to_file_or_stdout(path, &output)?;
         }
         Some(("create-storage", sub_matches)) => {
-            let file = get_required_arg::<StorageFileSelection>(sub_matches, "file")
+            let file = get_required_arg::<StorageFileType>(sub_matches, "file")
                 .context("Invalid storage file selection")?;
             let cache = open_zero_or_more_files(sub_matches, "cache")?;
             let container = get_required_arg::<String>(sub_matches, "container")?;
diff --git a/tools/aconfig/aconfig/src/storage/flag_table.rs b/tools/aconfig/aconfig/src/storage/flag_table.rs
index 1381e89..b339821 100644
--- a/tools/aconfig/aconfig/src/storage/flag_table.rs
+++ b/tools/aconfig/aconfig/src/storage/flag_table.rs
@@ -16,8 +16,10 @@
 
 use crate::commands::assign_flag_ids;
 use crate::storage::FlagPackage;
+use aconfig_protos::ProtoFlagPermission;
 use aconfig_storage_file::{
-    get_table_size, FlagTable, FlagTableHeader, FlagTableNode, FILE_VERSION,
+    get_table_size, FlagTable, FlagTableHeader, FlagTableNode, StorageFileType, StoredFlagType,
+    FILE_VERSION,
 };
 use anyhow::{anyhow, Result};
 
@@ -25,6 +27,7 @@
     FlagTableHeader {
         version: FILE_VERSION,
         container: String::from(container),
+        file_type: StorageFileType::FlagMap as u8,
         file_size: 0,
         num_flags,
         bucket_offset: 0,
@@ -44,7 +47,7 @@
     fn new(
         package_id: u32,
         flag_name: &str,
-        flag_type: u16,
+        flag_type: StoredFlagType,
         flag_id: u16,
         num_buckets: u32,
     ) -> Self {
@@ -69,11 +72,14 @@
                 let fid = flag_ids
                     .get(pf.name())
                     .ok_or(anyhow!(format!("missing flag id for {}", pf.name())))?;
-                // all flags are boolean value at the moment, thus using the last bit.
-                // When more flag value types are supported, flag value type information
-                // should come from the parsed flag, and we will set the flag_type bit
-                // mask properly.
-                let flag_type = 1;
+                let flag_type = if pf.is_fixed_read_only() {
+                    StoredFlagType::FixedReadOnlyBoolean
+                } else {
+                    match pf.permission() {
+                        ProtoFlagPermission::READ_WRITE => StoredFlagType::ReadWriteBoolean,
+                        ProtoFlagPermission::READ_ONLY => StoredFlagType::ReadOnlyBoolean,
+                    }
+                };
                 Ok(Self::new(package.package_id, pf.name(), flag_type, *fid, num_buckets))
             })
             .collect::<Result<Vec<_>>>()
@@ -94,10 +100,10 @@
         .concat();
 
     // initialize all header fields
-    header.bucket_offset = header.as_bytes().len() as u32;
+    header.bucket_offset = header.into_bytes().len() as u32;
     header.node_offset = header.bucket_offset + num_buckets * 4;
     header.file_size = header.node_offset
-        + node_wrappers.iter().map(|x| x.node.as_bytes().len()).sum::<usize>() as u32;
+        + node_wrappers.iter().map(|x| x.node.into_bytes().len()).sum::<usize>() as u32;
 
     // sort nodes by bucket index for efficiency
     node_wrappers.sort_by(|a, b| a.bucket_index.cmp(&b.bucket_index));
@@ -115,7 +121,7 @@
         if buckets[node_bucket_idx as usize].is_none() {
             buckets[node_bucket_idx as usize] = Some(offset);
         }
-        offset += node_wrappers[i].node.as_bytes().len() as u32;
+        offset += node_wrappers[i].node.into_bytes().len() as u32;
 
         if let Some(index) = next_node_bucket_idx {
             if index == node_bucket_idx {
@@ -135,78 +141,18 @@
     use super::*;
     use crate::storage::{group_flags_by_package, tests::parse_all_test_flags};
 
-    // create test baseline, syntactic sugar
-    fn new_expected_node(
-        package_id: u32,
-        flag_name: &str,
-        flag_type: u16,
-        flag_id: u16,
-        next_offset: Option<u32>,
-    ) -> FlagTableNode {
-        FlagTableNode {
-            package_id,
-            flag_name: flag_name.to_string(),
-            flag_type,
-            flag_id,
-            next_offset,
-        }
-    }
-
-    fn create_test_flag_table() -> Result<FlagTable> {
+    fn create_test_flag_table_from_source() -> Result<FlagTable> {
         let caches = parse_all_test_flags();
         let packages = group_flags_by_package(caches.iter());
-        create_flag_table("system", &packages)
+        create_flag_table("mockup", &packages)
     }
 
     #[test]
     // this test point locks down the table creation and each field
     fn test_table_contents() {
-        let flag_table = create_test_flag_table();
+        let flag_table = create_test_flag_table_from_source();
         assert!(flag_table.is_ok());
-
-        let header: &FlagTableHeader = &flag_table.as_ref().unwrap().header;
-        let expected_header = FlagTableHeader {
-            version: FILE_VERSION,
-            container: String::from("system"),
-            file_size: 320,
-            num_flags: 8,
-            bucket_offset: 30,
-            node_offset: 98,
-        };
-        assert_eq!(header, &expected_header);
-
-        let buckets: &Vec<Option<u32>> = &flag_table.as_ref().unwrap().buckets;
-        let expected_bucket: Vec<Option<u32>> = vec![
-            Some(98),
-            Some(124),
-            None,
-            None,
-            None,
-            Some(177),
-            None,
-            Some(203),
-            None,
-            Some(261),
-            None,
-            None,
-            None,
-            None,
-            None,
-            Some(293),
-            None,
-        ];
-        assert_eq!(buckets, &expected_bucket);
-
-        let nodes: &Vec<FlagTableNode> = &flag_table.as_ref().unwrap().nodes;
-        assert_eq!(nodes.len(), 8);
-
-        assert_eq!(nodes[0], new_expected_node(0, "enabled_ro", 1, 1, None));
-        assert_eq!(nodes[1], new_expected_node(0, "enabled_rw", 1, 2, Some(150)));
-        assert_eq!(nodes[2], new_expected_node(1, "disabled_ro", 1, 0, None));
-        assert_eq!(nodes[3], new_expected_node(2, "enabled_ro", 1, 1, None));
-        assert_eq!(nodes[4], new_expected_node(1, "enabled_fixed_ro", 1, 1, Some(235)));
-        assert_eq!(nodes[5], new_expected_node(1, "enabled_ro", 1, 2, None));
-        assert_eq!(nodes[6], new_expected_node(2, "enabled_fixed_ro", 1, 0, None));
-        assert_eq!(nodes[7], new_expected_node(0, "disabled_rw", 1, 0, None));
+        let expected_flag_table = aconfig_storage_file::test_utils::create_test_flag_table();
+        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 0d4b5b4..a37ad9f 100644
--- a/tools/aconfig/aconfig/src/storage/flag_value.rs
+++ b/tools/aconfig/aconfig/src/storage/flag_value.rs
@@ -17,13 +17,14 @@
 use crate::commands::assign_flag_ids;
 use crate::storage::FlagPackage;
 use aconfig_protos::ProtoFlagState;
-use aconfig_storage_file::{FlagValueHeader, FlagValueList, FILE_VERSION};
+use aconfig_storage_file::{FlagValueHeader, FlagValueList, StorageFileType, FILE_VERSION};
 use anyhow::{anyhow, Result};
 
 fn new_header(container: &str, num_flags: u32) -> FlagValueHeader {
     FlagValueHeader {
         version: FILE_VERSION,
         container: String::from(container),
+        file_type: StorageFileType::FlagVal as u8,
         file_size: 0,
         num_flags,
         boolean_value_offset: 0,
@@ -52,7 +53,7 @@
     }
 
     // initialize all header fields
-    list.header.boolean_value_offset = list.header.as_bytes().len() as u32;
+    list.header.boolean_value_offset = list.header.into_bytes().len() as u32;
     list.header.file_size = list.header.boolean_value_offset + num_flags;
 
     Ok(list)
@@ -63,30 +64,19 @@
     use super::*;
     use crate::storage::{group_flags_by_package, tests::parse_all_test_flags};
 
-    pub fn create_test_flag_value_list() -> Result<FlagValueList> {
+    pub fn create_test_flag_value_list_from_source() -> Result<FlagValueList> {
         let caches = parse_all_test_flags();
         let packages = group_flags_by_package(caches.iter());
-        create_flag_value("system", &packages)
+        create_flag_value("mockup", &packages)
     }
 
     #[test]
     // this test point locks down the flag value creation and each field
     fn test_list_contents() {
-        let flag_value_list = create_test_flag_value_list();
+        let flag_value_list = create_test_flag_value_list_from_source();
         assert!(flag_value_list.is_ok());
-
-        let header: &FlagValueHeader = &flag_value_list.as_ref().unwrap().header;
-        let expected_header = FlagValueHeader {
-            version: FILE_VERSION,
-            container: String::from("system"),
-            file_size: 34,
-            num_flags: 8,
-            boolean_value_offset: 26,
-        };
-        assert_eq!(header, &expected_header);
-
-        let booleans: &Vec<bool> = &flag_value_list.as_ref().unwrap().booleans;
-        let expected_booleans: Vec<bool> = vec![false; header.num_flags as usize];
-        assert_eq!(booleans, &expected_booleans);
+        let expected_flag_value_list =
+            aconfig_storage_file::test_utils::create_test_flag_value_list();
+        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 29eb9c8..30517de 100644
--- a/tools/aconfig/aconfig/src/storage/mod.rs
+++ b/tools/aconfig/aconfig/src/storage/mod.rs
@@ -18,7 +18,7 @@
 pub mod flag_value;
 pub mod package_table;
 
-use anyhow::Result;
+use anyhow::{anyhow, Result};
 use std::collections::{HashMap, HashSet};
 
 use crate::storage::{
@@ -26,7 +26,7 @@
     package_table::create_package_table,
 };
 use aconfig_protos::{ProtoParsedFlag, ProtoParsedFlags};
-use aconfig_storage_file::StorageFileSelection;
+use aconfig_storage_file::StorageFileType;
 
 pub struct FlagPackage<'a> {
     pub package_name: &'a str,
@@ -87,7 +87,7 @@
 pub fn generate_storage_file<'a, I>(
     container: &str,
     parsed_flags_vec_iter: I,
-    file: &StorageFileSelection,
+    file: &StorageFileType,
 ) -> Result<Vec<u8>>
 where
     I: Iterator<Item = &'a ProtoParsedFlags>,
@@ -95,18 +95,19 @@
     let packages = group_flags_by_package(parsed_flags_vec_iter);
 
     match file {
-        StorageFileSelection::PackageMap => {
+        StorageFileType::PackageMap => {
             let package_table = create_package_table(container, &packages)?;
-            Ok(package_table.as_bytes())
+            Ok(package_table.into_bytes())
         }
-        StorageFileSelection::FlagMap => {
+        StorageFileType::FlagMap => {
             let flag_table = create_flag_table(container, &packages)?;
-            Ok(flag_table.as_bytes())
+            Ok(flag_table.into_bytes())
         }
-        StorageFileSelection::FlagVal => {
+        StorageFileType::FlagVal => {
             let flag_value = create_flag_value(container, &packages)?;
-            Ok(flag_value.as_bytes())
+            Ok(flag_value.into_bytes())
         }
+        _ => Err(anyhow!("aconfig does not support the creation of this storage file type")),
     }
 }
 
@@ -121,30 +122,38 @@
                 "com.android.aconfig.storage.test_1",
                 "storage_test_1.aconfig",
                 include_bytes!("../../tests/storage_test_1.aconfig").as_slice(),
+                "storage_test_1.value",
+                include_bytes!("../../tests/storage_test_1.values").as_slice(),
             ),
             (
                 "com.android.aconfig.storage.test_2",
                 "storage_test_2.aconfig",
                 include_bytes!("../../tests/storage_test_2.aconfig").as_slice(),
+                "storage_test_2.value",
+                include_bytes!("../../tests/storage_test_2.values").as_slice(),
             ),
             (
                 "com.android.aconfig.storage.test_4",
                 "storage_test_4.aconfig",
                 include_bytes!("../../tests/storage_test_4.aconfig").as_slice(),
+                "storage_test_4.value",
+                include_bytes!("../../tests/storage_test_4.values").as_slice(),
             ),
         ];
-
         aconfig_files
             .into_iter()
-            .map(|(pkg, file, content)| {
+            .map(|(pkg, aconfig_file, aconfig_content, value_file, value_content)| {
                 let bytes = crate::commands::parse_flags(
                     pkg,
                     Some("system"),
                     vec![Input {
-                        source: format!("tests/{}", file).to_string(),
-                        reader: Box::new(content),
+                        source: format!("tests/{}", aconfig_file).to_string(),
+                        reader: Box::new(aconfig_content),
                     }],
-                    vec![],
+                    vec![Input {
+                        source: format!("tests/{}", value_file).to_string(),
+                        reader: Box::new(value_content),
+                    }],
                     crate::commands::DEFAULT_FLAG_PERMISSION,
                 )
                 .unwrap();
diff --git a/tools/aconfig/aconfig/src/storage/package_table.rs b/tools/aconfig/aconfig/src/storage/package_table.rs
index 4c08129..0a3df77 100644
--- a/tools/aconfig/aconfig/src/storage/package_table.rs
+++ b/tools/aconfig/aconfig/src/storage/package_table.rs
@@ -17,7 +17,8 @@
 use anyhow::Result;
 
 use aconfig_storage_file::{
-    get_table_size, PackageTable, PackageTableHeader, PackageTableNode, FILE_VERSION,
+    get_table_size, PackageTable, PackageTableHeader, PackageTableNode, StorageFileType,
+    FILE_VERSION,
 };
 
 use crate::storage::FlagPackage;
@@ -26,6 +27,7 @@
     PackageTableHeader {
         version: FILE_VERSION,
         container: String::from(container),
+        file_type: StorageFileType::PackageMap as u8,
         file_size: 0,
         num_packages,
         bucket_offset: 0,
@@ -64,10 +66,10 @@
         packages.iter().map(|pkg| PackageTableNodeWrapper::new(pkg, num_buckets)).collect();
 
     // initialize all header fields
-    header.bucket_offset = header.as_bytes().len() as u32;
+    header.bucket_offset = header.into_bytes().len() as u32;
     header.node_offset = header.bucket_offset + num_buckets * 4;
     header.file_size = header.node_offset
-        + node_wrappers.iter().map(|x| x.node.as_bytes().len()).sum::<usize>() as u32;
+        + node_wrappers.iter().map(|x| x.node.into_bytes().len()).sum::<usize>() as u32;
 
     // sort node_wrappers by bucket index for efficiency
     node_wrappers.sort_by(|a, b| a.bucket_index.cmp(&b.bucket_index));
@@ -85,7 +87,7 @@
         if buckets[node_bucket_idx as usize].is_none() {
             buckets[node_bucket_idx as usize] = Some(offset);
         }
-        offset += node_wrappers[i].node.as_bytes().len() as u32;
+        offset += node_wrappers[i].node.into_bytes().len() as u32;
 
         if let Some(index) = next_node_bucket_idx {
             if index == node_bucket_idx {
@@ -107,55 +109,18 @@
     use super::*;
     use crate::storage::{group_flags_by_package, tests::parse_all_test_flags};
 
-    pub fn create_test_package_table() -> Result<PackageTable> {
+    pub fn create_test_package_table_from_source() -> Result<PackageTable> {
         let caches = parse_all_test_flags();
         let packages = group_flags_by_package(caches.iter());
-        create_package_table("system", &packages)
+        create_package_table("mockup", &packages)
     }
 
     #[test]
     // this test point locks down the table creation and each field
     fn test_table_contents() {
-        let package_table = create_test_package_table();
+        let package_table = create_test_package_table_from_source();
         assert!(package_table.is_ok());
-
-        let header: &PackageTableHeader = &package_table.as_ref().unwrap().header;
-        let expected_header = PackageTableHeader {
-            version: FILE_VERSION,
-            container: String::from("system"),
-            file_size: 208,
-            num_packages: 3,
-            bucket_offset: 30,
-            node_offset: 58,
-        };
-        assert_eq!(header, &expected_header);
-
-        let buckets: &Vec<Option<u32>> = &package_table.as_ref().unwrap().buckets;
-        let expected: Vec<Option<u32>> = vec![Some(58), None, None, Some(108), None, None, None];
-        assert_eq!(buckets, &expected);
-
-        let nodes: &Vec<PackageTableNode> = &package_table.as_ref().unwrap().nodes;
-        assert_eq!(nodes.len(), 3);
-        let first_node_expected = PackageTableNode {
-            package_name: String::from("com.android.aconfig.storage.test_2"),
-            package_id: 1,
-            boolean_offset: 3,
-            next_offset: None,
-        };
-        assert_eq!(nodes[0], first_node_expected);
-        let second_node_expected = PackageTableNode {
-            package_name: String::from("com.android.aconfig.storage.test_1"),
-            package_id: 0,
-            boolean_offset: 0,
-            next_offset: Some(158),
-        };
-        assert_eq!(nodes[1], second_node_expected);
-        let third_node_expected = PackageTableNode {
-            package_name: String::from("com.android.aconfig.storage.test_4"),
-            package_id: 2,
-            boolean_offset: 6,
-            next_offset: None,
-        };
-        assert_eq!(nodes[2], third_node_expected);
+        let expected_package_table = aconfig_storage_file::test_utils::create_test_package_table();
+        assert_eq!(package_table.unwrap(), expected_package_table);
     }
 }
diff --git a/tools/aconfig/aconfig/templates/FakeFeatureFlagsImpl.java.template b/tools/aconfig/aconfig/templates/FakeFeatureFlagsImpl.java.template
index 28dddd8..177e711 100644
--- a/tools/aconfig/aconfig/templates/FakeFeatureFlagsImpl.java.template
+++ b/tools/aconfig/aconfig/templates/FakeFeatureFlagsImpl.java.template
@@ -1,7 +1,8 @@
 package {package_name};
+{{ if not library_exported- }}
 // TODO(b/303773055): Remove the annotation after access issue is resolved.
 import android.compat.annotation.UnsupportedAppUsage;
-
+{{ -endif }}
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -16,7 +17,7 @@
 
 {{ for item in flag_elements}}
     @Override
-    @UnsupportedAppUsage
+{{ if not library_exported }}    @UnsupportedAppUsage{{ -endif }}
     public boolean {item.method_name}() \{
         return getValue(Flags.FLAG_{item.flag_name_constant_suffix});
     }
@@ -33,7 +34,7 @@
             entry.setValue(null);
         }
     }
-
+{{ if not library_exported }}
     public boolean isFlagReadOnlyOptimized(String flagName) \{
         if (mReadOnlyFlagsSet.contains(flagName) &&
             isOptimizationEnabled()) \{
@@ -42,6 +43,11 @@
         return false;
     }
 
+    @com.android.aconfig.annotations.AssumeTrueForR8
+    private boolean isOptimizationEnabled() \{
+        return false;
+    }
+{{ -endif }}
     private boolean getValue(String flagName) \{
         Boolean value = this.mFlagMap.get(flagName);
         if (value == null) \{
@@ -50,10 +56,6 @@
         return value;
     }
 
-    @com.android.aconfig.annotations.AssumeTrueForR8
-    private boolean isOptimizationEnabled() \{
-        return false;
-    }
 
     private Map<String, Boolean> mFlagMap = new HashMap<>(
         Map.ofEntries(
diff --git a/tools/aconfig/aconfig/templates/FeatureFlags.java.template b/tools/aconfig/aconfig/templates/FeatureFlags.java.template
index 5e67b13..b90b201 100644
--- a/tools/aconfig/aconfig/templates/FeatureFlags.java.template
+++ b/tools/aconfig/aconfig/templates/FeatureFlags.java.template
@@ -1,23 +1,22 @@
 package {package_name};
+{{ if not library_exported- }}
 // TODO(b/303773055): Remove the annotation after access issue is resolved.
 import android.compat.annotation.UnsupportedAppUsage;
-
+{{ -endif }}
 /** @hide */
 public interface FeatureFlags \{
 {{ for item in flag_elements }}
-{{ -if library_exported }}
-    @UnsupportedAppUsage
-    boolean {item.method_name}();
-{{ -else }}
+    @com.android.aconfig.annotations.AconfigFlagAccessor
 {{ -if not item.is_read_write }}
 {{ -if item.default_value }}
     @com.android.aconfig.annotations.AssumeTrueForR8
 {{ -else }}
     @com.android.aconfig.annotations.AssumeFalseForR8
 {{ -endif- }}
-{{ endif }}
+{{ -endif }}
+{{ -if not library_exported }}
     @UnsupportedAppUsage
+{{ -endif }}
     boolean {item.method_name}();
-{{ endif }}
 {{ -endfor }}
-}
+}
\ No newline at end of file
diff --git a/tools/aconfig/aconfig/templates/FeatureFlagsImpl.java.template b/tools/aconfig/aconfig/templates/FeatureFlagsImpl.java.template
index 28baa41..704f25b 100644
--- a/tools/aconfig/aconfig/templates/FeatureFlagsImpl.java.template
+++ b/tools/aconfig/aconfig/templates/FeatureFlagsImpl.java.template
@@ -1,6 +1,8 @@
 package {package_name};
+{{ if not library_exported- }}
 // TODO(b/303773055): Remove the annotation after access issue is resolved.
 import android.compat.annotation.UnsupportedAppUsage;
+{{ -endif }}
 {{ -if not is_test_mode }}
 {{ -if runtime_lookup_required }}
 import android.provider.DeviceConfig;
@@ -14,12 +16,8 @@
 {{ -endfor- }}
 
 {{ for flag in flag_elements }}
-{{ -if library_exported }}
-    private static boolean {flag.method_name} = false;
-{{ -else }}
 {{- if flag.is_read_write }}
     private static boolean {flag.method_name} = {flag.default_value};
-{{- endif- }}
 {{ -endif }}
 {{ -endfor }}
 {{ for namespace_with_flags in namespace_flags }}
@@ -27,15 +25,10 @@
         try \{
             Properties properties = DeviceConfig.getProperties("{namespace_with_flags.namespace}");
 {{ -for flag in namespace_with_flags.flags }}
-{{ -if library_exported }}
-            {flag.method_name} =
-                properties.getBoolean("{flag.device_config_flag}", false);
-{{ -else }}
 {{ -if flag.is_read_write }}
             {flag.method_name} =
                 properties.getBoolean("{flag.device_config_flag}", {flag.default_value});
 {{ -endif }}
-{{ -endif }}
 {{ -endfor }}
         } catch (NullPointerException e) \{
             throw new RuntimeException(
@@ -52,15 +45,12 @@
 {{ endfor- }}
 {{ -endif }}{#- end of runtime_lookup_required #}
 {{ -for flag in flag_elements }}
+    @com.android.aconfig.annotations.AconfigFlagAccessor
     @Override
+{{ -if not library_exported }}
     @UnsupportedAppUsage
+{{ -endif }}
     public boolean {flag.method_name}() \{
-{{ -if library_exported }}
-        if (!{flag.device_config_namespace}_is_cached) \{
-            load_overrides_{flag.device_config_namespace}();
-        }
-        return {flag.method_name};
-{{ -else }}
 {{ -if flag.is_read_write }}
         if (!{flag.device_config_namespace}_is_cached) \{
             load_overrides_{flag.device_config_namespace}();
@@ -68,7 +58,6 @@
         return {flag.method_name};
 {{ -else }}
         return {flag.default_value};
-{{ -endif- }}
 {{ -endif }}
     }
 {{ endfor }}
@@ -78,8 +67,11 @@
 /** @hide */
 public final class FeatureFlagsImpl implements FeatureFlags \{
 {{ for flag in flag_elements }}
+    @com.android.aconfig.annotations.AconfigFlagAccessor
     @Override
+{{ -if not library_exported }}
     @UnsupportedAppUsage
+{{ -endif }}
     public boolean {flag.method_name}() \{
         throw new UnsupportedOperationException(
             "Method is not implemented.");
diff --git a/tools/aconfig/aconfig/templates/Flags.java.template b/tools/aconfig/aconfig/templates/Flags.java.template
index 34b8189..55db924 100644
--- a/tools/aconfig/aconfig/templates/Flags.java.template
+++ b/tools/aconfig/aconfig/templates/Flags.java.template
@@ -1,8 +1,8 @@
 package {package_name};
-
+{{ if not library_exported- }}
 // TODO(b/303773055): Remove the annotation after access issue is resolved.
 import android.compat.annotation.UnsupportedAppUsage;
-
+{{ -endif }}
 /** @hide */
 public final class Flags \{
 {{ -for item in flag_elements}}
@@ -10,12 +10,7 @@
     public static final String FLAG_{item.flag_name_constant_suffix} = "{item.device_config_flag}";
 {{- endfor }}
 {{ -for item in flag_elements}}
-{{ if library_exported }}
-    @UnsupportedAppUsage
-    public static boolean {item.method_name}() \{
-        return FEATURE_FLAGS.{item.method_name}();
-    }
-{{ -else }}
+    @com.android.aconfig.annotations.AconfigFlagAccessor
 {{ -if not item.is_read_write }}
 {{ -if item.default_value }}
     @com.android.aconfig.annotations.AssumeTrueForR8
@@ -23,11 +18,12 @@
     @com.android.aconfig.annotations.AssumeFalseForR8
 {{ -endif }}
 {{ -endif }}
+{{ -if not library_exported }}
     @UnsupportedAppUsage
+{{ -endif }}
     public static boolean {item.method_name}() \{
         return FEATURE_FLAGS.{item.method_name}();
     }
-{{ -endif }}
 {{ -endfor }}
 {{ -if is_test_mode }}
     public static void setFeatureFlags(FeatureFlags featureFlags) \{
diff --git a/tools/aconfig/aconfig/tests/storage_test_1.values b/tools/aconfig/aconfig/tests/storage_test_1.values
new file mode 100644
index 0000000..35548ae
--- /dev/null
+++ b/tools/aconfig/aconfig/tests/storage_test_1.values
@@ -0,0 +1,18 @@
+flag_value {
+    package: "com.android.aconfig.storage.test_1"
+    name: "enabled_rw"
+    state: ENABLED
+    permission: READ_WRITE
+}
+flag_value {
+    package: "com.android.aconfig.storage.test_1"
+    name: "disabled_rw"
+    state: DISABLED
+    permission: READ_WRITE
+}
+flag_value {
+    package: "com.android.aconfig.storage.test_1"
+    name: "enabled_ro"
+    state: ENABLED
+    permission: READ_ONLY
+}
diff --git a/tools/aconfig/aconfig/tests/storage_test_2.values b/tools/aconfig/aconfig/tests/storage_test_2.values
new file mode 100644
index 0000000..a7bb0b1
--- /dev/null
+++ b/tools/aconfig/aconfig/tests/storage_test_2.values
@@ -0,0 +1,18 @@
+flag_value {
+    package: "com.android.aconfig.storage.test_2"
+    name: "enabled_ro"
+    state: ENABLED
+    permission: READ_ONLY
+}
+flag_value {
+    package: "com.android.aconfig.storage.test_2"
+    name: "disabled_ro"
+    state: DISABLED
+    permission: READ_ONLY
+}
+flag_value {
+    package: "com.android.aconfig.storage.test_2"
+    name: "enabled_fixed_ro"
+    state: ENABLED
+    permission: READ_ONLY
+}
diff --git a/tools/aconfig/aconfig/tests/storage_test_4.values b/tools/aconfig/aconfig/tests/storage_test_4.values
new file mode 100644
index 0000000..fa21317
--- /dev/null
+++ b/tools/aconfig/aconfig/tests/storage_test_4.values
@@ -0,0 +1,12 @@
+flag_value {
+    package: "com.android.aconfig.storage.test_4"
+    name: "enabled_ro"
+    state: ENABLED
+    permission: READ_ONLY
+}
+flag_value {
+    package: "com.android.aconfig.storage.test_4"
+    name: "enabled_fixed_ro"
+    state: ENABLED
+    permission: READ_ONLY
+}
diff --git a/tools/aconfig/aconfig_protos/protos/aconfig.proto b/tools/aconfig/aconfig_protos/protos/aconfig.proto
index 8833722..9d1b8cb 100644
--- a/tools/aconfig/aconfig_protos/protos/aconfig.proto
+++ b/tools/aconfig/aconfig_protos/protos/aconfig.proto
@@ -22,16 +22,38 @@
 
 // This protobuf file defines messages used to represent and manage flags in the "aconfig" system
 // The following format requirements apply across various message fields:
-// # name: a lowercase string in snake_case format, no consecutive underscores, and no leading digit
-//    For example adjust_rate is a valid name, while AdjustRate, adjust__rate, and
-//    2adjust_rate are invalid
 //
-// # namespace: a lowercase string in snake_case format, no consecutive underscores, and no leading
-//    digit. For example android_bar_system
+// # name: name of the flag
 //
-// # package: lowercase strings in snake_case format, delimited by dots, no consecutive underscores
-//    and no leading digit in each string. For example com.android.mypackage is a valid name
-//    while com.android.myPackage, com.android.1mypackage are invalid
+//    format: a lowercase string in snake_case format, no consecutive underscores, and no leading
+//      digit. For example adjust_rate is a valid name, while AdjustRate, adjust__rate, and
+//      adjust_rate are invalid
+//
+// # namespace: namespace the flag belongs to
+//
+//    format: a lowercase string in snake_case format, no consecutive underscores, and no leading
+//      digit. For example android_bar_system
+//
+// # package: package to which the flag belongs
+//
+//    format: lowercase strings in snake_case format, delimited by dots, no consecutive underscores
+//      and no leading digit in each string. For example com.android.mypackage is a valid name
+//      while com.android.myPackage, com.android.1mypackage are invalid
+//
+// # container: container as software built in its entirety using the same build environment and
+//    always installed as a single unit
+//
+//    For example the following are all separate containers:
+//        * the system partition
+//        * the vendor partition
+//        * apexes: each APEX is its own container
+//        * APKs: for APKs which are released independently via Play, each APK is its own container.
+//            If an APK is released as part of a Mainline module, or as part of the system partition
+//            via OTA, then they are part of the apex or the system partition container
+//
+//    format: lowercase strings in snake_case format, delimited by dots if multiple, no consecutive
+//      underscores or leading digits in each string. The recommended container values are the
+//      partition names or the module names
 
 // messages used in both aconfig input and output
 
@@ -98,6 +120,7 @@
   repeated flag_declaration flag = 2;
 
   // Container the flag belongs to (optional)
+  // See # container for format detail
   optional string container = 3;
 };
 
@@ -160,6 +183,7 @@
   optional bool is_exported = 10;
 
   // Container the flag belongs to (optional)
+  // See # container for format detail
   optional string container = 11;
 
   // Additional information about the flag, including its purpose and form factors (optional)
diff --git a/tools/aconfig/aconfig_storage_file/Android.bp b/tools/aconfig/aconfig_storage_file/Android.bp
index 8922ba4..b590312 100644
--- a/tools/aconfig/aconfig_storage_file/Android.bp
+++ b/tools/aconfig/aconfig_storage_file/Android.bp
@@ -6,16 +6,13 @@
     name: "aconfig_storage_file.defaults",
     edition: "2021",
     lints: "none",
-    srcs: ["src/lib.rs"],
     rustlibs: [
         "libanyhow",
-        "libaconfig_storage_protos",
-        "libonce_cell",
-        "libprotobuf",
-        "libtempfile",
-        "libmemmap2",
-        "libcxx",
         "libthiserror",
+        "libtempfile",
+        "libprotobuf",
+        "libclap",
+        "libaconfig_storage_protos",
     ],
 }
 
@@ -24,38 +21,26 @@
     crate_name: "aconfig_storage_file",
     host_supported: true,
     defaults: ["aconfig_storage_file.defaults"],
+    srcs: ["src/lib.rs"],
+    apex_available: [
+        "//apex_available:platform",
+        "//apex_available:anyapex",
+    ],
+    min_sdk_version: "29",
 }
 
-genrule {
-    name: "ro.package.map",
-    out: ["tests/tmp.ro.package.map"],
-    srcs: ["tests/package.map"],
-    cmd: "rm -f $(out);cp -f $(in) $(out);chmod -w $(out)",
-}
-
-genrule {
-    name: "ro.flag.map",
-    out: ["tests/tmp.ro.flag.map"],
-    srcs: ["tests/flag.map"],
-    cmd: "rm -f $(out);cp -f $(in) $(out);chmod -w $(out)",
-}
-
-genrule {
-    name: "ro.flag.val",
-    out: ["tests/tmp.ro.flag.val"],
-    srcs: ["tests/flag.val"],
-    cmd: "rm -f $(out);cp -f $(in) $(out);chmod -w $(out)",
+rust_binary_host {
+    name: "aconfig-storage",
+    defaults: ["aconfig_storage_file.defaults"],
+    srcs: ["src/main.rs"],
+    rustlibs: ["libaconfig_storage_file"],
 }
 
 rust_test_host {
     name: "aconfig_storage_file.test",
     test_suites: ["general-tests"],
     defaults: ["aconfig_storage_file.defaults"],
-    data: [
-        "tests/package.map",
-        "tests/flag.map",
-        "tests/flag.val",
-    ],
+    srcs: ["src/lib.rs"],
 }
 
 rust_protobuf {
@@ -64,6 +49,11 @@
     crate_name: "aconfig_storage_protos",
     source_stem: "aconfig_storage_protos",
     host_supported: true,
+    apex_available: [
+        "//apex_available:platform",
+        "//apex_available:anyapex",
+    ],
+    min_sdk_version: "29",
 }
 
 cc_library_static {
@@ -79,38 +69,3 @@
     ],
     host_supported: true,
 }
-
-genrule {
-    name: "libcxx_aconfig_storage_bridge_code",
-    tools: ["cxxbridge"],
-    cmd: "$(location cxxbridge) $(in) > $(out)",
-    srcs: ["src/lib.rs"],
-    out: ["aconfig_storage/lib.rs.cc"],
-}
-
-genrule {
-    name: "libcxx_aconfig_storage_bridge_header",
-    tools: ["cxxbridge"],
-    cmd: "$(location cxxbridge) $(in) --header > $(out)",
-    srcs: ["src/lib.rs"],
-    out: ["aconfig_storage/lib.rs.h"],
-}
-
-rust_ffi_static {
-    name: "libaconfig_storage_cxx_bridge",
-    crate_name: "aconfig_storage_cxx_bridge",
-    host_supported: true,
-    defaults: ["aconfig_storage_file.defaults"],
-}
-
-cc_library_static {
-    name: "libaconfig_storage_cc",
-    srcs: ["aconfig_storage.cpp"],
-    generated_headers: [
-        "cxx-bridge-header",
-        "libcxx_aconfig_storage_bridge_header"
-    ],
-    generated_sources: ["libcxx_aconfig_storage_bridge_code"],
-    whole_static_libs: ["libaconfig_storage_cxx_bridge"],
-    export_include_dirs: ["include"],
-}
diff --git a/tools/aconfig/aconfig_storage_file/Cargo.toml b/tools/aconfig/aconfig_storage_file/Cargo.toml
index c4e2670..641f481 100644
--- a/tools/aconfig/aconfig_storage_file/Cargo.toml
+++ b/tools/aconfig/aconfig_storage_file/Cargo.toml
@@ -9,12 +9,14 @@
 
 [dependencies]
 anyhow = "1.0.69"
-memmap2 = "0.8.0"
 protobuf = "3.2.0"
-once_cell = "1.19.0"
 tempfile = "3.9.0"
-cxx = "1.0"
 thiserror = "1.0.56"
+clap = { version = "4.1.8", features = ["derive"] }
+
+[[bin]]
+name = "aconfig-storage"
+path = "src/main.rs"
 
 [build-dependencies]
 protobuf-codegen = "3.2.0"
diff --git a/tools/aconfig/aconfig_storage_file/aconfig_storage.cpp b/tools/aconfig/aconfig_storage_file/aconfig_storage.cpp
deleted file mode 100644
index ac64093..0000000
--- a/tools/aconfig/aconfig_storage_file/aconfig_storage.cpp
+++ /dev/null
@@ -1,106 +0,0 @@
-#include "aconfig_storage/aconfig_storage.hpp"
-
-#include "rust/cxx.h"
-#include "aconfig_storage/lib.rs.h"
-
-namespace aconfig_storage {
-
-/// Get package offset
-PackageOffsetQuery get_package_offset(
-    std::string const& container,
-    std::string const& package) {
-  auto offset_cxx =  get_package_offset_cxx(
-      rust::Str(container.c_str()),
-      rust::Str(package.c_str()));
-  auto offset = PackageOffsetQuery();
-  offset.query_success = offset_cxx.query_success;
-  offset.error_message = std::string(offset_cxx.error_message.c_str());
-  offset.package_exists = offset_cxx.package_exists;
-  offset.package_id = offset_cxx.package_id;
-  offset.boolean_offset = offset_cxx.boolean_offset;
-  return offset;
-}
-
-/// Get flag offset
-FlagOffsetQuery get_flag_offset(
-    std::string const& container,
-    uint32_t package_id,
-    std::string const& flag_name) {
-  auto offset_cxx =  get_flag_offset_cxx(
-      rust::Str(container.c_str()),
-      package_id,
-      rust::Str(flag_name.c_str()));
-  auto offset = FlagOffsetQuery();
-  offset.query_success = offset_cxx.query_success;
-  offset.error_message = std::string(offset_cxx.error_message.c_str());
-  offset.flag_exists = offset_cxx.flag_exists;
-  offset.flag_offset = offset_cxx.flag_offset;
-  return offset;
-}
-
-/// Get boolean flag value
-BooleanFlagValueQuery get_boolean_flag_value(
-    std::string const& container,
-    uint32_t offset) {
-  auto value_cxx =  get_boolean_flag_value_cxx(
-      rust::Str(container.c_str()),
-      offset);
-  auto value = BooleanFlagValueQuery();
-  value.query_success = value_cxx.query_success;
-  value.error_message = std::string(value_cxx.error_message.c_str());
-  value.flag_value = value_cxx.flag_value;
-  return value;
-}
-
-namespace test_only_api {
-PackageOffsetQuery get_package_offset_impl(
-    std::string const& pb_file,
-    std::string const& container,
-    std::string const& package) {
-  auto offset_cxx =  get_package_offset_cxx_impl(
-      rust::Str(pb_file.c_str()),
-      rust::Str(container.c_str()),
-      rust::Str(package.c_str()));
-  auto offset = PackageOffsetQuery();
-  offset.query_success = offset_cxx.query_success;
-  offset.error_message = std::string(offset_cxx.error_message.c_str());
-  offset.package_exists = offset_cxx.package_exists;
-  offset.package_id = offset_cxx.package_id;
-  offset.boolean_offset = offset_cxx.boolean_offset;
-  return offset;
-}
-
-FlagOffsetQuery get_flag_offset_impl(
-    std::string const& pb_file,
-    std::string const& container,
-    uint32_t package_id,
-    std::string const& flag_name) {
-  auto offset_cxx =  get_flag_offset_cxx_impl(
-      rust::Str(pb_file.c_str()),
-      rust::Str(container.c_str()),
-      package_id,
-      rust::Str(flag_name.c_str()));
-  auto offset = FlagOffsetQuery();
-  offset.query_success = offset_cxx.query_success;
-  offset.error_message = std::string(offset_cxx.error_message.c_str());
-  offset.flag_exists = offset_cxx.flag_exists;
-  offset.flag_offset = offset_cxx.flag_offset;
-  return offset;
-}
-
-BooleanFlagValueQuery get_boolean_flag_value_impl(
-    std::string const& pb_file,
-    std::string const& container,
-    uint32_t offset) {
-  auto value_cxx =  get_boolean_flag_value_cxx_impl(
-      rust::Str(pb_file.c_str()),
-      rust::Str(container.c_str()),
-      offset);
-  auto value = BooleanFlagValueQuery();
-  value.query_success = value_cxx.query_success;
-  value.error_message = std::string(value_cxx.error_message.c_str());
-  value.flag_value = value_cxx.flag_value;
-  return value;
-}
-} // namespace test_only_api
-} // namespace aconfig_storage
diff --git a/tools/aconfig/aconfig_storage_file/build.rs b/tools/aconfig/aconfig_storage_file/build.rs
index 894b71c..1feeb60 100644
--- a/tools/aconfig/aconfig_storage_file/build.rs
+++ b/tools/aconfig/aconfig_storage_file/build.rs
@@ -14,7 +14,4 @@
         .inputs(proto_files)
         .cargo_out_dir("aconfig_storage_protos")
         .run_from_script();
-
-    let _ = cxx_build::bridge("src/lib.rs");
-    println!("cargo:rerun-if-changed=src/lib.rs");
 }
diff --git a/tools/aconfig/aconfig_storage_file/include/aconfig_storage/aconfig_storage.hpp b/tools/aconfig/aconfig_storage_file/include/aconfig_storage/aconfig_storage.hpp
deleted file mode 100644
index 636fb7e..0000000
--- a/tools/aconfig/aconfig_storage_file/include/aconfig_storage/aconfig_storage.hpp
+++ /dev/null
@@ -1,76 +0,0 @@
-#pragma once
-
-#include <stdint.h>
-#include <string>
-
-namespace aconfig_storage {
-
-/// Package offset query result
-struct PackageOffsetQuery {
-  bool query_success;
-  std::string error_message;
-  bool package_exists;
-  uint32_t package_id;
-  uint32_t boolean_offset;
-};
-
-/// Flag offset query result
-struct FlagOffsetQuery {
-  bool query_success;
-  std::string error_message;
-  bool flag_exists;
-  uint16_t flag_offset;
-};
-
-/// Boolean flag value query result
-struct BooleanFlagValueQuery {
-  bool query_success;
-  std::string error_message;
-  bool flag_value;
-};
-
-/// Get package offset
-/// \input container: the flag container name
-/// \input package: the flag package name
-/// \returns a PackageOffsetQuery
-PackageOffsetQuery get_package_offset(
-    std::string const& container,
-    std::string const& package);
-
-/// Get flag offset
-/// \input container: the flag container name
-/// \input package_id: the flag package id obtained from package offset query
-/// \input flag_name: flag name
-/// \returns a FlagOffsetQuery
-FlagOffsetQuery get_flag_offset(
-    std::string const& container,
-    uint32_t package_id,
-    std::string const& flag_name);
-
-/// Get boolean flag value
-/// \input container: the flag container name
-/// \input offset: the boolean flag value byte offset in the file
-/// \returns a BooleanFlagValueQuery
-BooleanFlagValueQuery get_boolean_flag_value(
-    std::string const& container,
-    uint32_t offset);
-
-/// DO NOT USE APIS IN THE FOLLOWING NAMESPACE, TEST ONLY
-namespace test_only_api {
-PackageOffsetQuery get_package_offset_impl(
-    std::string const& pb_file,
-    std::string const& container,
-    std::string const& package);
-
-FlagOffsetQuery get_flag_offset_impl(
-    std::string const& pb_file,
-    std::string const& container,
-    uint32_t package_id,
-    std::string const& flag_name);
-
-BooleanFlagValueQuery get_boolean_flag_value_impl(
-    std::string const& pb_file,
-    std::string const& container,
-    uint32_t offset);
-} // namespace test_only_api
-} // namespace aconfig_storage
diff --git a/tools/aconfig/aconfig_storage_file/src/flag_info.rs b/tools/aconfig/aconfig_storage_file/src/flag_info.rs
new file mode 100644
index 0000000..3fff263
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_file/src/flag_info.rs
@@ -0,0 +1,243 @@
+/*
+ * 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.
+ */
+
+//! flag info module defines the flag info file format and methods for serialization
+//! and deserialization
+
+use crate::{read_str_from_bytes, read_u32_from_bytes, read_u8_from_bytes};
+use crate::{AconfigStorageError, StorageFileType};
+use anyhow::anyhow;
+use std::fmt;
+
+/// Flag info header struct
+#[derive(PartialEq)]
+pub struct FlagInfoHeader {
+    pub version: u32,
+    pub container: String,
+    pub file_type: u8,
+    pub file_size: u32,
+    pub num_flags: u32,
+    pub boolean_flag_offset: u32,
+}
+
+/// Implement debug print trait for header
+impl fmt::Debug for FlagInfoHeader {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        writeln!(
+            f,
+            "Version: {}, Container: {}, File Type: {:?}, File Size: {}",
+            self.version,
+            self.container,
+            StorageFileType::try_from(self.file_type),
+            self.file_size
+        )?;
+        writeln!(
+            f,
+            "Num of Flags: {}, Boolean Flag Offset:{}",
+            self.num_flags, self.boolean_flag_offset
+        )?;
+        Ok(())
+    }
+}
+
+impl FlagInfoHeader {
+    /// Serialize to bytes
+    pub fn into_bytes(&self) -> Vec<u8> {
+        let mut result = Vec::new();
+        result.extend_from_slice(&self.version.to_le_bytes());
+        let container_bytes = self.container.as_bytes();
+        result.extend_from_slice(&(container_bytes.len() as u32).to_le_bytes());
+        result.extend_from_slice(container_bytes);
+        result.extend_from_slice(&self.file_type.to_le_bytes());
+        result.extend_from_slice(&self.file_size.to_le_bytes());
+        result.extend_from_slice(&self.num_flags.to_le_bytes());
+        result.extend_from_slice(&self.boolean_flag_offset.to_le_bytes());
+        result
+    }
+
+    /// Deserialize from bytes
+    pub fn from_bytes(bytes: &[u8]) -> Result<Self, AconfigStorageError> {
+        let mut head = 0;
+        let list = Self {
+            version: read_u32_from_bytes(bytes, &mut head)?,
+            container: read_str_from_bytes(bytes, &mut head)?,
+            file_type: read_u8_from_bytes(bytes, &mut head)?,
+            file_size: read_u32_from_bytes(bytes, &mut head)?,
+            num_flags: read_u32_from_bytes(bytes, &mut head)?,
+            boolean_flag_offset: read_u32_from_bytes(bytes, &mut head)?,
+        };
+        if list.file_type != StorageFileType::FlagInfo as u8 {
+            return Err(AconfigStorageError::BytesParseFail(anyhow!(
+                "binary file is not a flag info file"
+            )));
+        }
+        Ok(list)
+    }
+}
+
+/// bit field for flag info
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub enum FlagInfoBit {
+    IsSticky = 0,
+    IsReadWrite = 1,
+    HasOverride = 2,
+}
+
+/// Flag info node struct
+#[derive(PartialEq, Clone)]
+pub struct FlagInfoNode {
+    pub attributes: u8,
+}
+
+/// Implement debug print trait for node
+impl fmt::Debug for FlagInfoNode {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        writeln!(
+            f,
+            "sticky: {}, readwrite: {}, override: {}",
+            self.attributes & (FlagInfoBit::IsSticky as u8),
+            self.attributes & (FlagInfoBit::IsReadWrite as u8),
+            self.attributes & (FlagInfoBit::HasOverride as u8),
+        )?;
+        Ok(())
+    }
+}
+
+impl FlagInfoNode {
+    /// Serialize to bytes
+    pub fn into_bytes(&self) -> Vec<u8> {
+        let mut result = Vec::new();
+        result.extend_from_slice(&self.attributes.to_le_bytes());
+        result
+    }
+
+    /// Deserialize from bytes
+    pub fn from_bytes(bytes: &[u8]) -> Result<Self, AconfigStorageError> {
+        let mut head = 0;
+        let node = Self { attributes: read_u8_from_bytes(bytes, &mut head)? };
+        Ok(node)
+    }
+
+    /// Create flag info node
+    pub fn create(is_flag_rw: bool) -> Self {
+        Self { attributes: if is_flag_rw { FlagInfoBit::IsReadWrite as u8 } else { 0u8 } }
+    }
+}
+
+/// Flag info list struct
+#[derive(PartialEq)]
+pub struct FlagInfoList {
+    pub header: FlagInfoHeader,
+    pub nodes: Vec<FlagInfoNode>,
+}
+
+/// Implement debug print trait for flag info list
+impl fmt::Debug for FlagInfoList {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        writeln!(f, "Header:")?;
+        write!(f, "{:?}", self.header)?;
+        writeln!(f, "Nodes:")?;
+        for node in self.nodes.iter() {
+            write!(f, "{:?}", node)?;
+        }
+        Ok(())
+    }
+}
+
+impl FlagInfoList {
+    /// Serialize to bytes
+    pub fn into_bytes(&self) -> Vec<u8> {
+        [
+            self.header.into_bytes(),
+            self.nodes.iter().map(|v| v.into_bytes()).collect::<Vec<_>>().concat(),
+        ]
+        .concat()
+    }
+
+    /// Deserialize from bytes
+    pub fn from_bytes(bytes: &[u8]) -> Result<Self, AconfigStorageError> {
+        let header = FlagInfoHeader::from_bytes(bytes)?;
+        let num_flags = header.num_flags;
+        let mut head = header.into_bytes().len();
+        let nodes = (0..num_flags)
+            .map(|_| {
+                let node = FlagInfoNode::from_bytes(&bytes[head..])?;
+                head += node.into_bytes().len();
+                Ok(node)
+            })
+            .collect::<Result<Vec<_>, AconfigStorageError>>()
+            .map_err(|errmsg| {
+                AconfigStorageError::BytesParseFail(anyhow!(
+                    "fail to parse flag info list: {}",
+                    errmsg
+                ))
+            })?;
+        let list = Self { header, nodes };
+        Ok(list)
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use crate::test_utils::create_test_flag_info_list;
+
+    #[test]
+    // this test point locks down the value list serialization
+    fn test_serialization() {
+        let flag_info_list = create_test_flag_info_list();
+
+        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);
+    }
+
+    #[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);
+    }
+
+    #[test]
+    // this test point locks down file type check
+    fn test_file_type_check() {
+        let mut flag_info_list = create_test_flag_info_list();
+        flag_info_list.header.file_type = 123u8;
+        let error = FlagInfoList::from_bytes(&flag_info_list.into_bytes()).unwrap_err();
+        assert_eq!(
+            format!("{:?}", error),
+            format!("BytesParseFail(binary file is not a flag info file)")
+        );
+    }
+}
diff --git a/tools/aconfig/aconfig_storage_file/src/flag_table.rs b/tools/aconfig/aconfig_storage_file/src/flag_table.rs
index 108804e..f41f4ce 100644
--- a/tools/aconfig/aconfig_storage_file/src/flag_table.rs
+++ b/tools/aconfig/aconfig_storage_file/src/flag_table.rs
@@ -17,30 +17,55 @@
 //! flag table module defines the flag table file format and methods for serialization
 //! and deserialization
 
-use crate::AconfigStorageError::{self, BytesParseFail, HigherStorageFileVersion};
-use crate::{get_bucket_index, read_str_from_bytes, read_u16_from_bytes, read_u32_from_bytes};
+use crate::{
+    get_bucket_index, read_str_from_bytes, read_u16_from_bytes, read_u32_from_bytes,
+    read_u8_from_bytes,
+};
+use crate::{AconfigStorageError, StorageFileType, StoredFlagType};
 use anyhow::anyhow;
-pub type FlagOffset = u16;
+use std::fmt;
 
 /// Flag table header struct
-#[derive(PartialEq, Debug)]
+#[derive(PartialEq)]
 pub struct FlagTableHeader {
     pub version: u32,
     pub container: String,
+    pub file_type: u8,
     pub file_size: u32,
     pub num_flags: u32,
     pub bucket_offset: u32,
     pub node_offset: u32,
 }
 
+/// Implement debug print trait for header
+impl fmt::Debug for FlagTableHeader {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        writeln!(
+            f,
+            "Version: {}, Container: {}, File Type: {:?}, File Size: {}",
+            self.version,
+            self.container,
+            StorageFileType::try_from(self.file_type),
+            self.file_size
+        )?;
+        writeln!(
+            f,
+            "Num of Flags: {}, Bucket Offset:{}, Node Offset: {}",
+            self.num_flags, self.bucket_offset, self.node_offset
+        )?;
+        Ok(())
+    }
+}
+
 impl FlagTableHeader {
     /// Serialize to bytes
-    pub fn as_bytes(&self) -> Vec<u8> {
+    pub fn into_bytes(&self) -> Vec<u8> {
         let mut result = Vec::new();
         result.extend_from_slice(&self.version.to_le_bytes());
         let container_bytes = self.container.as_bytes();
         result.extend_from_slice(&(container_bytes.len() as u32).to_le_bytes());
         result.extend_from_slice(container_bytes);
+        result.extend_from_slice(&self.file_type.to_le_bytes());
         result.extend_from_slice(&self.file_size.to_le_bytes());
         result.extend_from_slice(&self.num_flags.to_le_bytes());
         result.extend_from_slice(&self.bucket_offset.to_le_bytes());
@@ -51,36 +76,55 @@
     /// Deserialize from bytes
     pub fn from_bytes(bytes: &[u8]) -> Result<Self, AconfigStorageError> {
         let mut head = 0;
-        Ok(Self {
+        let table = Self {
             version: read_u32_from_bytes(bytes, &mut head)?,
             container: read_str_from_bytes(bytes, &mut head)?,
+            file_type: read_u8_from_bytes(bytes, &mut head)?,
             file_size: read_u32_from_bytes(bytes, &mut head)?,
             num_flags: read_u32_from_bytes(bytes, &mut head)?,
             bucket_offset: read_u32_from_bytes(bytes, &mut head)?,
             node_offset: read_u32_from_bytes(bytes, &mut head)?,
-        })
+        };
+        if table.file_type != StorageFileType::FlagMap as u8 {
+            return Err(AconfigStorageError::BytesParseFail(anyhow!(
+                "binary file is not a flag map"
+            )));
+        }
+        Ok(table)
     }
 }
 
 /// Flag table node struct
-#[derive(PartialEq, Debug, Clone)]
+#[derive(PartialEq, Clone)]
 pub struct FlagTableNode {
     pub package_id: u32,
     pub flag_name: String,
-    pub flag_type: u16,
+    pub flag_type: StoredFlagType,
     pub flag_id: u16,
     pub next_offset: Option<u32>,
 }
 
+/// Implement debug print trait for node
+impl fmt::Debug for FlagTableNode {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        writeln!(
+            f,
+            "Package Id: {}, Flag: {}, Type: {:?}, Offset: {}, Next: {:?}",
+            self.package_id, self.flag_name, self.flag_type, self.flag_id, self.next_offset
+        )?;
+        Ok(())
+    }
+}
+
 impl FlagTableNode {
     /// Serialize to bytes
-    pub fn as_bytes(&self) -> Vec<u8> {
+    pub fn into_bytes(&self) -> Vec<u8> {
         let mut result = Vec::new();
         result.extend_from_slice(&self.package_id.to_le_bytes());
         let name_bytes = self.flag_name.as_bytes();
         result.extend_from_slice(&(name_bytes.len() as u32).to_le_bytes());
         result.extend_from_slice(name_bytes);
-        result.extend_from_slice(&self.flag_type.to_le_bytes());
+        result.extend_from_slice(&(self.flag_type as u16).to_le_bytes());
         result.extend_from_slice(&self.flag_id.to_le_bytes());
         result.extend_from_slice(&self.next_offset.unwrap_or(0).to_le_bytes());
         result
@@ -92,7 +136,7 @@
         let node = Self {
             package_id: read_u32_from_bytes(bytes, &mut head)?,
             flag_name: read_str_from_bytes(bytes, &mut head)?,
-            flag_type: read_u16_from_bytes(bytes, &mut head)?,
+            flag_type: StoredFlagType::try_from(read_u16_from_bytes(bytes, &mut head)?)?,
             flag_id: read_u16_from_bytes(bytes, &mut head)?,
             next_offset: match read_u32_from_bytes(bytes, &mut head)? {
                 0 => None,
@@ -109,21 +153,36 @@
     }
 }
 
-#[derive(PartialEq, Debug)]
+#[derive(PartialEq)]
 pub struct FlagTable {
     pub header: FlagTableHeader,
     pub buckets: Vec<Option<u32>>,
     pub nodes: Vec<FlagTableNode>,
 }
 
+/// Implement debug print trait for flag table
+impl fmt::Debug for FlagTable {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        writeln!(f, "Header:")?;
+        write!(f, "{:?}", self.header)?;
+        writeln!(f, "Buckets:")?;
+        writeln!(f, "{:?}", self.buckets)?;
+        writeln!(f, "Nodes:")?;
+        for node in self.nodes.iter() {
+            write!(f, "{:?}", node)?;
+        }
+        Ok(())
+    }
+}
+
 /// Flag table struct
 impl FlagTable {
     /// Serialize to bytes
-    pub fn as_bytes(&self) -> Vec<u8> {
+    pub fn into_bytes(&self) -> Vec<u8> {
         [
-            self.header.as_bytes(),
+            self.header.into_bytes(),
             self.buckets.iter().map(|v| v.unwrap_or(0).to_le_bytes()).collect::<Vec<_>>().concat(),
-            self.nodes.iter().map(|v| v.as_bytes()).collect::<Vec<_>>().concat(),
+            self.nodes.iter().map(|v| v.into_bytes()).collect::<Vec<_>>().concat(),
         ]
         .concat()
     }
@@ -133,7 +192,7 @@
         let header = FlagTableHeader::from_bytes(bytes)?;
         let num_flags = header.num_flags;
         let num_buckets = crate::get_table_size(num_flags)?;
-        let mut head = header.as_bytes().len();
+        let mut head = header.into_bytes().len();
         let buckets = (0..num_buckets)
             .map(|_| match read_u32_from_bytes(bytes, &mut head).unwrap() {
                 0 => None,
@@ -143,112 +202,23 @@
         let nodes = (0..num_flags)
             .map(|_| {
                 let node = FlagTableNode::from_bytes(&bytes[head..])?;
-                head += node.as_bytes().len();
+                head += node.into_bytes().len();
                 Ok(node)
             })
             .collect::<Result<Vec<_>, AconfigStorageError>>()
-            .map_err(|errmsg| BytesParseFail(anyhow!("fail to parse flag table: {}", errmsg)))?;
+            .map_err(|errmsg| {
+                AconfigStorageError::BytesParseFail(anyhow!("fail to parse flag table: {}", errmsg))
+            })?;
 
         let table = Self { header, buckets, nodes };
         Ok(table)
     }
 }
 
-/// Query flag within package offset
-pub fn find_flag_offset(
-    buf: &[u8],
-    package_id: u32,
-    flag: &str,
-) -> Result<Option<FlagOffset>, AconfigStorageError> {
-    let interpreted_header = FlagTableHeader::from_bytes(buf)?;
-    if interpreted_header.version > crate::FILE_VERSION {
-        return Err(HigherStorageFileVersion(anyhow!(
-            "Cannot read storage file with a higher version of {} with lib version {}",
-            interpreted_header.version,
-            crate::FILE_VERSION
-        )));
-    }
-
-    let num_buckets = (interpreted_header.node_offset - interpreted_header.bucket_offset) / 4;
-    let bucket_index = FlagTableNode::find_bucket_index(package_id, flag, num_buckets);
-
-    let mut pos = (interpreted_header.bucket_offset + 4 * bucket_index) as usize;
-    let mut flag_node_offset = read_u32_from_bytes(buf, &mut pos)? as usize;
-    if flag_node_offset < interpreted_header.node_offset as usize
-        || flag_node_offset >= interpreted_header.file_size as usize
-    {
-        return Ok(None);
-    }
-
-    loop {
-        let interpreted_node = FlagTableNode::from_bytes(&buf[flag_node_offset..])?;
-        if interpreted_node.package_id == package_id && interpreted_node.flag_name == flag {
-            return Ok(Some(interpreted_node.flag_id));
-        }
-        match interpreted_node.next_offset {
-            Some(offset) => flag_node_offset = offset as usize,
-            None => return Ok(None),
-        }
-    }
-}
-
 #[cfg(test)]
 mod tests {
     use super::*;
-
-    impl FlagTableNode {
-        // create test baseline, syntactic sugar
-        fn new_expected(
-            package_id: u32,
-            flag_name: &str,
-            flag_type: u16,
-            flag_id: u16,
-            next_offset: Option<u32>,
-        ) -> Self {
-            Self { package_id, flag_name: flag_name.to_string(), flag_type, flag_id, next_offset }
-        }
-    }
-
-    pub fn create_test_flag_table() -> FlagTable {
-        let header = FlagTableHeader {
-            version: crate::FILE_VERSION,
-            container: String::from("system"),
-            file_size: 320,
-            num_flags: 8,
-            bucket_offset: 30,
-            node_offset: 98,
-        };
-        let buckets: Vec<Option<u32>> = vec![
-            Some(98),
-            Some(124),
-            None,
-            None,
-            None,
-            Some(177),
-            None,
-            Some(203),
-            None,
-            Some(261),
-            None,
-            None,
-            None,
-            None,
-            None,
-            Some(293),
-            None,
-        ];
-        let nodes = vec![
-            FlagTableNode::new_expected(0, "enabled_ro", 1, 1, None),
-            FlagTableNode::new_expected(0, "enabled_rw", 1, 2, Some(150)),
-            FlagTableNode::new_expected(1, "disabled_ro", 1, 0, None),
-            FlagTableNode::new_expected(2, "enabled_ro", 1, 1, None),
-            FlagTableNode::new_expected(1, "enabled_fixed_ro", 1, 1, Some(235)),
-            FlagTableNode::new_expected(1, "enabled_ro", 1, 2, None),
-            FlagTableNode::new_expected(2, "enabled_fixed_ro", 1, 0, None),
-            FlagTableNode::new_expected(0, "disabled_rw", 1, 0, None),
-        ];
-        FlagTable { header, buckets, nodes }
-    }
+    use crate::test_utils::create_test_flag_table;
 
     #[test]
     // this test point locks down the table serialization
@@ -256,66 +226,43 @@
         let flag_table = create_test_flag_table();
 
         let header: &FlagTableHeader = &flag_table.header;
-        let reinterpreted_header = FlagTableHeader::from_bytes(&header.as_bytes());
+        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.as_bytes()).unwrap();
+            let reinterpreted_node = FlagTableNode::from_bytes(&node.into_bytes()).unwrap();
             assert_eq!(node, &reinterpreted_node);
         }
 
-        let reinterpreted_table = FlagTable::from_bytes(&flag_table.as_bytes());
+        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);
     }
 
     #[test]
-    // this test point locks down table query
-    fn test_flag_query() {
-        let flag_table = create_test_flag_table().as_bytes();
-        let baseline = vec![
-            (0, "enabled_ro", 1u16),
-            (0, "enabled_rw", 2u16),
-            (1, "disabled_ro", 0u16),
-            (2, "enabled_ro", 1u16),
-            (1, "enabled_fixed_ro", 1u16),
-            (1, "enabled_ro", 2u16),
-            (2, "enabled_fixed_ro", 0u16),
-            (0, "disabled_rw", 0u16),
-        ];
-        for (package_id, flag_name, expected_offset) in baseline.into_iter() {
-            let flag_offset =
-                find_flag_offset(&flag_table[..], package_id, flag_name).unwrap().unwrap();
-            assert_eq!(flag_offset, expected_offset);
-        }
+    // 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);
     }
 
     #[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().as_bytes();
-        let flag_offset = find_flag_offset(&flag_table[..], 1, "disabled_fixed_ro").unwrap();
-        assert_eq!(flag_offset, None);
-        let flag_offset = find_flag_offset(&flag_table[..], 2, "disabled_rw").unwrap();
-        assert_eq!(flag_offset, None);
-    }
-
-    #[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();
-        table.header.version = crate::FILE_VERSION + 1;
-        let flag_table = table.as_bytes();
-        let error = find_flag_offset(&flag_table[..], 0, "enabled_ro").unwrap_err();
+    // this test point locks down file type check
+    fn test_file_type_check() {
+        let mut flag_table = create_test_flag_table();
+        flag_table.header.file_type = 123u8;
+        let error = FlagTable::from_bytes(&flag_table.into_bytes()).unwrap_err();
         assert_eq!(
             format!("{:?}", error),
-            format!(
-                "HigherStorageFileVersion(Cannot read storage file with a higher version of {} with lib version {})",
-                crate::FILE_VERSION + 1,
-                crate::FILE_VERSION
-            )
+            format!("BytesParseFail(binary file is not a flag map)")
         );
     }
 }
diff --git a/tools/aconfig/aconfig_storage_file/src/flag_value.rs b/tools/aconfig/aconfig_storage_file/src/flag_value.rs
index 0a6a37f..506924b 100644
--- a/tools/aconfig/aconfig_storage_file/src/flag_value.rs
+++ b/tools/aconfig/aconfig_storage_file/src/flag_value.rs
@@ -17,28 +17,51 @@
 //! flag value module defines the flag value file format and methods for serialization
 //! and deserialization
 
-use crate::AconfigStorageError::{self, HigherStorageFileVersion, InvalidStorageFileOffset};
 use crate::{read_str_from_bytes, read_u32_from_bytes, read_u8_from_bytes};
+use crate::{AconfigStorageError, StorageFileType};
 use anyhow::anyhow;
+use std::fmt;
 
 /// Flag value header struct
-#[derive(PartialEq, Debug)]
+#[derive(PartialEq)]
 pub struct FlagValueHeader {
     pub version: u32,
     pub container: String,
+    pub file_type: u8,
     pub file_size: u32,
     pub num_flags: u32,
     pub boolean_value_offset: u32,
 }
 
+/// Implement debug print trait for header
+impl fmt::Debug for FlagValueHeader {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        writeln!(
+            f,
+            "Version: {}, Container: {}, File Type: {:?}, File Size: {}",
+            self.version,
+            self.container,
+            StorageFileType::try_from(self.file_type),
+            self.file_size
+        )?;
+        writeln!(
+            f,
+            "Num of Flags: {}, Value Offset:{}",
+            self.num_flags, self.boolean_value_offset
+        )?;
+        Ok(())
+    }
+}
+
 impl FlagValueHeader {
     /// Serialize to bytes
-    pub fn as_bytes(&self) -> Vec<u8> {
+    pub fn into_bytes(&self) -> Vec<u8> {
         let mut result = Vec::new();
         result.extend_from_slice(&self.version.to_le_bytes());
         let container_bytes = self.container.as_bytes();
         result.extend_from_slice(&(container_bytes.len() as u32).to_le_bytes());
         result.extend_from_slice(container_bytes);
+        result.extend_from_slice(&self.file_type.to_le_bytes());
         result.extend_from_slice(&self.file_size.to_le_bytes());
         result.extend_from_slice(&self.num_flags.to_le_bytes());
         result.extend_from_slice(&self.boolean_value_offset.to_le_bytes());
@@ -48,28 +71,46 @@
     /// Deserialize from bytes
     pub fn from_bytes(bytes: &[u8]) -> Result<Self, AconfigStorageError> {
         let mut head = 0;
-        Ok(Self {
+        let list = Self {
             version: read_u32_from_bytes(bytes, &mut head)?,
             container: read_str_from_bytes(bytes, &mut head)?,
+            file_type: read_u8_from_bytes(bytes, &mut head)?,
             file_size: read_u32_from_bytes(bytes, &mut head)?,
             num_flags: read_u32_from_bytes(bytes, &mut head)?,
             boolean_value_offset: read_u32_from_bytes(bytes, &mut head)?,
-        })
+        };
+        if list.file_type != StorageFileType::FlagVal as u8 {
+            return Err(AconfigStorageError::BytesParseFail(anyhow!(
+                "binary file is not a flag value file"
+            )));
+        }
+        Ok(list)
     }
 }
 
 /// Flag value list struct
-#[derive(PartialEq, Debug)]
+#[derive(PartialEq)]
 pub struct FlagValueList {
     pub header: FlagValueHeader,
     pub booleans: Vec<bool>,
 }
 
+/// Implement debug print trait for flag value
+impl fmt::Debug for FlagValueList {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        writeln!(f, "Header:")?;
+        write!(f, "{:?}", self.header)?;
+        writeln!(f, "Values:")?;
+        writeln!(f, "{:?}", self.booleans)?;
+        Ok(())
+    }
+}
+
 impl FlagValueList {
     /// Serialize to bytes
-    pub fn as_bytes(&self) -> Vec<u8> {
+    pub fn into_bytes(&self) -> Vec<u8> {
         [
-            self.header.as_bytes(),
+            self.header.into_bytes(),
             self.booleans.iter().map(|&v| u8::from(v).to_le_bytes()).collect::<Vec<_>>().concat(),
         ]
         .concat()
@@ -79,7 +120,7 @@
     pub fn from_bytes(bytes: &[u8]) -> Result<Self, AconfigStorageError> {
         let header = FlagValueHeader::from_bytes(bytes)?;
         let num_flags = header.num_flags;
-        let mut head = header.as_bytes().len();
+        let mut head = header.into_bytes().len();
         let booleans =
             (0..num_flags).map(|_| read_u8_from_bytes(bytes, &mut head).unwrap() == 1).collect();
         let list = Self { header, booleans };
@@ -87,46 +128,10 @@
     }
 }
 
-/// Query flag value
-pub fn find_boolean_flag_value(buf: &[u8], flag_offset: u32) -> Result<bool, AconfigStorageError> {
-    let interpreted_header = FlagValueHeader::from_bytes(buf)?;
-    if interpreted_header.version > crate::FILE_VERSION {
-        return Err(HigherStorageFileVersion(anyhow!(
-            "Cannot read storage file with a higher version of {} with lib version {}",
-            interpreted_header.version,
-            crate::FILE_VERSION
-        )));
-    }
-
-    let mut head = (interpreted_header.boolean_value_offset + flag_offset) as usize;
-
-    // TODO: right now, there is only boolean flags, with more flag value types added
-    // later, the end of boolean flag value section should be updated (b/322826265).
-    if head >= interpreted_header.file_size as usize {
-        return Err(InvalidStorageFileOffset(anyhow!(
-            "Flag value offset goes beyond the end of the file."
-        )));
-    }
-
-    let val = read_u8_from_bytes(buf, &mut head)?;
-    Ok(val == 1)
-}
-
 #[cfg(test)]
 mod tests {
     use super::*;
-
-    pub fn create_test_flag_value_list() -> FlagValueList {
-        let header = FlagValueHeader {
-            version: crate::FILE_VERSION,
-            container: String::from("system"),
-            file_size: 34,
-            num_flags: 8,
-            boolean_value_offset: 26,
-        };
-        let booleans: Vec<bool> = vec![false, true, false, false, true, true, false, true];
-        FlagValueList { header, booleans }
-    }
+    use crate::test_utils::create_test_flag_value_list;
 
     #[test]
     // this test point locks down the value list serialization
@@ -134,51 +139,37 @@
         let flag_value_list = create_test_flag_value_list();
 
         let header: &FlagValueHeader = &flag_value_list.header;
-        let reinterpreted_header = FlagValueHeader::from_bytes(&header.as_bytes());
+        let reinterpreted_header = FlagValueHeader::from_bytes(&header.into_bytes());
         assert!(reinterpreted_header.is_ok());
         assert_eq!(header, &reinterpreted_header.unwrap());
 
-        let reinterpreted_value_list = FlagValueList::from_bytes(&flag_value_list.as_bytes());
+        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 flag value query
-    fn test_flag_value_query() {
-        let flag_value_list = create_test_flag_value_list().as_bytes();
-        let baseline: Vec<bool> = vec![false, true, false, false, true, true, false, true];
-        for (offset, expected_value) in baseline.into_iter().enumerate() {
-            let flag_value = find_boolean_flag_value(&flag_value_list[..], offset as u32).unwrap();
-            assert_eq!(flag_value, expected_value);
-        }
+    // 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 bytes = &flag_value_list.into_bytes();
+        let mut head = 0;
+        let version = read_u32_from_bytes(bytes, &mut head).unwrap();
+        assert_eq!(version, 1);
     }
 
     #[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().as_bytes();
-        let error = find_boolean_flag_value(&flag_value_list[..], 8).unwrap_err();
+    // this test point locks down file type check
+    fn test_file_type_check() {
+        let mut flag_value_list = create_test_flag_value_list();
+        flag_value_list.header.file_type = 123u8;
+        let error = FlagValueList::from_bytes(&flag_value_list.into_bytes()).unwrap_err();
         assert_eq!(
             format!("{:?}", error),
-            "InvalidStorageFileOffset(Flag value offset goes beyond the end of the file.)"
-        );
-    }
-
-    #[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();
-        value_list.header.version = crate::FILE_VERSION + 1;
-        let flag_value = value_list.as_bytes();
-        let error = find_boolean_flag_value(&flag_value[..], 4).unwrap_err();
-        assert_eq!(
-            format!("{:?}", error),
-            format!(
-                "HigherStorageFileVersion(Cannot read storage file with a higher version of {} with lib version {})",
-                crate::FILE_VERSION + 1,
-                crate::FILE_VERSION
-            )
+            format!("BytesParseFail(binary file is not a flag value file)")
         );
     }
 }
diff --git a/tools/aconfig/aconfig_storage_file/src/lib.rs b/tools/aconfig/aconfig_storage_file/src/lib.rs
index 84e0e90..3b975d9 100644
--- a/tools/aconfig/aconfig_storage_file/src/lib.rs
+++ b/tools/aconfig/aconfig_storage_file/src/lib.rs
@@ -32,25 +32,28 @@
 //! apis. DO NOT DIRECTLY USE THESE APIS IN YOUR SOURCE CODE. For auto generated flag apis
 //! please refer to the g3doc go/android-flags
 
+pub mod flag_info;
 pub mod flag_table;
 pub mod flag_value;
-pub mod mapped_file;
 pub mod package_table;
 pub mod protos;
-
-#[cfg(test)]
-mod test_utils;
+pub mod test_utils;
 
 use anyhow::anyhow;
+use std::cmp::Ordering;
 use std::collections::hash_map::DefaultHasher;
+use std::fs::File;
 use std::hash::{Hash, Hasher};
+use std::io::{Read, Write};
 
-pub use crate::flag_table::{FlagOffset, FlagTable, FlagTableHeader, FlagTableNode};
+pub use crate::flag_info::{FlagInfoHeader, FlagInfoList, FlagInfoNode};
+pub use crate::flag_table::{FlagTable, FlagTableHeader, FlagTableNode};
 pub use crate::flag_value::{FlagValueHeader, FlagValueList};
-pub use crate::package_table::{PackageOffset, PackageTable, PackageTableHeader, PackageTableNode};
-pub use crate::protos::ProtoStorageFiles;
+pub use crate::package_table::{PackageTable, PackageTableHeader, PackageTableNode};
 
-use crate::AconfigStorageError::{BytesParseFail, HashTableSizeLimit};
+use crate::AconfigStorageError::{
+    BytesParseFail, FileCreationFail, HashTableSizeLimit, InvalidStoredFlagType,
+};
 
 /// Storage file version
 pub const FILE_VERSION: u32 = 1;
@@ -62,18 +65,16 @@
     402653189, 805306457, 1610612741,
 ];
 
-/// Storage file location pb file
-pub const STORAGE_LOCATION_FILE: &str = "/metadata/aconfig/storage_files.pb";
-
 /// Storage file type enum
 #[derive(Clone, Debug, PartialEq, Eq)]
-pub enum StorageFileSelection {
-    PackageMap,
-    FlagMap,
-    FlagVal,
+pub enum StorageFileType {
+    PackageMap = 0,
+    FlagMap = 1,
+    FlagVal = 2,
+    FlagInfo = 3,
 }
 
-impl TryFrom<&str> for StorageFileSelection {
+impl TryFrom<&str> for StorageFileType {
     type Error = anyhow::Error;
 
     fn try_from(value: &str) -> std::result::Result<Self, Self::Error> {
@@ -81,11 +82,90 @@
             "package_map" => Ok(Self::PackageMap),
             "flag_map" => Ok(Self::FlagMap),
             "flag_val" => Ok(Self::FlagVal),
-            _ => Err(anyhow!("Invalid storage file to create")),
+            "flag_info" => Ok(Self::FlagInfo),
+            _ => Err(anyhow!(
+                "Invalid storage file type, valid types are package_map|flag_map|flag_val|flag_info"
+            )),
         }
     }
 }
 
+impl TryFrom<u8> for StorageFileType {
+    type Error = anyhow::Error;
+
+    fn try_from(value: u8) -> Result<Self, Self::Error> {
+        match value {
+            x if x == Self::PackageMap as u8 => Ok(Self::PackageMap),
+            x if x == Self::FlagMap as u8 => Ok(Self::FlagMap),
+            x if x == Self::FlagVal as u8 => Ok(Self::FlagVal),
+            x if x == Self::FlagInfo as u8 => Ok(Self::FlagInfo),
+            _ => Err(anyhow!("Invalid storage file type")),
+        }
+    }
+}
+
+/// Flag type enum as stored by storage file
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+pub enum StoredFlagType {
+    ReadWriteBoolean = 0,
+    ReadOnlyBoolean = 1,
+    FixedReadOnlyBoolean = 2,
+}
+
+impl TryFrom<u16> for StoredFlagType {
+    type Error = AconfigStorageError;
+
+    fn try_from(value: u16) -> Result<Self, Self::Error> {
+        match value {
+            x if x == Self::ReadWriteBoolean as u16 => Ok(Self::ReadWriteBoolean),
+            x if x == Self::ReadOnlyBoolean as u16 => Ok(Self::ReadOnlyBoolean),
+            x if x == Self::FixedReadOnlyBoolean as u16 => Ok(Self::FixedReadOnlyBoolean),
+            _ => Err(InvalidStoredFlagType(anyhow!("Invalid stored flag type"))),
+        }
+    }
+}
+
+/// Storage query api error
+#[non_exhaustive]
+#[derive(thiserror::Error, Debug)]
+pub enum AconfigStorageError {
+    #[error("failed to read the file")]
+    FileReadFail(#[source] anyhow::Error),
+
+    #[error("fail to parse protobuf")]
+    ProtobufParseFail(#[source] anyhow::Error),
+
+    #[error("storage files not found for this container")]
+    StorageFileNotFound(#[source] anyhow::Error),
+
+    #[error("fail to map storage file")]
+    MapFileFail(#[source] anyhow::Error),
+
+    #[error("fail to get mapped file")]
+    ObtainMappedFileFail(#[source] anyhow::Error),
+
+    #[error("fail to flush mapped storage file")]
+    MapFlushFail(#[source] anyhow::Error),
+
+    #[error("number of items in hash table exceed limit")]
+    HashTableSizeLimit(#[source] anyhow::Error),
+
+    #[error("failed to parse bytes into data")]
+    BytesParseFail(#[source] anyhow::Error),
+
+    #[error("cannot parse storage files with a higher version")]
+    HigherStorageFileVersion(#[source] anyhow::Error),
+
+    #[error("invalid storage file byte offset")]
+    InvalidStorageFileOffset(#[source] anyhow::Error),
+
+    #[error("failed to create file")]
+    FileCreationFail(#[source] anyhow::Error),
+
+    #[error("invalid stored flag type")]
+    InvalidStoredFlagType(#[source] anyhow::Error),
+}
+
 /// Get the right hash table size given number of entries in the table. Use a
 /// load factor of 0.5 for performance.
 pub fn get_table_size(entries: u32) -> Result<u32, AconfigStorageError> {
@@ -104,7 +184,7 @@
 }
 
 /// Read and parse bytes as u8
-pub(crate) fn read_u8_from_bytes(buf: &[u8], head: &mut usize) -> Result<u8, AconfigStorageError> {
+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))
@@ -127,10 +207,7 @@
 }
 
 /// Read and parse bytes as u32
-pub(crate) fn read_u32_from_bytes(
-    buf: &[u8],
-    head: &mut usize,
-) -> Result<u32, AconfigStorageError> {
+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))
@@ -151,420 +228,211 @@
     Ok(val)
 }
 
-/// Storage query api error
-#[non_exhaustive]
-#[derive(thiserror::Error, Debug)]
-pub enum AconfigStorageError {
-    #[error("failed to read the file")]
-    FileReadFail(#[source] anyhow::Error),
-
-    #[error("fail to parse protobuf")]
-    ProtobufParseFail(#[source] anyhow::Error),
-
-    #[error("storage files not found for this container")]
-    StorageFileNotFound(#[source] anyhow::Error),
-
-    #[error("fail to map storage file")]
-    MapFileFail(#[source] anyhow::Error),
-
-    #[error("number of items in hash table exceed limit")]
-    HashTableSizeLimit(#[source] anyhow::Error),
-
-    #[error("failed to parse bytes into data")]
-    BytesParseFail(#[source] anyhow::Error),
-
-    #[error("cannot parse storage files with a higher version")]
-    HigherStorageFileVersion(#[source] anyhow::Error),
-
-    #[error("invalid storage file byte offset")]
-    InvalidStorageFileOffset(#[source] anyhow::Error),
+/// Read in storage file as bytes
+pub fn read_file_to_bytes(file_path: &str) -> Result<Vec<u8>, AconfigStorageError> {
+    let mut file = File::open(file_path).map_err(|errmsg| {
+        AconfigStorageError::FileReadFail(anyhow!("Failed to open file {}: {}", file_path, errmsg))
+    })?;
+    let mut buffer = Vec::new();
+    file.read_to_end(&mut buffer).map_err(|errmsg| {
+        AconfigStorageError::FileReadFail(anyhow!(
+            "Failed to read bytes from file {}: {}",
+            file_path,
+            errmsg
+        ))
+    })?;
+    Ok(buffer)
 }
 
-/// Get package start offset implementation
-pub fn get_package_offset_impl(
-    pb_file: &str,
-    container: &str,
-    package: &str,
-) -> Result<Option<PackageOffset>, AconfigStorageError> {
-    let mapped_file =
-        crate::mapped_file::get_mapped_file(pb_file, container, StorageFileSelection::PackageMap)?;
-    crate::package_table::find_package_offset(&mapped_file, package)
-}
+/// List flag values from storage files
+pub fn list_flags(
+    package_map: &str,
+    flag_map: &str,
+    flag_val: &str,
+) -> Result<Vec<(String, String, StoredFlagType, bool)>, AconfigStorageError> {
+    let package_table = PackageTable::from_bytes(&read_file_to_bytes(package_map)?)?;
+    let flag_table = FlagTable::from_bytes(&read_file_to_bytes(flag_map)?)?;
+    let flag_value_list = FlagValueList::from_bytes(&read_file_to_bytes(flag_val)?)?;
 
-/// Get flag offset implementation
-pub fn get_flag_offset_impl(
-    pb_file: &str,
-    container: &str,
-    package_id: u32,
-    flag: &str,
-) -> Result<Option<FlagOffset>, AconfigStorageError> {
-    let mapped_file =
-        crate::mapped_file::get_mapped_file(pb_file, container, StorageFileSelection::FlagMap)?;
-    crate::flag_table::find_flag_offset(&mapped_file, package_id, flag)
-}
-
-/// Get boolean flag value implementation
-pub fn get_boolean_flag_value_impl(
-    pb_file: &str,
-    container: &str,
-    offset: u32,
-) -> Result<bool, AconfigStorageError> {
-    let mapped_file =
-        crate::mapped_file::get_mapped_file(pb_file, container, StorageFileSelection::FlagVal)?;
-    crate::flag_value::find_boolean_flag_value(&mapped_file, offset)
-}
-
-/// Get package start offset for flags given the container and package name.
-///
-/// This function would map the corresponding package map file if has not been mapped yet,
-/// and then look for the target package in this mapped file.
-///
-/// If a package is found, it returns Ok(Some(PackageOffset))
-/// If a package is not found, it returns Ok(None)
-/// If errors out such as no such package map file is found, it returns an Err(errmsg)
-pub fn get_package_offset(
-    container: &str,
-    package: &str,
-) -> Result<Option<PackageOffset>, AconfigStorageError> {
-    get_package_offset_impl(STORAGE_LOCATION_FILE, container, package)
-}
-
-/// Get flag offset within a package given the container name, package id and flag name.
-///
-/// This function would map the corresponding flag map file if has not been mapped yet,
-/// and then look for the target flag in this mapped file.
-///
-/// If a flag is found, it returns Ok(Some(u16))
-/// If a flag is not found, it returns Ok(None)
-/// If errors out such as no such flag map file is found, it returns an Err(errmsg)
-pub fn get_flag_offset(
-    container: &str,
-    package_id: u32,
-    flag: &str,
-) -> Result<Option<FlagOffset>, AconfigStorageError> {
-    get_flag_offset_impl(STORAGE_LOCATION_FILE, container, package_id, flag)
-}
-
-/// Get the boolean flag value given the container name and flag global offset
-///
-/// This function would map the corresponding flag value file if has not been mapped yet,
-/// and then look for the target flag value at the specified offset.
-///
-/// If flag value file is successfully mapped and the provide offset is valid, it returns
-/// the boolean flag value, otherwise it returns the error message.
-pub fn get_boolean_flag_value(container: &str, offset: u32) -> Result<bool, AconfigStorageError> {
-    get_boolean_flag_value_impl(STORAGE_LOCATION_FILE, container, offset)
-}
-
-#[cxx::bridge]
-mod ffi {
-    // Package table query return for cc interlop
-    pub struct PackageOffsetQueryCXX {
-        pub query_success: bool,
-        pub error_message: String,
-        pub package_exists: bool,
-        pub package_id: u32,
-        pub boolean_offset: u32,
+    let mut package_info = vec![("", 0); package_table.header.num_packages as usize];
+    for node in package_table.nodes.iter() {
+        package_info[node.package_id as usize] = (&node.package_name, node.boolean_offset);
     }
 
-    // Flag table query return for cc interlop
-    pub struct FlagOffsetQueryCXX {
-        pub query_success: bool,
-        pub error_message: String,
-        pub flag_exists: bool,
-        pub flag_offset: u16,
+    let mut flags = Vec::new();
+    for node in flag_table.nodes.iter() {
+        let (package_name, package_offset) = package_info[node.package_id as usize];
+        let flag_offset = package_offset + node.flag_id as u32;
+        let flag_value = flag_value_list.booleans[flag_offset as usize];
+        flags.push((
+            String::from(package_name),
+            node.flag_name.clone(),
+            node.flag_type,
+            flag_value,
+        ));
     }
 
-    // Flag value query return for cc interlop
-    pub struct BooleanFlagValueQueryCXX {
-        pub query_success: bool,
-        pub error_message: String,
-        pub flag_value: bool,
+    flags.sort_by(|v1, v2| match v1.0.cmp(&v2.0) {
+        Ordering::Equal => v1.1.cmp(&v2.1),
+        other => other,
+    });
+    Ok(flags)
+}
+
+/// Create flag info file
+pub fn create_flag_info(
+    package_map: &str,
+    flag_map: &str,
+    flag_info_out: &str,
+) -> Result<(), AconfigStorageError> {
+    let package_table = PackageTable::from_bytes(&read_file_to_bytes(package_map)?)?;
+    let flag_table = FlagTable::from_bytes(&read_file_to_bytes(flag_map)?)?;
+
+    if package_table.header.container != flag_table.header.container {
+        return Err(FileCreationFail(anyhow!(
+            "container for package map {} and flag map {} does not match",
+            package_table.header.container,
+            flag_table.header.container,
+        )));
     }
 
-    // Rust export to c++
-    extern "Rust" {
-        pub fn get_package_offset_cxx_impl(
-            pb_file: &str,
-            container: &str,
-            package: &str,
-        ) -> PackageOffsetQueryCXX;
-
-        pub fn get_flag_offset_cxx_impl(
-            pb_file: &str,
-            container: &str,
-            package_id: u32,
-            flag: &str,
-        ) -> FlagOffsetQueryCXX;
-
-        pub fn get_boolean_flag_value_cxx_impl(
-            pb_file: &str,
-            container: &str,
-            offset: u32,
-        ) -> BooleanFlagValueQueryCXX;
-
-        pub fn get_package_offset_cxx(container: &str, package: &str) -> PackageOffsetQueryCXX;
-
-        pub fn get_flag_offset_cxx(
-            container: &str,
-            package_id: u32,
-            flag: &str,
-        ) -> FlagOffsetQueryCXX;
-
-        pub fn get_boolean_flag_value_cxx(container: &str, offset: u32)
-            -> BooleanFlagValueQueryCXX;
+    let mut package_offsets = vec![0; package_table.header.num_packages as usize];
+    for node in package_table.nodes.iter() {
+        package_offsets[node.package_id as usize] = node.boolean_offset;
     }
-}
 
-/// Get package start offset impl cc interlop
-pub fn get_package_offset_cxx_impl(
-    pb_file: &str,
-    container: &str,
-    package: &str,
-) -> ffi::PackageOffsetQueryCXX {
-    ffi::PackageOffsetQueryCXX::new(get_package_offset_impl(pb_file, container, package))
-}
-
-/// Get flag start offset impl cc interlop
-pub fn get_flag_offset_cxx_impl(
-    pb_file: &str,
-    container: &str,
-    package_id: u32,
-    flag: &str,
-) -> ffi::FlagOffsetQueryCXX {
-    ffi::FlagOffsetQueryCXX::new(get_flag_offset_impl(pb_file, container, package_id, flag))
-}
-
-/// Get boolean flag value impl cc interlop
-pub fn get_boolean_flag_value_cxx_impl(
-    pb_file: &str,
-    container: &str,
-    offset: u32,
-) -> ffi::BooleanFlagValueQueryCXX {
-    ffi::BooleanFlagValueQueryCXX::new(get_boolean_flag_value_impl(pb_file, container, offset))
-}
-
-/// Get package start offset cc interlop
-pub fn get_package_offset_cxx(container: &str, package: &str) -> ffi::PackageOffsetQueryCXX {
-    ffi::PackageOffsetQueryCXX::new(get_package_offset(container, package))
-}
-
-/// Get flag start offset cc interlop
-pub fn get_flag_offset_cxx(
-    container: &str,
-    package_id: u32,
-    flag: &str,
-) -> ffi::FlagOffsetQueryCXX {
-    ffi::FlagOffsetQueryCXX::new(get_flag_offset(container, package_id, flag))
-}
-
-/// Get boolean flag value cc interlop
-pub fn get_boolean_flag_value_cxx(container: &str, offset: u32) -> ffi::BooleanFlagValueQueryCXX {
-    ffi::BooleanFlagValueQueryCXX::new(get_boolean_flag_value(container, offset))
-}
-
-impl ffi::PackageOffsetQueryCXX {
-    pub(crate) fn new(offset_result: Result<Option<PackageOffset>, AconfigStorageError>) -> Self {
-        match offset_result {
-            Ok(offset_opt) => match offset_opt {
-                Some(offset) => Self {
-                    query_success: true,
-                    error_message: String::from(""),
-                    package_exists: true,
-                    package_id: offset.package_id,
-                    boolean_offset: offset.boolean_offset,
-                },
-                None => Self {
-                    query_success: true,
-                    error_message: String::from(""),
-                    package_exists: false,
-                    package_id: 0,
-                    boolean_offset: 0,
-                },
-            },
-            Err(errmsg) => Self {
-                query_success: false,
-                error_message: format!("{:?}", errmsg),
-                package_exists: false,
-                package_id: 0,
-                boolean_offset: 0,
-            },
-        }
+    let mut is_flag_rw = vec![false; flag_table.header.num_flags as usize];
+    for node in flag_table.nodes.iter() {
+        let flag_offset = package_offsets[node.package_id as usize] + node.flag_id as u32;
+        is_flag_rw[flag_offset as usize] = node.flag_type == StoredFlagType::ReadWriteBoolean;
     }
-}
 
-impl ffi::FlagOffsetQueryCXX {
-    pub(crate) fn new(offset_result: Result<Option<FlagOffset>, AconfigStorageError>) -> Self {
-        match offset_result {
-            Ok(offset_opt) => match offset_opt {
-                Some(offset) => Self {
-                    query_success: true,
-                    error_message: String::from(""),
-                    flag_exists: true,
-                    flag_offset: offset,
-                },
-                None => Self {
-                    query_success: true,
-                    error_message: String::from(""),
-                    flag_exists: false,
-                    flag_offset: 0,
-                },
-            },
-            Err(errmsg) => Self {
-                query_success: false,
-                error_message: format!("{:?}", errmsg),
-                flag_exists: false,
-                flag_offset: 0,
-            },
-        }
-    }
-}
+    let mut list = FlagInfoList {
+        header: FlagInfoHeader {
+            version: FILE_VERSION,
+            container: flag_table.header.container,
+            file_type: StorageFileType::FlagInfo as u8,
+            file_size: 0,
+            num_flags: flag_table.header.num_flags,
+            boolean_flag_offset: 0,
+        },
+        nodes: is_flag_rw.iter().map(|&rw| FlagInfoNode::create(rw)).collect(),
+    };
 
-impl ffi::BooleanFlagValueQueryCXX {
-    pub(crate) fn new(value_result: Result<bool, AconfigStorageError>) -> Self {
-        match value_result {
-            Ok(value) => {
-                Self { query_success: true, error_message: String::from(""), flag_value: value }
-            }
-            Err(errmsg) => Self {
-                query_success: false,
-                error_message: format!("{:?}", errmsg),
-                flag_value: false,
-            },
-        }
-    }
+    list.header.boolean_flag_offset = list.header.into_bytes().len() as u32;
+    list.header.file_size = list.into_bytes().len() as u32;
+
+    let mut file = File::create(flag_info_out).map_err(|errmsg| {
+        FileCreationFail(anyhow!("fail to create file {}: {}", flag_info_out, errmsg))
+    })?;
+    file.write_all(&list.into_bytes()).map_err(|errmsg| {
+        FileCreationFail(anyhow!("fail to write to file {}: {}", flag_info_out, errmsg))
+    })?;
+
+    Ok(())
 }
 
 #[cfg(test)]
 mod tests {
     use super::*;
-    use crate::test_utils::{write_storage_text_to_temp_file, TestStorageFileSet};
-
-    fn create_test_storage_files(read_only: bool) -> TestStorageFileSet {
-        TestStorageFileSet::new(
-            "./tests/package.map",
-            "./tests/flag.map",
-            "./tests/flag.val",
-            read_only,
-        )
-        .unwrap()
-    }
+    use crate::test_utils::{
+        create_test_flag_info_list, create_test_flag_table, create_test_flag_value_list,
+        create_test_package_table, write_bytes_to_temp_file,
+    };
+    use tempfile::NamedTempFile;
 
     #[test]
-    // this test point locks down flag package offset query
-    fn test_package_offset_query() {
-        let ro_files = create_test_storage_files(true);
-        let text_proto = format!(
-            r#"
-files {{
-    version: 0
-    container: "system"
-    package_map: "{}"
-    flag_map: "{}"
-    flag_val: "{}"
-    timestamp: 12345
-}}
-"#,
-            ro_files.package_map.name, ro_files.flag_map.name, ro_files.flag_val.name
-        );
+    // 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();
 
-        let file = write_storage_text_to_temp_file(&text_proto).unwrap();
-        let file_full_path = file.path().display().to_string();
-        let package_offset = get_package_offset_impl(
-            &file_full_path,
-            "system",
-            "com.android.aconfig.storage.test_1",
-        )
-        .unwrap()
-        .unwrap();
-        let expected_package_offset = PackageOffset { package_id: 0, boolean_offset: 0 };
-        assert_eq!(package_offset, expected_package_offset);
+        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 package_offset = get_package_offset_impl(
-            &file_full_path,
-            "system",
-            "com.android.aconfig.storage.test_2",
-        )
-        .unwrap()
-        .unwrap();
-        let expected_package_offset = PackageOffset { package_id: 1, boolean_offset: 3 };
-        assert_eq!(package_offset, expected_package_offset);
-
-        let package_offset = get_package_offset_impl(
-            &file_full_path,
-            "system",
-            "com.android.aconfig.storage.test_4",
-        )
-        .unwrap()
-        .unwrap();
-        let expected_package_offset = PackageOffset { package_id: 2, boolean_offset: 6 };
-        assert_eq!(package_offset, expected_package_offset);
-    }
-
-    #[test]
-    // this test point locks down flag offset query
-    fn test_flag_offset_query() {
-        let ro_files = create_test_storage_files(true);
-        let text_proto = format!(
-            r#"
-files {{
-    version: 0
-    container: "system"
-    package_map: "{}"
-    flag_map: "{}"
-    flag_val: "{}"
-    timestamp: 12345
-}}
-"#,
-            ro_files.package_map.name, ro_files.flag_map.name, ro_files.flag_val.name
-        );
-
-        let file = write_storage_text_to_temp_file(&text_proto).unwrap();
-        let file_full_path = file.path().display().to_string();
-        let baseline = vec![
-            (0, "enabled_ro", 1u16),
-            (0, "enabled_rw", 2u16),
-            (1, "disabled_ro", 0u16),
-            (2, "enabled_ro", 1u16),
-            (1, "enabled_fixed_ro", 1u16),
-            (1, "enabled_ro", 2u16),
-            (2, "enabled_fixed_ro", 0u16),
-            (0, "disabled_rw", 0u16),
+        let flags =
+            list_flags(&package_table_path, &flag_table_path, &flag_value_list_path).unwrap();
+        let expected = [
+            (
+                String::from("com.android.aconfig.storage.test_1"),
+                String::from("disabled_rw"),
+                StoredFlagType::ReadWriteBoolean,
+                false,
+            ),
+            (
+                String::from("com.android.aconfig.storage.test_1"),
+                String::from("enabled_ro"),
+                StoredFlagType::ReadOnlyBoolean,
+                true,
+            ),
+            (
+                String::from("com.android.aconfig.storage.test_1"),
+                String::from("enabled_rw"),
+                StoredFlagType::ReadWriteBoolean,
+                true,
+            ),
+            (
+                String::from("com.android.aconfig.storage.test_2"),
+                String::from("disabled_ro"),
+                StoredFlagType::ReadOnlyBoolean,
+                false,
+            ),
+            (
+                String::from("com.android.aconfig.storage.test_2"),
+                String::from("enabled_fixed_ro"),
+                StoredFlagType::FixedReadOnlyBoolean,
+                true,
+            ),
+            (
+                String::from("com.android.aconfig.storage.test_2"),
+                String::from("enabled_ro"),
+                StoredFlagType::ReadOnlyBoolean,
+                true,
+            ),
+            (
+                String::from("com.android.aconfig.storage.test_4"),
+                String::from("enabled_fixed_ro"),
+                StoredFlagType::FixedReadOnlyBoolean,
+                true,
+            ),
+            (
+                String::from("com.android.aconfig.storage.test_4"),
+                String::from("enabled_ro"),
+                StoredFlagType::ReadOnlyBoolean,
+                true,
+            ),
         ];
-        for (package_id, flag_name, expected_offset) in baseline.into_iter() {
-            let flag_offset =
-                get_flag_offset_impl(&file_full_path, "system", package_id, flag_name)
-                    .unwrap()
-                    .unwrap();
-            assert_eq!(flag_offset, expected_offset);
-        }
+        assert_eq!(flags, expected);
+    }
+
+    fn create_empty_temp_file() -> Result<NamedTempFile, AconfigStorageError> {
+        let file = NamedTempFile::new().map_err(|_| {
+            AconfigStorageError::FileCreationFail(anyhow!("Failed to create temp file"))
+        })?;
+        Ok(file)
     }
 
     #[test]
-    // this test point locks down flag offset query
-    fn test_flag_value_query() {
-        let ro_files = create_test_storage_files(true);
-        let text_proto = format!(
-            r#"
-files {{
-    version: 0
-    container: "system"
-    package_map: "{}"
-    flag_map: "{}"
-    flag_val: "{}"
-    timestamp: 12345
-}}
-"#,
-            ro_files.package_map.name, ro_files.flag_map.name, ro_files.flag_val.name
-        );
+    // this test point locks down the flag info creation
+    fn test_create_flag_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_info = create_empty_temp_file().unwrap();
 
-        let file = write_storage_text_to_temp_file(&text_proto).unwrap();
-        let file_full_path = file.path().display().to_string();
-        let baseline: Vec<bool> = vec![false; 8];
-        for (offset, expected_value) in baseline.into_iter().enumerate() {
-            let flag_value =
-                get_boolean_flag_value_impl(&file_full_path, "system", offset as u32).unwrap();
-            assert_eq!(flag_value, expected_value);
-        }
+        let package_table_path = package_table.path().display().to_string();
+        let flag_table_path = flag_table.path().display().to_string();
+        let flag_info_path = flag_info.path().display().to_string();
+
+        assert!(create_flag_info(&package_table_path, &flag_table_path, &flag_info_path).is_ok());
+
+        let flag_info =
+            FlagInfoList::from_bytes(&read_file_to_bytes(&flag_info_path).unwrap()).unwrap();
+        let expected_flag_info = create_test_flag_info_list();
+        assert_eq!(flag_info, expected_flag_info);
     }
 }
diff --git a/tools/aconfig/aconfig_storage_file/src/main.rs b/tools/aconfig/aconfig_storage_file/src/main.rs
new file mode 100644
index 0000000..b686274
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_file/src/main.rs
@@ -0,0 +1,98 @@
+/*
+ * 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.
+ */
+
+//! `aconfig-storage` is a debugging tool to parse storage files
+
+use aconfig_storage_file::{
+    list_flags, read_file_to_bytes, AconfigStorageError, FlagInfoList, FlagTable, FlagValueList,
+    PackageTable, StorageFileType,
+};
+
+use clap::{builder::ArgAction, Arg, Command};
+
+fn cli() -> Command {
+    Command::new("aconfig-storage")
+        .subcommand_required(true)
+        .subcommand(
+            Command::new("print")
+                .arg(Arg::new("file").long("file").required(true).action(ArgAction::Set))
+                .arg(
+                    Arg::new("type")
+                        .long("type")
+                        .required(true)
+                        .value_parser(|s: &str| StorageFileType::try_from(s)),
+                ),
+        )
+        .subcommand(
+            Command::new("list")
+                .arg(
+                    Arg::new("package-map")
+                        .long("package-map")
+                        .required(true)
+                        .action(ArgAction::Set),
+                )
+                .arg(Arg::new("flag-map").long("flag-map").required(true).action(ArgAction::Set))
+                .arg(Arg::new("flag-val").long("flag-val").required(true).action(ArgAction::Set)),
+        )
+}
+
+fn print_storage_file(
+    file_path: &str,
+    file_type: &StorageFileType,
+) -> Result<(), AconfigStorageError> {
+    let bytes = read_file_to_bytes(file_path)?;
+    match file_type {
+        StorageFileType::PackageMap => {
+            let package_table = PackageTable::from_bytes(&bytes)?;
+            println!("{:?}", package_table);
+        }
+        StorageFileType::FlagMap => {
+            let flag_table = FlagTable::from_bytes(&bytes)?;
+            println!("{:?}", flag_table);
+        }
+        StorageFileType::FlagVal => {
+            let flag_value = FlagValueList::from_bytes(&bytes)?;
+            println!("{:?}", flag_value);
+        }
+        StorageFileType::FlagInfo => {
+            let flag_info = FlagInfoList::from_bytes(&bytes)?;
+            println!("{:?}", flag_info);
+        }
+    }
+    Ok(())
+}
+
+fn main() -> Result<(), AconfigStorageError> {
+    let matches = cli().get_matches();
+    match matches.subcommand() {
+        Some(("print", sub_matches)) => {
+            let file_path = sub_matches.get_one::<String>("file").unwrap();
+            let file_type = sub_matches.get_one::<StorageFileType>("type").unwrap();
+            print_storage_file(file_path, file_type)?
+        }
+        Some(("list", sub_matches)) => {
+            let package_map = sub_matches.get_one::<String>("package-map").unwrap();
+            let flag_map = sub_matches.get_one::<String>("flag-map").unwrap();
+            let flag_val = sub_matches.get_one::<String>("flag-val").unwrap();
+            let flags = list_flags(package_map, flag_map, flag_val)?;
+            for (package_name, flag_name, flag_type, flag_value) in flags.iter() {
+                println!("{} {} {:?} {}", package_name, flag_name, flag_type, flag_value);
+            }
+        }
+        _ => unreachable!(),
+    }
+    Ok(())
+}
diff --git a/tools/aconfig/aconfig_storage_file/src/mapped_file.rs b/tools/aconfig/aconfig_storage_file/src/mapped_file.rs
deleted file mode 100644
index d8f2570..0000000
--- a/tools/aconfig/aconfig_storage_file/src/mapped_file.rs
+++ /dev/null
@@ -1,328 +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.
- */
-
-use std::collections::HashMap;
-use std::fs::File;
-use std::io::{BufReader, Read};
-use std::sync::{Arc, Mutex};
-
-use anyhow::anyhow;
-use memmap2::Mmap;
-use once_cell::sync::Lazy;
-
-use crate::protos::{
-    storage_files::try_from_binary_proto, ProtoStorageFileInfo, ProtoStorageFiles,
-};
-use crate::AconfigStorageError::{
-    self, FileReadFail, MapFileFail, ProtobufParseFail, StorageFileNotFound,
-};
-use crate::StorageFileSelection;
-
-/// Cache for already mapped files
-static ALL_MAPPED_FILES: Lazy<Mutex<HashMap<String, MappedStorageFileSet>>> = Lazy::new(|| {
-    let mapped_files = HashMap::new();
-    Mutex::new(mapped_files)
-});
-
-/// Mapped storage files for a particular container
-#[derive(Debug)]
-struct MappedStorageFileSet {
-    package_map: Arc<Mmap>,
-    flag_map: Arc<Mmap>,
-    flag_val: Arc<Mmap>,
-}
-
-/// Find where storage files are stored for a particular container
-fn find_container_storage_location(
-    location_pb_file: &str,
-    container: &str,
-) -> Result<ProtoStorageFileInfo, AconfigStorageError> {
-    let file = File::open(location_pb_file).map_err(|errmsg| {
-        FileReadFail(anyhow!("Failed to open file {}: {}", location_pb_file, errmsg))
-    })?;
-    let mut reader = BufReader::new(file);
-    let mut bytes = Vec::new();
-    reader.read_to_end(&mut bytes).map_err(|errmsg| {
-        FileReadFail(anyhow!("Failed to read file {}: {}", location_pb_file, errmsg))
-    })?;
-    let storage_locations: ProtoStorageFiles = try_from_binary_proto(&bytes).map_err(|errmsg| {
-        ProtobufParseFail(anyhow!(
-            "Failed to parse storage location pb file {}: {}",
-            location_pb_file,
-            errmsg
-        ))
-    })?;
-    for location_info in storage_locations.files.iter() {
-        if location_info.container() == container {
-            return Ok(location_info.clone());
-        }
-    }
-    Err(StorageFileNotFound(anyhow!("Storage file does not exist for {}", container)))
-}
-
-/// Verify the file is read only and then map it
-fn verify_read_only_and_map(file_path: &str) -> Result<Mmap, AconfigStorageError> {
-    let file = File::open(file_path)
-        .map_err(|errmsg| FileReadFail(anyhow!("Failed to open file {}: {}", file_path, errmsg)))?;
-    let metadata = file.metadata().map_err(|errmsg| {
-        FileReadFail(anyhow!("Failed to find metadata for {}: {}", file_path, errmsg))
-    })?;
-
-    // ensure storage file is read only
-    if !metadata.permissions().readonly() {
-        return Err(MapFileFail(anyhow!("fail to map non read only storage file {}", file_path)));
-    }
-
-    // SAFETY:
-    //
-    // Mmap constructors are unsafe as it would have undefined behaviors if the file
-    // is modified after mapped (https://docs.rs/memmap2/latest/memmap2/struct.Mmap.html).
-    //
-    // We either have to make this api unsafe or ensure that the file will not be modified
-    // which means it is read only. Here in the code, we check explicitly that the file
-    // being mapped must only have read permission, otherwise, error out, thus making sure
-    // it is safe.
-    //
-    // We should remove this restriction if we need to support mmap non read only file in
-    // the future (by making this api unsafe). But for now, all flags are boot stable, so
-    // the boot flag file copy should be readonly.
-    unsafe {
-        let mapped_file = Mmap::map(&file).map_err(|errmsg| {
-            MapFileFail(anyhow!("fail to map storage file {}: {}", file_path, errmsg))
-        })?;
-        Ok(mapped_file)
-    }
-}
-
-/// Map all storage files for a particular container
-fn map_container_storage_files(
-    location_pb_file: &str,
-    container: &str,
-) -> Result<MappedStorageFileSet, AconfigStorageError> {
-    let files_location = find_container_storage_location(location_pb_file, container)?;
-    let package_map = Arc::new(verify_read_only_and_map(files_location.package_map())?);
-    let flag_map = Arc::new(verify_read_only_and_map(files_location.flag_map())?);
-    let flag_val = Arc::new(verify_read_only_and_map(files_location.flag_val())?);
-    Ok(MappedStorageFileSet { package_map, flag_map, flag_val })
-}
-
-/// Get a mapped storage file given the container and file type
-pub(crate) fn get_mapped_file(
-    location_pb_file: &str,
-    container: &str,
-    file_selection: StorageFileSelection,
-) -> Result<Arc<Mmap>, AconfigStorageError> {
-    let mut all_mapped_files = ALL_MAPPED_FILES.lock().unwrap();
-    match all_mapped_files.get(container) {
-        Some(mapped_files) => Ok(match file_selection {
-            StorageFileSelection::PackageMap => Arc::clone(&mapped_files.package_map),
-            StorageFileSelection::FlagMap => Arc::clone(&mapped_files.flag_map),
-            StorageFileSelection::FlagVal => Arc::clone(&mapped_files.flag_val),
-        }),
-        None => {
-            let mapped_files = map_container_storage_files(location_pb_file, container)?;
-            let file_ptr = match file_selection {
-                StorageFileSelection::PackageMap => Arc::clone(&mapped_files.package_map),
-                StorageFileSelection::FlagMap => Arc::clone(&mapped_files.flag_map),
-                StorageFileSelection::FlagVal => Arc::clone(&mapped_files.flag_val),
-            };
-            all_mapped_files.insert(container.to_string(), mapped_files);
-            Ok(file_ptr)
-        }
-    }
-}
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-    use crate::test_utils::{write_storage_text_to_temp_file, TestStorageFileSet};
-
-    #[test]
-    fn test_find_storage_file_location() {
-        let text_proto = r#"
-files {
-    version: 0
-    container: "system"
-    package_map: "/system/etc/package.map"
-    flag_map: "/system/etc/flag.map"
-    flag_val: "/metadata/aconfig/system.val"
-    timestamp: 12345
-}
-files {
-    version: 1
-    container: "product"
-    package_map: "/product/etc/package.map"
-    flag_map: "/product/etc/flag.map"
-    flag_val: "/metadata/aconfig/product.val"
-    timestamp: 54321
-}
-"#;
-        let file = write_storage_text_to_temp_file(text_proto).unwrap();
-        let file_full_path = file.path().display().to_string();
-        let file_info = find_container_storage_location(&file_full_path, "system").unwrap();
-        assert_eq!(file_info.version(), 0);
-        assert_eq!(file_info.container(), "system");
-        assert_eq!(file_info.package_map(), "/system/etc/package.map");
-        assert_eq!(file_info.flag_map(), "/system/etc/flag.map");
-        assert_eq!(file_info.flag_val(), "/metadata/aconfig/system.val");
-        assert_eq!(file_info.timestamp(), 12345);
-
-        let file_info = find_container_storage_location(&file_full_path, "product").unwrap();
-        assert_eq!(file_info.version(), 1);
-        assert_eq!(file_info.container(), "product");
-        assert_eq!(file_info.package_map(), "/product/etc/package.map");
-        assert_eq!(file_info.flag_map(), "/product/etc/flag.map");
-        assert_eq!(file_info.flag_val(), "/metadata/aconfig/product.val");
-        assert_eq!(file_info.timestamp(), 54321);
-
-        let err = find_container_storage_location(&file_full_path, "vendor").unwrap_err();
-        assert_eq!(
-            format!("{:?}", err),
-            "StorageFileNotFound(Storage file does not exist for vendor)"
-        );
-    }
-
-    fn map_and_verify(
-        location_pb_file: &str,
-        file_selection: StorageFileSelection,
-        actual_file: &str,
-    ) {
-        let mut opened_file = File::open(actual_file).unwrap();
-        let mut content = Vec::new();
-        opened_file.read_to_end(&mut content).unwrap();
-
-        let mmaped_file = get_mapped_file(location_pb_file, "system", file_selection).unwrap();
-        assert_eq!(mmaped_file[..], content[..]);
-    }
-
-    fn create_test_storage_files(read_only: bool) -> TestStorageFileSet {
-        TestStorageFileSet::new(
-            "./tests/package.map",
-            "./tests/flag.map",
-            "./tests/flag.val",
-            read_only,
-        )
-        .unwrap()
-    }
-
-    #[test]
-    fn test_mapped_file_contents() {
-        let ro_files = create_test_storage_files(true);
-        let text_proto = format!(
-            r#"
-files {{
-    version: 0
-    container: "system"
-    package_map: "{}"
-    flag_map: "{}"
-    flag_val: "{}"
-    timestamp: 12345
-}}
-"#,
-            ro_files.package_map.name, ro_files.flag_map.name, ro_files.flag_val.name
-        );
-
-        let file = write_storage_text_to_temp_file(&text_proto).unwrap();
-        let file_full_path = file.path().display().to_string();
-        map_and_verify(
-            &file_full_path,
-            StorageFileSelection::PackageMap,
-            &ro_files.package_map.name,
-        );
-        map_and_verify(&file_full_path, StorageFileSelection::FlagMap, &ro_files.flag_map.name);
-        map_and_verify(&file_full_path, StorageFileSelection::FlagVal, &ro_files.flag_val.name);
-    }
-
-    #[test]
-    fn test_map_non_read_only_file() {
-        let ro_files = create_test_storage_files(true);
-        let rw_files = create_test_storage_files(false);
-        let text_proto = format!(
-            r#"
-files {{
-    version: 0
-    container: "system"
-    package_map: "{}"
-    flag_map: "{}"
-    flag_val: "{}"
-    timestamp: 12345
-}}
-"#,
-            rw_files.package_map.name, ro_files.flag_map.name, ro_files.flag_val.name
-        );
-
-        let file = write_storage_text_to_temp_file(&text_proto).unwrap();
-        let file_full_path = file.path().display().to_string();
-        let error = map_container_storage_files(&file_full_path, "system").unwrap_err();
-        assert_eq!(
-            format!("{:?}", error),
-            format!(
-                "MapFileFail(fail to map non read only storage file {})",
-                rw_files.package_map.name
-            )
-        );
-
-        let text_proto = format!(
-            r#"
-files {{
-    version: 0
-    container: "system"
-    package_map: "{}"
-    flag_map: "{}"
-    flag_val: "{}"
-    timestamp: 12345
-}}
-"#,
-            ro_files.package_map.name, rw_files.flag_map.name, ro_files.flag_val.name
-        );
-
-        let file = write_storage_text_to_temp_file(&text_proto).unwrap();
-        let file_full_path = file.path().display().to_string();
-        let error = map_container_storage_files(&file_full_path, "system").unwrap_err();
-        assert_eq!(
-            format!("{:?}", error),
-            format!(
-                "MapFileFail(fail to map non read only storage file {})",
-                rw_files.flag_map.name
-            )
-        );
-
-        let text_proto = format!(
-            r#"
-files {{
-    version: 0
-    container: "system"
-    package_map: "{}"
-    flag_map: "{}"
-    flag_val: "{}"
-    timestamp: 12345
-}}
-"#,
-            ro_files.package_map.name, ro_files.flag_map.name, rw_files.flag_val.name
-        );
-
-        let file = write_storage_text_to_temp_file(&text_proto).unwrap();
-        let file_full_path = file.path().display().to_string();
-        let error = map_container_storage_files(&file_full_path, "system").unwrap_err();
-        assert_eq!(
-            format!("{:?}", error),
-            format!(
-                "MapFileFail(fail to map non read only storage file {})",
-                rw_files.flag_val.name
-            )
-        );
-    }
-}
diff --git a/tools/aconfig/aconfig_storage_file/src/package_table.rs b/tools/aconfig/aconfig_storage_file/src/package_table.rs
index 7308d7b..36b0493 100644
--- a/tools/aconfig/aconfig_storage_file/src/package_table.rs
+++ b/tools/aconfig/aconfig_storage_file/src/package_table.rs
@@ -17,29 +17,52 @@
 //! package table module defines the package table file format and methods for serialization
 //! and deserialization
 
-use crate::AconfigStorageError::{self, BytesParseFail, HigherStorageFileVersion};
-use crate::{get_bucket_index, read_str_from_bytes, read_u32_from_bytes};
+use crate::{get_bucket_index, read_str_from_bytes, read_u32_from_bytes, read_u8_from_bytes};
+use crate::{AconfigStorageError, StorageFileType};
 use anyhow::anyhow;
+use std::fmt;
 
 /// Package table header struct
-#[derive(PartialEq, Debug)]
+#[derive(PartialEq)]
 pub struct PackageTableHeader {
     pub version: u32,
     pub container: String,
+    pub file_type: u8,
     pub file_size: u32,
     pub num_packages: u32,
     pub bucket_offset: u32,
     pub node_offset: u32,
 }
 
+/// Implement debug print trait for header
+impl fmt::Debug for PackageTableHeader {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        writeln!(
+            f,
+            "Version: {}, Container: {}, File Type: {:?}, File Size: {}",
+            self.version,
+            self.container,
+            StorageFileType::try_from(self.file_type),
+            self.file_size
+        )?;
+        writeln!(
+            f,
+            "Num of Packages: {}, Bucket Offset:{}, Node Offset: {}",
+            self.num_packages, self.bucket_offset, self.node_offset
+        )?;
+        Ok(())
+    }
+}
+
 impl PackageTableHeader {
     /// Serialize to bytes
-    pub fn as_bytes(&self) -> Vec<u8> {
+    pub fn into_bytes(&self) -> Vec<u8> {
         let mut result = Vec::new();
         result.extend_from_slice(&self.version.to_le_bytes());
         let container_bytes = self.container.as_bytes();
         result.extend_from_slice(&(container_bytes.len() as u32).to_le_bytes());
         result.extend_from_slice(container_bytes);
+        result.extend_from_slice(&self.file_type.to_le_bytes());
         result.extend_from_slice(&self.file_size.to_le_bytes());
         result.extend_from_slice(&self.num_packages.to_le_bytes());
         result.extend_from_slice(&self.bucket_offset.to_le_bytes());
@@ -50,19 +73,26 @@
     /// Deserialize from bytes
     pub fn from_bytes(bytes: &[u8]) -> Result<Self, AconfigStorageError> {
         let mut head = 0;
-        Ok(Self {
+        let table = Self {
             version: read_u32_from_bytes(bytes, &mut head)?,
             container: read_str_from_bytes(bytes, &mut head)?,
+            file_type: read_u8_from_bytes(bytes, &mut head)?,
             file_size: read_u32_from_bytes(bytes, &mut head)?,
             num_packages: read_u32_from_bytes(bytes, &mut head)?,
             bucket_offset: read_u32_from_bytes(bytes, &mut head)?,
             node_offset: read_u32_from_bytes(bytes, &mut head)?,
-        })
+        };
+        if table.file_type != StorageFileType::PackageMap as u8 {
+            return Err(AconfigStorageError::BytesParseFail(anyhow!(
+                "binary file is not a package map"
+            )));
+        }
+        Ok(table)
     }
 }
 
 /// Package table node struct
-#[derive(PartialEq, Debug)]
+#[derive(PartialEq)]
 pub struct PackageTableNode {
     pub package_name: String,
     pub package_id: u32,
@@ -72,9 +102,21 @@
     pub next_offset: Option<u32>,
 }
 
+/// Implement debug print trait for node
+impl fmt::Debug for PackageTableNode {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        writeln!(
+            f,
+            "Package: {}, Id: {}, Offset: {}, Next: {:?}",
+            self.package_name, self.package_id, self.boolean_offset, self.next_offset
+        )?;
+        Ok(())
+    }
+}
+
 impl PackageTableNode {
     /// Serialize to bytes
-    pub fn as_bytes(&self) -> Vec<u8> {
+    pub fn into_bytes(&self) -> Vec<u8> {
         let mut result = Vec::new();
         let name_bytes = self.package_name.as_bytes();
         result.extend_from_slice(&(name_bytes.len() as u32).to_le_bytes());
@@ -109,20 +151,35 @@
 }
 
 /// Package table struct
-#[derive(PartialEq, Debug)]
+#[derive(PartialEq)]
 pub struct PackageTable {
     pub header: PackageTableHeader,
     pub buckets: Vec<Option<u32>>,
     pub nodes: Vec<PackageTableNode>,
 }
 
+/// Implement debug print trait for package table
+impl fmt::Debug for PackageTable {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        writeln!(f, "Header:")?;
+        write!(f, "{:?}", self.header)?;
+        writeln!(f, "Buckets:")?;
+        writeln!(f, "{:?}", self.buckets)?;
+        writeln!(f, "Nodes:")?;
+        for node in self.nodes.iter() {
+            write!(f, "{:?}", node)?;
+        }
+        Ok(())
+    }
+}
+
 impl PackageTable {
     /// Serialize to bytes
-    pub fn as_bytes(&self) -> Vec<u8> {
+    pub fn into_bytes(&self) -> Vec<u8> {
         [
-            self.header.as_bytes(),
+            self.header.into_bytes(),
             self.buckets.iter().map(|v| v.unwrap_or(0).to_le_bytes()).collect::<Vec<_>>().concat(),
-            self.nodes.iter().map(|v| v.as_bytes()).collect::<Vec<_>>().concat(),
+            self.nodes.iter().map(|v| v.into_bytes()).collect::<Vec<_>>().concat(),
         ]
         .concat()
     }
@@ -132,7 +189,7 @@
         let header = PackageTableHeader::from_bytes(bytes)?;
         let num_packages = header.num_packages;
         let num_buckets = crate::get_table_size(num_packages)?;
-        let mut head = header.as_bytes().len();
+        let mut head = header.into_bytes().len();
         let buckets = (0..num_buckets)
             .map(|_| match read_u32_from_bytes(bytes, &mut head).unwrap() {
                 0 => None,
@@ -142,173 +199,69 @@
         let nodes = (0..num_packages)
             .map(|_| {
                 let node = PackageTableNode::from_bytes(&bytes[head..])?;
-                head += node.as_bytes().len();
+                head += node.into_bytes().len();
                 Ok(node)
             })
             .collect::<Result<Vec<_>, AconfigStorageError>>()
-            .map_err(|errmsg| BytesParseFail(anyhow!("fail to parse package table: {}", errmsg)))?;
+            .map_err(|errmsg| {
+                AconfigStorageError::BytesParseFail(anyhow!(
+                    "fail to parse package table: {}",
+                    errmsg
+                ))
+            })?;
 
         let table = Self { header, buckets, nodes };
         Ok(table)
     }
 }
 
-/// Package table query return
-#[derive(PartialEq, Debug)]
-pub struct PackageOffset {
-    pub package_id: u32,
-    pub boolean_offset: u32,
-}
-
-/// Query package id and start offset
-pub fn find_package_offset(
-    buf: &[u8],
-    package: &str,
-) -> Result<Option<PackageOffset>, AconfigStorageError> {
-    let interpreted_header = PackageTableHeader::from_bytes(buf)?;
-    if interpreted_header.version > crate::FILE_VERSION {
-        return Err(HigherStorageFileVersion(anyhow!(
-            "Cannot read storage file with a higher version of {} with lib version {}",
-            interpreted_header.version,
-            crate::FILE_VERSION
-        )));
-    }
-
-    let num_buckets = (interpreted_header.node_offset - interpreted_header.bucket_offset) / 4;
-    let bucket_index = PackageTableNode::find_bucket_index(package, num_buckets);
-
-    let mut pos = (interpreted_header.bucket_offset + 4 * bucket_index) as usize;
-    let mut package_node_offset = read_u32_from_bytes(buf, &mut pos)? as usize;
-    if package_node_offset < interpreted_header.node_offset as usize
-        || package_node_offset >= interpreted_header.file_size as usize
-    {
-        return Ok(None);
-    }
-
-    loop {
-        let interpreted_node = PackageTableNode::from_bytes(&buf[package_node_offset..])?;
-        if interpreted_node.package_name == package {
-            return Ok(Some(PackageOffset {
-                package_id: interpreted_node.package_id,
-                boolean_offset: interpreted_node.boolean_offset,
-            }));
-        }
-        match interpreted_node.next_offset {
-            Some(offset) => package_node_offset = offset as usize,
-            None => return Ok(None),
-        }
-    }
-}
-
 #[cfg(test)]
 mod tests {
     use super::*;
-
-    pub fn create_test_package_table() -> PackageTable {
-        let header = PackageTableHeader {
-            version: crate::FILE_VERSION,
-            container: String::from("system"),
-            file_size: 208,
-            num_packages: 3,
-            bucket_offset: 30,
-            node_offset: 58,
-        };
-        let buckets: Vec<Option<u32>> = vec![Some(58), None, None, Some(108), None, None, None];
-        let first_node = PackageTableNode {
-            package_name: String::from("com.android.aconfig.storage.test_2"),
-            package_id: 1,
-            boolean_offset: 3,
-            next_offset: None,
-        };
-        let second_node = PackageTableNode {
-            package_name: String::from("com.android.aconfig.storage.test_1"),
-            package_id: 0,
-            boolean_offset: 0,
-            next_offset: Some(158),
-        };
-        let third_node = PackageTableNode {
-            package_name: String::from("com.android.aconfig.storage.test_4"),
-            package_id: 2,
-            boolean_offset: 6,
-            next_offset: None,
-        };
-        let nodes = vec![first_node, second_node, third_node];
-        PackageTable { header, buckets, nodes }
-    }
+    use crate::test_utils::create_test_package_table;
 
     #[test]
     // this test point locks down the table serialization
     fn test_serialization() {
         let package_table = create_test_package_table();
         let header: &PackageTableHeader = &package_table.header;
-        let reinterpreted_header = PackageTableHeader::from_bytes(&header.as_bytes());
+        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.as_bytes()).unwrap();
+            let reinterpreted_node = PackageTableNode::from_bytes(&node.into_bytes()).unwrap();
             assert_eq!(node, &reinterpreted_node);
         }
 
-        let reinterpreted_table = PackageTable::from_bytes(&package_table.as_bytes());
+        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]
-    // this test point locks down table query
-    fn test_package_query() {
-        let package_table = create_test_package_table().as_bytes();
-        let package_offset =
-            find_package_offset(&package_table[..], "com.android.aconfig.storage.test_1")
-                .unwrap()
-                .unwrap();
-        let expected_package_offset = PackageOffset { package_id: 0, boolean_offset: 0 };
-        assert_eq!(package_offset, expected_package_offset);
-        let package_offset =
-            find_package_offset(&package_table[..], "com.android.aconfig.storage.test_2")
-                .unwrap()
-                .unwrap();
-        let expected_package_offset = PackageOffset { package_id: 1, boolean_offset: 3 };
-        assert_eq!(package_offset, expected_package_offset);
-        let package_offset =
-            find_package_offset(&package_table[..], "com.android.aconfig.storage.test_4")
-                .unwrap()
-                .unwrap();
-        let expected_package_offset = PackageOffset { package_id: 2, boolean_offset: 6 };
-        assert_eq!(package_offset, expected_package_offset);
+    // 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 bytes = &package_table.into_bytes();
+        let mut head = 0;
+        let version = read_u32_from_bytes(bytes, &mut head).unwrap();
+        assert_eq!(version, 1);
     }
 
     #[test]
-    // 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().as_bytes();
-        let package_offset =
-            find_package_offset(&package_table[..], "com.android.aconfig.storage.test_3").unwrap();
-        assert_eq!(package_offset, None);
-        // this will land at the end of a linked list
-        let package_offset =
-            find_package_offset(&package_table[..], "com.android.aconfig.storage.test_5").unwrap();
-        assert_eq!(package_offset, None);
-    }
-
-    #[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();
-        table.header.version = crate::FILE_VERSION + 1;
-        let package_table = table.as_bytes();
-        let error = find_package_offset(&package_table[..], "com.android.aconfig.storage.test_1")
-            .unwrap_err();
+    // this test point locks down file type check
+    fn test_file_type_check() {
+        let mut package_table = create_test_package_table();
+        package_table.header.file_type = 123u8;
+        let error = PackageTable::from_bytes(&package_table.into_bytes()).unwrap_err();
         assert_eq!(
             format!("{:?}", error),
-            format!(
-                "HigherStorageFileVersion(Cannot read storage file with a higher version of {} with lib version {})",
-                crate::FILE_VERSION + 1,
-                crate::FILE_VERSION
-            )
+            format!("BytesParseFail(binary file is not a package map)")
         );
     }
 }
diff --git a/tools/aconfig/aconfig_storage_file/src/protos.rs b/tools/aconfig/aconfig_storage_file/src/protos.rs
index 37df3e1..8b86205 100644
--- a/tools/aconfig/aconfig_storage_file/src/protos.rs
+++ b/tools/aconfig/aconfig_storage_file/src/protos.rs
@@ -49,8 +49,11 @@
 pub use auto_generated::*;
 
 use anyhow::Result;
+use protobuf::Message;
+use std::io::Write;
+use tempfile::NamedTempFile;
 
-pub mod storage_files {
+pub mod storage_record_pb {
     use super::*;
     use anyhow::ensure;
 
@@ -80,15 +83,28 @@
         }
         Ok(())
     }
+
+    pub fn get_binary_proto_from_text_proto(text_proto: &str) -> Result<Vec<u8>> {
+        let storage_files: ProtoStorageFiles = protobuf::text_format::parse_from_str(text_proto)?;
+        let mut binary_proto = Vec::new();
+        storage_files.write_to_vec(&mut binary_proto)?;
+        Ok(binary_proto)
+    }
+
+    pub fn write_proto_to_temp_file(text_proto: &str) -> Result<NamedTempFile> {
+        let bytes = get_binary_proto_from_text_proto(text_proto).unwrap();
+        let mut file = NamedTempFile::new()?;
+        let _ = file.write_all(&bytes);
+        Ok(file)
+    }
 }
 
 #[cfg(test)]
 mod tests {
     use super::*;
-    use crate::test_utils::get_binary_storage_proto_bytes;
 
     #[test]
-    fn test_parse_storage_files() {
+    fn test_parse_storage_record_pb() {
         let text_proto = r#"
 files {
     version: 0
@@ -107,8 +123,9 @@
     timestamp: 54321
 }
 "#;
-        let binary_proto_bytes = get_binary_storage_proto_bytes(text_proto).unwrap();
-        let storage_files = storage_files::try_from_binary_proto(&binary_proto_bytes).unwrap();
+        let binary_proto_bytes =
+            storage_record_pb::get_binary_proto_from_text_proto(text_proto).unwrap();
+        let storage_files = storage_record_pb::try_from_binary_proto(&binary_proto_bytes).unwrap();
         assert_eq!(storage_files.files.len(), 2);
         let system_file = &storage_files.files[0];
         assert_eq!(system_file.version(), 0);
@@ -127,7 +144,7 @@
     }
 
     #[test]
-    fn test_parse_invalid_storage_files() {
+    fn test_parse_invalid_storage_record_pb() {
         let text_proto = r#"
 files {
     version: 0
@@ -138,8 +155,9 @@
     timestamp: 12345
 }
 "#;
-        let binary_proto_bytes = get_binary_storage_proto_bytes(text_proto).unwrap();
-        let err = storage_files::try_from_binary_proto(&binary_proto_bytes).unwrap_err();
+        let binary_proto_bytes =
+            storage_record_pb::get_binary_proto_from_text_proto(text_proto).unwrap();
+        let err = storage_record_pb::try_from_binary_proto(&binary_proto_bytes).unwrap_err();
         assert_eq!(
             format!("{:?}", err),
             "invalid storage file record: missing package map file for container system"
@@ -155,8 +173,9 @@
     timestamp: 12345
 }
 "#;
-        let binary_proto_bytes = get_binary_storage_proto_bytes(text_proto).unwrap();
-        let err = storage_files::try_from_binary_proto(&binary_proto_bytes).unwrap_err();
+        let binary_proto_bytes =
+            storage_record_pb::get_binary_proto_from_text_proto(text_proto).unwrap();
+        let err = storage_record_pb::try_from_binary_proto(&binary_proto_bytes).unwrap_err();
         assert_eq!(
             format!("{:?}", err),
             "invalid storage file record: missing flag map file for container system"
@@ -172,8 +191,9 @@
     timestamp: 12345
 }
 "#;
-        let binary_proto_bytes = get_binary_storage_proto_bytes(text_proto).unwrap();
-        let err = storage_files::try_from_binary_proto(&binary_proto_bytes).unwrap_err();
+        let binary_proto_bytes =
+            storage_record_pb::get_binary_proto_from_text_proto(text_proto).unwrap();
+        let err = storage_record_pb::try_from_binary_proto(&binary_proto_bytes).unwrap_err();
         assert_eq!(
             format!("{:?}", err),
             "invalid storage file record: missing flag val file for container system"
diff --git a/tools/aconfig/aconfig_storage_file/src/test_utils.rs b/tools/aconfig/aconfig_storage_file/src/test_utils.rs
index 7905d51..c0f647a 100644
--- a/tools/aconfig/aconfig_storage_file/src/test_utils.rs
+++ b/tools/aconfig/aconfig_storage_file/src/test_utils.rs
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2023 The Android Open Source Project
+ * 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.
@@ -14,79 +14,141 @@
  * limitations under the License.
  */
 
-use crate::protos::ProtoStorageFiles;
-use anyhow::Result;
-use protobuf::Message;
-use std::fs;
+use crate::flag_info::{FlagInfoHeader, FlagInfoList, FlagInfoNode};
+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};
+
+use anyhow::anyhow;
 use std::io::Write;
 use tempfile::NamedTempFile;
 
-pub(crate) fn get_binary_storage_proto_bytes(text_proto: &str) -> Result<Vec<u8>> {
-    let storage_files: ProtoStorageFiles = protobuf::text_format::parse_from_str(text_proto)?;
-    let mut binary_proto = Vec::new();
-    storage_files.write_to_vec(&mut binary_proto)?;
-    Ok(binary_proto)
+pub fn create_test_package_table() -> PackageTable {
+    let header = PackageTableHeader {
+        version: 1,
+        container: String::from("mockup"),
+        file_type: StorageFileType::PackageMap as u8,
+        file_size: 209,
+        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 first_node = PackageTableNode {
+        package_name: String::from("com.android.aconfig.storage.test_2"),
+        package_id: 1,
+        boolean_offset: 3,
+        next_offset: None,
+    };
+    let second_node = PackageTableNode {
+        package_name: String::from("com.android.aconfig.storage.test_1"),
+        package_id: 0,
+        boolean_offset: 0,
+        next_offset: Some(159),
+    };
+    let third_node = PackageTableNode {
+        package_name: String::from("com.android.aconfig.storage.test_4"),
+        package_id: 2,
+        boolean_offset: 6,
+        next_offset: None,
+    };
+    let nodes = vec![first_node, second_node, third_node];
+    PackageTable { header, buckets, nodes }
 }
 
-pub(crate) fn write_storage_text_to_temp_file(text_proto: &str) -> Result<NamedTempFile> {
-    let bytes = get_binary_storage_proto_bytes(text_proto).unwrap();
-    let mut file = NamedTempFile::new()?;
+impl FlagTableNode {
+    // create test baseline, syntactic sugar
+    fn new_expected(
+        package_id: u32,
+        flag_name: &str,
+        flag_type: u16,
+        flag_id: u16,
+        next_offset: Option<u32>,
+    ) -> Self {
+        Self {
+            package_id,
+            flag_name: flag_name.to_string(),
+            flag_type: StoredFlagType::try_from(flag_type).unwrap(),
+            flag_id,
+            next_offset,
+        }
+    }
+}
+
+pub fn create_test_flag_table() -> FlagTable {
+    let header = FlagTableHeader {
+        version: 1,
+        container: String::from("mockup"),
+        file_type: StorageFileType::FlagMap as u8,
+        file_size: 321,
+        num_flags: 8,
+        bucket_offset: 31,
+        node_offset: 99,
+    };
+    let buckets: Vec<Option<u32>> = vec![
+        Some(99),
+        Some(125),
+        None,
+        None,
+        None,
+        Some(178),
+        None,
+        Some(204),
+        None,
+        Some(262),
+        None,
+        None,
+        None,
+        None,
+        None,
+        Some(294),
+        None,
+    ];
+    let nodes = vec![
+        FlagTableNode::new_expected(0, "enabled_ro", 1, 1, None),
+        FlagTableNode::new_expected(0, "enabled_rw", 0, 2, Some(151)),
+        FlagTableNode::new_expected(1, "disabled_ro", 1, 0, None),
+        FlagTableNode::new_expected(2, "enabled_ro", 1, 1, None),
+        FlagTableNode::new_expected(1, "enabled_fixed_ro", 2, 1, Some(236)),
+        FlagTableNode::new_expected(1, "enabled_ro", 1, 2, None),
+        FlagTableNode::new_expected(2, "enabled_fixed_ro", 2, 0, None),
+        FlagTableNode::new_expected(0, "disabled_rw", 0, 0, None),
+    ];
+    FlagTable { header, buckets, nodes }
+}
+
+pub fn create_test_flag_value_list() -> FlagValueList {
+    let header = FlagValueHeader {
+        version: 1,
+        container: String::from("mockup"),
+        file_type: StorageFileType::FlagVal as u8,
+        file_size: 35,
+        num_flags: 8,
+        boolean_value_offset: 27,
+    };
+    let booleans: Vec<bool> = vec![false, true, true, false, true, true, true, true];
+    FlagValueList { header, booleans }
+}
+
+pub fn create_test_flag_info_list() -> FlagInfoList {
+    let header = FlagInfoHeader {
+        version: 1,
+        container: String::from("mockup"),
+        file_type: StorageFileType::FlagInfo as u8,
+        file_size: 35,
+        num_flags: 8,
+        boolean_flag_offset: 27,
+    };
+    let is_flag_rw = [true, false, true, false, false, false, false, false];
+    let nodes = is_flag_rw.iter().map(|&rw| FlagInfoNode::create(rw)).collect();
+    FlagInfoList { header, nodes }
+}
+
+pub fn write_bytes_to_temp_file(bytes: &[u8]) -> Result<NamedTempFile, AconfigStorageError> {
+    let mut file = NamedTempFile::new().map_err(|_| {
+        AconfigStorageError::FileCreationFail(anyhow!("Failed to create temp file"))
+    })?;
     let _ = file.write_all(&bytes);
     Ok(file)
 }
-
-fn set_file_read_only(file: &NamedTempFile) {
-    let mut perms = fs::metadata(file.path()).unwrap().permissions();
-    if !perms.readonly() {
-        perms.set_readonly(true);
-        fs::set_permissions(file.path(), perms).unwrap();
-    }
-}
-
-fn set_file_read_write(file: &NamedTempFile) {
-    let mut perms = fs::metadata(file.path()).unwrap().permissions();
-    if perms.readonly() {
-        perms.set_readonly(false);
-        fs::set_permissions(file.path(), perms).unwrap();
-    }
-}
-
-pub(crate) struct TestStorageFile {
-    pub file: NamedTempFile,
-    pub name: String,
-}
-
-impl TestStorageFile {
-    pub(crate) fn new(source_file: &str, read_only: bool) -> Result<Self> {
-        let file = NamedTempFile::new()?;
-        fs::copy(source_file, file.path())?;
-        if read_only {
-            set_file_read_only(&file);
-        } else {
-            set_file_read_write(&file);
-        }
-        let name = file.path().display().to_string();
-        Ok(Self { file, name })
-    }
-}
-
-pub(crate) struct TestStorageFileSet {
-    pub package_map: TestStorageFile,
-    pub flag_map: TestStorageFile,
-    pub flag_val: TestStorageFile,
-}
-
-impl TestStorageFileSet {
-    pub(crate) fn new(
-        package_map_path: &str,
-        flag_map_path: &str,
-        flag_val_path: &str,
-        read_only: bool,
-    ) -> Result<Self> {
-        Ok(Self {
-            package_map: TestStorageFile::new(package_map_path, read_only)?,
-            flag_map: TestStorageFile::new(flag_map_path, read_only)?,
-            flag_val: TestStorageFile::new(flag_val_path, read_only)?,
-        })
-    }
-}
diff --git a/tools/aconfig/aconfig_storage_file/tests/Android.bp b/tools/aconfig/aconfig_storage_file/tests/Android.bp
deleted file mode 100644
index b951273..0000000
--- a/tools/aconfig/aconfig_storage_file/tests/Android.bp
+++ /dev/null
@@ -1,42 +0,0 @@
-rust_test {
-    name: "aconfig_storage.test.rust",
-    srcs: [
-        "storage_lib_rust_test.rs"
-    ],
-    rustlibs: [
-        "libanyhow",
-        "libaconfig_storage_file",
-        "libprotobuf",
-        "libtempfile",
-    ],
-    data: [
-        ":ro.package.map",
-        ":ro.flag.map",
-        ":ro.flag.val",
-    ],
-    test_suites: ["general-tests"],
-}
-
-cc_test {
-    name: "aconfig_storage.test.cpp",
-    srcs: [
-        "storage_lib_cc_test.cpp",
-    ],
-    static_libs: [
-        "libgmock",
-        "libaconfig_storage_protos_cc",
-        "libprotobuf-cpp-lite",
-        "libaconfig_storage_cc",
-        "libbase",
-        "liblog",
-    ],
-    data: [
-        ":ro.package.map",
-        ":ro.flag.map",
-        ":ro.flag.val",
-    ],
-    test_suites: [
-        "device-tests",
-        "general-tests",
-    ],
-}
diff --git a/tools/aconfig/aconfig_storage_file/tests/flag.map b/tools/aconfig/aconfig_storage_file/tests/flag.map
deleted file mode 100644
index 43b6f9a..0000000
--- a/tools/aconfig/aconfig_storage_file/tests/flag.map
+++ /dev/null
Binary files differ
diff --git a/tools/aconfig/aconfig_storage_file/tests/flag.val b/tools/aconfig/aconfig_storage_file/tests/flag.val
deleted file mode 100644
index f39f8d3..0000000
--- a/tools/aconfig/aconfig_storage_file/tests/flag.val
+++ /dev/null
Binary files differ
diff --git a/tools/aconfig/aconfig_storage_file/tests/package.map b/tools/aconfig/aconfig_storage_file/tests/package.map
deleted file mode 100644
index 8ed4767..0000000
--- a/tools/aconfig/aconfig_storage_file/tests/package.map
+++ /dev/null
Binary files differ
diff --git a/tools/aconfig/aconfig_storage_file/tests/storage_lib_cc_test.cpp b/tools/aconfig/aconfig_storage_file/tests/storage_lib_cc_test.cpp
deleted file mode 100644
index 7d5ba0a..0000000
--- a/tools/aconfig/aconfig_storage_file/tests/storage_lib_cc_test.cpp
+++ /dev/null
@@ -1,161 +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.
- */
-
-#include <string>
-#include <vector>
-
-#include "aconfig_storage/aconfig_storage.hpp"
-#include <gtest/gtest.h>
-#include <protos/aconfig_storage_metadata.pb.h>
-#include <android-base/file.h>
-
-using android::aconfig_storage_metadata::storage_files;
-using ::android::base::WriteStringToFile;
-using ::aconfig_storage::test_only_api::get_package_offset_impl;
-using ::aconfig_storage::test_only_api::get_flag_offset_impl;
-using ::aconfig_storage::test_only_api::get_boolean_flag_value_impl;
-
-void write_storage_location_pb_to_file(std::string const& file_path) {
-  auto const test_dir = android::base::GetExecutableDirectory();
-  auto proto = storage_files();
-  auto* info = proto.add_files();
-  info->set_version(0);
-  info->set_container("system");
-  info->set_package_map(test_dir + "/tests/tmp.ro.package.map");
-  info->set_flag_map(test_dir + "/tests/tmp.ro.flag.map");
-  info->set_flag_val(test_dir + "/tests/tmp.ro.flag.val");
-  info->set_timestamp(12345);
-
-  auto content = std::string();
-  proto.SerializeToString(&content);
-  ASSERT_TRUE(WriteStringToFile(content, file_path))
-      << "Failed to write a file: " << file_path;
-}
-
-TEST(AconfigStorageTest, test_package_offset_query) {
-  auto pb_file = std::string("/tmp/test_package_offset_query.pb");
-  write_storage_location_pb_to_file(pb_file);
-
-  auto query = get_package_offset_impl(
-      pb_file, "system", "com.android.aconfig.storage.test_1");
-  ASSERT_EQ(query.error_message, std::string());
-  ASSERT_TRUE(query.query_success);
-  ASSERT_TRUE(query.package_exists);
-  ASSERT_EQ(query.package_id, 0);
-  ASSERT_EQ(query.boolean_offset, 0);
-
-  query = get_package_offset_impl(
-      pb_file, "system", "com.android.aconfig.storage.test_2");
-  ASSERT_EQ(query.error_message, std::string());
-  ASSERT_TRUE(query.query_success);
-  ASSERT_TRUE(query.package_exists);
-  ASSERT_EQ(query.package_id, 1);
-  ASSERT_EQ(query.boolean_offset, 3);
-
-  query = get_package_offset_impl(
-      pb_file, "system", "com.android.aconfig.storage.test_4");
-  ASSERT_EQ(query.error_message, std::string());
-  ASSERT_TRUE(query.query_success);
-  ASSERT_TRUE(query.package_exists);
-  ASSERT_EQ(query.package_id, 2);
-  ASSERT_EQ(query.boolean_offset, 6);
-}
-
-TEST(AconfigStorageTest, test_invalid_package_offset_query) {
-  auto pb_file = std::string("/tmp/test_package_offset_query.pb");
-  write_storage_location_pb_to_file(pb_file);
-
-  auto query = get_package_offset_impl(
-      pb_file, "system", "com.android.aconfig.storage.test_3");
-  ASSERT_EQ(query.error_message, std::string());
-  ASSERT_TRUE(query.query_success);
-  ASSERT_FALSE(query.package_exists);
-
-  query = get_package_offset_impl(
-      pb_file, "vendor", "com.android.aconfig.storage.test_1");
-  ASSERT_EQ(query.error_message,
-            std::string("StorageFileNotFound(Storage file does not exist for vendor)"));
-  ASSERT_FALSE(query.query_success);
-}
-
-TEST(AconfigStorageTest, test_flag_offset_query) {
-  auto pb_file = std::string("/tmp/test_package_offset_query.pb");
-  write_storage_location_pb_to_file(pb_file);
-
-  auto baseline = std::vector<std::tuple<int, std::string, int>>{
-    {0, "enabled_ro", 1},
-    {0, "enabled_rw", 2},
-    {1, "disabled_ro", 0},
-    {2, "enabled_ro", 1},
-    {1, "enabled_fixed_ro", 1},
-    {1, "enabled_ro", 2},
-    {2, "enabled_fixed_ro", 0},
-    {0, "disabled_rw", 0},
-  };
-  for (auto const&[package_id, flag_name, expected_offset] : baseline) {
-    auto query = get_flag_offset_impl(pb_file, "system", package_id, flag_name);
-    ASSERT_EQ(query.error_message, std::string());
-    ASSERT_TRUE(query.query_success);
-    ASSERT_TRUE(query.flag_exists);
-    ASSERT_EQ(query.flag_offset, expected_offset);
-  }
-}
-
-TEST(AconfigStorageTest, test_invalid_flag_offset_query) {
-  auto pb_file = std::string("/tmp/test_invalid_package_offset_query.pb");
-  write_storage_location_pb_to_file(pb_file);
-
-  auto query = get_flag_offset_impl(pb_file, "system", 0, "none_exist");
-  ASSERT_EQ(query.error_message, std::string());
-  ASSERT_TRUE(query.query_success);
-  ASSERT_FALSE(query.flag_exists);
-
-  query = get_flag_offset_impl(pb_file, "system", 3, "enabled_ro");
-  ASSERT_EQ(query.error_message, std::string());
-  ASSERT_TRUE(query.query_success);
-  ASSERT_FALSE(query.flag_exists);
-
-  query = get_flag_offset_impl(pb_file, "vendor", 0, "enabled_ro");
-  ASSERT_EQ(query.error_message,
-            std::string("StorageFileNotFound(Storage file does not exist for vendor)"));
-  ASSERT_FALSE(query.query_success);
-}
-
-TEST(AconfigStorageTest, test_boolean_flag_value_query) {
-  auto pb_file = std::string("/tmp/test_boolean_flag_value_query.pb");
-  write_storage_location_pb_to_file(pb_file);
-  for (int offset = 0; offset < 8; ++offset) {
-    auto query = get_boolean_flag_value_impl(pb_file, "system", offset);
-    ASSERT_EQ(query.error_message, std::string());
-    ASSERT_TRUE(query.query_success);
-    ASSERT_FALSE(query.flag_value);
-  }
-}
-
-TEST(AconfigStorageTest, test_invalid_boolean_flag_value_query) {
-  auto pb_file = std::string("/tmp/test_invalid_boolean_flag_value_query.pb");
-  write_storage_location_pb_to_file(pb_file);
-
-  auto query = get_boolean_flag_value_impl(pb_file, "vendor", 0);
-  ASSERT_EQ(query.error_message,
-            std::string("StorageFileNotFound(Storage file does not exist for vendor)"));
-  ASSERT_FALSE(query.query_success);
-
-  query = get_boolean_flag_value_impl(pb_file, "system", 8);
-  ASSERT_EQ(query.error_message,
-            std::string("InvalidStorageFileOffset(Flag value offset goes beyond the end of the file.)"));
-  ASSERT_FALSE(query.query_success);
-}
diff --git a/tools/aconfig/aconfig_storage_file/tests/storage_lib_rust_test.rs b/tools/aconfig/aconfig_storage_file/tests/storage_lib_rust_test.rs
deleted file mode 100644
index 9916915..0000000
--- a/tools/aconfig/aconfig_storage_file/tests/storage_lib_rust_test.rs
+++ /dev/null
@@ -1,174 +0,0 @@
-#[cfg(not(feature = "cargo"))]
-mod aconfig_storage_rust_test {
-    use aconfig_storage_file::{
-        get_boolean_flag_value_impl, get_flag_offset_impl, get_package_offset_impl, PackageOffset,
-        ProtoStorageFiles,
-    };
-    use protobuf::Message;
-    use std::io::Write;
-    use tempfile::NamedTempFile;
-
-    fn write_storage_location_file() -> NamedTempFile {
-        let text_proto = r#"
-files {
-    version: 0
-    container: "system"
-    package_map: "./tests/tmp.ro.package.map"
-    flag_map: "./tests/tmp.ro.flag.map"
-    flag_val: "./tests/tmp.ro.flag.val"
-    timestamp: 12345
-}
-"#;
-        let storage_files: ProtoStorageFiles =
-            protobuf::text_format::parse_from_str(text_proto).unwrap();
-        let mut binary_proto_bytes = Vec::new();
-        storage_files.write_to_vec(&mut binary_proto_bytes).unwrap();
-        let mut file = NamedTempFile::new().unwrap();
-        file.write_all(&binary_proto_bytes).unwrap();
-        file
-    }
-
-    #[test]
-    fn test_package_offset_query() {
-        let file = write_storage_location_file();
-        let file_full_path = file.path().display().to_string();
-
-        let package_offset = get_package_offset_impl(
-            &file_full_path,
-            "system",
-            "com.android.aconfig.storage.test_1",
-        )
-        .unwrap()
-        .unwrap();
-        let expected_package_offset = PackageOffset { package_id: 0, boolean_offset: 0 };
-        assert_eq!(package_offset, expected_package_offset);
-
-        let package_offset = get_package_offset_impl(
-            &file_full_path,
-            "system",
-            "com.android.aconfig.storage.test_2",
-        )
-        .unwrap()
-        .unwrap();
-        let expected_package_offset = PackageOffset { package_id: 1, boolean_offset: 3 };
-        assert_eq!(package_offset, expected_package_offset);
-
-        let package_offset = get_package_offset_impl(
-            &file_full_path,
-            "system",
-            "com.android.aconfig.storage.test_4",
-        )
-        .unwrap()
-        .unwrap();
-        let expected_package_offset = PackageOffset { package_id: 2, boolean_offset: 6 };
-        assert_eq!(package_offset, expected_package_offset);
-
-        let package_offset = get_package_offset_impl(
-            &file_full_path,
-            "system",
-            "com.android.aconfig.storage.test_3",
-        )
-        .unwrap();
-        assert_eq!(package_offset, None);
-    }
-
-    #[test]
-    fn test_invalid_package_offset_query() {
-        let file = write_storage_location_file();
-        let file_full_path = file.path().display().to_string();
-
-        let package_offset_option = get_package_offset_impl(
-            &file_full_path,
-            "system",
-            "com.android.aconfig.storage.test_3",
-        )
-        .unwrap();
-        assert_eq!(package_offset_option, None);
-
-        let err = get_package_offset_impl(
-            &file_full_path,
-            "vendor",
-            "com.android.aconfig.storage.test_1",
-        )
-        .unwrap_err();
-        assert_eq!(
-            format!("{:?}", err),
-            "StorageFileNotFound(Storage file does not exist for vendor)"
-        );
-    }
-
-    #[test]
-    fn test_flag_offset_query() {
-        let file = write_storage_location_file();
-        let file_full_path = file.path().display().to_string();
-
-        let baseline = vec![
-            (0, "enabled_ro", 1u16),
-            (0, "enabled_rw", 2u16),
-            (1, "disabled_ro", 0u16),
-            (2, "enabled_ro", 1u16),
-            (1, "enabled_fixed_ro", 1u16),
-            (1, "enabled_ro", 2u16),
-            (2, "enabled_fixed_ro", 0u16),
-            (0, "disabled_rw", 0u16),
-        ];
-        for (package_id, flag_name, expected_offset) in baseline.into_iter() {
-            let flag_offset =
-                get_flag_offset_impl(&file_full_path, "system", package_id, flag_name)
-                    .unwrap()
-                    .unwrap();
-            assert_eq!(flag_offset, expected_offset);
-        }
-    }
-
-    #[test]
-    fn test_invalid_flag_offset_query() {
-        let file = write_storage_location_file();
-        let file_full_path = file.path().display().to_string();
-
-        let flag_offset_option =
-            get_flag_offset_impl(&file_full_path, "system", 0, "none_exist").unwrap();
-        assert_eq!(flag_offset_option, None);
-
-        let flag_offset_option =
-            get_flag_offset_impl(&file_full_path, "system", 3, "enabled_ro").unwrap();
-        assert_eq!(flag_offset_option, None);
-
-        let err = get_flag_offset_impl(&file_full_path, "vendor", 0, "enabled_ro").unwrap_err();
-        assert_eq!(
-            format!("{:?}", err),
-            "StorageFileNotFound(Storage file does not exist for vendor)"
-        );
-    }
-
-    #[test]
-    fn test_boolean_flag_value_query() {
-        let file = write_storage_location_file();
-        let file_full_path = file.path().display().to_string();
-
-        let baseline: Vec<bool> = vec![false; 8];
-        for (offset, expected_value) in baseline.into_iter().enumerate() {
-            let flag_value =
-                get_boolean_flag_value_impl(&file_full_path, "system", offset as u32).unwrap();
-            assert_eq!(flag_value, expected_value);
-        }
-    }
-
-    #[test]
-    fn test_invalid_boolean_flag_value_query() {
-        let file = write_storage_location_file();
-        let file_full_path = file.path().display().to_string();
-
-        let err = get_boolean_flag_value_impl(&file_full_path, "vendor", 0u32).unwrap_err();
-        assert_eq!(
-            format!("{:?}", err),
-            "StorageFileNotFound(Storage file does not exist for vendor)"
-        );
-
-        let err = get_boolean_flag_value_impl(&file_full_path, "system", 8u32).unwrap_err();
-        assert_eq!(
-            format!("{:?}", err),
-            "InvalidStorageFileOffset(Flag value offset goes beyond the end of the file.)"
-        );
-    }
-}
diff --git a/tools/aconfig/aconfig_storage_read_api/Android.bp b/tools/aconfig/aconfig_storage_read_api/Android.bp
new file mode 100644
index 0000000..b252e9d
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_read_api/Android.bp
@@ -0,0 +1,86 @@
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+rust_defaults {
+    name: "aconfig_storage_read_api.defaults",
+    edition: "2021",
+    lints: "none",
+    srcs: ["src/lib.rs"],
+    rustlibs: [
+        "libanyhow",
+        "libonce_cell",
+        "libtempfile",
+        "libmemmap2",
+        "libcxx",
+        "libthiserror",
+        "libaconfig_storage_file",
+    ],
+}
+
+rust_library {
+    name: "libaconfig_storage_read_api",
+    crate_name: "aconfig_storage_read_api",
+    host_supported: true,
+    defaults: ["aconfig_storage_read_api.defaults"],
+    apex_available: [
+        "//apex_available:platform",
+        "//apex_available:anyapex",
+    ],
+    min_sdk_version: "29",
+}
+
+rust_test_host {
+    name: "aconfig_storage_read_api.test",
+    test_suites: ["general-tests"],
+    defaults: ["aconfig_storage_read_api.defaults"],
+    data: [
+        "tests/package.map",
+        "tests/flag.map",
+        "tests/flag.val",
+    ],
+}
+
+// cxx source codegen from rust api
+genrule {
+    name: "libcxx_aconfig_storage_read_api_bridge_code",
+    tools: ["cxxbridge"],
+    cmd: "$(location cxxbridge) $(in) > $(out)",
+    srcs: ["src/lib.rs"],
+    out: ["aconfig_storage/lib.rs.cc"],
+}
+
+// cxx header codegen from rust api
+genrule {
+    name: "libcxx_aconfig_storage_read_api_bridge_header",
+    tools: ["cxxbridge"],
+    cmd: "$(location cxxbridge) $(in) --header > $(out)",
+    srcs: ["src/lib.rs"],
+    out: ["aconfig_storage/lib.rs.h"],
+}
+
+// a static cc lib based on generated code
+rust_ffi_static {
+    name: "libaconfig_storage_read_api_cxx_bridge",
+    crate_name: "aconfig_storage_read_api_cxx_bridge",
+    host_supported: true,
+    defaults: ["aconfig_storage_read_api.defaults"],
+}
+
+// flag read api cc interface
+cc_library_static {
+    name: "libaconfig_storage_read_api_cc",
+    srcs: ["aconfig_storage_read_api.cpp"],
+    generated_headers: [
+        "cxx-bridge-header",
+        "libcxx_aconfig_storage_read_api_bridge_header",
+    ],
+    generated_sources: ["libcxx_aconfig_storage_read_api_bridge_code"],
+    whole_static_libs: ["libaconfig_storage_read_api_cxx_bridge"],
+    export_include_dirs: ["include"],
+    static_libs: [
+        "libaconfig_storage_protos_cc",
+        "libprotobuf-cpp-lite",
+        "libbase",
+    ],
+}
diff --git a/tools/aconfig/aconfig_storage_read_api/Cargo.toml b/tools/aconfig/aconfig_storage_read_api/Cargo.toml
new file mode 100644
index 0000000..30a4298
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_read_api/Cargo.toml
@@ -0,0 +1,21 @@
+[package]
+name = "aconfig_storage_read_api"
+version = "0.1.0"
+edition = "2021"
+
+[features]
+default = ["cargo"]
+cargo = []
+
+[dependencies]
+anyhow = "1.0.69"
+memmap2 = "0.8.0"
+once_cell = "1.19.0"
+tempfile = "3.9.0"
+cxx = "1.0"
+thiserror = "1.0.56"
+aconfig_storage_file = { path = "../aconfig_storage_file" }
+
+[build-dependencies]
+protobuf-codegen = "3.2.0"
+cxx-build = "1.0"
diff --git a/tools/aconfig/aconfig_storage_read_api/aconfig_storage_read_api.cpp b/tools/aconfig/aconfig_storage_read_api/aconfig_storage_read_api.cpp
new file mode 100644
index 0000000..2213831
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_read_api/aconfig_storage_read_api.cpp
@@ -0,0 +1,177 @@
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <protos/aconfig_storage_metadata.pb.h>
+
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include "rust/cxx.h"
+#include "aconfig_storage/lib.rs.h"
+#include "aconfig_storage/aconfig_storage_read_api.hpp"
+
+using storage_records_pb = android::aconfig_storage_metadata::storage_files;
+using storage_record_pb = android::aconfig_storage_metadata::storage_file_info;
+using namespace android::base;
+
+namespace aconfig_storage {
+
+/// Storage location pb file
+static constexpr char kAvailableStorageRecordsPb[] =
+    "/metadata/aconfig/boot/available_storage_file_records.pb";
+
+/// Read aconfig storage records pb file
+static Result<storage_records_pb> read_storage_records_pb(std::string const& pb_file) {
+  auto records = storage_records_pb();
+  auto content = std::string();
+  if (!ReadFileToString(pb_file, &content)) {
+    return ErrnoError() << "ReadFileToString failed";
+  }
+
+  if (!records.ParseFromString(content)) {
+    return ErrnoError() << "Unable to parse persistent storage records protobuf";
+  }
+  return records;
+}
+
+/// Get storage file path
+static Result<std::string> find_storage_file(
+    std::string const& pb_file,
+    std::string const& container,
+    StorageFileType file_type) {
+  auto records_pb = read_storage_records_pb(pb_file);
+  if (!records_pb.ok()) {
+    return Error() << "Unable to read storage records from " << pb_file
+                   << " : " << records_pb.error();
+  }
+
+  for (auto& entry : records_pb->files()) {
+    if (entry.container() == container) {
+      switch(file_type) {
+        case StorageFileType::package_map:
+          return entry.package_map();
+        case StorageFileType::flag_map:
+          return entry.flag_map();
+        case StorageFileType::flag_val:
+          return entry.flag_val();
+        default:
+          return Error() << "Invalid file type " << file_type;
+      }
+    }
+  }
+
+  return Error() << "Unable to find storage files for container " << container;;
+}
+
+/// Map a storage file
+static Result<MappedStorageFile> map_storage_file(std::string const& file) {
+  int fd = open(file.c_str(), O_CLOEXEC | O_NOFOLLOW | O_RDONLY);
+  if (fd == -1) {
+    return ErrnoError() << "failed to open " << file;
+  };
+
+  struct stat fd_stat;
+  if (fstat(fd, &fd_stat) < 0) {
+    return ErrnoError() << "fstat failed";
+  }
+  size_t file_size = fd_stat.st_size;
+
+  void* const map_result = mmap(nullptr, file_size, PROT_READ, MAP_SHARED, fd, 0);
+  if (map_result == MAP_FAILED) {
+    return ErrnoError() << "mmap failed";
+  }
+
+  auto mapped_file = MappedStorageFile();
+  mapped_file.file_ptr = map_result;
+  mapped_file.file_size = file_size;
+
+  return mapped_file;
+}
+
+namespace private_internal_api {
+
+/// Get mapped file implementation.
+Result<MappedStorageFile> get_mapped_file_impl(
+    std::string const& pb_file,
+    std::string const& container,
+    StorageFileType file_type) {
+  auto file_result = find_storage_file(pb_file, container, file_type);
+  if (!file_result.ok()) {
+    return Error() << file_result.error();
+  }
+  return map_storage_file(*file_result);
+}
+
+} // namespace private internal api
+
+/// Get mapped storage file
+Result<MappedStorageFile> get_mapped_file(
+    std::string const& container,
+    StorageFileType file_type) {
+  return private_internal_api::get_mapped_file_impl(
+      kAvailableStorageRecordsPb, container, file_type);
+}
+
+/// Get storage file version number
+Result<uint32_t> get_storage_file_version(
+    std::string const& file_path) {
+  auto version_cxx = get_storage_file_version_cxx(
+      rust::Str(file_path.c_str()));
+  if (version_cxx.query_success) {
+    return version_cxx.version_number;
+  } else {
+    return Error() << version_cxx.error_message;
+  }
+}
+
+/// Get package offset
+Result<PackageOffset> get_package_offset(
+    MappedStorageFile const& file,
+    std::string const& package) {
+  auto content = rust::Slice<const uint8_t>(
+      static_cast<uint8_t*>(file.file_ptr), file.file_size);
+  auto offset_cxx = get_package_offset_cxx(content, rust::Str(package.c_str()));
+  if (offset_cxx.query_success) {
+    auto offset = PackageOffset();
+    offset.package_exists = offset_cxx.package_exists;
+    offset.package_id = offset_cxx.package_id;
+    offset.boolean_offset = offset_cxx.boolean_offset;
+    return offset;
+  } else {
+    return Error() << offset_cxx.error_message;
+  }
+}
+
+/// Get flag offset
+Result<FlagOffset> get_flag_offset(
+    MappedStorageFile const& file,
+    uint32_t package_id,
+    std::string const& flag_name){
+  auto content = rust::Slice<const uint8_t>(
+      static_cast<uint8_t*>(file.file_ptr), file.file_size);
+  auto offset_cxx = get_flag_offset_cxx(content, package_id, rust::Str(flag_name.c_str()));
+  if (offset_cxx.query_success) {
+    auto offset = FlagOffset();
+    offset.flag_exists = offset_cxx.flag_exists;
+    offset.flag_offset = offset_cxx.flag_offset;
+    return offset;
+  } else {
+   return Error() << offset_cxx.error_message;
+  }
+}
+
+/// Get boolean flag value
+Result<bool> get_boolean_flag_value(
+    MappedStorageFile const& file,
+    uint32_t offset) {
+  auto content = rust::Slice<const uint8_t>(
+      static_cast<uint8_t*>(file.file_ptr), file.file_size);
+  auto value_cxx = get_boolean_flag_value_cxx(content, offset);
+  if (value_cxx.query_success) {
+    return value_cxx.flag_value;
+  } else {
+    return Error() << value_cxx.error_message;
+  }
+}
+
+} // namespace aconfig_storage
diff --git a/tools/aconfig/aconfig_storage_read_api/build.rs b/tools/aconfig/aconfig_storage_read_api/build.rs
new file mode 100644
index 0000000..7b1aa53
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_read_api/build.rs
@@ -0,0 +1,4 @@
+fn main() {
+    let _ = cxx_build::bridge("src/lib.rs");
+    println!("cargo:rerun-if-changed=src/lib.rs");
+}
diff --git a/tools/aconfig/aconfig_storage_read_api/include/aconfig_storage/aconfig_storage_read_api.hpp b/tools/aconfig/aconfig_storage_read_api/include/aconfig_storage/aconfig_storage_read_api.hpp
new file mode 100644
index 0000000..aa90f47
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_read_api/include/aconfig_storage/aconfig_storage_read_api.hpp
@@ -0,0 +1,85 @@
+#pragma once
+
+#include <stdint.h>
+#include <string>
+#include <android-base/result.h>
+
+namespace aconfig_storage {
+
+/// Storage file type enum
+enum StorageFileType {
+  package_map,
+  flag_map,
+  flag_val
+};
+
+/// Mapped storage file
+struct MappedStorageFile {
+  void* file_ptr;
+  size_t file_size;
+};
+
+/// Package offset query result
+struct PackageOffset {
+  bool package_exists;
+  uint32_t package_id;
+  uint32_t boolean_offset;
+};
+
+/// Flag offset query result
+struct FlagOffset {
+  bool flag_exists;
+  uint16_t flag_offset;
+};
+
+/// DO NOT USE APIS IN THE FOLLOWING NAMESPACE DIRECTLY
+namespace private_internal_api {
+
+android::base::Result<MappedStorageFile> get_mapped_file_impl(
+    std::string const& pb_file,
+    std::string const& container,
+    StorageFileType file_type);
+
+} // namespace private_internal_api
+
+/// Get mapped storage file
+/// \input container: stoarge container name
+/// \input file_type: storage file type enum
+/// \returns a MappedStorageFileQuery
+android::base::Result<MappedStorageFile> get_mapped_file(
+    std::string const& container,
+    StorageFileType file_type);
+
+/// Get storage file version number
+/// \input file_path: the path to the storage file
+/// \returns the storage file version
+android::base::Result<uint32_t> get_storage_file_version(
+    std::string const& file_path);
+
+/// Get package offset
+/// \input file: mapped storage file
+/// \input package: the flag package name
+/// \returns a package offset
+android::base::Result<PackageOffset> get_package_offset(
+    MappedStorageFile const& file,
+    std::string const& package);
+
+/// Get flag offset
+/// \input file: mapped storage file
+/// \input package_id: the flag package id obtained from package offset query
+/// \input flag_name: flag name
+/// \returns the flag offset
+android::base::Result<FlagOffset> get_flag_offset(
+    MappedStorageFile const& file,
+    uint32_t package_id,
+    std::string const& flag_name);
+
+/// Get boolean flag value
+/// \input file: mapped storage file
+/// \input offset: the boolean flag value byte offset in the file
+/// \returns the boolean flag value
+android::base::Result<bool> get_boolean_flag_value(
+    MappedStorageFile const& file,
+    uint32_t offset);
+
+} // namespace aconfig_storage
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
new file mode 100644
index 0000000..a251b41
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_read_api/src/flag_table_query.rs
@@ -0,0 +1,117 @@
+/*
+ * 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.
+ */
+
+//! flag table query module defines the flag table file read from mapped bytes
+
+use crate::{AconfigStorageError, FILE_VERSION};
+use aconfig_storage_file::{
+    flag_table::FlagTableHeader, flag_table::FlagTableNode, read_u32_from_bytes,
+};
+use anyhow::anyhow;
+
+pub type FlagOffset = u16;
+
+/// Query flag within package offset
+pub fn find_flag_offset(
+    buf: &[u8],
+    package_id: u32,
+    flag: &str,
+) -> Result<Option<FlagOffset>, AconfigStorageError> {
+    let interpreted_header = FlagTableHeader::from_bytes(buf)?;
+    if interpreted_header.version > crate::FILE_VERSION {
+        return Err(AconfigStorageError::HigherStorageFileVersion(anyhow!(
+            "Cannot read storage file with a higher version of {} with lib version {}",
+            interpreted_header.version,
+            FILE_VERSION
+        )));
+    }
+
+    let num_buckets = (interpreted_header.node_offset - interpreted_header.bucket_offset) / 4;
+    let bucket_index = FlagTableNode::find_bucket_index(package_id, flag, num_buckets);
+
+    let mut pos = (interpreted_header.bucket_offset + 4 * bucket_index) as usize;
+    let mut flag_node_offset = read_u32_from_bytes(buf, &mut pos)? as usize;
+    if flag_node_offset < interpreted_header.node_offset as usize
+        || flag_node_offset >= interpreted_header.file_size as usize
+    {
+        return Ok(None);
+    }
+
+    loop {
+        let interpreted_node = FlagTableNode::from_bytes(&buf[flag_node_offset..])?;
+        if interpreted_node.package_id == package_id && interpreted_node.flag_name == flag {
+            return Ok(Some(interpreted_node.flag_id));
+        }
+        match interpreted_node.next_offset {
+            Some(offset) => flag_node_offset = offset as usize,
+            None => return Ok(None),
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use aconfig_storage_file::test_utils::create_test_flag_table;
+
+    #[test]
+    // this test point locks down table query
+    fn test_flag_query() {
+        let flag_table = create_test_flag_table().into_bytes();
+        let baseline = vec![
+            (0, "enabled_ro", 1u16),
+            (0, "enabled_rw", 2u16),
+            (1, "disabled_ro", 0u16),
+            (2, "enabled_ro", 1u16),
+            (1, "enabled_fixed_ro", 1u16),
+            (1, "enabled_ro", 2u16),
+            (2, "enabled_fixed_ro", 0u16),
+            (0, "disabled_rw", 0u16),
+        ];
+        for (package_id, flag_name, expected_offset) in baseline.into_iter() {
+            let flag_offset =
+                find_flag_offset(&flag_table[..], package_id, flag_name).unwrap().unwrap();
+            assert_eq!(flag_offset, expected_offset);
+        }
+    }
+
+    #[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_offset = find_flag_offset(&flag_table[..], 1, "disabled_fixed_ro").unwrap();
+        assert_eq!(flag_offset, None);
+        let flag_offset = find_flag_offset(&flag_table[..], 2, "disabled_rw").unwrap();
+        assert_eq!(flag_offset, None);
+    }
+
+    #[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();
+        table.header.version = crate::FILE_VERSION + 1;
+        let flag_table = table.into_bytes();
+        let error = find_flag_offset(&flag_table[..], 0, "enabled_ro").unwrap_err();
+        assert_eq!(
+            format!("{:?}", error),
+            format!(
+                "HigherStorageFileVersion(Cannot read storage file with a higher version of {} with lib version {})",
+                crate::FILE_VERSION + 1,
+                crate::FILE_VERSION
+            )
+        );
+    }
+}
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
new file mode 100644
index 0000000..964cd69
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_read_api/src/flag_value_query.rs
@@ -0,0 +1,104 @@
+/*
+ * 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.
+ */
+
+//! flag value query module defines the flag value file read from mapped bytes
+
+use crate::{AconfigStorageError, FILE_VERSION};
+use aconfig_storage_file::{flag_value::FlagValueHeader, read_u8_from_bytes};
+use anyhow::anyhow;
+
+/// Query flag value
+pub fn find_boolean_flag_value(buf: &[u8], flag_offset: u32) -> Result<bool, AconfigStorageError> {
+    let interpreted_header = FlagValueHeader::from_bytes(buf)?;
+    if interpreted_header.version > crate::FILE_VERSION {
+        return Err(AconfigStorageError::HigherStorageFileVersion(anyhow!(
+            "Cannot read storage file with a higher version of {} with lib version {}",
+            interpreted_header.version,
+            FILE_VERSION
+        )));
+    }
+
+    let mut head = (interpreted_header.boolean_value_offset + flag_offset) as usize;
+
+    // TODO: right now, there is only boolean flags, with more flag value types added
+    // later, the end of boolean flag value section should be updated (b/322826265).
+    if head >= interpreted_header.file_size as usize {
+        return Err(AconfigStorageError::InvalidStorageFileOffset(anyhow!(
+            "Flag value offset goes beyond the end of the file."
+        )));
+    }
+
+    let val = read_u8_from_bytes(buf, &mut head)?;
+    Ok(val == 1)
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use aconfig_storage_file::{FlagValueList, StorageFileType};
+
+    pub fn create_test_flag_value_list() -> FlagValueList {
+        let header = FlagValueHeader {
+            version: crate::FILE_VERSION,
+            container: String::from("system"),
+            file_type: StorageFileType::FlagVal as u8,
+            file_size: 35,
+            num_flags: 8,
+            boolean_value_offset: 27,
+        };
+        let booleans: Vec<bool> = vec![false, true, false, false, true, true, false, true];
+        FlagValueList { header, booleans }
+    }
+
+    #[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 baseline: Vec<bool> = vec![false, true, false, false, true, true, false, true];
+        for (offset, expected_value) in baseline.into_iter().enumerate() {
+            let flag_value = find_boolean_flag_value(&flag_value_list[..], offset as u32).unwrap();
+            assert_eq!(flag_value, expected_value);
+        }
+    }
+
+    #[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 error = find_boolean_flag_value(&flag_value_list[..], 8).unwrap_err();
+        assert_eq!(
+            format!("{:?}", error),
+            "InvalidStorageFileOffset(Flag value offset goes beyond the end of the file.)"
+        );
+    }
+
+    #[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();
+        value_list.header.version = crate::FILE_VERSION + 1;
+        let flag_value = value_list.into_bytes();
+        let error = find_boolean_flag_value(&flag_value[..], 4).unwrap_err();
+        assert_eq!(
+            format!("{:?}", error),
+            format!(
+                "HigherStorageFileVersion(Cannot read storage file with a higher version of {} with lib version {})",
+                crate::FILE_VERSION + 1,
+                crate::FILE_VERSION
+            )
+        );
+    }
+}
diff --git a/tools/aconfig/aconfig_storage_read_api/src/lib.rs b/tools/aconfig/aconfig_storage_read_api/src/lib.rs
new file mode 100644
index 0000000..da64cb7
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_read_api/src/lib.rs
@@ -0,0 +1,421 @@
+/*
+ * Copyright (C) 2023 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.
+ */
+
+//! `aconfig_storage_read_api` is a crate that defines read apis to read flags from storage
+//! files. It provides four apis to interface with storage files:
+//!
+//! 1, function to get package flag value start offset
+//! pub fn get_package_offset(container: &str, package: &str) -> `Result<Option<PackageOffset>>>`
+//!
+//! 2, function to get flag offset within a specific package
+//! pub fn get_flag_offset(container: &str, package_id: u32, flag: &str) -> `Result<Option<u16>>>`
+//!
+//! 3, function to get the actual flag value given the global offset (combined package and
+//! flag offset).
+//! pub fn get_boolean_flag_value(container: &str, offset: u32) -> `Result<bool>`
+//!
+//! 4, function to get storage file version without mmapping the file.
+//! pub fn get_storage_file_version(file_path: &str) -> Result<u32, AconfigStorageError>
+//!
+//! Note these are low level apis that are expected to be only used in auto generated flag
+//! apis. DO NOT DIRECTLY USE THESE APIS IN YOUR SOURCE CODE. For auto generated flag apis
+//! please refer to the g3doc go/android-flags
+
+pub mod flag_table_query;
+pub mod flag_value_query;
+pub mod mapped_file;
+pub mod package_table_query;
+
+#[cfg(test)]
+mod test_utils;
+
+pub use aconfig_storage_file::{AconfigStorageError, StorageFileType};
+pub use flag_table_query::FlagOffset;
+pub use package_table_query::PackageOffset;
+
+use aconfig_storage_file::{read_u32_from_bytes, FILE_VERSION};
+use flag_table_query::find_flag_offset;
+use flag_value_query::find_boolean_flag_value;
+use package_table_query::find_package_offset;
+
+use anyhow::anyhow;
+use memmap2::Mmap;
+use std::fs::File;
+use std::io::Read;
+
+/// Storage file location pb file
+pub const STORAGE_LOCATION_FILE: &str = "/metadata/aconfig/boot/available_storage_file_records.pb";
+
+/// Get read only mapped storage files.
+///
+/// \input container: the flag package container
+/// \input file_type: stoarge file type enum
+/// \return a result of read only mapped file
+///
+/// # Safety
+///
+/// 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.
+pub unsafe fn get_mapped_storage_file(
+    container: &str,
+    file_type: StorageFileType,
+) -> Result<Mmap, AconfigStorageError> {
+    unsafe { crate::mapped_file::get_mapped_file(STORAGE_LOCATION_FILE, container, file_type) }
+}
+
+/// Get package start offset for flags.
+///
+/// \input file: mapped package file
+/// \input package: package name
+///
+/// \return
+/// If a package is found, it returns Ok(Some(PackageOffset))
+/// If a package is not found, it returns Ok(None)
+/// If errors out, it returns an Err(errmsg)
+pub fn get_package_offset(
+    file: &Mmap,
+    package: &str,
+) -> Result<Option<PackageOffset>, AconfigStorageError> {
+    find_package_offset(file, package)
+}
+
+/// Get flag offset within a package given.
+///
+/// \input file: mapped flag file
+/// \input package_id: package id obtained from package mapping file
+/// \input flag: flag name
+///
+/// \return
+/// If a flag is found, it returns Ok(Some(u16))
+/// If a flag is not found, it returns Ok(None)
+/// If errors out, it returns an Err(errmsg)
+pub fn get_flag_offset(
+    file: &Mmap,
+    package_id: u32,
+    flag: &str,
+) -> Result<Option<FlagOffset>, AconfigStorageError> {
+    find_flag_offset(file, package_id, flag)
+}
+
+/// Get the boolean flag value.
+///
+/// \input file: mapped flag file
+/// \input offset: flag value 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, offset: u32) -> Result<bool, AconfigStorageError> {
+    find_boolean_flag_value(file, offset)
+}
+
+/// Get storage file version number
+///
+/// This function would read the first four bytes of the file and interpret it as the
+/// version number of the file. There are unit tests in aconfig_storage_file crate to
+/// lock down that for all storage files, the first four bytes will be the version
+/// number of the storage file
+pub fn get_storage_file_version(file_path: &str) -> Result<u32, AconfigStorageError> {
+    let mut file = File::open(file_path).map_err(|errmsg| {
+        AconfigStorageError::FileReadFail(anyhow!("Failed to open file {}: {}", file_path, errmsg))
+    })?;
+    let mut buffer = [0; 4];
+    file.read(&mut buffer).map_err(|errmsg| {
+        AconfigStorageError::FileReadFail(anyhow!(
+            "Failed to read 4 bytes from file {}: {}",
+            file_path,
+            errmsg
+        ))
+    })?;
+    let mut head = 0;
+    read_u32_from_bytes(&buffer, &mut head)
+}
+
+// *************************************** //
+// CC INTERLOP
+// *************************************** //
+
+// Exported rust data structure and methods, c++ code will be generated
+#[cxx::bridge]
+mod ffi {
+    // Storage file version query return for cc interlop
+    pub struct VersionNumberQueryCXX {
+        pub query_success: bool,
+        pub error_message: String,
+        pub version_number: u32,
+    }
+
+    // Package table query return for cc interlop
+    pub struct PackageOffsetQueryCXX {
+        pub query_success: bool,
+        pub error_message: String,
+        pub package_exists: bool,
+        pub package_id: u32,
+        pub boolean_offset: u32,
+    }
+
+    // Flag table query return for cc interlop
+    pub struct FlagOffsetQueryCXX {
+        pub query_success: bool,
+        pub error_message: String,
+        pub flag_exists: bool,
+        pub flag_offset: u16,
+    }
+
+    // Flag value query return for cc interlop
+    pub struct BooleanFlagValueQueryCXX {
+        pub query_success: bool,
+        pub error_message: String,
+        pub flag_value: bool,
+    }
+
+    // Rust export to c++
+    extern "Rust" {
+        pub fn get_storage_file_version_cxx(file_path: &str) -> VersionNumberQueryCXX;
+
+        pub fn get_package_offset_cxx(file: &[u8], package: &str) -> PackageOffsetQueryCXX;
+
+        pub fn get_flag_offset_cxx(file: &[u8], package_id: u32, flag: &str) -> FlagOffsetQueryCXX;
+
+        pub fn get_boolean_flag_value_cxx(file: &[u8], offset: u32) -> BooleanFlagValueQueryCXX;
+    }
+}
+
+/// Implement the package offset interlop return type, create from actual package offset api return type
+impl ffi::PackageOffsetQueryCXX {
+    pub(crate) fn new(offset_result: Result<Option<PackageOffset>, AconfigStorageError>) -> Self {
+        match offset_result {
+            Ok(offset_opt) => match offset_opt {
+                Some(offset) => Self {
+                    query_success: true,
+                    error_message: String::from(""),
+                    package_exists: true,
+                    package_id: offset.package_id,
+                    boolean_offset: offset.boolean_offset,
+                },
+                None => Self {
+                    query_success: true,
+                    error_message: String::from(""),
+                    package_exists: false,
+                    package_id: 0,
+                    boolean_offset: 0,
+                },
+            },
+            Err(errmsg) => Self {
+                query_success: false,
+                error_message: format!("{:?}", errmsg),
+                package_exists: false,
+                package_id: 0,
+                boolean_offset: 0,
+            },
+        }
+    }
+}
+
+/// Implement the flag offset interlop return type, create from actual flag offset api return type
+impl ffi::FlagOffsetQueryCXX {
+    pub(crate) fn new(offset_result: Result<Option<FlagOffset>, AconfigStorageError>) -> Self {
+        match offset_result {
+            Ok(offset_opt) => match offset_opt {
+                Some(offset) => Self {
+                    query_success: true,
+                    error_message: String::from(""),
+                    flag_exists: true,
+                    flag_offset: offset,
+                },
+                None => Self {
+                    query_success: true,
+                    error_message: String::from(""),
+                    flag_exists: false,
+                    flag_offset: 0,
+                },
+            },
+            Err(errmsg) => Self {
+                query_success: false,
+                error_message: format!("{:?}", errmsg),
+                flag_exists: false,
+                flag_offset: 0,
+            },
+        }
+    }
+}
+
+/// Implement the flag value interlop return type, create from actual flag value api return type
+impl ffi::BooleanFlagValueQueryCXX {
+    pub(crate) fn new(value_result: Result<bool, AconfigStorageError>) -> Self {
+        match value_result {
+            Ok(value) => {
+                Self { query_success: true, error_message: String::from(""), flag_value: value }
+            }
+            Err(errmsg) => Self {
+                query_success: false,
+                error_message: format!("{:?}", errmsg),
+                flag_value: false,
+            },
+        }
+    }
+}
+
+/// Implement the storage version number interlop return type, create from actual version number
+/// api return type
+impl ffi::VersionNumberQueryCXX {
+    pub(crate) fn new(version_result: Result<u32, AconfigStorageError>) -> Self {
+        match version_result {
+            Ok(version) => Self {
+                query_success: true,
+                error_message: String::from(""),
+                version_number: version,
+            },
+            Err(errmsg) => Self {
+                query_success: false,
+                error_message: format!("{:?}", errmsg),
+                version_number: 0,
+            },
+        }
+    }
+}
+
+/// Get package start offset cc interlop
+pub fn get_package_offset_cxx(file: &[u8], package: &str) -> ffi::PackageOffsetQueryCXX {
+    ffi::PackageOffsetQueryCXX::new(find_package_offset(file, package))
+}
+
+/// Get flag start offset cc interlop
+pub fn get_flag_offset_cxx(file: &[u8], package_id: u32, flag: &str) -> ffi::FlagOffsetQueryCXX {
+    ffi::FlagOffsetQueryCXX::new(find_flag_offset(file, package_id, flag))
+}
+
+/// Get boolean flag value cc interlop
+pub fn get_boolean_flag_value_cxx(file: &[u8], offset: u32) -> ffi::BooleanFlagValueQueryCXX {
+    ffi::BooleanFlagValueQueryCXX::new(find_boolean_flag_value(file, offset))
+}
+
+/// Get storage version number cc interlop
+pub fn get_storage_file_version_cxx(file_path: &str) -> ffi::VersionNumberQueryCXX {
+    ffi::VersionNumberQueryCXX::new(get_storage_file_version(file_path))
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use crate::mapped_file::get_mapped_file;
+    use crate::test_utils::copy_to_temp_file;
+    use aconfig_storage_file::protos::storage_record_pb::write_proto_to_temp_file;
+    use tempfile::NamedTempFile;
+
+    fn create_test_storage_files() -> [NamedTempFile; 4] {
+        let package_map = copy_to_temp_file("./tests/package.map").unwrap();
+        let flag_map = copy_to_temp_file("./tests/flag.map").unwrap();
+        let flag_val = copy_to_temp_file("./tests/flag.val").unwrap();
+
+        let text_proto = format!(
+            r#"
+files {{
+    version: 0
+    container: "mockup"
+    package_map: "{}"
+    flag_map: "{}"
+    flag_val: "{}"
+    timestamp: 12345
+}}
+"#,
+            package_map.path().display(),
+            flag_map.path().display(),
+            flag_val.path().display()
+        );
+        let pb_file = write_proto_to_temp_file(&text_proto).unwrap();
+        [package_map, flag_map, flag_val, pb_file]
+    }
+
+    #[test]
+    // this test point locks down flag package offset query
+    fn test_package_offset_query() {
+        let [_package_map, _flag_map, _flag_val, pb_file] = create_test_storage_files();
+        let pb_file_path = pb_file.path().display().to_string();
+        let package_mapped_file = unsafe {
+            get_mapped_file(&pb_file_path, "mockup", StorageFileType::PackageMap).unwrap()
+        };
+
+        let package_offset =
+            get_package_offset(&package_mapped_file, "com.android.aconfig.storage.test_1")
+                .unwrap()
+                .unwrap();
+        let expected_package_offset = PackageOffset { package_id: 0, boolean_offset: 0 };
+        assert_eq!(package_offset, expected_package_offset);
+
+        let package_offset =
+            get_package_offset(&package_mapped_file, "com.android.aconfig.storage.test_2")
+                .unwrap()
+                .unwrap();
+        let expected_package_offset = PackageOffset { package_id: 1, boolean_offset: 3 };
+        assert_eq!(package_offset, expected_package_offset);
+
+        let package_offset =
+            get_package_offset(&package_mapped_file, "com.android.aconfig.storage.test_4")
+                .unwrap()
+                .unwrap();
+        let expected_package_offset = PackageOffset { package_id: 2, boolean_offset: 6 };
+        assert_eq!(package_offset, expected_package_offset);
+    }
+
+    #[test]
+    // this test point locks down flag offset query
+    fn test_flag_offset_query() {
+        let [_package_map, _flag_map, _flag_val, pb_file] = create_test_storage_files();
+        let pb_file_path = pb_file.path().display().to_string();
+        let flag_mapped_file =
+            unsafe { get_mapped_file(&pb_file_path, "mockup", StorageFileType::FlagMap).unwrap() };
+
+        let baseline = vec![
+            (0, "enabled_ro", 1u16),
+            (0, "enabled_rw", 2u16),
+            (1, "disabled_ro", 0u16),
+            (2, "enabled_ro", 1u16),
+            (1, "enabled_fixed_ro", 1u16),
+            (1, "enabled_ro", 2u16),
+            (2, "enabled_fixed_ro", 0u16),
+            (0, "disabled_rw", 0u16),
+        ];
+        for (package_id, flag_name, expected_offset) in baseline.into_iter() {
+            let flag_offset =
+                get_flag_offset(&flag_mapped_file, package_id, flag_name).unwrap().unwrap();
+            assert_eq!(flag_offset, expected_offset);
+        }
+    }
+
+    #[test]
+    // this test point locks down flag offset query
+    fn test_flag_value_query() {
+        let [_package_map, _flag_map, _flag_val, pb_file] = create_test_storage_files();
+        let pb_file_path = pb_file.path().display().to_string();
+        let flag_value_file =
+            unsafe { get_mapped_file(&pb_file_path, "mockup", StorageFileType::FlagVal).unwrap() };
+        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 = get_boolean_flag_value(&flag_value_file, offset as u32).unwrap();
+            assert_eq!(flag_value, expected_value);
+        }
+    }
+
+    #[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/package.map").unwrap(), 1);
+        assert_eq!(get_storage_file_version("./tests/flag.map").unwrap(), 1);
+        assert_eq!(get_storage_file_version("./tests/flag.val").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
new file mode 100644
index 0000000..51354db
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_read_api/src/mapped_file.rs
@@ -0,0 +1,206 @@
+/*
+ * 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.
+ */
+
+use std::fs::File;
+use std::io::{BufReader, Read};
+
+use anyhow::anyhow;
+use memmap2::Mmap;
+
+use crate::AconfigStorageError::{
+    self, FileReadFail, MapFileFail, ProtobufParseFail, StorageFileNotFound,
+};
+use crate::StorageFileType;
+use aconfig_storage_file::protos::{
+    storage_record_pb::try_from_binary_proto, ProtoStorageFileInfo, ProtoStorageFiles,
+};
+
+/// Find where storage files are stored for a particular container
+fn find_container_storage_location(
+    location_pb_file: &str,
+    container: &str,
+) -> Result<ProtoStorageFileInfo, AconfigStorageError> {
+    let file = File::open(location_pb_file).map_err(|errmsg| {
+        FileReadFail(anyhow!("Failed to open file {}: {}", location_pb_file, errmsg))
+    })?;
+    let mut reader = BufReader::new(file);
+    let mut bytes = Vec::new();
+    reader.read_to_end(&mut bytes).map_err(|errmsg| {
+        FileReadFail(anyhow!("Failed to read file {}: {}", location_pb_file, errmsg))
+    })?;
+    let storage_locations: ProtoStorageFiles = try_from_binary_proto(&bytes).map_err(|errmsg| {
+        ProtobufParseFail(anyhow!(
+            "Failed to parse storage location pb file {}: {}",
+            location_pb_file,
+            errmsg
+        ))
+    })?;
+    for location_info in storage_locations.files.iter() {
+        if location_info.container() == container {
+            return Ok(location_info.clone());
+        }
+    }
+    Err(StorageFileNotFound(anyhow!("Storage file does not exist for {}", container)))
+}
+
+/// Get the read only memory mapping of a storage file
+///
+/// # Safety
+///
+/// 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> {
+    let file = File::open(file_path)
+        .map_err(|errmsg| FileReadFail(anyhow!("Failed to open file {}: {}", file_path, errmsg)))?;
+    unsafe {
+        let mapped_file = Mmap::map(&file).map_err(|errmsg| {
+            MapFileFail(anyhow!("fail to map storage file {}: {}", file_path, errmsg))
+        })?;
+        Ok(mapped_file)
+    }
+}
+
+/// Get a mapped storage file given the container and file type
+///
+/// # Safety
+///
+/// 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.
+pub unsafe fn get_mapped_file(
+    location_pb_file: &str,
+    container: &str,
+    file_type: StorageFileType,
+) -> Result<Mmap, AconfigStorageError> {
+    let files_location = find_container_storage_location(location_pb_file, container)?;
+    match file_type {
+        StorageFileType::PackageMap => unsafe { map_file(files_location.package_map()) },
+        StorageFileType::FlagMap => unsafe { map_file(files_location.flag_map()) },
+        StorageFileType::FlagVal => unsafe { map_file(files_location.flag_val()) },
+        StorageFileType::FlagInfo => {
+            Err(MapFileFail(anyhow!("TODO: add support for flag info file")))
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use crate::test_utils::copy_to_temp_file;
+    use aconfig_storage_file::protos::storage_record_pb::write_proto_to_temp_file;
+    use tempfile::NamedTempFile;
+
+    #[test]
+    fn test_find_storage_file_location() {
+        let text_proto = r#"
+files {
+    version: 0
+    container: "system"
+    package_map: "/system/etc/package.map"
+    flag_map: "/system/etc/flag.map"
+    flag_val: "/metadata/aconfig/system.val"
+    timestamp: 12345
+}
+files {
+    version: 1
+    container: "product"
+    package_map: "/product/etc/package.map"
+    flag_map: "/product/etc/flag.map"
+    flag_val: "/metadata/aconfig/product.val"
+    timestamp: 54321
+}
+"#;
+        let file = write_proto_to_temp_file(&text_proto).unwrap();
+        let file_full_path = file.path().display().to_string();
+        let file_info = find_container_storage_location(&file_full_path, "system").unwrap();
+        assert_eq!(file_info.version(), 0);
+        assert_eq!(file_info.container(), "system");
+        assert_eq!(file_info.package_map(), "/system/etc/package.map");
+        assert_eq!(file_info.flag_map(), "/system/etc/flag.map");
+        assert_eq!(file_info.flag_val(), "/metadata/aconfig/system.val");
+        assert_eq!(file_info.timestamp(), 12345);
+
+        let file_info = find_container_storage_location(&file_full_path, "product").unwrap();
+        assert_eq!(file_info.version(), 1);
+        assert_eq!(file_info.container(), "product");
+        assert_eq!(file_info.package_map(), "/product/etc/package.map");
+        assert_eq!(file_info.flag_map(), "/product/etc/flag.map");
+        assert_eq!(file_info.flag_val(), "/metadata/aconfig/product.val");
+        assert_eq!(file_info.timestamp(), 54321);
+
+        let err = find_container_storage_location(&file_full_path, "vendor").unwrap_err();
+        assert_eq!(
+            format!("{:?}", err),
+            "StorageFileNotFound(Storage file does not exist for vendor)"
+        );
+    }
+
+    fn map_and_verify(location_pb_file: &str, file_type: StorageFileType, actual_file: &str) {
+        let mut opened_file = File::open(actual_file).unwrap();
+        let mut content = Vec::new();
+        opened_file.read_to_end(&mut content).unwrap();
+
+        let mmaped_file =
+            unsafe { get_mapped_file(location_pb_file, "system", file_type).unwrap() };
+        assert_eq!(mmaped_file[..], content[..]);
+    }
+
+    fn create_test_storage_files() -> [NamedTempFile; 4] {
+        let package_map = copy_to_temp_file("./tests/package.map").unwrap();
+        let flag_map = copy_to_temp_file("./tests/flag.map").unwrap();
+        let flag_val = copy_to_temp_file("./tests/package.map").unwrap();
+
+        let text_proto = format!(
+            r#"
+files {{
+    version: 0
+    container: "system"
+    package_map: "{}"
+    flag_map: "{}"
+    flag_val: "{}"
+    timestamp: 12345
+}}
+"#,
+            package_map.path().display(),
+            flag_map.path().display(),
+            flag_val.path().display()
+        );
+        let pb_file = write_proto_to_temp_file(&text_proto).unwrap();
+        [package_map, flag_map, flag_val, pb_file]
+    }
+
+    #[test]
+    fn test_mapped_file_contents() {
+        let [package_map, flag_map, flag_val, pb_file] = create_test_storage_files();
+        let pb_file_path = pb_file.path().display().to_string();
+        map_and_verify(
+            &pb_file_path,
+            StorageFileType::PackageMap,
+            &package_map.path().display().to_string(),
+        );
+        map_and_verify(
+            &pb_file_path,
+            StorageFileType::FlagMap,
+            &flag_map.path().display().to_string(),
+        );
+        map_and_verify(
+            &pb_file_path,
+            StorageFileType::FlagVal,
+            &flag_val.path().display().to_string(),
+        );
+    }
+}
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
new file mode 100644
index 0000000..d83844e
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_read_api/src/package_table_query.rs
@@ -0,0 +1,132 @@
+/*
+ * 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 table query module defines the package table file read from mapped bytes
+
+use crate::{AconfigStorageError, FILE_VERSION};
+use aconfig_storage_file::{
+    package_table::PackageTableHeader, package_table::PackageTableNode, read_u32_from_bytes,
+};
+use anyhow::anyhow;
+
+/// Package table query return
+#[derive(PartialEq, Debug)]
+pub struct PackageOffset {
+    pub package_id: u32,
+    pub boolean_offset: u32,
+}
+
+/// Query package id and start offset
+pub fn find_package_offset(
+    buf: &[u8],
+    package: &str,
+) -> Result<Option<PackageOffset>, AconfigStorageError> {
+    let interpreted_header = PackageTableHeader::from_bytes(buf)?;
+    if interpreted_header.version > FILE_VERSION {
+        return Err(AconfigStorageError::HigherStorageFileVersion(anyhow!(
+            "Cannot read storage file with a higher version of {} with lib version {}",
+            interpreted_header.version,
+            FILE_VERSION
+        )));
+    }
+
+    let num_buckets = (interpreted_header.node_offset - interpreted_header.bucket_offset) / 4;
+    let bucket_index = PackageTableNode::find_bucket_index(package, num_buckets);
+
+    let mut pos = (interpreted_header.bucket_offset + 4 * bucket_index) as usize;
+    let mut package_node_offset = read_u32_from_bytes(buf, &mut pos)? as usize;
+    if package_node_offset < interpreted_header.node_offset as usize
+        || package_node_offset >= interpreted_header.file_size as usize
+    {
+        return Ok(None);
+    }
+
+    loop {
+        let interpreted_node = PackageTableNode::from_bytes(&buf[package_node_offset..])?;
+        if interpreted_node.package_name == package {
+            return Ok(Some(PackageOffset {
+                package_id: interpreted_node.package_id,
+                boolean_offset: interpreted_node.boolean_offset,
+            }));
+        }
+        match interpreted_node.next_offset {
+            Some(offset) => package_node_offset = offset as usize,
+            None => return Ok(None),
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use aconfig_storage_file::test_utils::create_test_package_table;
+
+    #[test]
+    // this test point locks down table query
+    fn test_package_query() {
+        let package_table = create_test_package_table().into_bytes();
+        let package_offset =
+            find_package_offset(&package_table[..], "com.android.aconfig.storage.test_1")
+                .unwrap()
+                .unwrap();
+        let expected_package_offset = PackageOffset { package_id: 0, boolean_offset: 0 };
+        assert_eq!(package_offset, expected_package_offset);
+        let package_offset =
+            find_package_offset(&package_table[..], "com.android.aconfig.storage.test_2")
+                .unwrap()
+                .unwrap();
+        let expected_package_offset = PackageOffset { package_id: 1, boolean_offset: 3 };
+        assert_eq!(package_offset, expected_package_offset);
+        let package_offset =
+            find_package_offset(&package_table[..], "com.android.aconfig.storage.test_4")
+                .unwrap()
+                .unwrap();
+        let expected_package_offset = PackageOffset { package_id: 2, boolean_offset: 6 };
+        assert_eq!(package_offset, expected_package_offset);
+    }
+
+    #[test]
+    // 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_offset =
+            find_package_offset(&package_table[..], "com.android.aconfig.storage.test_3").unwrap();
+        assert_eq!(package_offset, None);
+        // this will land at the end of a linked list
+        let package_offset =
+            find_package_offset(&package_table[..], "com.android.aconfig.storage.test_5").unwrap();
+        assert_eq!(package_offset, None);
+    }
+
+    #[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();
+        table.header.version = crate::FILE_VERSION + 1;
+        let package_table = table.into_bytes();
+        let error = find_package_offset(&package_table[..], "com.android.aconfig.storage.test_1")
+            .unwrap_err();
+        assert_eq!(
+            format!("{:?}", error),
+            format!(
+                "HigherStorageFileVersion(Cannot read storage file with a higher version of {} with lib version {})",
+                crate::FILE_VERSION + 1,
+                crate::FILE_VERSION
+            )
+        );
+    }
+}
diff --git a/tools/aconfig/aconfig_storage_read_api/src/test_utils.rs b/tools/aconfig/aconfig_storage_read_api/src/test_utils.rs
new file mode 100644
index 0000000..84f31aa
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_read_api/src/test_utils.rs
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2023 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.
+ */
+
+use anyhow::Result;
+use std::fs;
+use tempfile::NamedTempFile;
+
+/// Create temp file copy
+pub(crate) fn copy_to_temp_file(source_file: &str) -> Result<NamedTempFile> {
+    let file = NamedTempFile::new()?;
+    fs::copy(source_file, file.path())?;
+    Ok(file)
+}
diff --git a/tools/aconfig/aconfig_storage_read_api/tests/Android.bp b/tools/aconfig/aconfig_storage_read_api/tests/Android.bp
new file mode 100644
index 0000000..d9cf238
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_read_api/tests/Android.bp
@@ -0,0 +1,43 @@
+rust_test {
+    name: "aconfig_storage_read_api.test.rust",
+    srcs: [
+        "storage_read_api_test.rs"
+    ],
+    rustlibs: [
+        "libanyhow",
+        "libaconfig_storage_file",
+        "libaconfig_storage_read_api",
+        "libprotobuf",
+        "libtempfile",
+    ],
+    data: [
+        "package.map",
+        "flag.map",
+        "flag.val",
+    ],
+    test_suites: ["general-tests"],
+}
+
+cc_test {
+    name: "aconfig_storage_read_api.test.cpp",
+    srcs: [
+        "storage_read_api_test.cpp",
+    ],
+    static_libs: [
+        "libgmock",
+        "libaconfig_storage_protos_cc",
+        "libprotobuf-cpp-lite",
+        "libaconfig_storage_read_api_cc",
+        "libbase",
+        "liblog",
+    ],
+    data: [
+        "package.map",
+        "flag.map",
+        "flag.val",
+    ],
+    test_suites: [
+        "device-tests",
+        "general-tests",
+    ],
+}
diff --git a/tools/aconfig/aconfig_storage_read_api/tests/flag.map b/tools/aconfig/aconfig_storage_read_api/tests/flag.map
new file mode 100644
index 0000000..d26e00f
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_read_api/tests/flag.map
Binary files differ
diff --git a/tools/aconfig/aconfig_storage_read_api/tests/flag.val b/tools/aconfig/aconfig_storage_read_api/tests/flag.val
new file mode 100644
index 0000000..ed203d4
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_read_api/tests/flag.val
Binary files differ
diff --git a/tools/aconfig/aconfig_storage_read_api/tests/package.map b/tools/aconfig/aconfig_storage_read_api/tests/package.map
new file mode 100644
index 0000000..6c46a03
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_read_api/tests/package.map
Binary files differ
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
new file mode 100644
index 0000000..539474b
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_read_api/tests/storage_read_api_test.cpp
@@ -0,0 +1,217 @@
+/*
+ * 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.
+ */
+
+#include <string>
+#include <vector>
+#include <cstdio>
+
+#include <sys/stat.h>
+#include "aconfig_storage/aconfig_storage_read_api.hpp"
+#include <gtest/gtest.h>
+#include <protos/aconfig_storage_metadata.pb.h>
+#include <android-base/file.h>
+#include <android-base/result.h>
+
+using android::aconfig_storage_metadata::storage_files;
+using namespace android::base;
+
+namespace api = aconfig_storage;
+namespace private_api = aconfig_storage::private_internal_api;
+
+class AconfigStorageTest : public ::testing::Test {
+ protected:
+  Result<std::string> copy_to_temp_file(std::string const& source_file) {
+    auto temp_file = std::string(std::tmpnam(nullptr));
+    auto content = std::string();
+    if (!ReadFileToString(source_file, &content)) {
+      return Error() << "failed to read file: " << source_file;
+    }
+    if (!WriteStringToFile(content, temp_file)) {
+      return Error() << "failed to copy file: " << source_file;
+    }
+    return temp_file;
+  }
+
+  Result<std::string> write_storage_location_pb_file(std::string const& package_map,
+                                                     std::string const& flag_map,
+                                                     std::string const& flag_val) {
+    auto temp_file = std::tmpnam(nullptr);
+    auto proto = storage_files();
+    auto* info = proto.add_files();
+    info->set_version(0);
+    info->set_container("mockup");
+    info->set_package_map(package_map);
+    info->set_flag_map(flag_map);
+    info->set_flag_val(flag_val);
+    info->set_timestamp(12345);
+
+    auto content = std::string();
+    proto.SerializeToString(&content);
+    if (!WriteStringToFile(content, temp_file)) {
+      return Error() << "failed to write storage records pb file";
+    }
+    return temp_file;
+  }
+
+  void SetUp() override {
+    auto const test_dir = android::base::GetExecutableDirectory();
+    package_map = *copy_to_temp_file(test_dir + "/package.map");
+    flag_map = *copy_to_temp_file(test_dir + "/flag.map");
+    flag_val = *copy_to_temp_file(test_dir + "/flag.val");
+    storage_record_pb = *write_storage_location_pb_file(
+        package_map, flag_map, flag_val);
+  }
+
+  void TearDown() override {
+    std::remove(package_map.c_str());
+    std::remove(flag_map.c_str());
+    std::remove(flag_val.c_str());
+    std::remove(storage_record_pb.c_str());
+  }
+
+  std::string package_map;
+  std::string flag_map;
+  std::string flag_val;
+  std::string storage_record_pb;
+};
+
+/// Test to lock down storage file version query api
+TEST_F(AconfigStorageTest, test_storage_version_query) {
+  auto version = api::get_storage_file_version(package_map);
+  ASSERT_TRUE(version.ok());
+  ASSERT_EQ(*version, 1);
+  version = api::get_storage_file_version(flag_map);
+  ASSERT_TRUE(version.ok());
+  ASSERT_EQ(*version, 1);
+  version = api::get_storage_file_version(flag_val);
+  ASSERT_TRUE(version.ok());
+  ASSERT_EQ(*version, 1);
+}
+
+/// Negative test to lock down the error when mapping none exist storage files
+TEST_F(AconfigStorageTest, test_none_exist_storage_file_mapping) {
+  auto mapped_file = private_api::get_mapped_file_impl(
+      storage_record_pb, "vendor", api::StorageFileType::package_map);
+  ASSERT_FALSE(mapped_file.ok());
+  ASSERT_EQ(mapped_file.error().message(),
+            "Unable to find storage files for container vendor");
+}
+
+/// Test to lock down storage package offset query api
+TEST_F(AconfigStorageTest, test_package_offset_query) {
+  auto mapped_file = private_api::get_mapped_file_impl(
+      storage_record_pb, "mockup", api::StorageFileType::package_map);
+  ASSERT_TRUE(mapped_file.ok());
+
+  auto offset = api::get_package_offset(
+      *mapped_file, "com.android.aconfig.storage.test_1");
+  ASSERT_TRUE(offset.ok());
+  ASSERT_TRUE(offset->package_exists);
+  ASSERT_EQ(offset->package_id, 0);
+  ASSERT_EQ(offset->boolean_offset, 0);
+
+  offset = api::get_package_offset(
+      *mapped_file, "com.android.aconfig.storage.test_2");
+  ASSERT_TRUE(offset.ok());
+  ASSERT_TRUE(offset->package_exists);
+  ASSERT_EQ(offset->package_id, 1);
+  ASSERT_EQ(offset->boolean_offset, 3);
+
+  offset = api::get_package_offset(
+      *mapped_file, "com.android.aconfig.storage.test_4");
+  ASSERT_TRUE(offset.ok());
+  ASSERT_TRUE(offset->package_exists);
+  ASSERT_EQ(offset->package_id, 2);
+  ASSERT_EQ(offset->boolean_offset, 6);
+}
+
+/// Test to lock down when querying none exist package
+TEST_F(AconfigStorageTest, test_none_existent_package_offset_query) {
+  auto mapped_file = private_api::get_mapped_file_impl(
+      storage_record_pb, "mockup", api::StorageFileType::package_map);
+  ASSERT_TRUE(mapped_file.ok());
+
+  auto offset = api::get_package_offset(
+      *mapped_file, "com.android.aconfig.storage.test_3");
+  ASSERT_TRUE(offset.ok());
+  ASSERT_FALSE(offset->package_exists);
+}
+
+/// Test to lock down storage flag offset query api
+TEST_F(AconfigStorageTest, test_flag_offset_query) {
+  auto mapped_file = private_api::get_mapped_file_impl(
+      storage_record_pb, "mockup", api::StorageFileType::flag_map);
+  ASSERT_TRUE(mapped_file.ok());
+
+  auto baseline = std::vector<std::tuple<int, std::string, int>>{
+    {0, "enabled_ro", 1},
+    {0, "enabled_rw", 2},
+    {1, "disabled_ro", 0},
+    {2, "enabled_ro", 1},
+    {1, "enabled_fixed_ro", 1},
+    {1, "enabled_ro", 2},
+    {2, "enabled_fixed_ro", 0},
+    {0, "disabled_rw", 0},
+  };
+  for (auto const&[package_id, flag_name, expected_offset] : baseline) {
+    auto offset = api::get_flag_offset(*mapped_file, package_id, flag_name);
+    ASSERT_TRUE(offset.ok());
+    ASSERT_TRUE(offset->flag_exists);
+    ASSERT_EQ(offset->flag_offset, expected_offset);
+  }
+}
+
+/// Test to lock down when querying none exist flag
+TEST_F(AconfigStorageTest, test_none_existent_flag_offset_query) {
+  auto mapped_file = private_api::get_mapped_file_impl(
+      storage_record_pb, "mockup", api::StorageFileType::flag_map);
+  ASSERT_TRUE(mapped_file.ok());
+
+  auto offset = api::get_flag_offset(*mapped_file, 0, "none_exist");
+  ASSERT_TRUE(offset.ok());
+  ASSERT_FALSE(offset->flag_exists);
+
+  offset = api::get_flag_offset(*mapped_file, 3, "enabled_ro");
+  ASSERT_TRUE(offset.ok());
+  ASSERT_FALSE(offset->flag_exists);
+}
+
+/// Test to lock down storage flag value query api
+TEST_F(AconfigStorageTest, test_boolean_flag_value_query) {
+  auto mapped_file = private_api::get_mapped_file_impl(
+      storage_record_pb, "mockup", api::StorageFileType::flag_val);
+  ASSERT_TRUE(mapped_file.ok());
+
+  auto expected_value = std::vector<bool>{
+    false, true, true, false, true, true, true, true};
+  for (int offset = 0; offset < 8; ++offset) {
+    auto value = api::get_boolean_flag_value(*mapped_file, offset);
+    ASSERT_TRUE(value.ok());
+    ASSERT_EQ(*value, expected_value[offset]);
+  }
+}
+
+/// Negative test to lock down the error when querying flag value out of range
+TEST_F(AconfigStorageTest, test_invalid_boolean_flag_value_query) {
+  auto mapped_file = private_api::get_mapped_file_impl(
+      storage_record_pb, "mockup", api::StorageFileType::flag_val);
+  ASSERT_TRUE(mapped_file.ok());
+
+  auto value = api::get_boolean_flag_value(*mapped_file, 8);
+  ASSERT_FALSE(value.ok());
+  ASSERT_EQ(value.error().message(),
+            std::string("InvalidStorageFileOffset(Flag value offset goes beyond the end of the file.)"));
+}
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
new file mode 100644
index 0000000..7687d0f
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_read_api/tests/storage_read_api_test.rs
@@ -0,0 +1,181 @@
+#[cfg(not(feature = "cargo"))]
+mod aconfig_storage_rust_test {
+    use aconfig_storage_file::protos::storage_record_pb::write_proto_to_temp_file;
+    use aconfig_storage_file::StorageFileType;
+    use aconfig_storage_read_api::{
+        get_boolean_flag_value, get_flag_offset, get_package_offset, get_storage_file_version,
+        mapped_file::get_mapped_file, PackageOffset,
+    };
+    use std::fs;
+    use tempfile::NamedTempFile;
+
+    pub fn copy_to_temp_file(source_file: &str) -> NamedTempFile {
+        let file = NamedTempFile::new().unwrap();
+        fs::copy(source_file, file.path()).unwrap();
+        file
+    }
+
+    fn create_test_storage_files() -> [NamedTempFile; 4] {
+        let package_map = copy_to_temp_file("./package.map");
+        let flag_map = copy_to_temp_file("./flag.map");
+        let flag_val = copy_to_temp_file("./flag.val");
+
+        let text_proto = format!(
+            r#"
+files {{
+    version: 0
+    container: "mockup"
+    package_map: "{}"
+    flag_map: "{}"
+    flag_val: "{}"
+    timestamp: 12345
+}}
+"#,
+            package_map.path().display(),
+            flag_map.path().display(),
+            flag_val.path().display()
+        );
+        let pb_file = write_proto_to_temp_file(&text_proto).unwrap();
+        [package_map, flag_map, flag_val, pb_file]
+    }
+
+    #[test]
+    fn test_unavailable_stoarge() {
+        let [_package_map, _flag_map, _flag_val, pb_file] = create_test_storage_files();
+        let pb_file_path = pb_file.path().display().to_string();
+        // SAFETY:
+        // The safety here is ensured as the test process will not write to temp storage file
+        let err = unsafe {
+            get_mapped_file(&pb_file_path, "vendor", StorageFileType::PackageMap).unwrap_err()
+        };
+        assert_eq!(
+            format!("{:?}", err),
+            "StorageFileNotFound(Storage file does not exist for vendor)"
+        );
+    }
+
+    #[test]
+    fn test_package_offset_query() {
+        let [_package_map, _flag_map, _flag_val, pb_file] = create_test_storage_files();
+        let pb_file_path = pb_file.path().display().to_string();
+        // SAFETY:
+        // The safety here is ensured as the test process will not write to temp storage file
+        let package_mapped_file = unsafe {
+            get_mapped_file(&pb_file_path, "mockup", StorageFileType::PackageMap).unwrap()
+        };
+
+        let package_offset =
+            get_package_offset(&package_mapped_file, "com.android.aconfig.storage.test_1")
+                .unwrap()
+                .unwrap();
+        let expected_package_offset = PackageOffset { package_id: 0, boolean_offset: 0 };
+        assert_eq!(package_offset, expected_package_offset);
+
+        let package_offset =
+            get_package_offset(&package_mapped_file, "com.android.aconfig.storage.test_2")
+                .unwrap()
+                .unwrap();
+        let expected_package_offset = PackageOffset { package_id: 1, boolean_offset: 3 };
+        assert_eq!(package_offset, expected_package_offset);
+
+        let package_offset =
+            get_package_offset(&package_mapped_file, "com.android.aconfig.storage.test_4")
+                .unwrap()
+                .unwrap();
+        let expected_package_offset = PackageOffset { package_id: 2, boolean_offset: 6 };
+        assert_eq!(package_offset, expected_package_offset);
+    }
+
+    #[test]
+    fn test_none_exist_package_offset_query() {
+        let [_package_map, _flag_map, _flag_val, pb_file] = create_test_storage_files();
+        let pb_file_path = pb_file.path().display().to_string();
+        // SAFETY:
+        // The safety here is ensured as the test process will not write to temp storage file
+        let package_mapped_file = unsafe {
+            get_mapped_file(&pb_file_path, "mockup", StorageFileType::PackageMap).unwrap()
+        };
+
+        let package_offset_option =
+            get_package_offset(&package_mapped_file, "com.android.aconfig.storage.test_3").unwrap();
+        assert_eq!(package_offset_option, None);
+    }
+
+    #[test]
+    fn test_flag_offset_query() {
+        let [_package_map, _flag_map, _flag_val, pb_file] = create_test_storage_files();
+        let pb_file_path = pb_file.path().display().to_string();
+        // SAFETY:
+        // The safety here is ensured as the test process will not write to temp storage file
+        let flag_mapped_file =
+            unsafe { get_mapped_file(&pb_file_path, "mockup", StorageFileType::FlagMap).unwrap() };
+
+        let baseline = vec![
+            (0, "enabled_ro", 1u16),
+            (0, "enabled_rw", 2u16),
+            (1, "disabled_ro", 0u16),
+            (2, "enabled_ro", 1u16),
+            (1, "enabled_fixed_ro", 1u16),
+            (1, "enabled_ro", 2u16),
+            (2, "enabled_fixed_ro", 0u16),
+            (0, "disabled_rw", 0u16),
+        ];
+        for (package_id, flag_name, expected_offset) in baseline.into_iter() {
+            let flag_offset =
+                get_flag_offset(&flag_mapped_file, package_id, flag_name).unwrap().unwrap();
+            assert_eq!(flag_offset, expected_offset);
+        }
+    }
+
+    #[test]
+    fn test_none_exist_flag_offset_query() {
+        let [_package_map, _flag_map, _flag_val, pb_file] = create_test_storage_files();
+        let pb_file_path = pb_file.path().display().to_string();
+        // SAFETY:
+        // The safety here is ensured as the test process will not write to temp storage file
+        let flag_mapped_file =
+            unsafe { get_mapped_file(&pb_file_path, "mockup", StorageFileType::FlagMap).unwrap() };
+        let flag_offset_option = get_flag_offset(&flag_mapped_file, 0, "none_exist").unwrap();
+        assert_eq!(flag_offset_option, None);
+
+        let flag_offset_option = get_flag_offset(&flag_mapped_file, 3, "enabled_ro").unwrap();
+        assert_eq!(flag_offset_option, None);
+    }
+
+    #[test]
+    fn test_boolean_flag_value_query() {
+        let [_package_map, _flag_map, _flag_val, pb_file] = create_test_storage_files();
+        let pb_file_path = pb_file.path().display().to_string();
+        // SAFETY:
+        // The safety here is ensured as the test process will not write to temp storage file
+        let flag_value_file =
+            unsafe { get_mapped_file(&pb_file_path, "mockup", StorageFileType::FlagVal).unwrap() };
+        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 = get_boolean_flag_value(&flag_value_file, offset as u32).unwrap();
+            assert_eq!(flag_value, expected_value);
+        }
+    }
+
+    #[test]
+    fn test_invalid_boolean_flag_value_query() {
+        let [_package_map, _flag_map, _flag_val, pb_file] = create_test_storage_files();
+        let pb_file_path = pb_file.path().display().to_string();
+        // SAFETY:
+        // The safety here is ensured as the test process will not write to temp storage file
+        let flag_value_file =
+            unsafe { get_mapped_file(&pb_file_path, "mockup", StorageFileType::FlagVal).unwrap() };
+        let err = get_boolean_flag_value(&flag_value_file, 8u32).unwrap_err();
+        assert_eq!(
+            format!("{:?}", err),
+            "InvalidStorageFileOffset(Flag value offset goes beyond the end of the file.)"
+        );
+    }
+
+    #[test]
+    fn test_storage_version_query() {
+        assert_eq!(get_storage_file_version("./package.map").unwrap(), 1);
+        assert_eq!(get_storage_file_version("./flag.map").unwrap(), 1);
+        assert_eq!(get_storage_file_version("./flag.val").unwrap(), 1);
+    }
+}
diff --git a/tools/aconfig/aconfig_storage_write_api/Android.bp b/tools/aconfig/aconfig_storage_write_api/Android.bp
new file mode 100644
index 0000000..0f15b9c
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_write_api/Android.bp
@@ -0,0 +1,81 @@
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+rust_defaults {
+    name: "aconfig_storage_write_api.defaults",
+    edition: "2021",
+    lints: "none",
+    srcs: ["src/lib.rs"],
+    rustlibs: [
+        "libanyhow",
+        "libtempfile",
+        "libmemmap2",
+        "libcxx",
+        "libthiserror",
+        "libaconfig_storage_file",
+    ],
+}
+
+rust_library {
+    name: "libaconfig_storage_write_api",
+    crate_name: "aconfig_storage_write_api",
+    host_supported: true,
+    defaults: ["aconfig_storage_write_api.defaults"],
+}
+
+rust_test_host {
+    name: "aconfig_storage_write_api.test",
+    test_suites: ["general-tests"],
+    defaults: ["aconfig_storage_write_api.defaults"],
+    data: [
+        "tests/flag.val",
+    ],
+    rustlibs: [
+        "libaconfig_storage_read_api",
+    ],
+}
+
+// cxx source codegen from rust api
+genrule {
+    name: "libcxx_aconfig_storage_write_api_bridge_code",
+    tools: ["cxxbridge"],
+    cmd: "$(location cxxbridge) $(in) > $(out)",
+    srcs: ["src/lib.rs"],
+    out: ["aconfig_storage/lib.rs.cc"],
+}
+
+// cxx header codegen from rust api
+genrule {
+    name: "libcxx_aconfig_storage_write_api_bridge_header",
+    tools: ["cxxbridge"],
+    cmd: "$(location cxxbridge) $(in) --header > $(out)",
+    srcs: ["src/lib.rs"],
+    out: ["aconfig_storage/lib.rs.h"],
+}
+
+// a static cc lib based on generated code
+rust_ffi_static {
+    name: "libaconfig_storage_write_api_cxx_bridge",
+    crate_name: "aconfig_storage_write_api_cxx_bridge",
+    host_supported: true,
+    defaults: ["aconfig_storage_write_api.defaults"],
+}
+
+// flag write api cc interface
+cc_library_static {
+    name: "libaconfig_storage_write_api_cc",
+    srcs: ["aconfig_storage_write_api.cpp"],
+    generated_headers: [
+        "cxx-bridge-header",
+        "libcxx_aconfig_storage_write_api_bridge_header"
+    ],
+    generated_sources: ["libcxx_aconfig_storage_write_api_bridge_code"],
+    whole_static_libs: ["libaconfig_storage_write_api_cxx_bridge"],
+    export_include_dirs: ["include"],
+    static_libs: [
+        "libaconfig_storage_protos_cc",
+        "libprotobuf-cpp-lite",
+        "libbase",
+    ],
+}
diff --git a/tools/aconfig/aconfig_storage_write_api/Cargo.toml b/tools/aconfig/aconfig_storage_write_api/Cargo.toml
new file mode 100644
index 0000000..eaa55f2
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_write_api/Cargo.toml
@@ -0,0 +1,21 @@
+[package]
+name = "aconfig_storage_write_api"
+version = "0.1.0"
+edition = "2021"
+
+[features]
+default = ["cargo"]
+cargo = []
+
+[dependencies]
+anyhow = "1.0.69"
+cxx = "1.0"
+memmap2 = "0.8.0"
+tempfile = "3.9.0"
+thiserror = "1.0.56"
+protobuf = "3.2.0"
+aconfig_storage_file = { path = "../aconfig_storage_file" }
+aconfig_storage_read_api = { path = "../aconfig_storage_read_api" }
+
+[build-dependencies]
+cxx-build = "1.0"
diff --git a/tools/aconfig/aconfig_storage_write_api/aconfig_storage_write_api.cpp b/tools/aconfig/aconfig_storage_write_api/aconfig_storage_write_api.cpp
new file mode 100644
index 0000000..e5155a4
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_write_api/aconfig_storage_write_api.cpp
@@ -0,0 +1,129 @@
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <protos/aconfig_storage_metadata.pb.h>
+
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include "rust/cxx.h"
+#include "aconfig_storage/lib.rs.h"
+#include "aconfig_storage/aconfig_storage_write_api.hpp"
+
+using storage_records_pb = android::aconfig_storage_metadata::storage_files;
+using storage_record_pb = android::aconfig_storage_metadata::storage_file_info;
+using namespace android::base;
+
+namespace aconfig_storage {
+
+/// Storage location pb file
+static constexpr char kPersistStorageRecordsPb[] =
+    "/metadata/aconfig/persistent_storage_file_records.pb";
+
+/// Read aconfig storage records pb file
+static Result<storage_records_pb> read_storage_records_pb(std::string const& pb_file) {
+  auto records = storage_records_pb();
+  auto content = std::string();
+  if (!ReadFileToString(pb_file, &content)) {
+    return ErrnoError() << "ReadFileToString failed";
+  }
+
+  if (!records.ParseFromString(content)) {
+    return ErrnoError() << "Unable to parse persistent storage records protobuf";
+  }
+  return records;
+}
+
+/// Get storage file path
+static Result<std::string> find_storage_file(
+    std::string const& pb_file,
+    std::string const& container) {
+  auto records_pb = read_storage_records_pb(pb_file);
+  if (!records_pb.ok()) {
+    return Error() << "Unable to read storage records from " << pb_file
+                   << " : " << records_pb.error();
+  }
+
+  for (auto& entry : records_pb->files()) {
+    if (entry.container() == container) {
+        return entry.flag_val();
+    }
+  }
+
+  return Error() << "Unable to find storage files for container " << container;;
+}
+
+/// Map a storage file
+static Result<MappedFlagValueFile> map_storage_file(std::string const& file) {
+  struct stat file_stat;
+  if (stat(file.c_str(), &file_stat) < 0) {
+    return ErrnoError() << "stat failed";
+  }
+
+  if ((file_stat.st_mode & (S_IWUSR | S_IWGRP | S_IWOTH)) == 0) {
+    return Error() << "cannot map nonwriteable file";
+  }
+
+  size_t file_size = file_stat.st_size;
+
+  const int fd = open(file.c_str(), O_RDWR | O_NOFOLLOW | O_CLOEXEC);
+  if (fd == -1) {
+    return ErrnoError() << "failed to open " << file;
+  };
+
+  void* const map_result =
+      mmap(nullptr, file_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+  if (map_result == MAP_FAILED) {
+    return ErrnoError() << "mmap failed";
+  }
+
+  auto mapped_file = MappedFlagValueFile();
+  mapped_file.file_ptr = map_result;
+  mapped_file.file_size = file_size;
+
+  return mapped_file;
+}
+
+namespace private_internal_api {
+
+/// Get mapped file implementation.
+Result<MappedFlagValueFile> get_mapped_flag_value_file_impl(
+    std::string const& pb_file,
+    std::string const& container) {
+  auto file_result = find_storage_file(pb_file, container);
+  if (!file_result.ok()) {
+    return Error() << file_result.error();
+  }
+  auto mapped_result = map_storage_file(*file_result);
+  if (!mapped_result.ok()) {
+    return Error() << "failed to map " << *file_result << ": "
+                   << mapped_result.error();
+  }
+  return *mapped_result;
+}
+
+} // namespace private internal api
+
+/// Get mapped writeable flag value file
+Result<MappedFlagValueFile> get_mapped_flag_value_file(
+    std::string const& container) {
+  return private_internal_api::get_mapped_flag_value_file_impl(
+      kPersistStorageRecordsPb, container);
+}
+
+/// Set boolean flag value
+Result<void> set_boolean_flag_value(
+    const MappedFlagValueFile& file,
+    uint32_t offset,
+    bool value) {
+  auto content = rust::Slice<uint8_t>(
+      static_cast<uint8_t*>(file.file_ptr), file.file_size);
+  auto update_cxx = update_boolean_flag_value_cxx(content, offset, value);
+  if (!update_cxx.update_success) {
+    return Error() << std::string(update_cxx.error_message.c_str());
+  }
+  return {};
+}
+
+} // namespace aconfig_storage
diff --git a/tools/aconfig/aconfig_storage_write_api/build.rs b/tools/aconfig/aconfig_storage_write_api/build.rs
new file mode 100644
index 0000000..7b1aa53
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_write_api/build.rs
@@ -0,0 +1,4 @@
+fn main() {
+    let _ = cxx_build::bridge("src/lib.rs");
+    println!("cargo:rerun-if-changed=src/lib.rs");
+}
diff --git a/tools/aconfig/aconfig_storage_write_api/include/aconfig_storage/aconfig_storage_write_api.hpp b/tools/aconfig/aconfig_storage_write_api/include/aconfig_storage/aconfig_storage_write_api.hpp
new file mode 100644
index 0000000..9e6332a
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_write_api/include/aconfig_storage/aconfig_storage_write_api.hpp
@@ -0,0 +1,37 @@
+#pragma once
+
+#include <stdint.h>
+#include <string>
+
+#include <android-base/result.h>
+
+using namespace android::base;
+
+namespace aconfig_storage {
+
+/// Mapped flag value file
+struct MappedFlagValueFile{
+  void* file_ptr;
+  size_t file_size;
+};
+
+/// DO NOT USE APIS IN THE FOLLOWING NAMESPACE DIRECTLY
+namespace private_internal_api {
+
+Result<MappedFlagValueFile> get_mapped_flag_value_file_impl(
+    std::string const& pb_file,
+    std::string const& container);
+
+} // namespace private_internal_api
+
+/// Get mapped writeable flag value file
+Result<MappedFlagValueFile> get_mapped_flag_value_file(
+    std::string const& container);
+
+/// Set boolean flag value
+Result<void> set_boolean_flag_value(
+    const MappedFlagValueFile& file,
+    uint32_t offset,
+    bool value);
+
+} // namespace aconfig_storage
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
new file mode 100644
index 0000000..4cb7939
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_write_api/src/flag_value_update.rs
@@ -0,0 +1,114 @@
+/*
+ * 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.
+ */
+
+//! flag value update module defines the flag value file write to mapped bytes
+
+use aconfig_storage_file::{AconfigStorageError, FlagValueHeader, FILE_VERSION};
+use anyhow::anyhow;
+
+/// Set flag value
+pub fn update_boolean_flag_value(
+    buf: &mut [u8],
+    flag_offset: u32,
+    flag_value: bool,
+) -> Result<(), AconfigStorageError> {
+    let interpreted_header = FlagValueHeader::from_bytes(buf)?;
+    if interpreted_header.version > FILE_VERSION {
+        return Err(AconfigStorageError::HigherStorageFileVersion(anyhow!(
+            "Cannot write to storage file with a higher version of {} with lib version {}",
+            interpreted_header.version,
+            FILE_VERSION
+        )));
+    }
+
+    let head = (interpreted_header.boolean_value_offset + flag_offset) as usize;
+
+    // TODO: right now, there is only boolean flags, with more flag value types added
+    // later, the end of boolean flag value section should be updated (b/322826265).
+    if head >= interpreted_header.file_size as usize {
+        return Err(AconfigStorageError::InvalidStorageFileOffset(anyhow!(
+            "Flag value offset goes beyond the end of the file."
+        )));
+    }
+
+    buf[head] = u8::from(flag_value).to_le_bytes()[0];
+    Ok(())
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use aconfig_storage_file::{FlagValueList, StorageFileType};
+
+    pub fn create_test_flag_value_list() -> FlagValueList {
+        let header = FlagValueHeader {
+            version: FILE_VERSION,
+            container: String::from("system"),
+            file_type: StorageFileType::FlagVal as u8,
+            file_size: 35,
+            num_flags: 8,
+            boolean_value_offset: 27,
+        };
+        let booleans: Vec<bool> = vec![false; 8];
+        FlagValueList { header, booleans }
+    }
+
+    #[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 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];
+        let false_byte = u8::from(false).to_le_bytes()[0];
+
+        for i in 0..flag_value_list.header.num_flags {
+            let offset = (value_offset + i) as usize;
+            update_boolean_flag_value(&mut content, i, true).unwrap();
+            assert_eq!(content[offset], true_byte);
+            update_boolean_flag_value(&mut content, i, false).unwrap();
+            assert_eq!(content[offset], false_byte);
+        }
+    }
+
+    #[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 error = update_boolean_flag_value(&mut flag_value_list[..], 8, true).unwrap_err();
+        assert_eq!(
+            format!("{:?}", error),
+            "InvalidStorageFileOffset(Flag value offset goes beyond the end of the file.)"
+        );
+    }
+
+    #[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();
+        value_list.header.version = FILE_VERSION + 1;
+        let mut flag_value = value_list.into_bytes();
+        let error = update_boolean_flag_value(&mut flag_value[..], 4, true).unwrap_err();
+        assert_eq!(
+            format!("{:?}", error),
+            format!(
+                "HigherStorageFileVersion(Cannot write to storage file with a higher version of {} with lib version {})",
+                FILE_VERSION + 1,
+                FILE_VERSION
+            )
+        );
+    }
+}
diff --git a/tools/aconfig/aconfig_storage_write_api/src/lib.rs b/tools/aconfig/aconfig_storage_write_api/src/lib.rs
new file mode 100644
index 0000000..5562d6a
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_write_api/src/lib.rs
@@ -0,0 +1,159 @@
+/*
+ * 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.
+ */
+
+//! `aconfig_storage_write_api` is a crate that defines write apis to update flag value
+//! in storage file. It provides one api to interface with storage files.
+
+pub mod flag_value_update;
+pub mod mapped_file;
+
+#[cfg(test)]
+mod test_utils;
+
+use aconfig_storage_file::AconfigStorageError;
+
+use anyhow::anyhow;
+use memmap2::MmapMut;
+
+/// Storage file location pb file
+pub const STORAGE_LOCATION_FILE: &str = "/metadata/aconfig/persistent_storage_file_records.pb";
+
+/// Get mmaped flag value file given the container name
+///
+/// \input container: the flag package container
+/// \return a result of mapped file
+///
+///
+/// # Safety
+///
+/// The memory mapped file may have undefined behavior if there are writes to this
+/// file not thru this memory mapped file or there are concurrent writes to this
+/// memory mapped file. Ensure all writes to the underlying file are thru this memory
+/// mapped file and there are no concurrent writes.
+pub unsafe fn get_mapped_flag_value_file(container: &str) -> Result<MmapMut, AconfigStorageError> {
+    unsafe { crate::mapped_file::get_mapped_file(STORAGE_LOCATION_FILE, container) }
+}
+
+/// Set boolean flag value thru mapped file and flush the change to file
+///
+/// \input mapped_file: the mapped flag value file
+/// \input offset: flag value offset
+/// \input value: updated flag value
+/// \return a result of ()
+///
+pub fn set_boolean_flag_value(
+    file: &mut MmapMut,
+    offset: u32,
+    value: bool,
+) -> Result<(), AconfigStorageError> {
+    crate::flag_value_update::update_boolean_flag_value(file, offset, value)?;
+    file.flush().map_err(|errmsg| {
+        AconfigStorageError::MapFlushFail(anyhow!("fail to flush storage file: {}", errmsg))
+    })
+}
+
+// *************************************** //
+// CC INTERLOP
+// *************************************** //
+
+// Exported rust data structure and methods, c++ code will be generated
+#[cxx::bridge]
+mod ffi {
+    // Flag value update return for cc interlop
+    pub struct BooleanFlagValueUpdateCXX {
+        pub update_success: bool,
+        pub error_message: String,
+    }
+
+    // Rust export to c++
+    extern "Rust" {
+        pub fn update_boolean_flag_value_cxx(
+            file: &mut [u8],
+            offset: u32,
+            value: bool,
+        ) -> BooleanFlagValueUpdateCXX;
+    }
+}
+
+pub(crate) fn update_boolean_flag_value_cxx(
+    file: &mut [u8],
+    offset: u32,
+    value: bool,
+) -> ffi::BooleanFlagValueUpdateCXX {
+    match crate::flag_value_update::update_boolean_flag_value(file, offset, value) {
+        Ok(()) => {
+            ffi::BooleanFlagValueUpdateCXX { update_success: true, error_message: String::from("") }
+        }
+        Err(errmsg) => ffi::BooleanFlagValueUpdateCXX {
+            update_success: false,
+            error_message: format!("{:?}", errmsg),
+        },
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use crate::test_utils::copy_to_temp_file;
+    use aconfig_storage_file::protos::storage_record_pb::write_proto_to_temp_file;
+    use aconfig_storage_read_api::flag_value_query::find_boolean_flag_value;
+    use std::fs::File;
+    use std::io::Read;
+
+    fn get_boolean_flag_value_at_offset(file: &str, offset: u32) -> bool {
+        let mut f = File::open(&file).unwrap();
+        let mut bytes = Vec::new();
+        f.read_to_end(&mut bytes).unwrap();
+        find_boolean_flag_value(&bytes, offset).unwrap()
+    }
+
+    #[test]
+    fn test_set_boolean_flag_value() {
+        let flag_value_file = copy_to_temp_file("./tests/flag.val", false).unwrap();
+        let flag_value_path = flag_value_file.path().display().to_string();
+        let text_proto = format!(
+            r#"
+files {{
+    version: 0
+    container: "system"
+    package_map: "some_package.map"
+    flag_map: "some_flag.map"
+    flag_val: "{}"
+    timestamp: 12345
+}}
+"#,
+            flag_value_path
+        );
+        let record_pb_file = write_proto_to_temp_file(&text_proto).unwrap();
+        let record_pb_path = record_pb_file.path().display().to_string();
+
+        // SAFETY:
+        // The safety here is guaranteed as only this single threaded test process will
+        // write to this file
+        unsafe {
+            let mut file = crate::mapped_file::get_mapped_file(&record_pb_path, "system").unwrap();
+            for i in 0..8 {
+                set_boolean_flag_value(&mut file, i, true).unwrap();
+                let value = get_boolean_flag_value_at_offset(&flag_value_path, i);
+                assert_eq!(value, true);
+
+                set_boolean_flag_value(&mut file, i, false).unwrap();
+                let value = get_boolean_flag_value_at_offset(&flag_value_path, i);
+                assert_eq!(value, false);
+            }
+        }
+    }
+}
diff --git a/tools/aconfig/aconfig_storage_write_api/src/mapped_file.rs b/tools/aconfig/aconfig_storage_write_api/src/mapped_file.rs
new file mode 100644
index 0000000..4c98be4
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_write_api/src/mapped_file.rs
@@ -0,0 +1,189 @@
+/*
+ * 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.
+ */
+
+use std::fs::{self, File, OpenOptions};
+use std::io::{BufReader, Read};
+
+use anyhow::anyhow;
+use memmap2::MmapMut;
+
+use aconfig_storage_file::protos::{storage_record_pb::try_from_binary_proto, ProtoStorageFiles};
+use aconfig_storage_file::AconfigStorageError::{
+    self, FileReadFail, MapFileFail, ProtobufParseFail, StorageFileNotFound,
+};
+
+/// Find where persistent storage value file is for a particular container
+fn find_persist_flag_value_file(
+    location_pb_file: &str,
+    container: &str,
+) -> Result<String, AconfigStorageError> {
+    let file = File::open(location_pb_file).map_err(|errmsg| {
+        FileReadFail(anyhow!("Failed to open file {}: {}", location_pb_file, errmsg))
+    })?;
+    let mut reader = BufReader::new(file);
+    let mut bytes = Vec::new();
+    reader.read_to_end(&mut bytes).map_err(|errmsg| {
+        FileReadFail(anyhow!("Failed to read file {}: {}", location_pb_file, errmsg))
+    })?;
+    let storage_locations: ProtoStorageFiles = try_from_binary_proto(&bytes).map_err(|errmsg| {
+        ProtobufParseFail(anyhow!(
+            "Failed to parse storage location pb file {}: {}",
+            location_pb_file,
+            errmsg
+        ))
+    })?;
+    for location_info in storage_locations.files.iter() {
+        if location_info.container() == container {
+            return Ok(location_info.flag_val().to_string());
+        }
+    }
+    Err(StorageFileNotFound(anyhow!("Persistent flag value file does not exist for {}", container)))
+}
+
+/// Get a mapped storage file given the container and file type
+///
+/// # Safety
+///
+/// The memory mapped file may have undefined behavior if there are writes to this
+/// file not thru this memory mapped file or there are concurrent writes to this
+/// memory mapped file. Ensure all writes to the underlying file are thru this memory
+/// mapped file and there are no concurrent writes.
+pub unsafe fn get_mapped_file(
+    location_pb_file: &str,
+    container: &str,
+) -> Result<MmapMut, AconfigStorageError> {
+    let file_path = find_persist_flag_value_file(location_pb_file, container)?;
+
+    // make sure file has read write permission
+    let perms = fs::metadata(&file_path).unwrap().permissions();
+    if perms.readonly() {
+        return Err(MapFileFail(anyhow!("fail to map non read write storage file {}", file_path)));
+    }
+
+    let file =
+        OpenOptions::new().read(true).write(true).open(&file_path).map_err(|errmsg| {
+            FileReadFail(anyhow!("Failed to open file {}: {}", file_path, errmsg))
+        })?;
+
+    unsafe {
+        MmapMut::map_mut(&file).map_err(|errmsg| {
+            MapFileFail(anyhow!("fail to map storage file {}: {}", file_path, errmsg))
+        })
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use crate::test_utils::copy_to_temp_file;
+    use aconfig_storage_file::protos::storage_record_pb::write_proto_to_temp_file;
+
+    #[test]
+    fn test_find_persist_flag_value_file_location() {
+        let text_proto = r#"
+files {
+    version: 0
+    container: "system"
+    package_map: "/system/etc/package.map"
+    flag_map: "/system/etc/flag.map"
+    flag_val: "/metadata/aconfig/system.val"
+    timestamp: 12345
+}
+files {
+    version: 1
+    container: "product"
+    package_map: "/product/etc/package.map"
+    flag_map: "/product/etc/flag.map"
+    flag_val: "/metadata/aconfig/product.val"
+    timestamp: 54321
+}
+"#;
+        let file = write_proto_to_temp_file(&text_proto).unwrap();
+        let file_full_path = file.path().display().to_string();
+        let flag_value_file = find_persist_flag_value_file(&file_full_path, "system").unwrap();
+        assert_eq!(flag_value_file, "/metadata/aconfig/system.val");
+        let flag_value_file = find_persist_flag_value_file(&file_full_path, "product").unwrap();
+        assert_eq!(flag_value_file, "/metadata/aconfig/product.val");
+        let err = find_persist_flag_value_file(&file_full_path, "vendor").unwrap_err();
+        assert_eq!(
+            format!("{:?}", err),
+            "StorageFileNotFound(Persistent flag value file does not exist for vendor)"
+        );
+    }
+
+    #[test]
+    fn test_mapped_file_contents() {
+        let mut rw_file = copy_to_temp_file("./tests/flag.val", false).unwrap();
+        let text_proto = format!(
+            r#"
+files {{
+    version: 0
+    container: "system"
+    package_map: "some_package.map"
+    flag_map: "some_flag.map"
+    flag_val: "{}"
+    timestamp: 12345
+}}
+"#,
+            rw_file.path().display().to_string()
+        );
+        let storage_record_file = write_proto_to_temp_file(&text_proto).unwrap();
+        let storage_record_file_path = storage_record_file.path().display().to_string();
+
+        let mut content = Vec::new();
+        rw_file.read_to_end(&mut content).unwrap();
+
+        // SAFETY:
+        // The safety here is guaranteed here as no writes happens to this temp file
+        unsafe {
+            let mmaped_file = get_mapped_file(&storage_record_file_path, "system").unwrap();
+            assert_eq!(mmaped_file[..], content[..]);
+        }
+    }
+
+    #[test]
+    fn test_mapped_read_only_file() {
+        let ro_file = copy_to_temp_file("./tests/flag.val", true).unwrap();
+        let text_proto = format!(
+            r#"
+files {{
+    version: 0
+    container: "system"
+    package_map: "some_package.map"
+    flag_map: "some_flag.map"
+    flag_val: "{}"
+    timestamp: 12345
+}}
+"#,
+            ro_file.path().display().to_string()
+        );
+        let storage_record_file = write_proto_to_temp_file(&text_proto).unwrap();
+        let storage_record_file_path = storage_record_file.path().display().to_string();
+
+        // SAFETY:
+        // The safety here is guaranteed here as no writes happens to this temp file
+        unsafe {
+            let error = get_mapped_file(&storage_record_file_path, "system").unwrap_err();
+            assert_eq!(
+                format!("{:?}", error),
+                format!(
+                    "MapFileFail(fail to map non read write storage file {})",
+                    ro_file.path().display().to_string()
+                )
+            );
+        }
+    }
+}
diff --git a/tools/aconfig/aconfig_storage_write_api/src/test_utils.rs b/tools/aconfig/aconfig_storage_write_api/src/test_utils.rs
new file mode 100644
index 0000000..06e2e22
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_write_api/src/test_utils.rs
@@ -0,0 +1,32 @@
+/*
+ * 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.
+ */
+
+use anyhow::Result;
+use std::fs;
+use tempfile::NamedTempFile;
+
+/// Create temp file copy
+pub(crate) fn copy_to_temp_file(source_file: &str, read_only: bool) -> Result<NamedTempFile> {
+    let file = NamedTempFile::new()?;
+    fs::copy(source_file, file.path())?;
+    if read_only {
+        let file_name = file.path().display().to_string();
+        let mut perms = fs::metadata(file_name).unwrap().permissions();
+        perms.set_readonly(true);
+        fs::set_permissions(file.path(), perms.clone()).unwrap();
+    }
+    Ok(file)
+}
diff --git a/tools/aconfig/aconfig_storage_write_api/tests/Android.bp b/tools/aconfig/aconfig_storage_write_api/tests/Android.bp
new file mode 100644
index 0000000..d2a52fe
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_write_api/tests/Android.bp
@@ -0,0 +1,42 @@
+
+rust_test {
+    name: "aconfig_storage_write_api.test.rust",
+    srcs: [
+        "storage_write_api_test.rs"
+    ],
+    rustlibs: [
+        "libanyhow",
+        "libaconfig_storage_file",
+        "libaconfig_storage_read_api",
+        "libaconfig_storage_write_api",
+        "libprotobuf",
+        "libtempfile",
+    ],
+    data: [
+        "flag.val",
+    ],
+    test_suites: ["general-tests"],
+}
+
+cc_test {
+    name: "aconfig_storage_write_api.test.cpp",
+    srcs: [
+        "storage_write_api_test.cpp",
+    ],
+    static_libs: [
+        "libgmock",
+        "libaconfig_storage_protos_cc",
+        "libprotobuf-cpp-lite",
+        "libaconfig_storage_read_api_cc",
+        "libaconfig_storage_write_api_cc",
+        "libbase",
+        "liblog",
+    ],
+    data: [
+        "flag.val",
+    ],
+    test_suites: [
+        "device-tests",
+        "general-tests",
+    ],
+}
diff --git a/tools/aconfig/aconfig_storage_write_api/tests/flag.val b/tools/aconfig/aconfig_storage_write_api/tests/flag.val
new file mode 100644
index 0000000..ed203d4
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_write_api/tests/flag.val
Binary files differ
diff --git a/tools/aconfig/aconfig_storage_write_api/tests/storage_write_api_test.cpp b/tools/aconfig/aconfig_storage_write_api/tests/storage_write_api_test.cpp
new file mode 100644
index 0000000..00b737c
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_write_api/tests/storage_write_api_test.cpp
@@ -0,0 +1,135 @@
+/*
+ * 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.
+ */
+
+#include <string>
+#include <vector>
+#include <cstdio>
+
+#include <sys/stat.h>
+#include "aconfig_storage/aconfig_storage_read_api.hpp"
+#include "aconfig_storage/aconfig_storage_write_api.hpp"
+#include <gtest/gtest.h>
+#include <protos/aconfig_storage_metadata.pb.h>
+#include <android-base/file.h>
+#include <android-base/result.h>
+
+using android::aconfig_storage_metadata::storage_files;
+using namespace android::base;
+
+namespace api = aconfig_storage;
+namespace private_api = aconfig_storage::private_internal_api;
+
+class AconfigStorageTest : public ::testing::Test {
+ protected:
+  Result<std::string> copy_to_rw_temp_file(std::string const& source_file) {
+    auto temp_file = std::string(std::tmpnam(nullptr));
+    auto content = std::string();
+    if (!ReadFileToString(source_file, &content)) {
+      return Error() << "failed to read file: " << source_file;
+    }
+    if (!WriteStringToFile(content, temp_file)) {
+      return Error() << "failed to copy file: " << source_file;
+    }
+    if (chmod(temp_file.c_str(),
+              S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH) == -1) {
+      return Error() << "failed to chmod";
+    }
+    return temp_file;
+  }
+
+  Result<std::string> write_storage_location_pb_file(std::string const& flag_val) {
+    auto temp_file = std::tmpnam(nullptr);
+    auto proto = storage_files();
+    auto* info = proto.add_files();
+    info->set_version(0);
+    info->set_container("mockup");
+    info->set_package_map("some_package.map");
+    info->set_flag_map("some_flag.map");
+    info->set_flag_val(flag_val);
+    info->set_timestamp(12345);
+
+    auto content = std::string();
+    proto.SerializeToString(&content);
+    if (!WriteStringToFile(content, temp_file)) {
+      return Error() << "failed to write storage records pb file";
+    }
+    return temp_file;
+  }
+
+  void SetUp() override {
+    auto const test_dir = android::base::GetExecutableDirectory();
+    flag_val = *copy_to_rw_temp_file(test_dir + "/flag.val");
+    storage_record_pb = *write_storage_location_pb_file(flag_val);
+  }
+
+  void TearDown() override {
+    std::remove(flag_val.c_str());
+    std::remove(storage_record_pb.c_str());
+  }
+
+  std::string flag_val;
+  std::string storage_record_pb;
+};
+
+/// Negative test to lock down the error when mapping none exist storage files
+TEST_F(AconfigStorageTest, test_none_exist_storage_file_mapping) {
+  auto mapped_file_result = private_api::get_mapped_flag_value_file_impl(
+      storage_record_pb, "vendor");
+  ASSERT_FALSE(mapped_file_result.ok());
+  ASSERT_EQ(mapped_file_result.error().message(),
+            "Unable to find storage files for container vendor");
+}
+
+/// Negative test to lock down the error when mapping a non writeable storage file
+TEST_F(AconfigStorageTest, test_non_writable_storage_file_mapping) {
+  ASSERT_TRUE(chmod(flag_val.c_str(), S_IRUSR | S_IRGRP | S_IROTH) != -1);
+  auto mapped_file_result = private_api::get_mapped_flag_value_file_impl(
+      storage_record_pb, "mockup");
+  ASSERT_FALSE(mapped_file_result.ok());
+  auto it = mapped_file_result.error().message().find("cannot map nonwriteable file");
+  ASSERT_TRUE(it != std::string::npos) << mapped_file_result.error().message();
+}
+
+/// Test to lock down storage flag value update api
+TEST_F(AconfigStorageTest, test_boolean_flag_value_update) {
+  auto mapped_file_result = private_api::get_mapped_flag_value_file_impl(
+      storage_record_pb, "mockup");
+  ASSERT_TRUE(mapped_file_result.ok());
+  auto mapped_file = *mapped_file_result;
+
+  for (int offset = 0; offset < 8; ++offset) {
+    auto update_result = api::set_boolean_flag_value(mapped_file, offset, true);
+    ASSERT_TRUE(update_result.ok());
+    auto ro_mapped_file = api::MappedStorageFile();
+    ro_mapped_file.file_ptr = mapped_file.file_ptr;
+    ro_mapped_file.file_size = mapped_file.file_size;
+    auto value = api::get_boolean_flag_value(ro_mapped_file, offset);
+    ASSERT_TRUE(value.ok());
+    ASSERT_TRUE(*value);
+  }
+}
+
+/// Negative test to lock down the error when querying flag value out of range
+TEST_F(AconfigStorageTest, test_invalid_boolean_flag_value_update) {
+  auto mapped_file_result = private_api::get_mapped_flag_value_file_impl(
+      storage_record_pb, "mockup");
+  ASSERT_TRUE(mapped_file_result.ok());
+  auto mapped_file = *mapped_file_result;
+  auto update_result = api::set_boolean_flag_value(mapped_file, 8, true);
+  ASSERT_FALSE(update_result.ok());
+  ASSERT_EQ(update_result.error().message(),
+            std::string("InvalidStorageFileOffset(Flag value offset goes beyond the end of the file.)"));
+}
diff --git a/tools/aconfig/aconfig_storage_write_api/tests/storage_write_api_test.rs b/tools/aconfig/aconfig_storage_write_api/tests/storage_write_api_test.rs
new file mode 100644
index 0000000..4bda54c
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_write_api/tests/storage_write_api_test.rs
@@ -0,0 +1,73 @@
+#[cfg(not(feature = "cargo"))]
+mod aconfig_storage_write_api_test {
+    use aconfig_storage_file::protos::ProtoStorageFiles;
+    use aconfig_storage_read_api::flag_value_query::find_boolean_flag_value;
+    use aconfig_storage_write_api::{mapped_file::get_mapped_file, set_boolean_flag_value};
+
+    use protobuf::Message;
+    use std::fs::{self, File};
+    use std::io::{Read, Write};
+    use tempfile::NamedTempFile;
+
+    /// Write storage location record pb to a temp file
+    fn write_storage_record_file(flag_val: &str) -> NamedTempFile {
+        let text_proto = format!(
+            r#"
+files {{
+    version: 0
+    container: "mockup"
+    package_map: "some_package_map"
+    flag_map: "some_flag_map"
+    flag_val: "{}"
+    timestamp: 12345
+}}
+"#,
+            flag_val
+        );
+        let storage_files: ProtoStorageFiles =
+            protobuf::text_format::parse_from_str(&text_proto).unwrap();
+        let mut binary_proto_bytes = Vec::new();
+        storage_files.write_to_vec(&mut binary_proto_bytes).unwrap();
+        let mut file = NamedTempFile::new().unwrap();
+        file.write_all(&binary_proto_bytes).unwrap();
+        file
+    }
+
+    /// Create temp file copy
+    fn copy_to_temp_rw_file(source_file: &str) -> NamedTempFile {
+        let file = NamedTempFile::new().unwrap();
+        fs::copy(source_file, file.path()).unwrap();
+        file
+    }
+
+    /// Get boolean flag value from offset
+    fn get_boolean_flag_value_at_offset(file: &str, offset: u32) -> bool {
+        let mut f = File::open(file).unwrap();
+        let mut bytes = Vec::new();
+        f.read_to_end(&mut bytes).unwrap();
+        find_boolean_flag_value(&bytes, offset).unwrap()
+    }
+
+    #[test]
+    /// Test to lock down flag value update api
+    fn test_boolean_flag_value_update() {
+        let flag_value_file = copy_to_temp_rw_file("./flag.val");
+        let flag_value_path = flag_value_file.path().display().to_string();
+        let record_pb_file = write_storage_record_file(&flag_value_path);
+        let record_pb_path = record_pb_file.path().display().to_string();
+
+        // SAFETY:
+        // The safety here is ensured as only this single threaded test process will
+        // write to this file
+        let mut file = unsafe { get_mapped_file(&record_pb_path, "mockup").unwrap() };
+        for i in 0..8 {
+            set_boolean_flag_value(&mut file, i, true).unwrap();
+            let value = get_boolean_flag_value_at_offset(&flag_value_path, i);
+            assert!(value);
+
+            set_boolean_flag_value(&mut file, i, false).unwrap();
+            let value = get_boolean_flag_value_at_offset(&flag_value_path, i);
+            assert!(!value);
+        }
+    }
+}
diff --git a/tools/aconfig/aflags/Android.bp b/tools/aconfig/aflags/Android.bp
index c65da97..4920a6f 100644
--- a/tools/aconfig/aflags/Android.bp
+++ b/tools/aconfig/aflags/Android.bp
@@ -10,8 +10,11 @@
     srcs: ["src/main.rs"],
     rustlibs: [
         "libaconfig_protos",
+        "libaconfig_storage_read_api",
+        "libaconfig_storage_file",
         "libanyhow",
         "libclap",
+        "libnix",
         "libprotobuf",
         "libregex",
     ],
diff --git a/tools/aconfig/aflags/Cargo.toml b/tools/aconfig/aflags/Cargo.toml
index 3350a6cd..cce7f9d 100644
--- a/tools/aconfig/aflags/Cargo.toml
+++ b/tools/aconfig/aflags/Cargo.toml
@@ -6,7 +6,10 @@
 [dependencies]
 anyhow = "1.0.69"
 paste = "1.0.11"
-clap = { version = "4", features = ["derive"] }
 protobuf = "3.2.0"
 regex = "1.10.3"
 aconfig_protos = { path = "../aconfig_protos" }
+nix = { version = "0.28.0", features = ["user"] }
+aconfig_storage_file = { version = "0.1.0", path = "../aconfig_storage_file" }
+aconfig_storage_read_api = { version = "0.1.0", path = "../aconfig_storage_read_api" }
+clap = {version = "4.5.2" }
diff --git a/tools/aconfig/aflags/src/aconfig_storage_source.rs b/tools/aconfig/aflags/src/aconfig_storage_source.rs
new file mode 100644
index 0000000..a3ca221
--- /dev/null
+++ b/tools/aconfig/aflags/src/aconfig_storage_source.rs
@@ -0,0 +1,56 @@
+use crate::{Flag, FlagPermission, FlagSource, FlagValue, ValuePickedFrom};
+use anyhow::{anyhow, Result};
+
+use std::fs::File;
+use std::io::Read;
+
+pub struct AconfigStorageSource {}
+
+use aconfig_storage_file::protos::ProtoStorageFiles;
+
+static STORAGE_INFO_FILE_PATH: &str = "/metadata/aconfig/persistent_storage_file_records.pb";
+
+impl FlagSource for AconfigStorageSource {
+    fn list_flags() -> Result<Vec<Flag>> {
+        let mut result = Vec::new();
+
+        let mut file = File::open(STORAGE_INFO_FILE_PATH)?;
+        let mut bytes = Vec::new();
+        file.read_to_end(&mut bytes)?;
+        let storage_file_info: ProtoStorageFiles = protobuf::Message::parse_from_bytes(&bytes)?;
+
+        for file_info in storage_file_info.files {
+            let package_map =
+                file_info.package_map.ok_or(anyhow!("storage file is missing package map"))?;
+            let flag_map = file_info.flag_map.ok_or(anyhow!("storage file is missing flag map"))?;
+            let flag_val = file_info.flag_val.ok_or(anyhow!("storage file is missing flag val"))?;
+            let container =
+                file_info.container.ok_or(anyhow!("storage file is missing container"))?;
+
+            for (package, name, _flag_type, val) in
+                aconfig_storage_file::list_flags(&package_map, &flag_map, &flag_val)?
+            {
+                result.push(Flag {
+                    name: name.to_string(),
+                    package: package.to_string(),
+                    value: FlagValue::try_from(val.to_string().as_str())?,
+                    container: container.to_string(),
+
+                    // TODO(b/324436145): delete namespace field once DeviceConfig isn't in CLI.
+                    namespace: "-".to_string(),
+
+                    // TODO(b/324436145): Populate with real values once API is available.
+                    staged_value: None,
+                    permission: FlagPermission::ReadOnly,
+                    value_picked_from: ValuePickedFrom::Default,
+                });
+            }
+        }
+
+        Ok(result)
+    }
+
+    fn override_flag(_namespace: &str, _qualified_name: &str, _value: &str) -> Result<()> {
+        todo!()
+    }
+}
diff --git a/tools/aconfig/aflags/src/device_config_source.rs b/tools/aconfig/aflags/src/device_config_source.rs
index 12a62cf..089f33d 100644
--- a/tools/aconfig/aflags/src/device_config_source.rs
+++ b/tools/aconfig/aflags/src/device_config_source.rs
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-use crate::{Flag, FlagPermission, FlagSource, ValuePickedFrom};
+use crate::{Flag, FlagPermission, FlagSource, FlagValue, ValuePickedFrom};
 use aconfig_protos::ProtoFlagPermission as ProtoPermission;
 use aconfig_protos::ProtoFlagState as ProtoState;
 use aconfig_protos::ProtoParsedFlag;
@@ -40,10 +40,9 @@
     };
 
     let value = match flag.state() {
-        ProtoState::ENABLED => "true",
-        ProtoState::DISABLED => "false",
-    }
-    .to_string();
+        ProtoState::ENABLED => FlagValue::Enabled,
+        ProtoState::DISABLED => FlagValue::Disabled,
+    };
 
     let permission = match flag.permission() {
         ProtoPermission::READ_ONLY => FlagPermission::ReadOnly,
@@ -56,6 +55,7 @@
         name,
         container,
         value,
+        staged_value: None,
         permission,
         value_picked_from: ValuePickedFrom::Default,
     }
@@ -64,7 +64,7 @@
 fn read_pb_files() -> Result<Vec<Flag>> {
     let mut flags: BTreeMap<String, Flag> = BTreeMap::new();
     for partition in ["system", "system_ext", "product", "vendor"] {
-        let path = format!("/{}/etc/aconfig_flags.pb", partition);
+        let path = format!("/{partition}/etc/aconfig_flags.pb");
         let Ok(bytes) = fs::read(&path) else {
             eprintln!("warning: failed to read {}", path);
             continue;
@@ -86,53 +86,94 @@
     Ok(flags.values().cloned().collect())
 }
 
-fn parse_device_config(raw: &str) -> Result<HashMap<String, String>> {
+fn parse_device_config(raw: &str) -> Result<HashMap<String, FlagValue>> {
     let mut flags = HashMap::new();
     let regex = Regex::new(r"(?m)^([[[:alnum:]]_]+/[[[:alnum:]]_\.]+)=(true|false)$")?;
     for capture in regex.captures_iter(raw) {
         let key =
             capture.get(1).ok_or(anyhow!("invalid device_config output"))?.as_str().to_string();
-        let value = capture.get(2).ok_or(anyhow!("invalid device_config output"))?.as_str();
-        flags.insert(key, value.to_string());
+        let value = FlagValue::try_from(
+            capture.get(2).ok_or(anyhow!("invalid device_config output"))?.as_str(),
+        )?;
+        flags.insert(key, value);
     }
     Ok(flags)
 }
 
-fn read_device_config_output(command: &str) -> Result<String> {
-    let output = Command::new("/system/bin/device_config").arg(command).output()?;
+fn read_device_config_output(command: &[&str]) -> Result<String> {
+    let output = Command::new("/system/bin/device_config").args(command).output()?;
     if !output.status.success() {
         let reason = match output.status.code() {
-            Some(code) => format!("exit code {}", code),
+            Some(code) => {
+                let output = str::from_utf8(&output.stdout)?;
+                if !output.is_empty() {
+                    format!("exit code {code}, output was {output}")
+                } else {
+                    format!("exit code {code}")
+                }
+            }
             None => "terminated by signal".to_string(),
         };
-        bail!("failed to execute device_config: {}", reason);
+        bail!("failed to access flag storage: {}", reason);
     }
     Ok(str::from_utf8(&output.stdout)?.to_string())
 }
 
-fn read_device_config_flags() -> Result<HashMap<String, String>> {
-    let list_output = read_device_config_output("list")?;
+fn read_device_config_flags() -> Result<HashMap<String, FlagValue>> {
+    let list_output = read_device_config_output(&["list"])?;
     parse_device_config(&list_output)
 }
 
-fn reconcile(pb_flags: &[Flag], dc_flags: HashMap<String, String>) -> Vec<Flag> {
+/// Parse the list of newline-separated staged flags.
+///
+/// The output is a newline-sepaarated list of entries which follow this format:
+///   `namespace*flagname=value`
+///
+/// The resulting map maps from `namespace/flagname` to `value`, if a staged flag exists for
+/// `namespace/flagname`.
+fn parse_staged_flags(raw: &str) -> Result<HashMap<String, FlagValue>> {
+    let mut flags = HashMap::new();
+    for line in raw.split('\n') {
+        match (line.find('*'), line.find('=')) {
+            (Some(star_index), Some(equal_index)) => {
+                let namespace = &line[..star_index];
+                let flag = &line[star_index + 1..equal_index];
+                if let Ok(value) = FlagValue::try_from(&line[equal_index + 1..]) {
+                    flags.insert(namespace.to_owned() + "/" + flag, value);
+                }
+            }
+            _ => continue,
+        };
+    }
+    Ok(flags)
+}
+
+fn read_staged_flags() -> Result<HashMap<String, FlagValue>> {
+    let staged_flags_output = read_device_config_output(&["list", "staged"])?;
+    parse_staged_flags(&staged_flags_output)
+}
+
+fn reconcile(
+    pb_flags: &[Flag],
+    dc_flags: HashMap<String, FlagValue>,
+    staged_flags: HashMap<String, FlagValue>,
+) -> Vec<Flag> {
     pb_flags
         .iter()
         .map(|f| {
-            dc_flags
-                .get(&format!("{}/{}.{}", f.namespace, f.package, f.name))
-                .map(|value| {
-                    if value.eq(&f.value) {
-                        Flag { value_picked_from: ValuePickedFrom::Default, ..f.clone() }
-                    } else {
-                        Flag {
-                            value_picked_from: ValuePickedFrom::Server,
-                            value: value.to_string(),
-                            ..f.clone()
-                        }
-                    }
-                })
-                .unwrap_or(f.clone())
+            let server_override = dc_flags.get(&format!("{}/{}", f.namespace, f.qualified_name()));
+            let (value_picked_from, selected_value) = match server_override {
+                Some(value) if *value != f.value => (ValuePickedFrom::Server, *value),
+                _ => (ValuePickedFrom::Default, f.value),
+            };
+            Flag { value_picked_from, value: selected_value, ..f.clone() }
+        })
+        .map(|f| {
+            let staged_value = staged_flags
+                .get(&format!("{}/{}", f.namespace, f.qualified_name()))
+                .map(|value| if *value != f.value { Some(*value) } else { None })
+                .unwrap_or(None);
+            Flag { staged_value, ..f }
         })
         .collect()
 }
@@ -141,10 +182,15 @@
     fn list_flags() -> Result<Vec<Flag>> {
         let pb_flags = read_pb_files()?;
         let dc_flags = read_device_config_flags()?;
+        let staged_flags = read_staged_flags()?;
 
-        let flags = reconcile(&pb_flags, dc_flags);
+        let flags = reconcile(&pb_flags, dc_flags, staged_flags);
         Ok(flags)
     }
+
+    fn override_flag(namespace: &str, qualified_name: &str, value: &str) -> Result<()> {
+        read_device_config_output(&["put", namespace, qualified_name, value]).map(|_| ())
+    }
 }
 
 #[cfg(test)]
@@ -161,9 +207,9 @@
 namespace_two/android.flag_two=nonsense
 "#;
         let expected = HashMap::from([
-            ("namespace_one/com.foo.bar.flag_one".to_string(), "true".to_string()),
-            ("namespace_one/com.foo.bar.flag_two".to_string(), "false".to_string()),
-            ("namespace_two/android.flag_one".to_string(), "true".to_string()),
+            ("namespace_one/com.foo.bar.flag_one".to_string(), FlagValue::Enabled),
+            ("namespace_one/com.foo.bar.flag_two".to_string(), FlagValue::Disabled),
+            ("namespace_two/android.flag_one".to_string(), FlagValue::Enabled),
         ]);
         let actual = parse_device_config(input).unwrap();
         assert_eq!(expected, actual);
diff --git a/tools/aconfig/aflags/src/main.rs b/tools/aconfig/aflags/src/main.rs
index 1e2a7a0..1c453c5 100644
--- a/tools/aconfig/aflags/src/main.rs
+++ b/tools/aconfig/aflags/src/main.rs
@@ -16,13 +16,16 @@
 
 //! `aflags` is a device binary to read and write aconfig flags.
 
-use anyhow::Result;
+use anyhow::{anyhow, ensure, Result};
 use clap::Parser;
 
 mod device_config_source;
 use device_config_source::DeviceConfigSource;
 
-#[derive(Clone)]
+mod aconfig_storage_source;
+use aconfig_storage_source::AconfigStorageSource;
+
+#[derive(Clone, PartialEq, Debug)]
 enum FlagPermission {
     ReadOnly,
     ReadWrite,
@@ -37,7 +40,7 @@
     }
 }
 
-#[derive(Clone)]
+#[derive(Clone, Debug)]
 enum ValuePickedFrom {
     Default,
     Server,
@@ -52,19 +55,66 @@
     }
 }
 
-#[derive(Clone)]
+#[derive(Clone, Copy, PartialEq, Eq, Debug)]
+enum FlagValue {
+    Enabled,
+    Disabled,
+}
+
+impl TryFrom<&str> for FlagValue {
+    type Error = anyhow::Error;
+
+    fn try_from(value: &str) -> std::result::Result<Self, Self::Error> {
+        match value {
+            "true" | "enabled" => Ok(Self::Enabled),
+            "false" | "disabled" => Ok(Self::Disabled),
+            _ => Err(anyhow!("cannot convert string '{}' to FlagValue", value)),
+        }
+    }
+}
+
+impl ToString for FlagValue {
+    fn to_string(&self) -> String {
+        match &self {
+            Self::Enabled => "enabled".into(),
+            Self::Disabled => "disabled".into(),
+        }
+    }
+}
+
+#[derive(Clone, Debug)]
 struct Flag {
     namespace: String,
     name: String,
     package: String,
     container: String,
-    value: String,
+    value: FlagValue,
+    staged_value: Option<FlagValue>,
     permission: FlagPermission,
     value_picked_from: ValuePickedFrom,
 }
 
+impl Flag {
+    fn qualified_name(&self) -> String {
+        format!("{}.{}", self.package, self.name)
+    }
+
+    fn display_staged_value(&self) -> String {
+        match self.staged_value {
+            Some(v) => format!("(->{})", v.to_string()),
+            None => "-".to_string(),
+        }
+    }
+}
+
 trait FlagSource {
     fn list_flags() -> Result<Vec<Flag>>;
+    fn override_flag(namespace: &str, qualified_name: &str, value: &str) -> Result<()>;
+}
+
+enum FlagSourceType {
+    DeviceConfig,
+    AconfigStorage,
 }
 
 const ABOUT_TEXT: &str = "Tool for reading and writing flags.
@@ -76,6 +126,10 @@
   * `package`: package set for this flag in its .aconfig definition.
   * `flag_name`: flag name, also set in definition.
   * `value`: the value read from the flag.
+  * `staged_value`: the value on next boot:
+    + `-`: same as current value
+    + `(->enabled) flipped to enabled on boot.
+    + `(->disabled) flipped to disabled on boot.
   * `provenance`: one of:
     + `default`: the flag value comes from its build-time default.
     + `server`: the flag value comes from a server override.
@@ -93,26 +147,42 @@
 #[derive(Parser, Debug)]
 enum Command {
     /// List all aconfig flags on this device.
-    List,
+    List {
+        /// Read from the new flag storage.
+        #[clap(long)]
+        use_new_storage: bool,
+    },
+
+    /// Enable an aconfig flag on this device, on the next boot.
+    Enable {
+        /// <package>.<flag_name>
+        qualified_name: String,
+    },
+
+    /// Disable an aconfig flag on this device, on the next boot.
+    Disable {
+        /// <package>.<flag_name>
+        qualified_name: String,
+    },
 }
 
 struct PaddingInfo {
-    longest_package_col: usize,
-    longest_name_col: usize,
+    longest_flag_col: usize,
     longest_val_col: usize,
+    longest_staged_val_col: usize,
     longest_value_picked_from_col: usize,
     longest_permission_col: usize,
 }
 
 fn format_flag_row(flag: &Flag, info: &PaddingInfo) -> String {
-    let pkg = &flag.package;
-    let p0 = info.longest_package_col + 1;
-
-    let name = &flag.name;
-    let p1 = info.longest_name_col + 1;
+    let full_name = flag.qualified_name();
+    let p0 = info.longest_flag_col + 1;
 
     let val = flag.value.to_string();
-    let p2 = info.longest_val_col + 1;
+    let p1 = info.longest_val_col + 1;
+
+    let staged_val = flag.display_staged_value();
+    let p2 = info.longest_staged_val_col + 1;
 
     let value_picked_from = flag.value_picked_from.to_string();
     let p3 = info.longest_value_picked_from_col + 1;
@@ -122,15 +192,40 @@
 
     let container = &flag.container;
 
-    format!("{pkg:p0$}{name:p1$}{val:p2$}{value_picked_from:p3$}{perm:p4$}{container}\n")
+    format!(
+        "{full_name:p0$}{val:p1$}{staged_val:p2$}{value_picked_from:p3$}{perm:p4$}{container}\n"
+    )
 }
 
-fn list() -> Result<String> {
-    let flags = DeviceConfigSource::list_flags()?;
+fn set_flag(qualified_name: &str, value: &str) -> Result<()> {
+    ensure!(nix::unistd::Uid::current().is_root(), "must be root to mutate flags");
+
+    let flags_binding = DeviceConfigSource::list_flags()?;
+    let flag = flags_binding.iter().find(|f| f.qualified_name() == qualified_name).ok_or(
+        anyhow!("no aconfig flag '{qualified_name}'. Does the flag have an .aconfig definition?"),
+    )?;
+
+    ensure!(flag.permission == FlagPermission::ReadWrite,
+            format!("could not write flag '{qualified_name}', it is read-only for the current release configuration."));
+
+    DeviceConfigSource::override_flag(&flag.namespace, qualified_name, value)?;
+
+    Ok(())
+}
+
+fn list(source_type: FlagSourceType) -> Result<String> {
+    let flags = match source_type {
+        FlagSourceType::DeviceConfig => DeviceConfigSource::list_flags()?,
+        FlagSourceType::AconfigStorage => AconfigStorageSource::list_flags()?,
+    };
     let padding_info = PaddingInfo {
-        longest_package_col: flags.iter().map(|f| f.package.len()).max().unwrap_or(0),
-        longest_name_col: flags.iter().map(|f| f.name.len()).max().unwrap_or(0),
+        longest_flag_col: flags.iter().map(|f| f.qualified_name().len()).max().unwrap_or(0),
         longest_val_col: flags.iter().map(|f| f.value.to_string().len()).max().unwrap_or(0),
+        longest_staged_val_col: flags
+            .iter()
+            .map(|f| f.display_staged_value().len())
+            .max()
+            .unwrap_or(0),
         longest_value_picked_from_col: flags
             .iter()
             .map(|f| f.value_picked_from.to_string().len())
@@ -154,10 +249,14 @@
 fn main() {
     let cli = Cli::parse();
     let output = match cli.command {
-        Command::List => list(),
+        Command::List { use_new_storage: true } => list(FlagSourceType::AconfigStorage).map(Some),
+        Command::List { use_new_storage: false } => list(FlagSourceType::DeviceConfig).map(Some),
+        Command::Enable { qualified_name } => set_flag(&qualified_name, "true").map(|_| None),
+        Command::Disable { qualified_name } => set_flag(&qualified_name, "false").map(|_| None),
     };
     match output {
-        Ok(text) => println!("{text}"),
-        Err(msg) => println!("Error: {}", msg),
+        Ok(Some(text)) => println!("{text}"),
+        Ok(None) => (),
+        Err(message) => println!("Error: {message}"),
     }
 }
diff --git a/tools/aconfig/printflags/src/main.rs b/tools/aconfig/printflags/src/main.rs
index a0c9ee8..7838b51 100644
--- a/tools/aconfig/printflags/src/main.rs
+++ b/tools/aconfig/printflags/src/main.rs
@@ -67,9 +67,23 @@
     let device_config_flags = parse_device_config(dc_stdout);
 
     // read aconfig_flags.pb files
+    let apex_pattern = Regex::new(r"^/apex/[^@]+\.[^@]+$").unwrap();
+    let mut mount_points = vec![
+        "system".to_string(),
+        "system_ext".to_string(),
+        "product".to_string(),
+        "vendor".to_string(),
+    ];
+    for apex in fs::read_dir("/apex")? {
+        let path_name = apex?.path().display().to_string();
+        if let Some(canonical_path) = apex_pattern.captures(&path_name) {
+            mount_points.push(canonical_path.get(0).unwrap().as_str().to_owned());
+        }
+    }
+
     let mut flags: BTreeMap<String, Vec<String>> = BTreeMap::new();
-    for partition in ["system", "system_ext", "product", "vendor"] {
-        let path = format!("/{}/etc/aconfig_flags.pb", partition);
+    for mount_point in mount_points {
+        let path = format!("/{}/etc/aconfig_flags.pb", mount_point);
         let Ok(bytes) = fs::read(&path) else {
             eprintln!("warning: failed to read {}", path);
             continue;
@@ -80,7 +94,7 @@
             })?;
         for flag in parsed_flags.parsed_flag {
             let key = format!("{}/{}.{}", flag.namespace(), flag.package(), flag.name());
-            let value = format!("{:?} + {:?} ({})", flag.permission(), flag.state(), partition);
+            let value = format!("{:?} + {:?} ({})", flag.permission(), flag.state(), mount_point);
             flags.entry(key).or_default().push(value);
         }
     }
diff --git a/tools/buildinfo.sh b/tools/buildinfo.sh
deleted file mode 100755
index 0ed9453..0000000
--- a/tools/buildinfo.sh
+++ /dev/null
@@ -1,59 +0,0 @@
-#!/bin/bash
-
-echo "# begin build properties"
-echo "# autogenerated by buildinfo.sh"
-
-# The ro.build.id will be set dynamically by init, by appending the unique vbmeta digest.
-if [ "$BOARD_USE_VBMETA_DIGTEST_IN_FINGERPRINT" = "true" ] ; then
-  echo "ro.build.legacy.id=$BUILD_ID"
-else
-  echo "ro.build.id?=$BUILD_ID"
-fi
-echo "ro.build.display.id?=$BUILD_DISPLAY_ID"
-echo "ro.build.version.incremental=$BUILD_NUMBER"
-echo "ro.build.version.sdk=$PLATFORM_SDK_VERSION"
-echo "ro.build.version.preview_sdk=$PLATFORM_PREVIEW_SDK_VERSION"
-echo "ro.build.version.preview_sdk_fingerprint=$PLATFORM_PREVIEW_SDK_FINGERPRINT"
-echo "ro.build.version.codename=$PLATFORM_VERSION_CODENAME"
-echo "ro.build.version.all_codenames=$PLATFORM_VERSION_ALL_CODENAMES"
-echo "ro.build.version.known_codenames=$PLATFORM_VERSION_KNOWN_CODENAMES"
-echo "ro.build.version.release=$PLATFORM_VERSION_LAST_STABLE"
-echo "ro.build.version.release_or_codename=$PLATFORM_VERSION"
-echo "ro.build.version.release_or_preview_display=$PLATFORM_DISPLAY_VERSION"
-echo "ro.build.version.security_patch=$PLATFORM_SECURITY_PATCH"
-echo "ro.build.version.base_os=$PLATFORM_BASE_OS"
-echo "ro.build.version.min_supported_target_sdk=$PLATFORM_MIN_SUPPORTED_TARGET_SDK_VERSION"
-echo "ro.build.date=`$DATE`"
-echo "ro.build.date.utc=`$DATE +%s`"
-echo "ro.build.type=$TARGET_BUILD_TYPE"
-echo "ro.build.user=$BUILD_USERNAME"
-echo "ro.build.host=$BUILD_HOSTNAME"
-# TODO: Remove any tag-related optional property declarations once the goals
-# from go/arc-android-sigprop-changes have been achieved.
-echo "ro.build.tags?=$BUILD_VERSION_TAGS"
-echo "ro.build.flavor=$TARGET_BUILD_FLAVOR"
-
-# These values are deprecated, use "ro.product.cpu.abilist"
-# instead (see below).
-echo "# ro.product.cpu.abi and ro.product.cpu.abi2 are obsolete,"
-echo "# use ro.product.cpu.abilist instead."
-echo "ro.product.cpu.abi=$TARGET_CPU_ABI"
-if [ -n "$TARGET_CPU_ABI2" ] ; then
-  echo "ro.product.cpu.abi2=$TARGET_CPU_ABI2"
-fi
-
-if [ -n "$PRODUCT_DEFAULT_LOCALE" ] ; then
-  echo "ro.product.locale=$PRODUCT_DEFAULT_LOCALE"
-fi
-echo "ro.wifi.channels=$PRODUCT_DEFAULT_WIFI_CHANNELS"
-
-echo "# ro.build.product is obsolete; use ro.product.device"
-echo "ro.build.product=$TARGET_DEVICE"
-
-echo "# Do not try to parse description or thumbprint"
-echo "ro.build.description?=$PRIVATE_BUILD_DESC"
-if [ -n "$BUILD_THUMBPRINT" ] ; then
-  echo "ro.build.thumbprint=$BUILD_THUMBPRINT"
-fi
-
-echo "# end build properties"
diff --git a/tools/finalization/OWNERS b/tools/finalization/OWNERS
index 518b60d..b00b774 100644
--- a/tools/finalization/OWNERS
+++ b/tools/finalization/OWNERS
@@ -1,5 +1,7 @@
 include platform/build/soong:/OWNERS
-smoreland@google.com
-alexbuy@google.com
+amhk@google.com
+gurpreetgs@google.com
+michaelwr@google.com
 patb@google.com
+smoreland@google.com
 zyy@google.com
diff --git a/tools/finalization/finalize-aidl-vndk-sdk-resources.sh b/tools/finalization/finalize-aidl-vndk-sdk-resources.sh
index 37c0011..671b036 100755
--- a/tools/finalization/finalize-aidl-vndk-sdk-resources.sh
+++ b/tools/finalization/finalize-aidl-vndk-sdk-resources.sh
@@ -69,6 +69,33 @@
     rm "$tmpfile"
 }
 
+function bumpSdkExtensionsVersion() {
+    local SDKEXT="packages/modules/SdkExtensions/"
+
+    # This used to call bump_sdk.sh utility.
+    # However due to TS, we have to build the gen_sdk with a correct set of settings.
+
+    # "$top/packages/modules/SdkExtensions/gen_sdk/bump_sdk.sh" ${FINAL_MAINLINE_EXTENSION}
+    # Leave the last commit as a set of modified files.
+    # The code to create a finalization topic will pick it up later.
+    # git -C ${SDKEXT} reset HEAD~1
+
+    local sdk="${FINAL_MAINLINE_EXTENSION}"
+    local modules_arg=
+
+    TARGET_PRODUCT=aosp_arm64 \
+        TARGET_RELEASE=fina_1 \
+        TARGET_BUILD_VARIANT=userdebug \
+        DIST_DIR=out/dist \
+        $top/build/soong/soong_ui.bash --make-mode --soong-only gen_sdk
+
+    ANDROID_BUILD_TOP="$top" out/soong/host/linux-x86/bin/gen_sdk \
+        --database ${SDKEXT}/gen_sdk/extensions_db.textpb \
+        --action new_sdk \
+        --sdk "$sdk" \
+        $modules_arg
+}
+
 function finalize_aidl_vndk_sdk_resources() {
     local top="$(dirname "$0")"/../../../..
     source $top/build/make/tools/finalization/environment.sh
@@ -76,9 +103,6 @@
     local SDK_CODENAME="public static final int $FINAL_PLATFORM_CODENAME_JAVA = CUR_DEVELOPMENT;"
     local SDK_VERSION="public static final int $FINAL_PLATFORM_CODENAME_JAVA = $FINAL_PLATFORM_SDK_VERSION;"
 
-    # default target to modify tree and build SDK
-    local m="$top/build/soong/soong_ui.bash --make-mode TARGET_PRODUCT=aosp_arm64 TARGET_BUILD_VARIANT=userdebug DIST_DIR=out/dist"
-
     # The full process can be found at (INTERNAL) go/android-sdk-finalization.
 
     # apply droidstubs hack to prevent tools from incrementing an API version
@@ -87,20 +111,12 @@
     # bionic/NDK
     finalize_bionic_ndk
 
-    # VNDK definitions for new SDK version
-    cp "$top/development/vndk/tools/definition-tool/datasets/vndk-lib-extra-list-current.txt" \
-       "$top/development/vndk/tools/definition-tool/datasets/vndk-lib-extra-list-$FINAL_PLATFORM_SDK_VERSION.txt"
+    # pre-finalization build target (trunk)
+    local aidl_m="$top/build/soong/soong_ui.bash --make-mode TARGET_PRODUCT=aosp_arm64 TARGET_RELEASE=trunk TARGET_BUILD_VARIANT=userdebug DIST_DIR=out/dist"
+    AIDL_TRANSITIVE_FREEZE=true $aidl_m aidl-freeze-api
 
-    AIDL_TRANSITIVE_FREEZE=true $m aidl-freeze-api create_reference_dumps
-
-    # Generate ABI dumps
-    ANDROID_BUILD_TOP="$top" out/host/linux-x86/bin/create_reference_dumps
-
-    echo "NOTE: THIS INTENTIONALLY MAY FAIL AND REPAIR ITSELF (until 'DONE')"
-    # Update new versions of files. See update-vndk-list.sh (which requires envsetup.sh)
-    $m check-vndk-list || \
-        { cp $top/out/soong/vndk/vndk.libraries.txt $top/build/make/target/product/gsi/current.txt; }
-    echo "DONE: THIS INTENTIONALLY MAY FAIL AND REPAIR ITSELF"
+    # TODO(b/309880485)
+    # Add back create_reference_dumps and $top/build/make/target/product/gsi/current.txt
 
     # Finalize SDK
 
@@ -114,9 +130,6 @@
     sed -i -e 's/Pkg\.Revision.*/Pkg\.Revision=${PLATFORM_SDK_VERSION}.0.0/g' $build_tools_source
 
     # build/make
-    local version_defaults="$top/build/make/core/version_defaults.mk"
-    sed -i -e "s/PLATFORM_SDK_VERSION := .*/PLATFORM_SDK_VERSION := ${FINAL_PLATFORM_SDK_VERSION}/g" $version_defaults
-    sed -i -e "s/PLATFORM_VERSION_LAST_STABLE := .*/PLATFORM_VERSION_LAST_STABLE := ${FINAL_PLATFORM_VERSION}/g" $version_defaults
     sed -i -e "s/sepolicy_major_vers := .*/sepolicy_major_vers := ${FINAL_PLATFORM_SDK_VERSION}/g" "$top/build/make/core/config.mk"
     cp "$top/build/make/target/product/gsi/current.txt" "$top/build/make/target/product/gsi/$FINAL_PLATFORM_SDK_VERSION.txt"
 
@@ -156,18 +169,14 @@
     sed -i -e "/=.*$((${FINAL_PLATFORM_SDK_VERSION}-1)),/a \\  SDK_${FINAL_PLATFORM_CODENAME_JAVA} = ${FINAL_PLATFORM_SDK_VERSION}," "$top/frameworks/base/tools/aapt2/SdkConstants.h"
 
     # Bump Mainline SDK extension version.
-    local SDKEXT="packages/modules/SdkExtensions/"
-    "$top/packages/modules/SdkExtensions/gen_sdk/bump_sdk.sh" ${FINAL_MAINLINE_EXTENSION}
-    # Leave the last commit as a set of modified files.
-    # The code to create a finalization topic will pick it up later.
-    git -C ${SDKEXT} reset HEAD~1
+    bumpSdkExtensionsVersion
 
-    local version_defaults="$top/build/make/core/version_defaults.mk"
-    sed -i -e "s/PLATFORM_SDK_EXTENSION_VERSION := .*/PLATFORM_SDK_EXTENSION_VERSION := ${FINAL_MAINLINE_EXTENSION}/g" $version_defaults
+    # target to build SDK
+    local sdk_m="$top/build/soong/soong_ui.bash --make-mode TARGET_PRODUCT=aosp_arm64 TARGET_RELEASE=fina_1 TARGET_BUILD_VARIANT=userdebug DIST_DIR=out/dist"
 
     # Force update current.txt
-    $m clobber
-    $m update-api
+    $sdk_m clobber
+    $sdk_m update-api
 }
 
 finalize_aidl_vndk_sdk_resources
diff --git a/tools/finalization/finalize-sdk-rel.sh b/tools/finalization/finalize-sdk-rel.sh
index d4ed380..245305b 100755
--- a/tools/finalization/finalize-sdk-rel.sh
+++ b/tools/finalization/finalize-sdk-rel.sh
@@ -33,10 +33,6 @@
     # in REL mode, resources would correctly set the resources_sdk_int, no fix required
     revert_resources_sdk_int_fix
 
-    # build/make/core/version_defaults.mk
-    # Mark all versions "released".
-    sed -i 's/\(PLATFORM_VERSION_CODENAME\.[^[:space:]]*\) := [^[:space:]]*/\1 := REL/g' "$top/build/make/core/version_defaults.mk"
-
     # cts
     echo "$FINAL_PLATFORM_VERSION" > "$top/cts/tests/tests/os/assets/platform_versions.txt"
     if [ "$FINAL_PLATFORM_CODENAME" != "$CURRENT_PLATFORM_CODENAME" ]; then
@@ -47,18 +43,19 @@
     # system/sepolicy
     system/sepolicy/tools/finalize-sdk-rel.sh "$top" "$FINAL_PLATFORM_SDK_VERSION"
 
-    # prebuilts/abi-dumps/ndk
-    mkdir -p "$top/prebuilts/abi-dumps/ndk/$FINAL_PLATFORM_SDK_VERSION"
-    cp -r "$top/prebuilts/abi-dumps/ndk/current/64/" "$top/prebuilts/abi-dumps/ndk/$FINAL_PLATFORM_SDK_VERSION/"
-
     # prebuilts/abi-dumps/platform
     mkdir -p "$top/prebuilts/abi-dumps/platform/$FINAL_PLATFORM_SDK_VERSION"
     cp -r "$top/prebuilts/abi-dumps/platform/current/64/" "$top/prebuilts/abi-dumps/platform/$FINAL_PLATFORM_SDK_VERSION/"
 
-    if [ "$FINAL_STATE" != "sdk" ] || [ "$FINAL_PLATFORM_CODENAME" == "$CURRENT_PLATFORM_CODENAME" ] ; then
+    # TODO(b/309880485)
+    # uncomment and update
+    # prebuilts/abi-dumps/ndk
+    #mkdir -p "$top/prebuilts/abi-dumps/ndk/$FINAL_PLATFORM_SDK_VERSION"
+    #cp -r "$top/prebuilts/abi-dumps/ndk/current/64/" "$top/prebuilts/abi-dumps/ndk/$FINAL_PLATFORM_SDK_VERSION/"
+    #if [ "$FINAL_STATE" != "sdk" ] || [ "$FINAL_PLATFORM_CODENAME" == "$CURRENT_PLATFORM_CODENAME" ] ; then
         # prebuilts/abi-dumps/vndk
-        mv "$top/prebuilts/abi-dumps/vndk/$CURRENT_PLATFORM_CODENAME" "$top/prebuilts/abi-dumps/vndk/$FINAL_PLATFORM_SDK_VERSION"
-    fi;
+        #mv "$top/prebuilts/abi-dumps/vndk/$CURRENT_PLATFORM_CODENAME" "$top/prebuilts/abi-dumps/vndk/$FINAL_PLATFORM_SDK_VERSION"
+    #fi;
 }
 
 finalize_sdk_rel
diff --git a/tools/finalization/localonly-steps.sh b/tools/finalization/localonly-steps.sh
index 7318ca1..bebd563 100755
--- a/tools/finalization/localonly-steps.sh
+++ b/tools/finalization/localonly-steps.sh
@@ -7,17 +7,17 @@
     source $top/build/make/tools/finalization/environment.sh
 
     # default target to modify tree and build SDK
-    local m="$top/build/soong/soong_ui.bash --make-mode TARGET_PRODUCT=aosp_arm64 TARGET_BUILD_VARIANT=userdebug DIST_DIR=out/dist"
+    local m="$top/build/soong/soong_ui.bash --make-mode TARGET_PRODUCT=aosp_arm64 TARGET_RELEASE=fina_1 TARGET_BUILD_VARIANT=userdebug DIST_DIR=out/dist"
 
     # adb keys
     $m adb
     LOGNAME=android-eng HOSTNAME=google.com "$top/out/host/linux-x86/bin/adb" keygen "$top/vendor/google/security/adb/${FINAL_PLATFORM_VERSION}.adb_key"
 
     # Build Platform SDKs.
-    $top/build/soong/soong_ui.bash --make-mode TARGET_PRODUCT=sdk TARGET_BUILD_VARIANT=userdebug sdk dist sdk_repo DIST_DIR=out/dist
+    $top/build/soong/soong_ui.bash --make-mode TARGET_PRODUCT=sdk TARGET_RELEASE=fina_1 TARGET_BUILD_VARIANT=userdebug sdk dist sdk_repo DIST_DIR=out/dist
 
     # Build Modules SDKs.
-    TARGET_BUILD_VARIANT=userdebug UNBUNDLED_BUILD_SDKS_FROM_SOURCE=true DIST_DIR=out/dist "$top/vendor/google/build/mainline_modules_sdks.sh" --build-release=latest
+    TARGET_RELEASE=fina_1 TARGET_BUILD_VARIANT=userdebug UNBUNDLED_BUILD_SDKS_FROM_SOURCE=true DIST_DIR=out/dist "$top/vendor/google/build/mainline_modules_sdks.sh" --build-release=latest
 
     # Update prebuilts.
     "$top/prebuilts/build-tools/path/linux-x86/python3" -W ignore::DeprecationWarning "$top/prebuilts/sdk/update_prebuilts.py" --local_mode -f ${FINAL_PLATFORM_SDK_VERSION} -e ${FINAL_MAINLINE_EXTENSION} --bug 1 1
diff --git a/tools/fs_config/Android.bp b/tools/fs_config/Android.bp
index 55fdca4..bd9543a 100644
--- a/tools/fs_config/Android.bp
+++ b/tools/fs_config/Android.bp
@@ -35,7 +35,6 @@
     srcs: ["fs_config.c"],
     shared_libs: [
         "libcutils",
-        "libselinux",
     ],
     cflags: ["-Werror"],
 }
diff --git a/tools/fs_config/fs_config.c b/tools/fs_config/fs_config.c
index 2a75add..80bd3c1 100644
--- a/tools/fs_config/fs_config.c
+++ b/tools/fs_config/fs_config.c
@@ -22,9 +22,6 @@
 #include <string.h>
 #include <inttypes.h>
 
-#include <selinux/selinux.h>
-#include <selinux/label.h>
-
 #include "private/android_filesystem_config.h"
 #include "private/fs_config.h"
 
@@ -35,8 +32,8 @@
 //
 // After the first 4 columns, optional key=value pairs are emitted
 // for each file.  Currently, the following keys are supported:
-// * -S: selabel=[selinux_label]
-// * -C: capabilities=[hex capabilities value]
+//
+//   -C: capabilities=[hex capabilities value]
 //
 // Example input:
 //
@@ -48,45 +45,24 @@
 //      system/etc/dbus.conf 1002 1002 440
 //      data/app 1000 1000 771
 //
-//   or if, for example, -S is used:
-//
-//      system/etc/dbus.conf 1002 1002 440 selabel=u:object_r:system_file:s0
-//      data/app 1000 1000 771 selabel=u:object_r:apk_data_file:s0
-//
 // Note that the output will omit the trailing slash from
 // directories.
 
-static struct selabel_handle* get_sehnd(const char* context_file) {
-  struct selinux_opt seopts[] = { { SELABEL_OPT_PATH, context_file } };
-  struct selabel_handle* sehnd = selabel_open(SELABEL_CTX_FILE, seopts, 1);
-
-  if (!sehnd) {
-    perror("error running selabel_open");
-    exit(EXIT_FAILURE);
-  }
-  return sehnd;
-}
-
 static void usage() {
-  fprintf(stderr, "Usage: fs_config [-D product_out_path] [-S context_file] [-R root] [-C]\n");
+  fprintf(stderr, "Usage: fs_config [-D product_out_path] [-R root] [-C]\n");
 }
 
 int main(int argc, char** argv) {
   char buffer[1024];
-  const char* context_file = NULL;
   const char* product_out_path = NULL;
   char* root_path = NULL;
-  struct selabel_handle* sehnd = NULL;
   int print_capabilities = 0;
   int opt;
-  while((opt = getopt(argc, argv, "CS:R:D:")) != -1) {
+  while((opt = getopt(argc, argv, "CR:D:")) != -1) {
     switch(opt) {
     case 'C':
       print_capabilities = 1;
       break;
-    case 'S':
-      context_file = optarg;
-      break;
     case 'R':
       root_path = optarg;
       break;
@@ -99,10 +75,6 @@
     }
   }
 
-  if (context_file != NULL) {
-    sehnd = get_sehnd(context_file);
-  }
-
   if (root_path != NULL) {
     size_t root_len = strlen(root_path);
     /* Trim any trailing slashes from the root path. */
@@ -141,33 +113,6 @@
     }
     printf("%s %d %d %o", buffer, uid, gid, mode);
 
-    if (sehnd != NULL) {
-      size_t buffer_strlen = strnlen(buffer, sizeof(buffer));
-      if (buffer_strlen >= sizeof(buffer)) {
-        fprintf(stderr, "non null terminated buffer, aborting\n");
-        exit(EXIT_FAILURE);
-      }
-      size_t full_name_size = buffer_strlen + 2;
-      char* full_name = (char*) malloc(full_name_size);
-      if (full_name == NULL) {
-        perror("malloc");
-        exit(EXIT_FAILURE);
-      }
-
-      full_name[0] = '/';
-      strncpy(full_name + 1, buffer, full_name_size - 1);
-      full_name[full_name_size - 1] = '\0';
-
-      char* secontext;
-      if (selabel_lookup(sehnd, &secontext, full_name, ( mode | (is_dir ? S_IFDIR : S_IFREG)))) {
-        secontext = strdup("u:object_r:unlabeled:s0");
-      }
-
-      printf(" selabel=%s", secontext);
-      free(full_name);
-      freecon(secontext);
-    }
-
     if (print_capabilities) {
       printf(" capabilities=0x%" PRIx64, capabilities);
     }
diff --git a/tools/fs_get_stats/Android.bp b/tools/fs_get_stats/Android.bp
deleted file mode 100644
index 0697999..0000000
--- a/tools/fs_get_stats/Android.bp
+++ /dev/null
@@ -1,14 +0,0 @@
-package {
-    // See: http://go/android-license-faq
-    default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-cc_binary_host {
-    name: "fs_get_stats",
-    srcs: ["fs_get_stats.c"],
-    cflags: ["-Wall", "-Werror"],
-    shared_libs: [
-        "libcutils",
-        "liblog",
-    ],
-}
diff --git a/tools/fs_get_stats/fs_get_stats.c b/tools/fs_get_stats/fs_get_stats.c
deleted file mode 100644
index 64ef0e2..0000000
--- a/tools/fs_get_stats/fs_get_stats.c
+++ /dev/null
@@ -1,67 +0,0 @@
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <unistd.h>
-
-#include <private/android_filesystem_config.h>
-#include <private/fs_config.h>
-
-#define DO_DEBUG 1
-
-#define ERROR(fmt,args...) \
-	do { \
-		fprintf(stderr, "%s:%d: ERROR: " fmt,  \
-		        __FILE__, __LINE__, ##args);    \
-	} while (0)
-
-#if DO_DEBUG
-#define DEBUG(fmt,args...) \
-	do { fprintf(stderr, "DEBUG: " fmt, ##args); } while(0)
-#else
-#define DEBUG(x...)               do {} while(0)
-#endif
-
-void
-print_help(void)
-{
-	fprintf(stderr, "fs_get_stats: retrieve the target file stats "
-	        "for the specified file\n");
-	fprintf(stderr, "usage: fs_get_stats cur_perms is_dir filename targetout\n");
-	fprintf(stderr, "\tcur_perms - The current permissions of "
-	        "the file\n");
-	fprintf(stderr, "\tis_dir    - Is filename is a dir, 1. Otherwise, 0.\n");
-	fprintf(stderr, "\tfilename  - The filename to lookup\n");
-	fprintf(stderr, "\ttargetout - The target out path to query device specific FS configs\n");
-	fprintf(stderr, "\n");
-}
-
-int
-main(int argc, const char *argv[])
-{
-	char *endptr;
-	char is_dir = 0;
-	unsigned perms = 0;
-	unsigned uid = (unsigned)-1;
-	unsigned gid = (unsigned)-1;
-
-	if (argc < 5) {
-		ERROR("Invalid arguments\n");
-		print_help();
-		exit(-1);
-	}
-
-	perms = (unsigned)strtoul(argv[1], &endptr, 0);
-	if (!endptr || (endptr == argv[1]) || (*endptr != '\0')) {
-		ERROR("current permissions must be a number. Got '%s'.\n", argv[1]);
-		exit(-1);
-	}
-
-	if (!strcmp(argv[2], "1"))
-		is_dir = 1;
-
-	uint64_t capabilities;
-	fs_config(argv[3], is_dir, argv[4], &uid, &gid, &perms, &capabilities);
-	fprintf(stdout, "%d %d 0%o\n", uid, gid, perms);
-
-	return 0;
-}
diff --git a/tools/ide_query/cc_analyzer/Android.bp b/tools/ide_query/cc_analyzer/Android.bp
new file mode 100644
index 0000000..3cbbb05
--- /dev/null
+++ b/tools/ide_query/cc_analyzer/Android.bp
@@ -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 {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+cc_defaults {
+    name: "ide_query_cc_analyzer_defaults",
+    compile_multilib: "64",
+    defaults: [
+        "llvm-build-host-tools-defaults",
+    ],
+    cflags: [
+        // LLVM Sources do have unused parameters :(
+        "-Wno-unused-parameter",
+    ],
+    target: {
+        host: {
+            cppflags: [
+                "-fno-rtti",
+            ],
+        },
+    },
+}
+
+cc_library_host_static {
+    name: "builtin_headers",
+    srcs: ["builtin_headers.cc"],
+    generated_headers: ["clang_builtin_headers_resources"],
+    defaults: ["ide_query_cc_analyzer_defaults"],
+}
+
+cc_library_host_static {
+    name: "include_scanner",
+    srcs: ["include_scanner.cc"],
+    shared_libs: ["libclang-cpp_host"],
+    static_libs: ["builtin_headers"],
+    defaults: ["ide_query_cc_analyzer_defaults"],
+}
+
+cc_library_host_static {
+    name: "analyzer",
+    srcs: ["analyzer.cc"],
+    shared_libs: ["libclang-cpp_host"],
+    static_libs: [
+        "include_scanner",
+        "ide_query_proto",
+    ],
+    defaults: ["ide_query_cc_analyzer_defaults"],
+}
+
+cc_binary_host {
+    name: "ide_query_cc_analyzer",
+    defaults: ["ide_query_cc_analyzer_defaults"],
+    srcs: ["main.cc"],
+    shared_libs: [
+        "libclang-cpp_host",
+        "libprotobuf-cpp-full",
+    ],
+    static_libs: [
+        "ide_query_proto",
+        "builtin_headers",
+        "include_scanner",
+        "analyzer",
+    ],
+}
diff --git a/tools/ide_query/cc_analyzer/analyzer.cc b/tools/ide_query/cc_analyzer/analyzer.cc
new file mode 100644
index 0000000..bb7ca0b
--- /dev/null
+++ b/tools/ide_query/cc_analyzer/analyzer.cc
@@ -0,0 +1,147 @@
+/*
+ * 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.
+ */
+#include "analyzer.h"
+
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "clang/Tooling/CompilationDatabase.h"
+#include "clang/Tooling/JSONCompilationDatabase.h"
+#include "ide_query.pb.h"
+#include "include_scanner.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/Twine.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/VirtualFileSystem.h"
+
+namespace tools::ide_query::cc_analyzer {
+namespace {
+llvm::Expected<std::unique_ptr<clang::tooling::CompilationDatabase>> LoadCompDB(
+    llvm::StringRef comp_db_path) {
+  std::string err;
+  std::unique_ptr<clang::tooling::CompilationDatabase> db =
+      clang::tooling::JSONCompilationDatabase::loadFromFile(
+          comp_db_path, err, clang::tooling::JSONCommandLineSyntax::AutoDetect);
+  if (!db) {
+    return llvm::createStringError(llvm::inconvertibleErrorCode(),
+                                   "Failed to load CDB: " + err);
+  }
+  // Provide some heuristic support for missing files.
+  return inferMissingCompileCommands(std::move(db));
+}
+}  // namespace
+
+::ide_query::DepsResponse GetDeps(::ide_query::RepoState state) {
+  ::ide_query::DepsResponse results;
+  auto db = LoadCompDB(state.comp_db_path());
+  if (!db) {
+    results.mutable_status()->set_code(::ide_query::Status::FAILURE);
+    results.mutable_status()->set_message(llvm::toString(db.takeError()));
+    return results;
+  }
+  for (llvm::StringRef active_file : state.active_file_path()) {
+    auto& result = *results.add_deps();
+
+    llvm::SmallString<256> abs_file(state.repo_dir());
+    llvm::sys::path::append(abs_file, active_file);
+    auto cmds = db->get()->getCompileCommands(active_file);
+    if (cmds.empty()) {
+      result.mutable_status()->set_code(::ide_query::Status::FAILURE);
+      result.mutable_status()->set_message(
+          llvm::Twine("Can't find compile flags for file: ", abs_file).str());
+      continue;
+    }
+    result.set_source_file(active_file.str());
+    llvm::StringRef file = cmds[0].Filename;
+    if (llvm::StringRef actual_file(cmds[0].Heuristic);
+        actual_file.consume_front("inferred from ")) {
+      file = actual_file;
+    }
+    // TODO: Query ninja graph to figure out a minimal set of targets to build.
+    result.add_build_target(file.str() + "^");
+  }
+  return results;
+}
+
+::ide_query::IdeAnalysis GetBuildInputs(::ide_query::RepoState state) {
+  auto db = LoadCompDB(state.comp_db_path());
+  ::ide_query::IdeAnalysis results;
+  if (!db) {
+    results.mutable_status()->set_code(::ide_query::Status::FAILURE);
+    results.mutable_status()->set_message(llvm::toString(db.takeError()));
+    return results;
+  }
+  std::string repo_dir = state.repo_dir();
+  if (!repo_dir.empty() && repo_dir.back() == '/') repo_dir.pop_back();
+
+  llvm::SmallString<256> genfile_root_abs(repo_dir);
+  llvm::sys::path::append(genfile_root_abs, state.out_dir());
+  if (genfile_root_abs.empty() || genfile_root_abs.back() != '/') {
+    genfile_root_abs.push_back('/');
+  }
+
+  results.set_build_artifact_root(state.out_dir());
+  for (llvm::StringRef active_file : state.active_file_path()) {
+    auto& result = *results.add_sources();
+    result.set_path(active_file.str());
+
+    llvm::SmallString<256> abs_file(repo_dir);
+    llvm::sys::path::append(abs_file, active_file);
+    auto cmds = db->get()->getCompileCommands(abs_file);
+    if (cmds.empty()) {
+      result.mutable_status()->set_code(::ide_query::Status::FAILURE);
+      result.mutable_status()->set_message(
+          llvm::Twine("Can't find compile flags for file: ", abs_file).str());
+      continue;
+    }
+    const auto& cmd = cmds.front();
+    llvm::StringRef working_dir = cmd.Directory;
+    if (!working_dir.consume_front(repo_dir)) {
+      result.mutable_status()->set_code(::ide_query::Status::FAILURE);
+      result.mutable_status()->set_message("Command working dir " +
+                                           working_dir.str() +
+                                           " outside repository " + repo_dir);
+      continue;
+    }
+    working_dir = working_dir.ltrim('/');
+    result.set_working_dir(working_dir.str());
+    for (auto& arg : cmd.CommandLine) result.add_compiler_arguments(arg);
+
+    auto includes =
+        ScanIncludes(cmds.front(), llvm::vfs::createPhysicalFileSystem());
+    if (!includes) {
+      result.mutable_status()->set_code(::ide_query::Status::FAILURE);
+      result.mutable_status()->set_message(
+          llvm::toString(includes.takeError()));
+      continue;
+    }
+
+    for (auto& [req_input, contents] : *includes) {
+      llvm::StringRef req_input_ref(req_input);
+      // We're only interested in generated files.
+      if (!req_input_ref.consume_front(genfile_root_abs)) continue;
+      auto& genfile = *result.add_generated();
+      genfile.set_path(req_input_ref.str());
+      genfile.set_contents(std::move(contents));
+    }
+  }
+  return results;
+}
+}  // namespace tools::ide_query::cc_analyzer
diff --git a/tools/ide_query/cc_analyzer/analyzer.h b/tools/ide_query/cc_analyzer/analyzer.h
new file mode 100644
index 0000000..3133795
--- /dev/null
+++ b/tools/ide_query/cc_analyzer/analyzer.h
@@ -0,0 +1,34 @@
+/*
+ * 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.
+ */
+
+#ifndef _TOOLS_IDE_QUERY_CC_ANALYZER_ANALYZER_H_
+#define _TOOLS_IDE_QUERY_CC_ANALYZER_ANALYZER_H_
+
+#include "ide_query.pb.h"
+
+namespace tools::ide_query::cc_analyzer {
+
+// Scans the build graph and returns target names from the build graph to
+// generate all the dependencies for the active files.
+::ide_query::DepsResponse GetDeps(::ide_query::RepoState state);
+
+// Scans the sources and returns all the source files required for analyzing the
+// active files.
+::ide_query::IdeAnalysis GetBuildInputs(::ide_query::RepoState state);
+
+}  // namespace tools::ide_query::cc_analyzer
+
+#endif
diff --git a/tools/ide_query/cc_analyzer/builtin_headers.cc b/tools/ide_query/cc_analyzer/builtin_headers.cc
new file mode 100644
index 0000000..6d44ce7
--- /dev/null
+++ b/tools/ide_query/cc_analyzer/builtin_headers.cc
@@ -0,0 +1,25 @@
+/*
+ * 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.
+ */
+#include "builtin_headers.h"
+
+#include <cstddef>
+
+#include "clang_builtin_headers_resources.inc"
+
+const struct FileToc *builtin_headers_create() { return kPackedFiles; }
+size_t builtin_headers_size() {
+  return sizeof(kPackedFiles) / sizeof(FileToc) - 1;
+}
diff --git a/tools/ide_query/cc_analyzer/builtin_headers.h b/tools/ide_query/cc_analyzer/builtin_headers.h
new file mode 100644
index 0000000..eda722f
--- /dev/null
+++ b/tools/ide_query/cc_analyzer/builtin_headers.h
@@ -0,0 +1,29 @@
+/*
+ * 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.
+ */
+#ifndef _TOOLS_IDE_QUERY_CC_ANALYZER_BUILTIN_HEADERS_H_
+#define _TOOLS_IDE_QUERY_CC_ANALYZER_BUILTIN_HEADERS_H_
+
+#include <cstddef>
+
+struct FileToc {
+  const char *name;
+  const char *data;
+};
+
+const struct FileToc *builtin_headers_create();
+size_t builtin_headers_size();
+
+#endif
diff --git a/tools/ide_query/cc_analyzer/include_scanner.cc b/tools/ide_query/cc_analyzer/include_scanner.cc
new file mode 100644
index 0000000..8916a3e
--- /dev/null
+++ b/tools/ide_query/cc_analyzer/include_scanner.cc
@@ -0,0 +1,176 @@
+/*
+ * 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.
+ */
+#include "include_scanner.h"
+
+#include <memory>
+#include <string>
+#include <unordered_map>
+#include <utility>
+#include <vector>
+
+#include "builtin_headers.h"
+#include "clang/Basic/FileEntry.h"
+#include "clang/Basic/FileManager.h"
+#include "clang/Basic/Module.h"
+#include "clang/Basic/SourceLocation.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Frontend/ASTUnit.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Frontend/FrontendActions.h"
+#include "clang/Lex/PPCallbacks.h"
+#include "clang/Tooling/ArgumentsAdjusters.h"
+#include "clang/Tooling/CompilationDatabase.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/IntrusiveRefCntPtr.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/Twine.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/VirtualFileSystem.h"
+
+namespace tools::ide_query::cc_analyzer {
+namespace {
+std::string CleanPath(llvm::StringRef path) {
+  // both ./ and ../ has `./` in them.
+  if (!path.contains("./")) return path.str();
+  llvm::SmallString<256> clean_path(path);
+  llvm::sys::path::remove_dots(clean_path, /*remove_dot_dot=*/true);
+  return clean_path.str().str();
+}
+
+// Returns the absolute path to file_name, treating it as relative to cwd if it
+// isn't already absolute.
+std::string GetAbsolutePath(llvm::StringRef cwd, llvm::StringRef file_name) {
+  if (llvm::sys::path::is_absolute(file_name)) return CleanPath(file_name);
+  llvm::SmallString<256> abs_path(cwd);
+  llvm::sys::path::append(abs_path, file_name);
+  llvm::sys::path::remove_dots(abs_path, /*remove_dot_dot=*/true);
+  return abs_path.str().str();
+}
+
+class IncludeRecordingPP : public clang::PPCallbacks {
+ public:
+  explicit IncludeRecordingPP(
+      std::unordered_map<std::string, std::string> &abs_paths, std::string cwd,
+      const clang::SourceManager &sm)
+      : abs_paths_(abs_paths), cwd_(std::move(cwd)), sm_(sm) {}
+
+  void LexedFileChanged(clang::FileID FID, LexedFileChangeReason Reason,
+                        clang::SrcMgr::CharacteristicKind FileType,
+                        clang::FileID PrevFID,
+                        clang::SourceLocation Loc) override {
+    auto file_entry = sm_.getFileEntryRefForID(FID);
+    if (!file_entry) return;
+    auto abs_path = GetAbsolutePath(cwd_, file_entry->getName());
+    auto [it, inserted] = abs_paths_.try_emplace(abs_path);
+    if (inserted) it->second = sm_.getBufferData(FID);
+  }
+
+  std::unordered_map<std::string, std::string> &abs_paths_;
+  const std::string cwd_;
+  const clang::SourceManager &sm_;
+};
+
+class IncludeScanningAction final : public clang::PreprocessOnlyAction {
+ public:
+  explicit IncludeScanningAction(
+      std::unordered_map<std::string, std::string> &abs_paths)
+      : abs_paths_(abs_paths) {}
+  bool BeginSourceFileAction(clang::CompilerInstance &ci) override {
+    std::string cwd;
+    auto cwd_or_err = ci.getVirtualFileSystem().getCurrentWorkingDirectory();
+    if (!cwd_or_err || cwd_or_err.get().empty()) return false;
+    cwd = cwd_or_err.get();
+    ci.getPreprocessor().addPPCallbacks(std::make_unique<IncludeRecordingPP>(
+        abs_paths_, std::move(cwd), ci.getSourceManager()));
+    return true;
+  }
+
+ private:
+  std::unordered_map<std::string, std::string> &abs_paths_;
+};
+
+llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> OverlayBuiltinHeaders(
+    std::vector<std::string> &argv,
+    llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> base) {
+  static constexpr llvm::StringLiteral kResourceDir = "/resources";
+  llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> overlay(
+      new llvm::vfs::OverlayFileSystem(std::move(base)));
+  llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> builtin_headers(
+      new llvm::vfs::InMemoryFileSystem);
+
+  llvm::SmallString<256> file_path;
+  for (const auto &builtin_header :
+       llvm::ArrayRef(builtin_headers_create(), builtin_headers_size())) {
+    file_path.clear();
+    llvm::sys::path::append(file_path, kResourceDir, "include",
+                            builtin_header.name);
+    builtin_headers->addFile(
+        file_path,
+        /*ModificationTime=*/0,
+        llvm::MemoryBuffer::getMemBuffer(builtin_header.data));
+  }
+  overlay->pushOverlay(std::move(builtin_headers));
+  argv.insert(llvm::find(argv, "--"),
+              llvm::Twine("-resource-dir=", kResourceDir).str());
+  return overlay;
+}
+
+}  // namespace
+
+llvm::Expected<std::vector<std::pair<std::string, std::string>>> ScanIncludes(
+    const clang::tooling::CompileCommand &cmd,
+    llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs) {
+  if (fs->setCurrentWorkingDirectory(cmd.Directory)) {
+    return llvm::createStringError(
+        llvm::inconvertibleErrorCode(),
+        "Failed to set working directory to: " + cmd.Directory);
+  }
+
+  auto main_file = fs->getBufferForFile(cmd.Filename);
+  if (!main_file) {
+    return llvm::createStringError(llvm::inconvertibleErrorCode(),
+                                   "Main file doesn't exist: " + cmd.Filename);
+  }
+  std::unordered_map<std::string, std::string> abs_paths;
+  abs_paths.try_emplace(GetAbsolutePath(cmd.Directory, cmd.Filename),
+                        main_file.get()->getBuffer().str());
+
+  std::vector<std::string> argv = cmd.CommandLine;
+  fs = OverlayBuiltinHeaders(argv, std::move(fs));
+
+  llvm::IntrusiveRefCntPtr<clang::FileManager> files(
+      new clang::FileManager(/*FileSystemOpts=*/{}, std::move(fs)));
+  clang::tooling::ToolInvocation tool(
+      argv, std::make_unique<IncludeScanningAction>(abs_paths), files.get());
+  if (!tool.run()) {
+    return llvm::createStringError(
+        llvm::inconvertibleErrorCode(),
+        "Failed to scan includes for: " + cmd.Filename);
+  }
+
+  std::vector<std::pair<std::string, std::string>> result;
+  result.reserve(abs_paths.size());
+  for (auto &entry : abs_paths) {
+    result.emplace_back(entry.first, std::move(entry.second));
+  }
+  return result;
+}
+}  // namespace tools::ide_query::cc_analyzer
diff --git a/tools/ide_query/cc_analyzer/include_scanner.h b/tools/ide_query/cc_analyzer/include_scanner.h
new file mode 100644
index 0000000..e814e72
--- /dev/null
+++ b/tools/ide_query/cc_analyzer/include_scanner.h
@@ -0,0 +1,38 @@
+/*
+ * 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.
+ */
+#ifndef _TOOLS_IDE_QUERY_CC_ANALYZER_INCLUDE_SCANNER_H_
+#define _TOOLS_IDE_QUERY_CC_ANALYZER_INCLUDE_SCANNER_H_
+
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "clang/Tooling/CompilationDatabase.h"
+#include "llvm/ADT/IntrusiveRefCntPtr.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/VirtualFileSystem.h"
+
+namespace tools::ide_query::cc_analyzer {
+
+// Returns absolute paths and contents for all the includes necessary for
+// compiling source file in command.
+llvm::Expected<std::vector<std::pair<std::string, std::string>>> ScanIncludes(
+    const clang::tooling::CompileCommand &cmd,
+    llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs);
+
+}  // namespace tools::ide_query::cc_analyzer
+
+#endif
diff --git a/tools/ide_query/cc_analyzer/main.cc b/tools/ide_query/cc_analyzer/main.cc
new file mode 100644
index 0000000..8e00c63
--- /dev/null
+++ b/tools/ide_query/cc_analyzer/main.cc
@@ -0,0 +1,92 @@
+/*
+ * 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.
+ */
+
+// Driver for c++ extractor. Operates in two modes:
+// - DEPS, scans build graph for active files and reports targets that need to
+// be build for analyzing that file.
+// - INPUTS, scans the source code for active files and returns all the sources
+// required for analyzing that file.
+//
+// Uses stdin/stdout to take in requests and provide responses.
+#include <unistd.h>
+
+#include <memory>
+#include <utility>
+
+#include "analyzer.h"
+#include "google/protobuf/message.h"
+#include "ide_query.pb.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/TargetSelect.h"
+#include "llvm/Support/raw_ostream.h"
+
+namespace {
+enum class OpMode {
+  DEPS = 0,
+  INPUTS = 1,
+};
+llvm::cl::opt<OpMode> mode{
+    "mode",
+    llvm::cl::values(clEnumValN(OpMode::DEPS, "deps",
+                                "Figure out targets that need to be build"),
+                     clEnumValN(OpMode::INPUTS, "inputs",
+                                "Figure out generated files used")),
+    llvm::cl::desc("Print the list of headers to insert and remove"),
+};
+
+ide_query::IdeAnalysis ReturnError(llvm::StringRef message) {
+  ide_query::IdeAnalysis result;
+  result.mutable_status()->set_code(ide_query::Status::FAILURE);
+  result.mutable_status()->set_message(message.str());
+  return result;
+}
+
+}  // namespace
+
+int main(int argc, char* argv[]) {
+  llvm::InitializeAllTargetInfos();
+  llvm::cl::ParseCommandLineOptions(argc, argv);
+
+  ide_query::RepoState state;
+  if (!state.ParseFromFileDescriptor(STDIN_FILENO)) {
+    llvm::errs() << "Failed to parse input!\n";
+    return 1;
+  }
+
+  std::unique_ptr<google::protobuf::Message> result;
+  switch (mode) {
+    case OpMode::DEPS: {
+      result = std::make_unique<ide_query::DepsResponse>(
+          tools::ide_query::cc_analyzer::GetDeps(std::move(state)));
+      break;
+    }
+    case OpMode::INPUTS: {
+      result = std::make_unique<ide_query::IdeAnalysis>(
+          tools::ide_query::cc_analyzer::GetBuildInputs(std::move(state)));
+      break;
+    }
+    default:
+      llvm::errs() << "Unknown operation mode!\n";
+      return 1;
+  }
+  if (!result->SerializeToFileDescriptor(STDOUT_FILENO)) {
+    llvm::errs() << "Failed to serialize result!\n";
+    return 1;
+  }
+
+  return 0;
+}
diff --git a/tools/ide_query/ide_query.go b/tools/ide_query/ide_query.go
index c1c4da0..50264fd 100644
--- a/tools/ide_query/ide_query.go
+++ b/tools/ide_query/ide_query.go
@@ -1,8 +1,25 @@
+/*
+ * 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.
+ */
+
 // Binary ide_query generates and analyzes build artifacts.
 // The produced result can be consumed by IDEs to provide language features.
 package main
 
 import (
+	"bytes"
 	"container/list"
 	"context"
 	"encoding/json"
@@ -21,9 +38,13 @@
 
 // Env contains information about the current environment.
 type Env struct {
-	LunchTarget LunchTarget
-	RepoDir     string
-	OutDir      string
+	LunchTarget    LunchTarget
+	RepoDir        string
+	OutDir         string
+	ClangToolsRoot string
+
+	CcFiles   []string
+	JavaFiles []string
 }
 
 // LunchTarget is a parsed Android lunch target.
@@ -64,6 +85,7 @@
 	var env Env
 	env.OutDir = os.Getenv("OUT_DIR")
 	env.RepoDir = os.Getenv("ANDROID_BUILD_TOP")
+	env.ClangToolsRoot = os.Getenv("PREBUILTS_CLANG_TOOLS_ROOT")
 	flag.Var(&env.LunchTarget, "lunch_target", "The lunch target to query")
 	flag.Parse()
 	files := flag.Args()
@@ -73,64 +95,169 @@
 		return
 	}
 
-	var javaFiles []string
 	for _, f := range files {
 		switch {
 		case strings.HasSuffix(f, ".java") || strings.HasSuffix(f, ".kt"):
-			javaFiles = append(javaFiles, f)
+			env.JavaFiles = append(env.JavaFiles, f)
+		case strings.HasSuffix(f, ".cc") || strings.HasSuffix(f, ".cpp") || strings.HasSuffix(f, ".h"):
+			env.CcFiles = append(env.CcFiles, f)
 		default:
 			log.Printf("File %q is supported - will be skipped.", f)
 		}
 	}
 
 	ctx := context.Background()
-	javaDepsPath := path.Join(env.RepoDir, env.OutDir, "soong/module_bp_java_deps.json")
-	// TODO(michaelmerg): Figure out if module_bp_java_deps.json is outdated.
+	// TODO(michaelmerg): Figure out if module_bp_java_deps.json and compile_commands.json is outdated.
 	runMake(ctx, env, "nothing")
 
-	javaModules, err := loadJavaModules(javaDepsPath)
+	javaModules, javaFileToModuleMap, err := loadJavaModules(&env)
 	if err != nil {
-		log.Fatalf("Failed to load java modules: %v", err)
+		log.Printf("Failed to load java modules: %v", err)
+	}
+	toMake := getJavaTargets(javaFileToModuleMap)
+
+	ccTargets, status := getCCTargets(ctx, &env)
+	if status != nil && status.Code != pb.Status_OK {
+		log.Fatalf("Failed to query cc targets: %v", *status.Message)
+	}
+	toMake = append(toMake, ccTargets...)
+	fmt.Fprintf(os.Stderr, "Running make for modules: %v\n", strings.Join(toMake, ", "))
+	if err := runMake(ctx, env, toMake...); err != nil {
+		log.Printf("Building deps failed: %v", err)
 	}
 
-	fileToModule := make(map[string]*javaModule) // file path -> module
-	for _, f := range javaFiles {
-		for _, m := range javaModules {
-			if !slices.Contains(m.Srcs, f) {
-				continue
-			}
-			if fileToModule[f] != nil {
-				// TODO(michaelmerg): Handle the case where a file is covered by multiple modules.
-				log.Printf("File %q found in module %q but is already covered by module %q", f, m.Name, fileToModule[f].Name)
-				continue
-			}
-			fileToModule[f] = m
+	res := getJavaInputs(&env, javaModules, javaFileToModuleMap)
+	ccAnalysis := getCCInputs(ctx, &env)
+	proto.Merge(res, ccAnalysis)
+
+	res.BuildArtifactRoot = env.OutDir
+	data, err := proto.Marshal(res)
+	if err != nil {
+		log.Fatalf("Failed to marshal result proto: %v", err)
+	}
+
+	_, err = os.Stdout.Write(data)
+	if err != nil {
+		log.Fatalf("Failed to write result proto: %v", err)
+	}
+
+	for _, s := range res.Sources {
+		fmt.Fprintf(os.Stderr, "%s: %v (Deps: %d, Generated: %d)\n", s.GetPath(), s.GetStatus(), len(s.GetDeps()), len(s.GetGenerated()))
+	}
+}
+
+func repoState(env *Env) *pb.RepoState {
+	const compDbPath = "soong/development/ide/compdb/compile_commands.json"
+	return &pb.RepoState{
+		RepoDir:        env.RepoDir,
+		ActiveFilePath: env.CcFiles,
+		OutDir:         env.OutDir,
+		CompDbPath:     path.Join(env.OutDir, compDbPath),
+	}
+}
+
+func runCCanalyzer(ctx context.Context, env *Env, mode string, in []byte) ([]byte, error) {
+	ccAnalyzerPath := path.Join(env.ClangToolsRoot, "bin/ide_query_cc_analyzer")
+	outBuffer := new(bytes.Buffer)
+
+	inBuffer := new(bytes.Buffer)
+	inBuffer.Write(in)
+
+	cmd := exec.CommandContext(ctx, ccAnalyzerPath, "--mode="+mode)
+	cmd.Dir = env.RepoDir
+
+	cmd.Stdin = inBuffer
+	cmd.Stdout = outBuffer
+	cmd.Stderr = os.Stderr
+
+	err := cmd.Run()
+
+	return outBuffer.Bytes(), err
+}
+
+// Execute cc_analyzer and get all the targets that needs to be build for analyzing files.
+func getCCTargets(ctx context.Context, env *Env) ([]string, *pb.Status) {
+	state := repoState(env)
+	bytes, err := proto.Marshal(state)
+	if err != nil {
+		log.Fatalln("Failed to serialize state:", err)
+	}
+
+	resp := new(pb.DepsResponse)
+	result, err := runCCanalyzer(ctx, env, "deps", bytes)
+	if marshal_err := proto.Unmarshal(result, resp); marshal_err != nil {
+		return nil, &pb.Status{
+			Code:    pb.Status_FAILURE,
+			Message: proto.String("Malformed response from cc_analyzer: " + marshal_err.Error()),
 		}
 	}
 
-	var toMake []string
-	for _, m := range fileToModule {
-		toMake = append(toMake, m.Name)
+	var targets []string
+	if resp.Status != nil && resp.Status.Code != pb.Status_OK {
+		return targets, resp.Status
 	}
-	fmt.Printf("Running make for modules: %v\n", strings.Join(toMake, ", "))
-	if err := runMake(ctx, env, toMake...); err != nil {
-		log.Fatalf("Failed to run make: %v", err)
+	for _, deps := range resp.Deps {
+		targets = append(targets, deps.BuildTarget...)
 	}
 
+	status := &pb.Status{Code: pb.Status_OK}
+	if err != nil {
+		status = &pb.Status{
+			Code:    pb.Status_FAILURE,
+			Message: proto.String(err.Error()),
+		}
+	}
+	return targets, status
+}
+
+func getCCInputs(ctx context.Context, env *Env) *pb.IdeAnalysis {
+	state := repoState(env)
+	bytes, err := proto.Marshal(state)
+	if err != nil {
+		log.Fatalln("Failed to serialize state:", err)
+	}
+
+	resp := new(pb.IdeAnalysis)
+	result, err := runCCanalyzer(ctx, env, "inputs", bytes)
+	if marshal_err := proto.Unmarshal(result, resp); marshal_err != nil {
+		resp.Status = &pb.Status{
+			Code:    pb.Status_FAILURE,
+			Message: proto.String("Malformed response from cc_analyzer: " + marshal_err.Error()),
+		}
+		return resp
+	}
+
+	if err != nil && (resp.Status == nil || resp.Status.Code == pb.Status_OK) {
+		resp.Status = &pb.Status{
+			Code:    pb.Status_FAILURE,
+			Message: proto.String(err.Error()),
+		}
+	}
+	return resp
+}
+
+func getJavaTargets(javaFileToModuleMap map[string]*javaModule) []string {
+	var targets []string
+	for _, m := range javaFileToModuleMap {
+		targets = append(targets, m.Name)
+	}
+	return targets
+}
+
+func getJavaInputs(env *Env, javaModules map[string]*javaModule, javaFileToModuleMap map[string]*javaModule) *pb.IdeAnalysis {
 	var sources []*pb.SourceFile
 	type depsAndGenerated struct {
 		Deps      []string
 		Generated []*pb.GeneratedFile
 	}
 	moduleToDeps := make(map[string]*depsAndGenerated)
-	for _, f := range files {
+	for _, f := range env.JavaFiles {
 		file := &pb.SourceFile{
-			Path:       f,
-			WorkingDir: env.RepoDir,
+			Path: f,
 		}
 		sources = append(sources, file)
 
-		m := fileToModule[f]
+		m := javaFileToModuleMap[f]
 		if m == nil {
 			file.Status = &pb.Status{
 				Code:    pb.Status_FAILURE,
@@ -167,24 +294,8 @@
 		file.Generated = generated
 		file.Deps = deps
 	}
-
-	res := &pb.IdeAnalysis{
-		BuildArtifactRoot: env.OutDir,
-		Sources:           sources,
-		Status:            &pb.Status{Code: pb.Status_OK},
-	}
-	data, err := proto.Marshal(res)
-	if err != nil {
-		log.Fatalf("Failed to marshal result proto: %v", err)
-	}
-
-	err = os.WriteFile(path.Join(env.OutDir, "ide_query.pb"), data, 0644)
-	if err != nil {
-		log.Fatalf("Failed to write result proto: %v", err)
-	}
-
-	for _, s := range sources {
-		fmt.Printf("%s: %v (Deps: %d, Generated: %d)\n", s.GetPath(), s.GetStatus(), len(s.GetDeps()), len(s.GetGenerated()))
+	return &pb.IdeAnalysis{
+		Sources: sources,
 	}
 }
 
@@ -196,11 +307,12 @@
 		"TARGET_PRODUCT=" + env.LunchTarget.Product,
 		"TARGET_RELEASE=" + env.LunchTarget.Release,
 		"TARGET_BUILD_VARIANT=" + env.LunchTarget.Variant,
+		"-k",
 	}
 	args = append(args, modules...)
 	cmd := exec.CommandContext(ctx, "build/soong/soong_ui.bash", args...)
 	cmd.Dir = env.RepoDir
-	cmd.Stdout = os.Stdout
+	cmd.Stdout = os.Stderr
 	cmd.Stderr = os.Stderr
 	return cmd.Run()
 }
@@ -214,26 +326,40 @@
 	SrcJars []string `json:"srcjars,omitempty"`
 }
 
-func loadJavaModules(path string) (map[string]*javaModule, error) {
-	data, err := os.ReadFile(path)
+func loadJavaModules(env *Env) (map[string]*javaModule, map[string]*javaModule, error) {
+	javaDepsPath := path.Join(env.RepoDir, env.OutDir, "soong/module_bp_java_deps.json")
+	data, err := os.ReadFile(javaDepsPath)
 	if err != nil {
-		return nil, err
+		return nil, nil, err
 	}
 
-	var ret map[string]*javaModule // module name -> module
-	if err = json.Unmarshal(data, &ret); err != nil {
-		return nil, err
+	var moduleMapping map[string]*javaModule // module name -> module
+	if err = json.Unmarshal(data, &moduleMapping); err != nil {
+		return nil, nil, err
 	}
 
-	for name, module := range ret {
+	javaModules := make(map[string]*javaModule)
+	javaFileToModuleMap := make(map[string]*javaModule)
+	for name, module := range moduleMapping {
 		if strings.HasSuffix(name, "-jarjar") || strings.HasSuffix(name, ".impl") {
-			delete(ret, name)
 			continue
 		}
-
 		module.Name = name
+		javaModules[name] = module
+		for _, src := range module.Srcs {
+			if !slices.Contains(env.JavaFiles, src) {
+				// We are only interested in active files.
+				continue
+			}
+			if javaFileToModuleMap[src] != nil {
+				// TODO(michaelmerg): Handle the case where a file is covered by multiple modules.
+				log.Printf("File %q found in module %q but is already covered by module %q", src, module.Name, javaFileToModuleMap[src].Name)
+				continue
+			}
+			javaFileToModuleMap[src] = module
+		}
 	}
-	return ret, nil
+	return javaModules, javaFileToModuleMap, nil
 }
 
 func transitiveDeps(m *javaModule, modules map[string]*javaModule) []string {
diff --git a/tools/ide_query/ide_query.sh b/tools/ide_query/ide_query.sh
index 663c4dc..2df48d0 100755
--- a/tools/ide_query/ide_query.sh
+++ b/tools/ide_query/ide_query.sh
@@ -1,5 +1,19 @@
 #!/bin/bash -e
 
+# 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.
+
 cd $(dirname $BASH_SOURCE)
 source $(pwd)/../../shell_utils.sh
 require_top
@@ -7,6 +21,17 @@
 # Ensure cogsetup (out/ will be symlink outside the repo)
 . ${TOP}/build/make/cogsetup.sh
 
+case $(uname -s) in
+    Linux)
+      export PREBUILTS_CLANG_TOOLS_ROOT="${TOP}/prebuilts/clang-tools/linux-x86/"
+      PREBUILTS_GO_ROOT="${TOP}/prebuilts/go/linux-x86/"
+      ;;
+    *)
+      echo "Only supported for linux hosts" >&2
+      exit 1
+      ;;
+esac
+
 export ANDROID_BUILD_TOP=$TOP
 export OUT_DIR=${OUT_DIR}
-exec "${TOP}/prebuilts/go/linux-x86/bin/go" "run" "ide_query" "$@"
+exec "${PREBUILTS_GO_ROOT}/bin/go" "run" "ide_query" "$@"
diff --git a/tools/ide_query/ide_query_proto/Android.bp b/tools/ide_query/ide_query_proto/Android.bp
new file mode 100644
index 0000000..70f15cd
--- /dev/null
+++ b/tools/ide_query/ide_query_proto/Android.bp
@@ -0,0 +1,33 @@
+/*
+ * 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 {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+cc_library_host_static {
+    name: "ide_query_proto",
+    srcs: [
+        "ide_query.proto",
+    ],
+    proto: {
+        export_proto_headers: true,
+        type: "full",
+        canonical_path_from_root: false,
+    },
+    compile_multilib: "64",
+    shared_libs: ["libprotobuf-cpp-full"],
+}
diff --git a/tools/ide_query/ide_query_proto/ide_query.pb.go b/tools/ide_query/ide_query_proto/ide_query.pb.go
index 30571cc..f3a016d 100644
--- a/tools/ide_query/ide_query_proto/ide_query.pb.go
+++ b/tools/ide_query/ide_query_proto/ide_query.pb.go
@@ -1,6 +1,6 @@
 // Code generated by protoc-gen-go. DO NOT EDIT.
 // versions:
-// 	protoc-gen-go v1.30.0
+// 	protoc-gen-go v1.25.0-devel
 // 	protoc        v3.21.12
 // source: ide_query.proto
 
@@ -72,7 +72,7 @@
 	sizeCache     protoimpl.SizeCache
 	unknownFields protoimpl.UnknownFields
 
-	Code Status_Code `protobuf:"varint,1,opt,name=code,proto3,enum=cider.build.companion.Status_Code" json:"code,omitempty"`
+	Code Status_Code `protobuf:"varint,1,opt,name=code,proto3,enum=ide_query.Status_Code" json:"code,omitempty"`
 	// Details about the status, might be displayed to user.
 	Message *string `protobuf:"bytes,2,opt,name=message,proto3,oneof" json:"message,omitempty"`
 }
@@ -123,6 +123,142 @@
 	return ""
 }
 
+// Represents an Android checkout on user's workstation.
+type RepoState struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// Absolute path for the checkout in the workstation.
+	// e.g. /home/user/work/android/
+	RepoDir string `protobuf:"bytes,1,opt,name=repo_dir,json=repoDir,proto3" json:"repo_dir,omitempty"`
+	// Relative to repo_dir.
+	ActiveFilePath []string `protobuf:"bytes,2,rep,name=active_file_path,json=activeFilePath,proto3" json:"active_file_path,omitempty"`
+	// Repository relative path to output directory in workstation.
+	OutDir string `protobuf:"bytes,3,opt,name=out_dir,json=outDir,proto3" json:"out_dir,omitempty"`
+	// Repository relative path to compile_commands.json in workstation.
+	CompDbPath string `protobuf:"bytes,4,opt,name=comp_db_path,json=compDbPath,proto3" json:"comp_db_path,omitempty"`
+}
+
+func (x *RepoState) Reset() {
+	*x = RepoState{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_ide_query_proto_msgTypes[1]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *RepoState) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*RepoState) ProtoMessage() {}
+
+func (x *RepoState) ProtoReflect() protoreflect.Message {
+	mi := &file_ide_query_proto_msgTypes[1]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use RepoState.ProtoReflect.Descriptor instead.
+func (*RepoState) Descriptor() ([]byte, []int) {
+	return file_ide_query_proto_rawDescGZIP(), []int{1}
+}
+
+func (x *RepoState) GetRepoDir() string {
+	if x != nil {
+		return x.RepoDir
+	}
+	return ""
+}
+
+func (x *RepoState) GetActiveFilePath() []string {
+	if x != nil {
+		return x.ActiveFilePath
+	}
+	return nil
+}
+
+func (x *RepoState) GetOutDir() string {
+	if x != nil {
+		return x.OutDir
+	}
+	return ""
+}
+
+func (x *RepoState) GetCompDbPath() string {
+	if x != nil {
+		return x.CompDbPath
+	}
+	return ""
+}
+
+// Provides all the targets that are pre-requisities for running language
+// services on active_file_paths.
+type DepsResponse struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Deps   []*DepsResponse_Deps `protobuf:"bytes,1,rep,name=deps,proto3" json:"deps,omitempty"`
+	Status *Status              `protobuf:"bytes,2,opt,name=status,proto3,oneof" json:"status,omitempty"`
+}
+
+func (x *DepsResponse) Reset() {
+	*x = DepsResponse{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_ide_query_proto_msgTypes[2]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *DepsResponse) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*DepsResponse) ProtoMessage() {}
+
+func (x *DepsResponse) ProtoReflect() protoreflect.Message {
+	mi := &file_ide_query_proto_msgTypes[2]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use DepsResponse.ProtoReflect.Descriptor instead.
+func (*DepsResponse) Descriptor() ([]byte, []int) {
+	return file_ide_query_proto_rawDescGZIP(), []int{2}
+}
+
+func (x *DepsResponse) GetDeps() []*DepsResponse_Deps {
+	if x != nil {
+		return x.Deps
+	}
+	return nil
+}
+
+func (x *DepsResponse) GetStatus() *Status {
+	if x != nil {
+		return x.Status
+	}
+	return nil
+}
+
+// Returns all the information necessary for providing language services for the
+// active files.
 type GeneratedFile struct {
 	state         protoimpl.MessageState
 	sizeCache     protoimpl.SizeCache
@@ -130,8 +266,7 @@
 
 	// Path to the file relative to IdeAnalysis.build_artifact_root.
 	Path string `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"`
-	//	The text of the generated file, if not provided contents will be read
-	//
+	// The text of the generated file, if not provided contents will be read
 	// from the path above in user's workstation.
 	Contents []byte `protobuf:"bytes,2,opt,name=contents,proto3,oneof" json:"contents,omitempty"`
 }
@@ -139,7 +274,7 @@
 func (x *GeneratedFile) Reset() {
 	*x = GeneratedFile{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_ide_query_proto_msgTypes[1]
+		mi := &file_ide_query_proto_msgTypes[3]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -152,7 +287,7 @@
 func (*GeneratedFile) ProtoMessage() {}
 
 func (x *GeneratedFile) ProtoReflect() protoreflect.Message {
-	mi := &file_ide_query_proto_msgTypes[1]
+	mi := &file_ide_query_proto_msgTypes[3]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -165,7 +300,7 @@
 
 // Deprecated: Use GeneratedFile.ProtoReflect.Descriptor instead.
 func (*GeneratedFile) Descriptor() ([]byte, []int) {
-	return file_ide_query_proto_rawDescGZIP(), []int{1}
+	return file_ide_query_proto_rawDescGZIP(), []int{3}
 }
 
 func (x *GeneratedFile) GetPath() string {
@@ -187,11 +322,11 @@
 	sizeCache     protoimpl.SizeCache
 	unknownFields protoimpl.UnknownFields
 
-	// Repo root relative path to the source file in the tree.
+	// Path to the source file relative to repository root.
 	Path string `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"`
 	// Working directory used by the build system. All the relative
 	// paths in compiler_arguments should be relative to this path.
-	// Relative to workspace root.
+	// Relative to repository root.
 	WorkingDir string `protobuf:"bytes,2,opt,name=working_dir,json=workingDir,proto3" json:"working_dir,omitempty"`
 	// Compiler arguments to compile the source file. If multiple variants
 	// of the module being compiled are possible, the query script will choose
@@ -200,12 +335,12 @@
 	// Any generated files that are used in compiling the file.
 	Generated []*GeneratedFile `protobuf:"bytes,4,rep,name=generated,proto3" json:"generated,omitempty"`
 	// Paths to all of the sources, like build files, code generators,
-	// proto files etc. that were used  during analysis. Used to figure
+	// proto files etc. that were used during analysis. Used to figure
 	// out when a set of build artifacts are stale and the query tool
 	// must be re-run.
-	// Relative to workspace root.
+	// Relative to repository root.
 	Deps []string `protobuf:"bytes,5,rep,name=deps,proto3" json:"deps,omitempty"`
-	// Represensts analysis status for this particular file. e.g. not part
+	// Represents analysis status for this particular file. e.g. not part
 	// of the build graph.
 	Status *Status `protobuf:"bytes,6,opt,name=status,proto3,oneof" json:"status,omitempty"`
 }
@@ -213,7 +348,7 @@
 func (x *SourceFile) Reset() {
 	*x = SourceFile{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_ide_query_proto_msgTypes[2]
+		mi := &file_ide_query_proto_msgTypes[4]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -226,7 +361,7 @@
 func (*SourceFile) ProtoMessage() {}
 
 func (x *SourceFile) ProtoReflect() protoreflect.Message {
-	mi := &file_ide_query_proto_msgTypes[2]
+	mi := &file_ide_query_proto_msgTypes[4]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -239,7 +374,7 @@
 
 // Deprecated: Use SourceFile.ProtoReflect.Descriptor instead.
 func (*SourceFile) Descriptor() ([]byte, []int) {
-	return file_ide_query_proto_rawDescGZIP(), []int{2}
+	return file_ide_query_proto_rawDescGZIP(), []int{4}
 }
 
 func (x *SourceFile) GetPath() string {
@@ -289,21 +424,20 @@
 	sizeCache     protoimpl.SizeCache
 	unknownFields protoimpl.UnknownFields
 
-	// Path relative to workspace root, containing all the artifacts
+	// Path relative to repository root, containing all the artifacts
 	// generated by the build system. GeneratedFile.path are always
 	// relative to this directory.
 	BuildArtifactRoot string        `protobuf:"bytes,1,opt,name=build_artifact_root,json=buildArtifactRoot,proto3" json:"build_artifact_root,omitempty"`
 	Sources           []*SourceFile `protobuf:"bytes,2,rep,name=sources,proto3" json:"sources,omitempty"`
 	// Status representing overall analysis.
-	// Should fail only when no analysis can be performed, e.g. workspace
-	// isn't setup.
+	// Should fail only when no analysis can be performed.
 	Status *Status `protobuf:"bytes,3,opt,name=status,proto3,oneof" json:"status,omitempty"`
 }
 
 func (x *IdeAnalysis) Reset() {
 	*x = IdeAnalysis{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_ide_query_proto_msgTypes[3]
+		mi := &file_ide_query_proto_msgTypes[5]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -316,7 +450,7 @@
 func (*IdeAnalysis) ProtoMessage() {}
 
 func (x *IdeAnalysis) ProtoReflect() protoreflect.Message {
-	mi := &file_ide_query_proto_msgTypes[3]
+	mi := &file_ide_query_proto_msgTypes[5]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -329,7 +463,7 @@
 
 // Deprecated: Use IdeAnalysis.ProtoReflect.Descriptor instead.
 func (*IdeAnalysis) Descriptor() ([]byte, []int) {
-	return file_ide_query_proto_rawDescGZIP(), []int{3}
+	return file_ide_query_proto_rawDescGZIP(), []int{5}
 }
 
 func (x *IdeAnalysis) GetBuildArtifactRoot() string {
@@ -353,58 +487,144 @@
 	return nil
 }
 
+// Build dependencies of a source file for providing language services.
+type DepsResponse_Deps struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// Relative to repo_dir.
+	SourceFile string `protobuf:"bytes,1,opt,name=source_file,json=sourceFile,proto3" json:"source_file,omitempty"`
+	// Build target to execute for generating dep.
+	BuildTarget []string `protobuf:"bytes,2,rep,name=build_target,json=buildTarget,proto3" json:"build_target,omitempty"`
+	Status      *Status  `protobuf:"bytes,3,opt,name=status,proto3,oneof" json:"status,omitempty"`
+}
+
+func (x *DepsResponse_Deps) Reset() {
+	*x = DepsResponse_Deps{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_ide_query_proto_msgTypes[6]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *DepsResponse_Deps) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*DepsResponse_Deps) ProtoMessage() {}
+
+func (x *DepsResponse_Deps) ProtoReflect() protoreflect.Message {
+	mi := &file_ide_query_proto_msgTypes[6]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use DepsResponse_Deps.ProtoReflect.Descriptor instead.
+func (*DepsResponse_Deps) Descriptor() ([]byte, []int) {
+	return file_ide_query_proto_rawDescGZIP(), []int{2, 0}
+}
+
+func (x *DepsResponse_Deps) GetSourceFile() string {
+	if x != nil {
+		return x.SourceFile
+	}
+	return ""
+}
+
+func (x *DepsResponse_Deps) GetBuildTarget() []string {
+	if x != nil {
+		return x.BuildTarget
+	}
+	return nil
+}
+
+func (x *DepsResponse_Deps) GetStatus() *Status {
+	if x != nil {
+		return x.Status
+	}
+	return nil
+}
+
 var File_ide_query_proto protoreflect.FileDescriptor
 
 var file_ide_query_proto_rawDesc = []byte{
 	0x0a, 0x0f, 0x69, 0x64, 0x65, 0x5f, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74,
-	0x6f, 0x12, 0x15, 0x63, 0x69, 0x64, 0x65, 0x72, 0x2e, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x2e, 0x63,
-	0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x69, 0x6f, 0x6e, 0x22, 0x88, 0x01, 0x0a, 0x06, 0x53, 0x74, 0x61,
-	0x74, 0x75, 0x73, 0x12, 0x36, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28,
-	0x0e, 0x32, 0x22, 0x2e, 0x63, 0x69, 0x64, 0x65, 0x72, 0x2e, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x2e,
-	0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x69, 0x6f, 0x6e, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73,
-	0x2e, 0x43, 0x6f, 0x64, 0x65, 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x1d, 0x0a, 0x07, 0x6d,
-	0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x07,
-	0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x88, 0x01, 0x01, 0x22, 0x1b, 0x0a, 0x04, 0x43, 0x6f,
-	0x64, 0x65, 0x12, 0x06, 0x0a, 0x02, 0x4f, 0x4b, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x46, 0x41,
-	0x49, 0x4c, 0x55, 0x52, 0x45, 0x10, 0x01, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x6d, 0x65, 0x73, 0x73,
-	0x61, 0x67, 0x65, 0x22, 0x51, 0x0a, 0x0d, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64,
-	0x46, 0x69, 0x6c, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01,
-	0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x1f, 0x0a, 0x08, 0x63, 0x6f, 0x6e, 0x74,
-	0x65, 0x6e, 0x74, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x08, 0x63, 0x6f,
-	0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x88, 0x01, 0x01, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x63, 0x6f,
-	0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x8f, 0x02, 0x0a, 0x0a, 0x53, 0x6f, 0x75, 0x72, 0x63,
-	0x65, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20,
-	0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x1f, 0x0a, 0x0b, 0x77, 0x6f, 0x72,
-	0x6b, 0x69, 0x6e, 0x67, 0x5f, 0x64, 0x69, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a,
-	0x77, 0x6f, 0x72, 0x6b, 0x69, 0x6e, 0x67, 0x44, 0x69, 0x72, 0x12, 0x2d, 0x0a, 0x12, 0x63, 0x6f,
-	0x6d, 0x70, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73,
-	0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x11, 0x63, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x72,
-	0x41, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x42, 0x0a, 0x09, 0x67, 0x65, 0x6e,
-	0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x63,
-	0x69, 0x64, 0x65, 0x72, 0x2e, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61,
-	0x6e, 0x69, 0x6f, 0x6e, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x46, 0x69,
-	0x6c, 0x65, 0x52, 0x09, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x12, 0x12, 0x0a,
-	0x04, 0x64, 0x65, 0x70, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x64, 0x65, 0x70,
-	0x73, 0x12, 0x3a, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28,
-	0x0b, 0x32, 0x1d, 0x2e, 0x63, 0x69, 0x64, 0x65, 0x72, 0x2e, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x2e,
-	0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x69, 0x6f, 0x6e, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73,
-	0x48, 0x00, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x88, 0x01, 0x01, 0x42, 0x09, 0x0a,
-	0x07, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0xc1, 0x01, 0x0a, 0x0b, 0x49, 0x64, 0x65,
-	0x41, 0x6e, 0x61, 0x6c, 0x79, 0x73, 0x69, 0x73, 0x12, 0x2e, 0x0a, 0x13, 0x62, 0x75, 0x69, 0x6c,
-	0x64, 0x5f, 0x61, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18,
-	0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x41, 0x72, 0x74, 0x69,
-	0x66, 0x61, 0x63, 0x74, 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x3b, 0x0a, 0x07, 0x73, 0x6f, 0x75, 0x72,
-	0x63, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x63, 0x69, 0x64, 0x65,
-	0x72, 0x2e, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x69, 0x6f,
-	0x6e, 0x2e, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x07, 0x73, 0x6f,
-	0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x3a, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18,
-	0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x63, 0x69, 0x64, 0x65, 0x72, 0x2e, 0x62, 0x75,
-	0x69, 0x6c, 0x64, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x69, 0x6f, 0x6e, 0x2e, 0x53, 0x74,
-	0x61, 0x74, 0x75, 0x73, 0x48, 0x00, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x88, 0x01,
-	0x01, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x42, 0x1b, 0x5a, 0x19,
-	0x69, 0x64, 0x65, 0x5f, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2f, 0x69, 0x64, 0x65, 0x5f, 0x71, 0x75,
-	0x65, 0x72, 0x79, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f,
-	0x33,
+	0x6f, 0x12, 0x09, 0x69, 0x64, 0x65, 0x5f, 0x71, 0x75, 0x65, 0x72, 0x79, 0x22, 0x7c, 0x0a, 0x06,
+	0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x2a, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x01,
+	0x20, 0x01, 0x28, 0x0e, 0x32, 0x16, 0x2e, 0x69, 0x64, 0x65, 0x5f, 0x71, 0x75, 0x65, 0x72, 0x79,
+	0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x2e, 0x43, 0x6f, 0x64, 0x65, 0x52, 0x04, 0x63, 0x6f,
+	0x64, 0x65, 0x12, 0x1d, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20,
+	0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x88, 0x01,
+	0x01, 0x22, 0x1b, 0x0a, 0x04, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x06, 0x0a, 0x02, 0x4f, 0x4b, 0x10,
+	0x00, 0x12, 0x0b, 0x0a, 0x07, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x10, 0x01, 0x42, 0x0a,
+	0x0a, 0x08, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x8b, 0x01, 0x0a, 0x09, 0x52,
+	0x65, 0x70, 0x6f, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x72, 0x65, 0x70, 0x6f,
+	0x5f, 0x64, 0x69, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x72, 0x65, 0x70, 0x6f,
+	0x44, 0x69, 0x72, 0x12, 0x28, 0x0a, 0x10, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x66, 0x69,
+	0x6c, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0e, 0x61,
+	0x63, 0x74, 0x69, 0x76, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x50, 0x61, 0x74, 0x68, 0x12, 0x17, 0x0a,
+	0x07, 0x6f, 0x75, 0x74, 0x5f, 0x64, 0x69, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06,
+	0x6f, 0x75, 0x74, 0x44, 0x69, 0x72, 0x12, 0x20, 0x0a, 0x0c, 0x63, 0x6f, 0x6d, 0x70, 0x5f, 0x64,
+	0x62, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6f,
+	0x6d, 0x70, 0x44, 0x62, 0x50, 0x61, 0x74, 0x68, 0x22, 0x83, 0x02, 0x0a, 0x0c, 0x44, 0x65, 0x70,
+	0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x30, 0x0a, 0x04, 0x64, 0x65, 0x70,
+	0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x69, 0x64, 0x65, 0x5f, 0x71, 0x75,
+	0x65, 0x72, 0x79, 0x2e, 0x44, 0x65, 0x70, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
+	0x2e, 0x44, 0x65, 0x70, 0x73, 0x52, 0x04, 0x64, 0x65, 0x70, 0x73, 0x12, 0x2e, 0x0a, 0x06, 0x73,
+	0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x69, 0x64,
+	0x65, 0x5f, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x48, 0x00,
+	0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x88, 0x01, 0x01, 0x1a, 0x85, 0x01, 0x0a, 0x04,
+	0x44, 0x65, 0x70, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x66,
+	0x69, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x6f, 0x75, 0x72, 0x63,
+	0x65, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x74,
+	0x61, 0x72, 0x67, 0x65, 0x74, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0b, 0x62, 0x75, 0x69,
+	0x6c, 0x64, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x12, 0x2e, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74,
+	0x75, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x69, 0x64, 0x65, 0x5f, 0x71,
+	0x75, 0x65, 0x72, 0x79, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x48, 0x00, 0x52, 0x06, 0x73,
+	0x74, 0x61, 0x74, 0x75, 0x73, 0x88, 0x01, 0x01, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x73, 0x74, 0x61,
+	0x74, 0x75, 0x73, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x51,
+	0x0a, 0x0d, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x46, 0x69, 0x6c, 0x65, 0x12,
+	0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70,
+	0x61, 0x74, 0x68, 0x12, 0x1f, 0x0a, 0x08, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x18,
+	0x02, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x08, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74,
+	0x73, 0x88, 0x01, 0x01, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74,
+	0x73, 0x22, 0xf7, 0x01, 0x0a, 0x0a, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x46, 0x69, 0x6c, 0x65,
+	0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04,
+	0x70, 0x61, 0x74, 0x68, 0x12, 0x1f, 0x0a, 0x0b, 0x77, 0x6f, 0x72, 0x6b, 0x69, 0x6e, 0x67, 0x5f,
+	0x64, 0x69, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x77, 0x6f, 0x72, 0x6b, 0x69,
+	0x6e, 0x67, 0x44, 0x69, 0x72, 0x12, 0x2d, 0x0a, 0x12, 0x63, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65,
+	0x72, 0x5f, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28,
+	0x09, 0x52, 0x11, 0x63, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x72, 0x41, 0x72, 0x67, 0x75, 0x6d,
+	0x65, 0x6e, 0x74, 0x73, 0x12, 0x36, 0x0a, 0x09, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65,
+	0x64, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x69, 0x64, 0x65, 0x5f, 0x71, 0x75,
+	0x65, 0x72, 0x79, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x46, 0x69, 0x6c,
+	0x65, 0x52, 0x09, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x12, 0x12, 0x0a, 0x04,
+	0x64, 0x65, 0x70, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x64, 0x65, 0x70, 0x73,
+	0x12, 0x2e, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b,
+	0x32, 0x11, 0x2e, 0x69, 0x64, 0x65, 0x5f, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2e, 0x53, 0x74, 0x61,
+	0x74, 0x75, 0x73, 0x48, 0x00, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x88, 0x01, 0x01,
+	0x42, 0x09, 0x0a, 0x07, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0xa9, 0x01, 0x0a, 0x0b,
+	0x49, 0x64, 0x65, 0x41, 0x6e, 0x61, 0x6c, 0x79, 0x73, 0x69, 0x73, 0x12, 0x2e, 0x0a, 0x13, 0x62,
+	0x75, 0x69, 0x6c, 0x64, 0x5f, 0x61, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x5f, 0x72, 0x6f,
+	0x6f, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x41,
+	0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x2f, 0x0a, 0x07, 0x73,
+	0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x69,
+	0x64, 0x65, 0x5f, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2e, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x46,
+	0x69, 0x6c, 0x65, 0x52, 0x07, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x2e, 0x0a, 0x06,
+	0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x69,
+	0x64, 0x65, 0x5f, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x48,
+	0x00, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x88, 0x01, 0x01, 0x42, 0x09, 0x0a, 0x07,
+	0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x42, 0x1b, 0x5a, 0x19, 0x69, 0x64, 0x65, 0x5f, 0x71,
+	0x75, 0x65, 0x72, 0x79, 0x2f, 0x69, 0x64, 0x65, 0x5f, 0x71, 0x75, 0x65, 0x72, 0x79, 0x5f, 0x70,
+	0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
 }
 
 var (
@@ -420,25 +640,31 @@
 }
 
 var file_ide_query_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
-var file_ide_query_proto_msgTypes = make([]protoimpl.MessageInfo, 4)
+var file_ide_query_proto_msgTypes = make([]protoimpl.MessageInfo, 7)
 var file_ide_query_proto_goTypes = []interface{}{
-	(Status_Code)(0),      // 0: cider.build.companion.Status.Code
-	(*Status)(nil),        // 1: cider.build.companion.Status
-	(*GeneratedFile)(nil), // 2: cider.build.companion.GeneratedFile
-	(*SourceFile)(nil),    // 3: cider.build.companion.SourceFile
-	(*IdeAnalysis)(nil),   // 4: cider.build.companion.IdeAnalysis
+	(Status_Code)(0),          // 0: ide_query.Status.Code
+	(*Status)(nil),            // 1: ide_query.Status
+	(*RepoState)(nil),         // 2: ide_query.RepoState
+	(*DepsResponse)(nil),      // 3: ide_query.DepsResponse
+	(*GeneratedFile)(nil),     // 4: ide_query.GeneratedFile
+	(*SourceFile)(nil),        // 5: ide_query.SourceFile
+	(*IdeAnalysis)(nil),       // 6: ide_query.IdeAnalysis
+	(*DepsResponse_Deps)(nil), // 7: ide_query.DepsResponse.Deps
 }
 var file_ide_query_proto_depIdxs = []int32{
-	0, // 0: cider.build.companion.Status.code:type_name -> cider.build.companion.Status.Code
-	2, // 1: cider.build.companion.SourceFile.generated:type_name -> cider.build.companion.GeneratedFile
-	1, // 2: cider.build.companion.SourceFile.status:type_name -> cider.build.companion.Status
-	3, // 3: cider.build.companion.IdeAnalysis.sources:type_name -> cider.build.companion.SourceFile
-	1, // 4: cider.build.companion.IdeAnalysis.status:type_name -> cider.build.companion.Status
-	5, // [5:5] is the sub-list for method output_type
-	5, // [5:5] is the sub-list for method input_type
-	5, // [5:5] is the sub-list for extension type_name
-	5, // [5:5] is the sub-list for extension extendee
-	0, // [0:5] is the sub-list for field type_name
+	0, // 0: ide_query.Status.code:type_name -> ide_query.Status.Code
+	7, // 1: ide_query.DepsResponse.deps:type_name -> ide_query.DepsResponse.Deps
+	1, // 2: ide_query.DepsResponse.status:type_name -> ide_query.Status
+	4, // 3: ide_query.SourceFile.generated:type_name -> ide_query.GeneratedFile
+	1, // 4: ide_query.SourceFile.status:type_name -> ide_query.Status
+	5, // 5: ide_query.IdeAnalysis.sources:type_name -> ide_query.SourceFile
+	1, // 6: ide_query.IdeAnalysis.status:type_name -> ide_query.Status
+	1, // 7: ide_query.DepsResponse.Deps.status:type_name -> ide_query.Status
+	8, // [8:8] is the sub-list for method output_type
+	8, // [8:8] is the sub-list for method input_type
+	8, // [8:8] is the sub-list for extension type_name
+	8, // [8:8] is the sub-list for extension extendee
+	0, // [0:8] is the sub-list for field type_name
 }
 
 func init() { file_ide_query_proto_init() }
@@ -460,7 +686,7 @@
 			}
 		}
 		file_ide_query_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*GeneratedFile); i {
+			switch v := v.(*RepoState); i {
 			case 0:
 				return &v.state
 			case 1:
@@ -472,7 +698,7 @@
 			}
 		}
 		file_ide_query_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*SourceFile); i {
+			switch v := v.(*DepsResponse); i {
 			case 0:
 				return &v.state
 			case 1:
@@ -484,6 +710,30 @@
 			}
 		}
 		file_ide_query_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*GeneratedFile); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_ide_query_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*SourceFile); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_ide_query_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
 			switch v := v.(*IdeAnalysis); i {
 			case 0:
 				return &v.state
@@ -495,18 +745,32 @@
 				return nil
 			}
 		}
+		file_ide_query_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*DepsResponse_Deps); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
 	}
 	file_ide_query_proto_msgTypes[0].OneofWrappers = []interface{}{}
-	file_ide_query_proto_msgTypes[1].OneofWrappers = []interface{}{}
 	file_ide_query_proto_msgTypes[2].OneofWrappers = []interface{}{}
 	file_ide_query_proto_msgTypes[3].OneofWrappers = []interface{}{}
+	file_ide_query_proto_msgTypes[4].OneofWrappers = []interface{}{}
+	file_ide_query_proto_msgTypes[5].OneofWrappers = []interface{}{}
+	file_ide_query_proto_msgTypes[6].OneofWrappers = []interface{}{}
 	type x struct{}
 	out := protoimpl.TypeBuilder{
 		File: protoimpl.DescBuilder{
 			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
 			RawDescriptor: file_ide_query_proto_rawDesc,
 			NumEnums:      1,
-			NumMessages:   4,
+			NumMessages:   7,
 			NumExtensions: 0,
 			NumServices:   0,
 		},
diff --git a/tools/ide_query/ide_query_proto/ide_query.proto b/tools/ide_query/ide_query_proto/ide_query.proto
index 63eea39..3d7a8e7 100644
--- a/tools/ide_query/ide_query_proto/ide_query.proto
+++ b/tools/ide_query/ide_query_proto/ide_query.proto
@@ -1,3 +1,18 @@
+/*
+ * 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.
+ */
 syntax = "proto3";
 
 package ide_query;
@@ -14,6 +29,36 @@
   optional string message = 2;
 }
 
+// Represents an Android checkout on user's workstation.
+message RepoState {
+  // Absolute path for the checkout in the workstation.
+  // e.g. /home/user/work/android/
+  string repo_dir = 1;
+  // Relative to repo_dir.
+  repeated string active_file_path = 2;
+  // Repository relative path to output directory in workstation.
+  string out_dir = 3;
+  // Repository relative path to compile_commands.json in workstation.
+  string comp_db_path = 4;
+}
+
+// Provides all the targets that are pre-requisities for running language
+// services on active_file_paths.
+message DepsResponse {
+  // Build dependencies of a source file for providing language services.
+  message Deps {
+    // Relative to repo_dir.
+    string source_file = 1;
+    // Build target to execute for generating dep.
+    repeated string build_target = 2;
+    optional Status status = 3;
+  }
+  repeated Deps deps = 1;
+  optional Status status = 2;
+}
+
+// Returns all the information necessary for providing language services for the
+// active files.
 message GeneratedFile {
   // Path to the file relative to IdeAnalysis.build_artifact_root.
   string path = 1;
diff --git a/tools/protos/metadata_file.proto b/tools/protos/metadata_file.proto
index 47562c5..5c89618 100644
--- a/tools/protos/metadata_file.proto
+++ b/tools/protos/metadata_file.proto
@@ -282,7 +282,7 @@
   optional string element_id = 3;
 }
 
-// Identifier for a third-package package.
+// Identifier for a third-party package.
 // See go/tp-metadata-id.
 message Identifier {
   // The type of the identifier. Either an "ecosystem" value from
@@ -338,7 +338,19 @@
   //  - "PrebuiltByAlphabet": This type should be used for archives of primarily
   //  Google-owned source code (may contain non-Google-owned dependencies),
   //  which has been built using production Google infrastructure, and copied
-  //  into third_party.
+  //  into Android. The "value" field is the URL of the prebuilt artifact or
+  //  the relative path of the artifact to the root of a package.
+  //  Example:
+  //    identifier {
+  //      type: "PrebuiltByAlphabet",
+  //      version: "1",
+  //      value: "v1/arm84_hdpi.apk",
+  //    }
+  //    identifier {
+  //      type: "PrebuiltByAlphabet",
+  //      version: "2",
+  //      value: "v2/x86_64_xhdpi.apk",
+  //    }
   //
   //  - "LocalSource": The "value" field is the URL identifying where the local
   //  copy of the package source code can be found.
diff --git a/tools/releasetools/build_image.py b/tools/releasetools/build_image.py
index a7b1d8c..464ad9b 100755
--- a/tools/releasetools/build_image.py
+++ b/tools/releasetools/build_image.py
@@ -294,7 +294,7 @@
     build_command = [prop_dict["ext_mkuserimg"]]
     if "extfs_sparse_flag" in prop_dict and not disable_sparse:
       build_command.append(prop_dict["extfs_sparse_flag"])
-      run_e2fsck = RunE2fsck
+      run_fsck = RunE2fsck
     build_command.extend([in_dir, out_file, fs_type,
                           prop_dict["mount_point"]])
     build_command.append(prop_dict["image_size"])
diff --git a/tools/releasetools/check_target_files_vintf.py b/tools/releasetools/check_target_files_vintf.py
index d31f87e..b8dcd84 100755
--- a/tools/releasetools/check_target_files_vintf.py
+++ b/tools/releasetools/check_target_files_vintf.py
@@ -215,7 +215,7 @@
 
   This simulates how apexd activates APEXes.
   1. create {inp}/APEX which is treated as a "/apex" on device.
-  2. invoke apexd_host with vendor APEXes.
+  2. invoke apexd_host with APEXes.
   """
 
   apex_dir = common.MakeTempDir('APEX')
@@ -225,12 +225,13 @@
   # Always create /apex directory for dirmap
   os.makedirs(apex_dir, exist_ok=True)
 
-  # Invoke apexd_host to activate vendor APEXes for checkvintf
+  # Invoke apexd_host to activate APEXes for checkvintf
   apex_host = os.path.join(OPTIONS.search_path, 'bin', 'apexd_host')
   cmd = [apex_host, '--tool_path', OPTIONS.search_path]
   cmd += ['--apex_path', dirmap['/apex']]
-  if '/vendor' in dirmap:
-      cmd += ['--vendor_path', dirmap['/vendor']]
+  for p in ['system', 'system_ext', 'product', 'vendor']:
+    if '/' + p in dirmap:
+      cmd += ['--' + p + '_path', dirmap['/' + p]]
   common.RunAndCheckOutput(cmd)
 
 
diff --git a/tools/releasetools/common.py b/tools/releasetools/common.py
index 8a8a613..8836248 100644
--- a/tools/releasetools/common.py
+++ b/tools/releasetools/common.py
@@ -1313,7 +1313,11 @@
     key = "super_%s_partition_list" % partition_group
     merged_dict[key] = uniq_concat(
         framework_dict.get(key, ""), vendor_dict.get(key, ""))
-
+  # in the case that vendor is on s build, but is taking a v3 -> v3 vabc ota, we want to fallback to v2
+  if "vabc_cow_version" not in vendor_dict or "vabc_cow_version" not in framework_dict:
+    merged_dict["vabc_cow_version"] = '2'
+  else:
+    merged_dict["vabc_cow_version"] = min(vendor_dict["vabc_cow_version"], framework_dict["vabc_cow_version"])
   # Various other flags should be copied from the vendor dict, if defined.
   for key in ("virtual_ab", "virtual_ab_retrofit", "lpmake",
               "super_metadata_device", "super_partition_error_limit",
@@ -2456,7 +2460,7 @@
     m = re.match(r'(?:minSdkVersion|sdkVersion):\'([^\']*)\'', line)
     if m:
       return m.group(1)
-  raise ExternalError("No minSdkVersion returned by aapt2")
+  raise ExternalError("No minSdkVersion returned by aapt2 for apk: {}".format(apk_name))
 
 
 def GetMinSdkVersionInt(apk_name, codename_to_api_level_map):
diff --git a/tools/releasetools/create_brick_ota.py b/tools/releasetools/create_brick_ota.py
index f290323..9e040a5 100644
--- a/tools/releasetools/create_brick_ota.py
+++ b/tools/releasetools/create_brick_ota.py
@@ -45,13 +45,17 @@
   partitions_to_wipe = PARTITIONS_TO_WIPE
   if extra_wipe_partitions is not None:
     partitions_to_wipe = PARTITIONS_TO_WIPE + extra_wipe_partitions.split(",")
+    ota_metadata = ["ota-type=BRICK", "post-timestamp=9999999999",
+                    "pre-device=" + product_name]
+    if serialno is not None:
+        ota_metadata.append("serialno=" + serialno)
   # recovery requiers product name to be a | separated list
   product_name = product_name.replace(",", "|")
   with zipfile.ZipFile(output_path, "w") as zfp:
     zfp.writestr("recovery.wipe", "\n".join(partitions_to_wipe))
     zfp.writestr("payload.bin", "")
     zfp.writestr("META-INF/com/android/metadata", "\n".join(
-        ["ota-type=BRICK", "post-timestamp=9999999999", "pre-device=" + product_name, "serialno=" + serialno]))
+        ota_metadata))
 
 
 def main(argv):
diff --git a/tools/releasetools/ota_from_target_files.py b/tools/releasetools/ota_from_target_files.py
index c0ff5d2..67438e6 100755
--- a/tools/releasetools/ota_from_target_files.py
+++ b/tools/releasetools/ota_from_target_files.py
@@ -872,6 +872,7 @@
   target_info = common.BuildInfo(OPTIONS.info_dict, OPTIONS.oem_dicts)
   if OPTIONS.disable_vabc and target_info.is_release_key:
     raise ValueError("Disabling VABC on release-key builds is not supported.")
+
   ValidateCompressionParam(target_info)
   vabc_compression_param = target_info.vabc_compression_param
 
@@ -907,7 +908,6 @@
       logger.info("Source build and target build use different compression methods {} vs {}, default to source builds parameter {}".format(
           source_info.vabc_compression_param, target_info.vabc_compression_param, source_info.vabc_compression_param))
       vabc_compression_param = source_info.vabc_compression_param
-
     # Virtual AB Cow version 3 is introduced in Android U with improved memory
     # and install time performance. All OTA's with
     # both the source build and target build with VIRTUAL_AB_COW_VERSION = 3
@@ -918,6 +918,7 @@
     elif source_info.vabc_cow_version != target_info.vabc_cow_version:
       logger.info("Source and Target have different cow VABC_COW_VERSION specified, default to minimum version")
       OPTIONS.vabc_cow_version = min(source_info.vabc_cow_version, target_info.vabc_cow_version)
+
     # Virtual AB Compression was introduced in Androd S.
     # Later, we backported VABC to Android R. But verity support was not
     # backported, so if VABC is used and we are on Android R, disable
@@ -930,6 +931,19 @@
     assert "ab_partitions" in OPTIONS.info_dict, \
         "META/ab_partitions.txt is required for ab_update."
     source_info = None
+    if not target_info.vabc_cow_version:
+        OPTIONS.vabc_cow_version = 2
+    elif target_info.vabc_cow_version >= "3" and target_info.vendor_api_level < 35:
+      logger.warning(
+            "This full OTA is configured to use VABC cow version"
+            " 3 which is supported since"
+            " Android API level 35, but device is "
+            "launched with {} . If this full OTA is"
+            " served to a device running old build, OTA might fail due to "
+            "unsupported vabc cow version. For safety, version 2 is used because "
+            "it's supported since day 1.".format(
+                target_info.vendor_api_level))
+      OPTIONS.vabc_cow_version = 2
     if OPTIONS.vabc_compression_param is None and vabc_compression_param:
       minimum_api_level_required = VABC_COMPRESSION_PARAM_SUPPORT[
           vabc_compression_param]
diff --git a/tools/releasetools/sign_target_files_apks.py b/tools/releasetools/sign_target_files_apks.py
index bf69dec..b8f848f 100755
--- a/tools/releasetools/sign_target_files_apks.py
+++ b/tools/releasetools/sign_target_files_apks.py
@@ -271,6 +271,10 @@
 
 def IsEntryOtaPackage(input_zip, filename):
   with input_zip.open(filename, "r") as fp:
+    external_attr = input_zip.getinfo(filename).external_attr
+    if stat.S_ISLNK(external_attr >> 16):
+      return IsEntryOtaPackage(input_zip,
+          os.path.join(os.path.dirname(filename), fp.read().decode()))
     return IsOtaPackage(fp)
 
 
@@ -795,6 +799,16 @@
         # Copy it verbatim if we allow the file to exist.
         common.ZipWriteStr(output_tf_zip, out_info, data)
 
+    # Sign microdroid_vendor.img.
+    elif filename == "VENDOR/etc/avf/microdroid/microdroid_vendor.img":
+      vendor_key = OPTIONS.avb_keys.get("vendor")
+      vendor_algorithm = OPTIONS.avb_algorithms.get("vendor")
+      with tempfile.NamedTemporaryFile() as image:
+        image.write(data)
+        image.flush()
+        ReplaceKeyInAvbHashtreeFooter(image, vendor_key, vendor_algorithm,
+            misc_info)
+        common.ZipWrite(output_tf_zip, image.name, filename)
     # A non-APK file; copy it verbatim.
     else:
       common.ZipWriteStr(output_tf_zip, out_info, data)
@@ -812,6 +826,108 @@
   # Write back misc_info with the latest values.
   ReplaceMiscInfoTxt(input_tf_zip, output_tf_zip, misc_info)
 
+# Parse string output of `avbtool info_image`.
+def ParseAvbInfo(info_raw):
+  # line_matcher is for parsing each output line of `avbtool info_image`.
+  # example string input: "      Hash Algorithm:        sha1"
+  # example matched input: ("      ", "Hash Algorithm", "sha1")
+  line_matcher = re.compile(r'^(\s*)([^:]+):\s*(.*)$')
+  # prop_matcher is for parsing value part of 'Prop' in `avbtool info_image`.
+  # example string input: "example_prop_key -> 'example_prop_value'"
+  # example matched output: ("example_prop_key", "example_prop_value")
+  prop_matcher = re.compile(r"(.+)\s->\s'(.+)'")
+  info = {}
+  indent_stack = [[-1, info]]
+  for line_info_raw in info_raw.split('\n'):
+    # Parse the line
+    line_info_parsed = line_matcher.match(line_info_raw)
+    if not line_info_parsed:
+      continue
+    indent = len(line_info_parsed.group(1))
+    key = line_info_parsed.group(2).strip()
+    value = line_info_parsed.group(3).strip()
+
+    # Pop indentation stack
+    while indent <= indent_stack[-1][0]:
+      del indent_stack[-1]
+
+    # Insert information into 'info'.
+    cur_info = indent_stack[-1][1]
+    if value == "":
+      if key == "Descriptors":
+        empty_list = []
+        cur_info[key] = empty_list
+        indent_stack.append([indent, empty_list])
+      else:
+        empty_dict = {}
+        cur_info.append({key:empty_dict})
+        indent_stack.append([indent, empty_dict])
+    elif key == "Prop":
+      prop_parsed = prop_matcher.match(value)
+      if not prop_parsed:
+        raise ValueError(
+            "Failed to parse prop while getting avb information.")
+      cur_info.append({key:{prop_parsed.group(1):prop_parsed.group(2)}})
+    else:
+      cur_info[key] = value
+  return info
+
+def ReplaceKeyInAvbHashtreeFooter(image, new_key, new_algorithm, misc_info):
+  # Get avb information about the image by parsing avbtool info_image.
+  def GetAvbInfo(avbtool, image_name):
+    # Get information with raw string by `avbtool info_image`.
+    info_raw = common.RunAndCheckOutput([
+      avbtool, 'info_image',
+      '--image', image_name
+    ])
+    return ParseAvbInfo(info_raw)
+
+  # Get hashtree descriptor from info
+  def GetAvbHashtreeDescriptor(avb_info):
+    hashtree_descriptors = tuple(filter(lambda x: "Hashtree descriptor" in x,
+        info.get('Descriptors')))
+    if len(hashtree_descriptors) != 1:
+      raise ValueError("The number of hashtree descriptor is not 1.")
+    return hashtree_descriptors[0]["Hashtree descriptor"]
+
+  # Get avb info
+  avbtool = misc_info['avb_avbtool']
+  info = GetAvbInfo(avbtool, image.name)
+  hashtree_descriptor = GetAvbHashtreeDescriptor(info)
+
+  # Generate command
+  cmd = [avbtool, 'add_hashtree_footer',
+    '--key', new_key,
+    '--algorithm', new_algorithm,
+    '--partition_name', hashtree_descriptor.get("Partition Name"),
+    '--partition_size', info.get("Image size").removesuffix(" bytes"),
+    '--hash_algorithm', hashtree_descriptor.get("Hash Algorithm"),
+    '--salt', hashtree_descriptor.get("Salt"),
+    '--do_not_generate_fec',
+    '--image', image.name
+  ]
+
+  # Append properties into command
+  props = map(lambda x: x.get("Prop"), filter(lambda x: "Prop" in x,
+      info.get('Descriptors')))
+  for prop_wrapped in props:
+    prop = tuple(prop_wrapped.items())
+    if len(prop) != 1:
+      raise ValueError("The number of property is not 1.")
+    cmd.append('--prop')
+    cmd.append(prop[0][0] + ':' + prop[0][1])
+
+  # Replace Hashtree Footer with new key
+  common.RunAndCheckOutput(cmd)
+
+  # Check root digest is not changed
+  new_info = GetAvbInfo(avbtool, image.name)
+  new_hashtree_descriptor = GetAvbHashtreeDescriptor(info)
+  root_digest = hashtree_descriptor.get("Root Digest")
+  new_root_digest = new_hashtree_descriptor.get("Root Digest")
+  assert root_digest == new_root_digest, \
+      ("Root digest in hashtree descriptor shouldn't be changed. Old: {}, New: "
+       "{}").format(root_digest, new_root_digest)
 
 def ReplaceCerts(data):
   """Replaces all the occurences of X.509 certs with the new ones.
diff --git a/tools/releasetools/test_common.py b/tools/releasetools/test_common.py
index 2989338..89933a0 100644
--- a/tools/releasetools/test_common.py
+++ b/tools/releasetools/test_common.py
@@ -1515,6 +1515,7 @@
         'super_group_a_group_size': '1000',
         'super_group_b_partition_list': 'product',
         'super_group_b_group_size': '2000',
+        'vabc_cow_version': '2',
     }
     self.assertEqual(merged_dict, expected_merged_dict)
 
@@ -1525,6 +1526,7 @@
         'dynamic_partition_list': 'system',
         'super_group_a_partition_list': 'system',
         'super_group_a_group_size': '5000',
+        'vabc_cow_version': '3',
     }
     vendor_dict = {
         'use_dynamic_partitions': 'true',
@@ -1546,6 +1548,7 @@
         'super_group_a_group_size': '1000',
         'super_group_b_partition_list': 'product',
         'super_group_b_group_size': '2000',
+        'vabc_cow_version': '2',
     }
     self.assertEqual(merged_dict, expected_merged_dict)
 
diff --git a/tools/releasetools/test_sign_target_files_apks.py b/tools/releasetools/test_sign_target_files_apks.py
index 9cc6df4..7ac1cff 100644
--- a/tools/releasetools/test_sign_target_files_apks.py
+++ b/tools/releasetools/test_sign_target_files_apks.py
@@ -22,8 +22,9 @@
 import common
 import test_utils
 from sign_target_files_apks import (
-    CheckApkAndApexKeysAvailable, EditTags, GetApkFileInfo, ReadApexKeysInfo,
-    ReplaceCerts, RewriteAvbProps, RewriteProps, WriteOtacerts)
+    CheckApkAndApexKeysAvailable, EditTags, GetApkFileInfo, ParseAvbInfo,
+    ReadApexKeysInfo, ReplaceCerts, RewriteAvbProps, RewriteProps,
+    WriteOtacerts)
 
 
 class SignTargetFilesApksTest(test_utils.ReleaseToolsTestCase):
@@ -535,3 +536,86 @@
             'system/apex/apexd/apexd_testdata/com.android.apex.test_package_2.pem',
             'build/make/target/product/security/testkey', None),
         }, keys_info)
+
+  def test_ParseAvbInfo(self):
+    avb_info_string = """
+    Footer version:           1.0
+    Image size:               9999999 bytes
+    Original image size:      8888888 bytes
+    VBMeta offset:            7777777
+    VBMeta size:              1111 bytes
+    --
+    Minimum libavb version:   1.0
+    Header Block:             222 bytes
+    Authentication Block:     333 bytes
+    Auxiliary Block:          888 bytes
+    Public key (sha1):        abababababababababababababababababababab
+    Algorithm:                SHA256_RSA2048
+    Rollback Index:           0
+    Flags:                    0
+    Rollback Index Location:  0
+    Release String:           'avbtool 1.3.0'
+    Descriptors:
+        Hashtree descriptor:
+          Version of dm-verity:  1
+          Image Size:            8888888 bytes
+          Tree Offset:           8888888
+          Tree Size:             44444 bytes
+          Data Block Size:       4444 bytes
+          Hash Block Size:       4444 bytes
+          FEC num roots:         0
+          FEC offset:            0
+          FEC size:              0 bytes
+          Hash Algorithm:        sha1
+          Partition Name:        partition-name
+          Salt:                  cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd
+          Root Digest:           efefefefefefefefefefefefefefefefefef
+          Flags:                 0
+        Prop: prop.key -> 'prop.value'
+    """
+
+    self.assertEqual(
+        {
+            'Footer version': '1.0',
+            'Image size': '9999999 bytes',
+            'Original image size': '8888888 bytes',
+            'VBMeta offset': '7777777',
+            'VBMeta size': '1111 bytes',
+            'Minimum libavb version': '1.0',
+            'Header Block': '222 bytes',
+            'Authentication Block': '333 bytes',
+            'Auxiliary Block': '888 bytes',
+            'Public key (sha1)': 'abababababababababababababababababababab',
+            'Algorithm': 'SHA256_RSA2048',
+            'Rollback Index': '0',
+            'Flags': '0',
+            'Rollback Index Location': '0',
+            'Release String': "'avbtool 1.3.0'",
+            'Descriptors': [
+                {
+                    'Hashtree descriptor': {
+                        'Version of dm-verity': '1',
+                        'Image Size': '8888888 bytes',
+                        'Tree Offset': '8888888',
+                        'Tree Size': '44444 bytes',
+                        'Data Block Size': '4444 bytes',
+                        'Hash Block Size': '4444 bytes',
+                        'FEC num roots': '0',
+                        'FEC offset': '0',
+                        'FEC size': '0 bytes',
+                        'Hash Algorithm': 'sha1',
+                        'Partition Name': 'partition-name',
+                        'Salt': 'cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd',
+                        'Root Digest': 'efefefefefefefefefefefefefefefefefef',
+                        'Flags': '0',
+                    }
+                },
+                {
+                    'Prop': {
+                        'prop.key': 'prop.value',
+                    }
+                },
+            ],
+        },
+        ParseAvbInfo(avb_info_string),
+    )
\ No newline at end of file
diff --git a/tools/signapk/src/com/android/signapk/SignApk.java b/tools/signapk/src/com/android/signapk/SignApk.java
index 2f2b833..6b2341b 100644
--- a/tools/signapk/src/com/android/signapk/SignApk.java
+++ b/tools/signapk/src/com/android/signapk/SignApk.java
@@ -986,15 +986,17 @@
     }
 
     private static class ZipSections {
-        ByteBuffer beforeCentralDir;
+        DataSource beforeCentralDir;
+
+        // The following fields are still valid after closing the backing DataSource.
+        long beforeCentralDirSize;
         ByteBuffer centralDir;
         ByteBuffer eocd;
     }
 
-    private static ZipSections findMainZipSections(ByteBuffer apk)
+    private static ZipSections findMainZipSections(DataSource apk)
             throws IOException, ZipFormatException {
-        apk.slice();
-        ApkUtils.ZipSections sections = ApkUtils.findZipSections(DataSources.asDataSource(apk));
+        ApkUtils.ZipSections sections = ApkUtils.findZipSections(apk);
         long centralDirStartOffset = sections.getZipCentralDirectoryOffset();
         long centralDirSizeBytes = sections.getZipCentralDirectorySizeBytes();
         long centralDirEndOffset = centralDirStartOffset + centralDirSizeBytes;
@@ -1005,25 +1007,20 @@
                             + ". CD end: " + centralDirEndOffset
                             + ", EoCD start: " + eocdStartOffset);
         }
-        apk.position(0);
-        apk.limit((int) centralDirStartOffset);
-        ByteBuffer beforeCentralDir = apk.slice();
-
-        apk.position((int) centralDirStartOffset);
-        apk.limit((int) centralDirEndOffset);
-        ByteBuffer centralDir = apk.slice();
-
-        apk.position((int) eocdStartOffset);
-        apk.limit(apk.capacity());
-        ByteBuffer eocd = apk.slice();
-
-        apk.position(0);
-        apk.limit(apk.capacity());
 
         ZipSections result = new ZipSections();
-        result.beforeCentralDir = beforeCentralDir;
-        result.centralDir = centralDir;
-        result.eocd = eocd;
+
+        result.beforeCentralDir = apk.slice(0, centralDirStartOffset);
+        result.beforeCentralDirSize = result.beforeCentralDir.size();
+
+        long centralDirSize = centralDirEndOffset - centralDirStartOffset;
+        if (centralDirSize >= Integer.MAX_VALUE) throw new IndexOutOfBoundsException();
+        result.centralDir = apk.getByteBuffer(centralDirStartOffset, (int)centralDirSize);
+
+        long eocdSize = apk.size() - eocdStartOffset;
+        if (eocdSize >= Integer.MAX_VALUE) throw new IndexOutOfBoundsException();
+        result.eocd = apk.getByteBuffer(eocdStartOffset, (int)eocdSize);
+
         return result;
     }
 
@@ -1278,11 +1275,8 @@
                     // signatures)
                     apkSigner.inputApkSigningBlock(null);
 
-                    // Build the output APK in memory, by copying input APK's ZIP entries across
-                    // and then signing the output APK.
-                    ByteArrayOutputStream v1SignedApkBuf = new ByteArrayOutputStream();
                     CountingOutputStream outputJarCounter =
-                            new CountingOutputStream(v1SignedApkBuf);
+                            new CountingOutputStream(outputFile);
                     JarOutputStream outputJar = new JarOutputStream(outputJarCounter);
                     // Use maximum compression for compressed entries because the APK lives forever
                     // on the system partition.
@@ -1295,24 +1289,31 @@
                         addV1Signature(apkSigner, addV1SignatureRequest, outputJar, timestamp);
                         addV1SignatureRequest.done();
                     }
-                    outputJar.close();
-                    ByteBuffer v1SignedApk = ByteBuffer.wrap(v1SignedApkBuf.toByteArray());
-                    v1SignedApkBuf.reset();
-                    ByteBuffer[] outputChunks = new ByteBuffer[] {v1SignedApk};
 
-                    ZipSections zipSections = findMainZipSections(v1SignedApk);
+                    // close output and switch to input mode
+                    outputJar.close();
+                    outputJar = null;
+                    outputJarCounter = null;
+                    outputFile = null;
+                    RandomAccessFile v1SignedApk = new RandomAccessFile(outputFilename, "r");
+
+                    ZipSections zipSections = findMainZipSections(DataSources.asDataSource(
+                            v1SignedApk));
 
                     ByteBuffer eocd = ByteBuffer.allocate(zipSections.eocd.remaining());
                     eocd.put(zipSections.eocd);
                     eocd.flip();
                     eocd.order(ByteOrder.LITTLE_ENDIAN);
+
+                    ByteBuffer[] outputChunks = new ByteBuffer[] {};
+
                     // This loop is supposed to be iterated twice at most.
                     // The second pass is to align the file size after amending EOCD comments
                     // with assumption that re-generated signing block would be the same size.
                     while (true) {
                         ApkSignerEngine.OutputApkSigningBlockRequest2 addV2SignatureRequest =
                                 apkSigner.outputZipSections2(
-                                        DataSources.asDataSource(zipSections.beforeCentralDir),
+                                        zipSections.beforeCentralDir,
                                         DataSources.asDataSource(zipSections.centralDir),
                                         DataSources.asDataSource(eocd));
                         if (addV2SignatureRequest == null) break;
@@ -1330,11 +1331,10 @@
                         modifiedEocd.order(ByteOrder.LITTLE_ENDIAN);
                         ApkUtils.setZipEocdCentralDirectoryOffset(
                                 modifiedEocd,
-                                zipSections.beforeCentralDir.remaining() + padding +
+                                zipSections.beforeCentralDir.size() + padding +
                                 apkSigningBlock.length);
                         outputChunks =
                                 new ByteBuffer[] {
-                                        zipSections.beforeCentralDir,
                                         ByteBuffer.allocate(padding),
                                         ByteBuffer.wrap(apkSigningBlock),
                                         zipSections.centralDir,
@@ -1348,7 +1348,7 @@
 
                         // Calculate the file size
                         eocd = modifiedEocd;
-                        int fileSize = 0;
+                        long fileSize = zipSections.beforeCentralDirSize;
                         for (ByteBuffer buf : outputChunks) {
                             fileSize += buf.remaining();
                         }
@@ -1357,7 +1357,7 @@
                             break;
                         }
                         // Pad EOCD comment to align the file size.
-                        int commentLen = alignment - fileSize % alignment;
+                        int commentLen = alignment - (int)(fileSize % alignment);
                         modifiedEocd = ByteBuffer.allocate(eocd.remaining() + commentLen);
                         modifiedEocd.put(eocd);
                         modifiedEocd.rewind();
@@ -1368,6 +1368,12 @@
                         eocd = modifiedEocd;
                     }
 
+                    // close input and switch back to output mode
+                    v1SignedApk.close();
+                    v1SignedApk = null;
+                    outputFile = new FileOutputStream(outputFilename, true);
+                    outputFile.getChannel().truncate(zipSections.beforeCentralDirSize);
+
                     // This assumes outputChunks are array-backed. To avoid this assumption, the
                     // code could be rewritten to use FileChannel.
                     for (ByteBuffer outputChunk : outputChunks) {