Merge "Move frameworks/base/Android.mk to build/core" into main
diff --git a/core/Makefile b/core/Makefile
index f5672ef..9d77ec1 100644
--- a/core/Makefile
+++ b/core/Makefile
@@ -3448,14 +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 $@
-
 # $(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 :=
@@ -5112,15 +5146,17 @@
 # -- 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 :=
 
@@ -5129,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))
diff --git a/core/config.mk b/core/config.mk
index 546858a..c9f752d 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
@@ -690,7 +691,6 @@
 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 +885,7 @@
     32.0 \
     33.0 \
     34.0 \
+    202404 \
     )
 
 .KATI_READONLY := \
diff --git a/core/release_config.mk b/core/release_config.mk
index 8d19bc7..a6152bd 100644
--- a/core/release_config.mk
+++ b/core/release_config.mk
@@ -41,7 +41,9 @@
 # which has OWNERS control.  If it isn't let others define their own.
 # TODO: Remove wildcard for build/release one when all branch manifests
 # have updated.
-config_map_files := $(wildcard build/release/release_config_map.mk) \
+config_map_files := $(wildcard build/trunk_release/release_config_map.mk) \
+    $(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 \
diff --git a/core/soong_config.mk b/core/soong_config.mk
index b43a952..d4c56e5 100644
--- a/core/soong_config.mk
+++ b/core/soong_config.mk
@@ -404,6 +404,8 @@
 
 $(call add_json_bool, ExportRuntimeApis, $(filter true,$(PRODUCT_EXPORT_RUNTIME_APIS)))
 
+$(call add_json_str, AconfigContainerValidation, $(ACONFIG_CONTAINER_VALIDATION))
+
 $(call json_end)
 
 $(file >$(SOONG_VARIABLES).tmp,$(json_contents))
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 3d057e2..cd45e2a 100644
--- a/target/product/base_system.mk
+++ b/target/product/base_system.mk
@@ -374,14 +374,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)
@@ -424,13 +420,12 @@
     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
@@ -495,5 +490,8 @@
 
 $(call inherit-product, $(SRC_TARGET_DIR)/product/runtime_libart.mk)
 
+# Use the configured release of sqlite
+$(call soong_config_set, libsqlite3, release_package_libsqlite3, $(RELEASE_PACKAGE_LIBSQLITE3))
+
 # Use "image" APEXes always.
 $(call inherit-product,$(SRC_TARGET_DIR)/product/updatable_apex.mk)
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/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/sdk.mk b/target/product/sdk.mk
index 650f8e9..009a9d4 100644
--- a/target/product/sdk.mk
+++ b/target/product/sdk.mk
@@ -29,4 +29,8 @@
 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
+
+ifeq ($(WITHOUT_CHECK_API),true)
+  $(error WITHOUT_CHECK_API cannot be set to true for SDK product builds)
+endif
diff --git a/tools/aconfig/TEST_MAPPING b/tools/aconfig/TEST_MAPPING
index 26d6172..0ea8fea 100644
--- a/tools/aconfig/TEST_MAPPING
+++ b/tools/aconfig/TEST_MAPPING
@@ -70,10 +70,22 @@
   ],
   "postsubmit": [
     {
+      // 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 write api cpp integration tests
+      "name": "aconfig_storage_write_api.test.cpp"
+    },
+    {
       // aconfig_storage read api rust integration tests
       "name": "aconfig_storage_read_api.test.rust"
     },
diff --git a/tools/aconfig/aconfig/src/codegen/java.rs b/tools/aconfig/aconfig/src/codegen/java.rs
index a18f9a8..fab2fa3 100644
--- a/tools/aconfig/aconfig/src/codegen/java.rs
+++ b/tools/aconfig/aconfig/src/codegen/java.rs
@@ -351,6 +351,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 +362,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),
@@ -558,8 +558,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 */
@@ -569,15 +567,12 @@
             /** @hide */
             public static final String FLAG_ENABLED_RO_EXPORTED = "com.android.aconfig.test.enabled_ro_exported";
 
-            @UnsupportedAppUsage
             public static boolean disabledRwExported() {
                 return FEATURE_FLAGS.disabledRwExported();
             }
-            @UnsupportedAppUsage
             public static boolean enabledFixedRoExported() {
                 return FEATURE_FLAGS.enabledFixedRoExported();
             }
-            @UnsupportedAppUsage
             public static boolean enabledRoExported() {
                 return FEATURE_FLAGS.enabledRoExported();
             }
@@ -587,23 +582,16 @@
 
         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
             boolean disabledRwExported();
-            @UnsupportedAppUsage
             boolean enabledFixedRoExported();
-            @UnsupportedAppUsage
             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 */
@@ -637,7 +625,6 @@
             }
 
             @Override
-            @UnsupportedAppUsage
             public boolean disabledRwExported() {
                 if (!aconfig_test_is_cached) {
                     load_overrides_aconfig_test();
@@ -646,7 +633,6 @@
             }
 
             @Override
-            @UnsupportedAppUsage
             public boolean enabledFixedRoExported() {
                 if (!aconfig_test_is_cached) {
                     load_overrides_aconfig_test();
@@ -655,7 +641,6 @@
             }
 
             @Override
-            @UnsupportedAppUsage
             public boolean enabledRoExported() {
                 if (!aconfig_test_is_cached) {
                     load_overrides_aconfig_test();
@@ -666,8 +651,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 +662,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 +684,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 +691,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),
@@ -1065,6 +1034,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 +1045,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 c1df16b..98dde44 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,
@@ -237,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()
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..b861c1f 100644
--- a/tools/aconfig/aconfig/src/storage/flag_table.rs
+++ b/tools/aconfig/aconfig/src/storage/flag_table.rs
@@ -17,7 +17,7 @@
 use crate::commands::assign_flag_ids;
 use crate::storage::FlagPackage;
 use aconfig_storage_file::{
-    get_table_size, FlagTable, FlagTableHeader, FlagTableNode, FILE_VERSION,
+    get_table_size, FlagTable, FlagTableHeader, FlagTableNode, FILE_VERSION, StorageFileType
 };
 use anyhow::{anyhow, Result};
 
@@ -25,6 +25,7 @@
     FlagTableHeader {
         version: FILE_VERSION,
         container: String::from(container),
+        file_type: StorageFileType::FlagMap as u8,
         file_size: 0,
         num_flags,
         bucket_offset: 0,
@@ -168,31 +169,32 @@
         let expected_header = FlagTableHeader {
             version: FILE_VERSION,
             container: String::from("system"),
-            file_size: 320,
+            file_type: StorageFileType::FlagMap as u8,
+            file_size: 321,
             num_flags: 8,
-            bucket_offset: 30,
-            node_offset: 98,
+            bucket_offset: 31,
+            node_offset: 99,
         };
         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),
+            Some(99),
+            Some(125),
             None,
             None,
             None,
-            Some(177),
+            Some(178),
             None,
-            Some(203),
+            Some(204),
             None,
-            Some(261),
+            Some(262),
             None,
             None,
             None,
             None,
             None,
-            Some(293),
+            Some(294),
             None,
         ];
         assert_eq!(buckets, &expected_bucket);
@@ -201,10 +203,10 @@
         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[1], new_expected_node(0, "enabled_rw", 1, 2, Some(151)));
         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[4], new_expected_node(1, "enabled_fixed_ro", 1, 1, Some(236)));
         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));
diff --git a/tools/aconfig/aconfig/src/storage/flag_value.rs b/tools/aconfig/aconfig/src/storage/flag_value.rs
index 0d4b5b4..e40bbc1 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, FILE_VERSION, StorageFileType};
 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,
@@ -79,9 +80,10 @@
         let expected_header = FlagValueHeader {
             version: FILE_VERSION,
             container: String::from("system"),
-            file_size: 34,
+            file_type: StorageFileType::FlagVal as u8,
+            file_size: 35,
             num_flags: 8,
-            boolean_value_offset: 26,
+            boolean_value_offset: 27,
         };
         assert_eq!(header, &expected_header);
 
diff --git a/tools/aconfig/aconfig/src/storage/mod.rs b/tools/aconfig/aconfig/src/storage/mod.rs
index 29eb9c8..c818d79 100644
--- a/tools/aconfig/aconfig/src/storage/mod.rs
+++ b/tools/aconfig/aconfig/src/storage/mod.rs
@@ -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,15 +95,15 @@
     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())
         }
-        StorageFileSelection::FlagMap => {
+        StorageFileType::FlagMap => {
             let flag_table = create_flag_table(container, &packages)?;
             Ok(flag_table.as_bytes())
         }
-        StorageFileSelection::FlagVal => {
+        StorageFileType::FlagVal => {
             let flag_value = create_flag_value(container, &packages)?;
             Ok(flag_value.as_bytes())
         }
diff --git a/tools/aconfig/aconfig/src/storage/package_table.rs b/tools/aconfig/aconfig/src/storage/package_table.rs
index 4c08129..bc2da4d 100644
--- a/tools/aconfig/aconfig/src/storage/package_table.rs
+++ b/tools/aconfig/aconfig/src/storage/package_table.rs
@@ -17,7 +17,7 @@
 use anyhow::Result;
 
 use aconfig_storage_file::{
-    get_table_size, PackageTable, PackageTableHeader, PackageTableNode, FILE_VERSION,
+    get_table_size, PackageTable, PackageTableHeader, PackageTableNode, FILE_VERSION, StorageFileType
 };
 
 use crate::storage::FlagPackage;
@@ -26,6 +26,7 @@
     PackageTableHeader {
         version: FILE_VERSION,
         container: String::from(container),
+        file_type: StorageFileType::PackageMap as u8,
         file_size: 0,
         num_packages,
         bucket_offset: 0,
@@ -123,15 +124,16 @@
         let expected_header = PackageTableHeader {
             version: FILE_VERSION,
             container: String::from("system"),
-            file_size: 208,
+            file_type: StorageFileType::PackageMap as u8,
+            file_size: 209,
             num_packages: 3,
-            bucket_offset: 30,
-            node_offset: 58,
+            bucket_offset: 31,
+            node_offset: 59,
         };
         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];
+        let expected: Vec<Option<u32>> = vec![Some(59), None, None, Some(109), None, None, None];
         assert_eq!(buckets, &expected);
 
         let nodes: &Vec<PackageTableNode> = &package_table.as_ref().unwrap().nodes;
@@ -147,7 +149,7 @@
             package_name: String::from("com.android.aconfig.storage.test_1"),
             package_id: 0,
             boolean_offset: 0,
-            next_offset: Some(158),
+            next_offset: Some(159),
         };
         assert_eq!(nodes[1], second_node_expected);
         let third_node_expected = PackageTableNode {
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..13edcb4 100644
--- a/tools/aconfig/aconfig/templates/FeatureFlags.java.template
+++ b/tools/aconfig/aconfig/templates/FeatureFlags.java.template
@@ -1,23 +1,21 @@
 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 }}
 {{ -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..12b2fc1 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(
@@ -53,14 +46,10 @@
 {{ -endif }}{#- end of runtime_lookup_required #}
 {{ -for flag in flag_elements }}
     @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 +57,6 @@
         return {flag.method_name};
 {{ -else }}
         return {flag.default_value};
-{{ -endif- }}
 {{ -endif }}
     }
 {{ endfor }}
@@ -79,7 +67,9 @@
 public final class FeatureFlagsImpl implements FeatureFlags \{
 {{ for flag in flag_elements }}
     @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..e105991 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,6 @@
     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 }}
 {{ -if not item.is_read_write }}
 {{ -if item.default_value }}
     @com.android.aconfig.annotations.AssumeTrueForR8
@@ -23,11 +17,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_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 c089d54..2a606bf 100644
--- a/tools/aconfig/aconfig_storage_file/Android.bp
+++ b/tools/aconfig/aconfig_storage_file/Android.bp
@@ -6,7 +6,6 @@
     name: "aconfig_storage_file.defaults",
     edition: "2021",
     lints: "none",
-    srcs: ["src/lib.rs"],
     rustlibs: [
         "libanyhow",
         "libthiserror",
@@ -22,12 +21,21 @@
     crate_name: "aconfig_storage_file",
     host_supported: true,
     defaults: ["aconfig_storage_file.defaults"],
+    srcs: ["src/lib.rs"],
+}
+
+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"],
+    srcs: ["src/lib.rs"],
 }
 
 rust_protobuf {
diff --git a/tools/aconfig/aconfig_storage_file/Cargo.toml b/tools/aconfig/aconfig_storage_file/Cargo.toml
index 9b9a615..641f481 100644
--- a/tools/aconfig/aconfig_storage_file/Cargo.toml
+++ b/tools/aconfig/aconfig_storage_file/Cargo.toml
@@ -14,6 +14,10 @@
 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"
 cxx-build = "1.0"
diff --git a/tools/aconfig/aconfig_storage_file/src/flag_table.rs b/tools/aconfig/aconfig_storage_file/src/flag_table.rs
index d6e5c62..f9b3158 100644
--- a/tools/aconfig/aconfig_storage_file/src/flag_table.rs
+++ b/tools/aconfig/aconfig_storage_file/src/flag_table.rs
@@ -17,8 +17,11 @@
 //! flag table module defines the flag table file format and methods for serialization
 //! and deserialization
 
-use crate::AconfigStorageError::{self, BytesParseFail};
-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};
 use anyhow::anyhow;
 use std::fmt;
 
@@ -27,6 +30,7 @@
 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,
@@ -38,8 +42,11 @@
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         writeln!(
             f,
-            "Version: {}, Container: {}, File Size: {}",
-            self.version, self.container, self.file_size
+            "Version: {}, Container: {}, File Type: {:?}, File Size: {}",
+            self.version,
+            self.container,
+            StorageFileType::try_from(self.file_type),
+            self.file_size
         )?;
         writeln!(
             f,
@@ -58,6 +65,7 @@
         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());
@@ -68,14 +76,21 @@
     /// 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)
     }
 }
 
@@ -191,7 +206,9 @@
                 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)
@@ -219,9 +236,11 @@
             assert_eq!(node, &reinterpreted_node);
         }
 
-        let reinterpreted_table = FlagTable::from_bytes(&flag_table.as_bytes());
+        let flag_table_bytes = flag_table.as_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]
@@ -234,4 +253,16 @@
         let version = read_u32_from_bytes(bytes, &mut head).unwrap();
         assert_eq!(version, 1234)
     }
+
+    #[test]
+    // 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.as_bytes()).unwrap_err();
+        assert_eq!(
+            format!("{:?}", error),
+            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 021546c..c9d09a1 100644
--- a/tools/aconfig/aconfig_storage_file/src/flag_value.rs
+++ b/tools/aconfig/aconfig_storage_file/src/flag_value.rs
@@ -17,8 +17,9 @@
 //! flag value module defines the flag value file format and methods for serialization
 //! and deserialization
 
-use crate::AconfigStorageError;
 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
@@ -26,6 +27,7 @@
 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,
@@ -36,8 +38,11 @@
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         writeln!(
             f,
-            "Version: {}, Container: {}, File Size: {}",
-            self.version, self.container, self.file_size
+            "Version: {}, Container: {}, File Type: {:?}, File Size: {}",
+            self.version,
+            self.container,
+            StorageFileType::try_from(self.file_type),
+            self.file_size
         )?;
         writeln!(
             f,
@@ -56,6 +61,7 @@
         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());
@@ -65,13 +71,20 @@
     /// 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)
     }
 }
 
@@ -130,9 +143,11 @@
         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.as_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]
@@ -145,4 +160,16 @@
         let version = read_u32_from_bytes(bytes, &mut head).unwrap();
         assert_eq!(version, 1234)
     }
+
+    #[test]
+    // 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.as_bytes()).unwrap_err();
+        assert_eq!(
+            format!("{:?}", error),
+            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 ec41a4e..24b16a1 100644
--- a/tools/aconfig/aconfig_storage_file/src/lib.rs
+++ b/tools/aconfig/aconfig_storage_file/src/lib.rs
@@ -64,13 +64,13 @@
 
 /// Storage file type enum
 #[derive(Clone, Debug, PartialEq, Eq)]
-pub enum StorageFileSelection {
-    PackageMap,
-    FlagMap,
-    FlagVal,
+pub enum StorageFileType {
+    PackageMap = 0,
+    FlagMap = 1,
+    FlagVal = 2,
 }
 
-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> {
@@ -78,7 +78,22 @@
             "package_map" => Ok(Self::PackageMap),
             "flag_map" => Ok(Self::FlagMap),
             "flag_val" => Ok(Self::FlagVal),
-            _ => Err(anyhow!("Invalid storage file to create")),
+            _ => Err(anyhow!(
+                "Invalid storage file type, valid types are package_map|flag_map|flag_val"
+            )),
+        }
+    }
+}
+
+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),
+            _ => Err(anyhow!("Invalid storage file type")),
         }
     }
 }
@@ -161,6 +176,12 @@
     #[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),
 
diff --git a/tools/aconfig/aconfig_storage_file/src/main.rs b/tools/aconfig/aconfig_storage_file/src/main.rs
index 2c7b87c..293d018 100644
--- a/tools/aconfig/aconfig_storage_file/src/main.rs
+++ b/tools/aconfig/aconfig_storage_file/src/main.rs
@@ -14,17 +14,17 @@
  * limitations under the License.
  */
 
-//! `aconfig_storage` is a debugging tool to parse storage files
+//! `aconfig-storage` is a debugging tool to parse storage files
 
 use aconfig_storage_file::{
     list_flags, read_file_to_bytes, AconfigStorageError, FlagTable, FlagValueList, PackageTable,
-    StorageFileSelection,
+    StorageFileType,
 };
 
 use clap::{builder::ArgAction, Arg, Command};
 
 fn cli() -> Command {
-    Command::new("aconfig_storage_file")
+    Command::new("aconfig-storage")
         .subcommand_required(true)
         .subcommand(
             Command::new("print")
@@ -33,37 +33,37 @@
                     Arg::new("type")
                         .long("type")
                         .required(true)
-                        .value_parser(|s: &str| StorageFileSelection::try_from(s)),
+                        .value_parser(|s: &str| StorageFileType::try_from(s)),
                 ),
         )
         .subcommand(
             Command::new("list")
                 .arg(
-                    Arg::new("package_map")
-                        .long("package_map")
+                    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)),
+                .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: &StorageFileSelection,
+    file_type: &StorageFileType,
 ) -> Result<(), AconfigStorageError> {
     let bytes = read_file_to_bytes(file_path)?;
     match file_type {
-        StorageFileSelection::PackageMap => {
+        StorageFileType::PackageMap => {
             let package_table = PackageTable::from_bytes(&bytes)?;
             println!("{:?}", package_table);
         }
-        StorageFileSelection::FlagMap => {
+        StorageFileType::FlagMap => {
             let flag_table = FlagTable::from_bytes(&bytes)?;
             println!("{:?}", flag_table);
         }
-        StorageFileSelection::FlagVal => {
+        StorageFileType::FlagVal => {
             let flag_value = FlagValueList::from_bytes(&bytes)?;
             println!("{:?}", flag_value);
         }
@@ -76,13 +76,13 @@
     match matches.subcommand() {
         Some(("print", sub_matches)) => {
             let file_path = sub_matches.get_one::<String>("file").unwrap();
-            let file_type = sub_matches.get_one::<StorageFileSelection>("type").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 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 flag in flags.iter() {
                 println!("{}: {}", flag.0, flag.1);
diff --git a/tools/aconfig/aconfig_storage_file/src/package_table.rs b/tools/aconfig/aconfig_storage_file/src/package_table.rs
index f7435b0..7cb60eb 100644
--- a/tools/aconfig/aconfig_storage_file/src/package_table.rs
+++ b/tools/aconfig/aconfig_storage_file/src/package_table.rs
@@ -17,8 +17,8 @@
 //! package table module defines the package table file format and methods for serialization
 //! and deserialization
 
-use crate::AconfigStorageError::{self, BytesParseFail};
-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;
 
@@ -27,6 +27,7 @@
 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,
@@ -38,8 +39,11 @@
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         writeln!(
             f,
-            "Version: {}, Container: {}, File Size: {}",
-            self.version, self.container, self.file_size
+            "Version: {}, Container: {}, File Type: {:?}, File Size: {}",
+            self.version,
+            self.container,
+            StorageFileType::try_from(self.file_type),
+            self.file_size
         )?;
         writeln!(
             f,
@@ -58,6 +62,7 @@
         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());
@@ -68,14 +73,21 @@
     /// 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)
     }
 }
 
@@ -191,7 +203,12 @@
                 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)
@@ -218,9 +235,11 @@
             assert_eq!(node, &reinterpreted_node);
         }
 
-        let reinterpreted_table = PackageTable::from_bytes(&package_table.as_bytes());
+        let package_table_bytes = package_table.as_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]
@@ -233,4 +252,16 @@
         let version = read_u32_from_bytes(bytes, &mut head).unwrap();
         assert_eq!(version, 1234)
     }
+
+    #[test]
+    // 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.as_bytes()).unwrap_err();
+        assert_eq!(
+            format!("{:?}", error),
+            format!("BytesParseFail(binary file is not a package map)")
+        );
+    }
 }
diff --git a/tools/aconfig/aconfig_storage_file/src/test_utils.rs b/tools/aconfig/aconfig_storage_file/src/test_utils.rs
index 7780044..586bb4c 100644
--- a/tools/aconfig/aconfig_storage_file/src/test_utils.rs
+++ b/tools/aconfig/aconfig_storage_file/src/test_utils.rs
@@ -17,7 +17,7 @@
 use crate::flag_table::{FlagTable, FlagTableHeader, FlagTableNode};
 use crate::flag_value::{FlagValueHeader, FlagValueList};
 use crate::package_table::{PackageTable, PackageTableHeader, PackageTableNode};
-use crate::AconfigStorageError;
+use crate::{AconfigStorageError, StorageFileType};
 
 use anyhow::anyhow;
 use std::io::Write;
@@ -27,12 +27,13 @@
     let header = PackageTableHeader {
         version: 1234,
         container: String::from("system"),
-        file_size: 208,
+        file_type: StorageFileType::PackageMap as u8,
+        file_size: 209,
         num_packages: 3,
-        bucket_offset: 30,
-        node_offset: 58,
+        bucket_offset: 31,
+        node_offset: 59,
     };
-    let buckets: Vec<Option<u32>> = vec![Some(58), None, None, Some(108), None, None, None];
+    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,
@@ -43,7 +44,7 @@
         package_name: String::from("com.android.aconfig.storage.test_1"),
         package_id: 0,
         boolean_offset: 0,
-        next_offset: Some(158),
+        next_offset: Some(159),
     };
     let third_node = PackageTableNode {
         package_name: String::from("com.android.aconfig.storage.test_4"),
@@ -72,36 +73,37 @@
     let header = FlagTableHeader {
         version: 1234,
         container: String::from("system"),
-        file_size: 320,
+        file_type: StorageFileType::FlagMap as u8,
+        file_size: 321,
         num_flags: 8,
-        bucket_offset: 30,
-        node_offset: 98,
+        bucket_offset: 31,
+        node_offset: 99,
     };
     let buckets: Vec<Option<u32>> = vec![
-        Some(98),
-        Some(124),
+        Some(99),
+        Some(125),
         None,
         None,
         None,
-        Some(177),
+        Some(178),
         None,
-        Some(203),
+        Some(204),
         None,
-        Some(261),
+        Some(262),
         None,
         None,
         None,
         None,
         None,
-        Some(293),
+        Some(294),
         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(0, "enabled_rw", 1, 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", 1, 1, Some(235)),
+        FlagTableNode::new_expected(1, "enabled_fixed_ro", 1, 1, Some(236)),
         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),
@@ -113,9 +115,10 @@
     let header = FlagValueHeader {
         version: 1234,
         container: String::from("system"),
-        file_size: 34,
+        file_type: StorageFileType::FlagVal as u8,
+        file_size: 35,
         num_flags: 8,
-        boolean_value_offset: 26,
+        boolean_value_offset: 27,
     };
     let booleans: Vec<bool> = vec![false, true, false, false, true, true, false, true];
     FlagValueList { header, booleans }
diff --git a/tools/aconfig/aconfig_storage_read_api/Android.bp b/tools/aconfig/aconfig_storage_read_api/Android.bp
index 721f9a5..5006161 100644
--- a/tools/aconfig/aconfig_storage_read_api/Android.bp
+++ b/tools/aconfig/aconfig_storage_read_api/Android.bp
@@ -73,25 +73,9 @@
     generated_sources: ["libcxx_aconfig_storage_read_api_bridge_code"],
     whole_static_libs: ["libaconfig_storage_read_api_cxx_bridge"],
     export_include_dirs: ["include"],
-}
-
-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)",
+    static_libs: [
+        "libaconfig_storage_protos_cc",
+        "libprotobuf-cpp-lite",
+        "libbase",
+    ],
 }
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
index c2da5ce..131dc9d 100644
--- a/tools/aconfig/aconfig_storage_read_api/aconfig_storage_read_api.cpp
+++ b/tools/aconfig/aconfig_storage_read_api/aconfig_storage_read_api.cpp
@@ -1,117 +1,182 @@
-#include "aconfig_storage/aconfig_storage_read_api.hpp"
+#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/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 Error() << "failed to open " << file;
+  };
+
+  struct stat fd_stat;
+  if (fstat(fd, &fd_stat) < 0) {
+    return Error() << "fstat failed";
+  }
+
+  if ((fd_stat.st_mode & (S_IWUSR | S_IWGRP | S_IWOTH)) != 0) {
+    return Error() << "cannot map writeable file";
+  }
+
+  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 Error() << "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
-VersionNumberQuery get_storage_file_version(
+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()));
-  auto version = VersionNumberQuery();
-  version.query_success = version_cxx.query_success;
-  version.error_message = std::string(version_cxx.error_message.c_str());
-  version.version_number = version_cxx.version_number;
-  return version;
+  if (version_cxx.query_success) {
+    return version_cxx.version_number;
+  } else {
+    return Error() << version_cxx.error_message;
+  }
 }
 
 /// Get package offset
-PackageOffsetQuery get_package_offset(
-    std::string const& container,
+Result<PackageOffset> get_package_offset(
+    MappedStorageFile const& file,
     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;
+  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
-FlagOffsetQuery get_flag_offset(
-    std::string const& container,
+Result<FlagOffset> get_flag_offset(
+    MappedStorageFile const& file,
     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;
+    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
-BooleanFlagValueQuery get_boolean_flag_value(
-    std::string const& container,
+Result<bool> get_boolean_flag_value(
+    MappedStorageFile const& file,
     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;
+  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 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_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
index 3741dc2..aa90f47 100644
--- 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
@@ -2,88 +2,84 @@
 
 #include <stdint.h>
 #include <string>
+#include <android-base/result.h>
 
 namespace aconfig_storage {
 
-/// Storage version number query result
-struct VersionNumberQuery {
-  bool query_success;
-  std::string error_message;
-  uint32_t version_number;
+/// 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 PackageOffsetQuery {
-  bool query_success;
-  std::string error_message;
+struct PackageOffset {
   bool package_exists;
   uint32_t package_id;
   uint32_t boolean_offset;
 };
 
 /// Flag offset query result
-struct FlagOffsetQuery {
-  bool query_success;
-  std::string error_message;
+struct FlagOffset {
   bool flag_exists;
   uint16_t flag_offset;
 };
 
-/// Boolean flag value query result
-struct BooleanFlagValueQuery {
-  bool query_success;
-  std::string error_message;
-  bool flag_value;
-};
+/// 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 a VersionNumberQuery
-VersionNumberQuery get_storage_file_version(
+/// \returns the storage file version
+android::base::Result<uint32_t> get_storage_file_version(
     std::string const& file_path);
 
 /// Get package offset
-/// \input container: the flag container name
+/// \input file: mapped storage file
 /// \input package: the flag package name
-/// \returns a PackageOffsetQuery
-PackageOffsetQuery get_package_offset(
-    std::string const& container,
+/// \returns a package offset
+android::base::Result<PackageOffset> get_package_offset(
+    MappedStorageFile const& file,
     std::string const& package);
 
 /// Get flag offset
-/// \input container: the flag container name
+/// \input file: mapped storage file
 /// \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,
+/// \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 container: the flag container name
+/// \input file: mapped storage file
 /// \input offset: the boolean flag value byte offset in the file
-/// \returns a BooleanFlagValueQuery
-BooleanFlagValueQuery get_boolean_flag_value(
-    std::string const& container,
+/// \returns the boolean flag value
+android::base::Result<bool> get_boolean_flag_value(
+    MappedStorageFile const& file,
     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_read_api/src/flag_table_query.rs b/tools/aconfig/aconfig_storage_read_api/src/flag_table_query.rs
index cc05557..43977ee 100644
--- a/tools/aconfig/aconfig_storage_read_api/src/flag_table_query.rs
+++ b/tools/aconfig/aconfig_storage_read_api/src/flag_table_query.rs
@@ -65,7 +65,7 @@
 #[cfg(test)]
 mod tests {
     use super::*;
-    use aconfig_storage_file::FlagTable;
+    use aconfig_storage_file::{FlagTable, StorageFileType};
 
     // create test baseline, syntactic sugar
     fn new_expected_node(
@@ -88,36 +88,37 @@
         let header = FlagTableHeader {
             version: crate::FILE_VERSION,
             container: String::from("system"),
-            file_size: 320,
+            file_type: StorageFileType::FlagMap as u8,
+            file_size: 321,
             num_flags: 8,
-            bucket_offset: 30,
-            node_offset: 98,
+            bucket_offset: 31,
+            node_offset: 99,
         };
         let buckets: Vec<Option<u32>> = vec![
-            Some(98),
-            Some(124),
+            Some(99),
+            Some(125),
             None,
             None,
             None,
-            Some(177),
+            Some(178),
             None,
-            Some(203),
+            Some(204),
             None,
-            Some(261),
+            Some(262),
             None,
             None,
             None,
             None,
             None,
-            Some(293),
+            Some(294),
             None,
         ];
         let nodes = vec![
             new_expected_node(0, "enabled_ro", 1, 1, None),
-            new_expected_node(0, "enabled_rw", 1, 2, Some(150)),
+            new_expected_node(0, "enabled_rw", 1, 2, Some(151)),
             new_expected_node(1, "disabled_ro", 1, 0, None),
             new_expected_node(2, "enabled_ro", 1, 1, None),
-            new_expected_node(1, "enabled_fixed_ro", 1, 1, Some(235)),
+            new_expected_node(1, "enabled_fixed_ro", 1, 1, Some(236)),
             new_expected_node(1, "enabled_ro", 1, 2, None),
             new_expected_node(2, "enabled_fixed_ro", 1, 0, None),
             new_expected_node(0, "disabled_rw", 1, 0, None),
diff --git a/tools/aconfig/aconfig_storage_read_api/src/flag_value_query.rs b/tools/aconfig/aconfig_storage_read_api/src/flag_value_query.rs
index 6ff6b05..88d2397 100644
--- a/tools/aconfig/aconfig_storage_read_api/src/flag_value_query.rs
+++ b/tools/aconfig/aconfig_storage_read_api/src/flag_value_query.rs
@@ -48,15 +48,16 @@
 #[cfg(test)]
 mod tests {
     use super::*;
-    use aconfig_storage_file::FlagValueList;
+    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_size: 34,
+            file_type: StorageFileType::FlagVal as u8,
+            file_size: 35,
             num_flags: 8,
-            boolean_value_offset: 26,
+            boolean_value_offset: 27,
         };
         let booleans: Vec<bool> = vec![false, true, false, false, true, true, false, true];
         FlagValueList { header, booleans }
diff --git a/tools/aconfig/aconfig_storage_read_api/src/lib.rs b/tools/aconfig/aconfig_storage_read_api/src/lib.rs
index 3958b2e..647ca55 100644
--- a/tools/aconfig/aconfig_storage_read_api/src/lib.rs
+++ b/tools/aconfig/aconfig_storage_read_api/src/lib.rs
@@ -42,54 +42,79 @@
 #[cfg(test)]
 mod test_utils;
 
-pub use aconfig_storage_file::{
-    protos::ProtoStorageFiles, read_u32_from_bytes, AconfigStorageError, StorageFileSelection,
-    FILE_VERSION,
-};
+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 mapped_file::get_mapped_file;
 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/available_storage_file_records.pb";
 
-/// Get package start offset implementation
-pub fn get_package_offset_impl(
-    pb_file: &str,
+/// 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
+pub fn get_mapped_storage_file(
     container: &str,
-    package: &str,
-) -> Result<Option<PackageOffset>, AconfigStorageError> {
-    let mapped_file = get_mapped_file(pb_file, container, StorageFileSelection::PackageMap)?;
-    find_package_offset(&mapped_file, package)
+    file_type: StorageFileType,
+) -> Result<Mmap, AconfigStorageError> {
+    crate::mapped_file::get_mapped_file(STORAGE_LOCATION_FILE, container, file_type)
 }
 
-/// Get flag offset implementation
-pub fn get_flag_offset_impl(
-    pb_file: &str,
-    container: &str,
+/// 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> {
-    let mapped_file = get_mapped_file(pb_file, container, StorageFileSelection::FlagMap)?;
-    find_flag_offset(&mapped_file, package_id, flag)
+    find_flag_offset(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 = get_mapped_file(pb_file, container, StorageFileSelection::FlagVal)?;
-    find_boolean_flag_value(&mapped_file, offset)
+/// 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
@@ -114,48 +139,6 @@
     read_u32_from_bytes(&buffer, &mut head)
 }
 
-/// 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)
-}
-
 // *************************************** //
 // CC INTERLOP
 // *************************************** //
@@ -196,37 +179,13 @@
 
     // 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_storage_file_version_cxx(file_path: &str) -> VersionNumberQueryCXX;
 
-        pub fn get_package_offset_cxx(container: &str, package: &str) -> PackageOffsetQueryCXX;
+        pub fn get_package_offset_cxx(file: &[u8], package: &str) -> PackageOffsetQueryCXX;
 
-        pub fn get_flag_offset_cxx(
-            container: &str,
-            package_id: u32,
-            flag: &str,
-        ) -> FlagOffsetQueryCXX;
+        pub fn get_flag_offset_cxx(file: &[u8], package_id: u32, flag: &str) -> FlagOffsetQueryCXX;
 
-        pub fn get_boolean_flag_value_cxx(container: &str, offset: u32)
-            -> BooleanFlagValueQueryCXX;
+        pub fn get_boolean_flag_value_cxx(file: &[u8], offset: u32) -> BooleanFlagValueQueryCXX;
     }
 }
 
@@ -324,32 +283,19 @@
     }
 }
 
-/// 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 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 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 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 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 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
@@ -357,45 +303,19 @@
     ffi::VersionNumberQueryCXX::new(get_storage_file_version(file_path))
 }
 
-/// 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))
-}
-
 #[cfg(test)]
 mod tests {
     use super::*;
-    use crate::test_utils::TestStorageFileSet;
+    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(read_only: bool) -> TestStorageFileSet {
-        TestStorageFileSet::new(
-            "./tests/package.map",
-            "./tests/flag.map",
-            "./tests/flag.val",
-            read_only,
-        )
-        .unwrap()
-    }
+    fn create_test_storage_files(read_only: bool) -> [NamedTempFile; 4] {
+        let package_map = copy_to_temp_file("./tests/package.map", read_only).unwrap();
+        let flag_map = copy_to_temp_file("./tests/flag.map", read_only).unwrap();
+        let flag_val = copy_to_temp_file("./tests/flag.val", read_only).unwrap();
 
-    #[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 {{
@@ -407,38 +327,40 @@
     timestamp: 12345
 }}
 "#,
-            ro_files.package_map.name, ro_files.flag_map.name, ro_files.flag_val.name
+            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]
+    }
 
-        let file = write_proto_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();
+    #[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(true);
+        let pb_file_path = pb_file.path().display().to_string();
+        let package_mapped_file =
+            get_mapped_file(&pb_file_path, "system", 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_impl(
-            &file_full_path,
-            "system",
-            "com.android.aconfig.storage.test_2",
-        )
-        .unwrap()
-        .unwrap();
+        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_impl(
-            &file_full_path,
-            "system",
-            "com.android.aconfig.storage.test_4",
-        )
-        .unwrap()
-        .unwrap();
+        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);
     }
@@ -446,23 +368,11 @@
     #[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 [package_map, flag_map, flag_val, pb_file] = create_test_storage_files(true);
+        let pb_file_path = pb_file.path().display().to_string();
+        let flag_mapped_file =
+            get_mapped_file(&pb_file_path, "system", StorageFileType::FlagMap).unwrap();
 
-        let file = write_proto_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),
@@ -475,9 +385,7 @@
         ];
         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();
+                get_flag_offset(&flag_mapped_file, package_id, flag_name).unwrap().unwrap();
             assert_eq!(flag_offset, expected_offset);
         }
     }
@@ -485,27 +393,13 @@
     #[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
-        );
-
-        let file = write_proto_to_temp_file(&text_proto).unwrap();
-        let file_full_path = file.path().display().to_string();
+        let [package_map, flag_map, flag_val, pb_file] = create_test_storage_files(true);
+        let pb_file_path = pb_file.path().display().to_string();
+        let flag_value_file =
+            get_mapped_file(&pb_file_path, "system", StorageFileType::FlagVal).unwrap();
         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();
+            let flag_value = get_boolean_flag_value(&flag_value_file, offset as u32).unwrap();
             assert_eq!(flag_value, expected_value);
         }
     }
diff --git a/tools/aconfig/aconfig_storage_read_api/src/mapped_file.rs b/tools/aconfig/aconfig_storage_read_api/src/mapped_file.rs
index e94c56f..09ecdb6 100644
--- a/tools/aconfig/aconfig_storage_read_api/src/mapped_file.rs
+++ b/tools/aconfig/aconfig_storage_read_api/src/mapped_file.rs
@@ -14,37 +14,20 @@
  * limitations under the License.
  */
 
-use std::collections::HashMap;
-use std::fs::File;
+use std::fs::{self, File};
 use std::io::{BufReader, Read};
-use std::sync::{Arc, Mutex};
 
 use anyhow::anyhow;
 use memmap2::Mmap;
-use once_cell::sync::Lazy;
 
 use crate::AconfigStorageError::{
     self, FileReadFail, MapFileFail, ProtobufParseFail, StorageFileNotFound,
 };
-use crate::StorageFileSelection;
+use crate::StorageFileType;
 use aconfig_storage_file::protos::{
     storage_record_pb::try_from_binary_proto, ProtoStorageFileInfo, ProtoStorageFiles,
 };
 
-/// 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,
@@ -75,17 +58,15 @@
 
 /// 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() {
+    // ensure file has read only permission
+    let perms = fs::metadata(file_path).unwrap().permissions();
+    if !perms.readonly() {
         return Err(MapFileFail(anyhow!("fail to map non read only storage file {}", file_path)));
     }
 
+    let file = File::open(file_path)
+        .map_err(|errmsg| FileReadFail(anyhow!("Failed to open file {}: {}", file_path, errmsg)))?;
+
     // SAFETY:
     //
     // Mmap constructors are unsafe as it would have undefined behaviors if the file
@@ -95,10 +76,6 @@
     // 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))
@@ -107,49 +84,26 @@
     }
 }
 
-/// 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(
+pub 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)
-        }
+    file_type: StorageFileType,
+) -> Result<Mmap, AconfigStorageError> {
+    let files_location = find_container_storage_location(location_pb_file, container)?;
+    match file_type {
+        StorageFileType::PackageMap => verify_read_only_and_map(files_location.package_map()),
+        StorageFileType::FlagMap => verify_read_only_and_map(files_location.flag_map()),
+        StorageFileType::FlagVal => verify_read_only_and_map(files_location.flag_val()),
     }
 }
 
 #[cfg(test)]
 mod tests {
     use super::*;
-    use crate::test_utils::TestStorageFileSet;
+    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() {
@@ -196,133 +150,87 @@
         );
     }
 
-    fn map_and_verify(
-        location_pb_file: &str,
-        file_selection: StorageFileSelection,
-        actual_file: &str,
-    ) {
+    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 = get_mapped_file(location_pb_file, "system", file_selection).unwrap();
+        let mmaped_file = get_mapped_file(location_pb_file, "system", file_type).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()
+    fn create_test_storage_files(read_only: bool) -> [NamedTempFile; 4] {
+        let package_map = copy_to_temp_file("./tests/package.map", read_only).unwrap();
+        let flag_map = copy_to_temp_file("./tests/flag.map", read_only).unwrap();
+        let flag_val = copy_to_temp_file("./tests/package.map", read_only).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 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_proto_to_temp_file(&text_proto).unwrap();
-        let file_full_path = file.path().display().to_string();
+        let [package_map, flag_map, flag_val, pb_file] = create_test_storage_files(true);
+        let pb_file_path = pb_file.path().display().to_string();
         map_and_verify(
-            &file_full_path,
-            StorageFileSelection::PackageMap,
-            &ro_files.package_map.name,
+            &pb_file_path,
+            StorageFileType::PackageMap,
+            &package_map.path().display().to_string(),
         );
-        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);
+        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(),
+        );
     }
 
     #[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_proto_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();
+        let [package_map, flag_map, flag_val, pb_file] = create_test_storage_files(false);
+        let pb_file_path = pb_file.path().display().to_string();
+        let error =
+            get_mapped_file(&pb_file_path, "system", StorageFileType::PackageMap).unwrap_err();
         assert_eq!(
             format!("{:?}", error),
             format!(
                 "MapFileFail(fail to map non read only storage file {})",
-                rw_files.package_map.name
+                package_map.path().display()
             )
         );
-
-        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_proto_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();
+        let error = get_mapped_file(&pb_file_path, "system", StorageFileType::FlagMap).unwrap_err();
         assert_eq!(
             format!("{:?}", error),
             format!(
                 "MapFileFail(fail to map non read only storage file {})",
-                rw_files.flag_map.name
+                flag_map.path().display()
             )
         );
-
-        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_proto_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();
+        let error = get_mapped_file(&pb_file_path, "system", StorageFileType::FlagVal).unwrap_err();
         assert_eq!(
             format!("{:?}", error),
             format!(
                 "MapFileFail(fail to map non read only storage file {})",
-                rw_files.flag_val.name
+                flag_val.path().display()
             )
         );
     }
diff --git a/tools/aconfig/aconfig_storage_read_api/src/package_table_query.rs b/tools/aconfig/aconfig_storage_read_api/src/package_table_query.rs
index 6d2ed5f7..3587e10 100644
--- a/tools/aconfig/aconfig_storage_read_api/src/package_table_query.rs
+++ b/tools/aconfig/aconfig_storage_read_api/src/package_table_query.rs
@@ -72,18 +72,19 @@
 #[cfg(test)]
 mod tests {
     use super::*;
-    use aconfig_storage_file::PackageTable;
+    use aconfig_storage_file::{PackageTable, StorageFileType};
 
     pub fn create_test_package_table() -> PackageTable {
         let header = PackageTableHeader {
             version: crate::FILE_VERSION,
             container: String::from("system"),
-            file_size: 208,
+            file_type: StorageFileType::PackageMap as u8,
+            file_size: 209,
             num_packages: 3,
-            bucket_offset: 30,
-            node_offset: 58,
+            bucket_offset: 31,
+            node_offset: 59,
         };
-        let buckets: Vec<Option<u32>> = vec![Some(58), None, None, Some(108), None, None, None];
+        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,
@@ -94,7 +95,7 @@
             package_name: String::from("com.android.aconfig.storage.test_1"),
             package_id: 0,
             boolean_offset: 0,
-            next_offset: Some(158),
+            next_offset: Some(159),
         };
         let third_node = PackageTableNode {
             package_name: String::from("com.android.aconfig.storage.test_4"),
diff --git a/tools/aconfig/aconfig_storage_read_api/src/test_utils.rs b/tools/aconfig/aconfig_storage_read_api/src/test_utils.rs
index cc5938d..ff72499 100644
--- a/tools/aconfig/aconfig_storage_read_api/src/test_utils.rs
+++ b/tools/aconfig/aconfig_storage_read_api/src/test_utils.rs
@@ -18,59 +18,15 @@
 use std::fs;
 use tempfile::NamedTempFile;
 
-fn set_file_read_only(file: &NamedTempFile) {
-    let mut perms = fs::metadata(file.path()).unwrap().permissions();
-    if !perms.readonly() {
+/// 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).unwrap();
+        fs::set_permissions(file.path(), perms.clone()).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();
-    }
-}
-
-#[allow(dead_code)]
-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)?,
-        })
-    }
+    Ok(file)
 }
diff --git a/tools/aconfig/aconfig_storage_read_api/tests/Android.bp b/tools/aconfig/aconfig_storage_read_api/tests/Android.bp
index 0bfc7bf..d9cf238 100644
--- a/tools/aconfig/aconfig_storage_read_api/tests/Android.bp
+++ b/tools/aconfig/aconfig_storage_read_api/tests/Android.bp
@@ -5,14 +5,15 @@
     ],
     rustlibs: [
         "libanyhow",
+        "libaconfig_storage_file",
         "libaconfig_storage_read_api",
         "libprotobuf",
         "libtempfile",
     ],
     data: [
-        ":ro.package.map",
-        ":ro.flag.map",
-        ":ro.flag.val",
+        "package.map",
+        "flag.map",
+        "flag.val",
     ],
     test_suites: ["general-tests"],
 }
@@ -31,9 +32,9 @@
         "liblog",
     ],
     data: [
-        ":ro.package.map",
-        ":ro.flag.map",
-        ":ro.flag.val",
+        "package.map",
+        "flag.map",
+        "flag.val",
     ],
     test_suites: [
         "device-tests",
diff --git a/tools/aconfig/aconfig_storage_read_api/tests/flag.map b/tools/aconfig/aconfig_storage_read_api/tests/flag.map
index 43b6f9a..5507894 100644
--- a/tools/aconfig/aconfig_storage_read_api/tests/flag.map
+++ 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
index f39f8d3..75b8564 100644
--- a/tools/aconfig/aconfig_storage_read_api/tests/flag.val
+++ 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
index 8ed4767..02267e5 100644
--- a/tools/aconfig/aconfig_storage_read_api/tests/package.map
+++ 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
index 6fecd08..377395a 100644
--- a/tools/aconfig/aconfig_storage_read_api/tests/storage_read_api_test.cpp
+++ b/tools/aconfig/aconfig_storage_read_api/tests/storage_read_api_test.cpp
@@ -16,101 +16,169 @@
 
 #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 ::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;
-using ::aconfig_storage::get_storage_file_version;
+using namespace android::base;
 
-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);
+namespace api = aconfig_storage;
+namespace private_api = aconfig_storage::private_internal_api;
 
-  auto content = std::string();
-  proto.SerializeToString(&content);
-  ASSERT_TRUE(WriteStringToFile(content, file_path))
-      << "Failed to write a file: " << file_path;
+class AconfigStorageTest : public ::testing::Test {
+ protected:
+  Result<std::string> copy_to_ro_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) == -1) {
+      return Error() << "failed to make file read only";
+    }
+    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("system");
+    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_ro_temp_file(test_dir + "/package.map");
+    flag_map = *copy_to_ro_temp_file(test_dir + "/flag.map");
+    flag_val = *copy_to_ro_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);
 }
 
-TEST(AconfigStorageTest, test_storage_version_query) {
-  auto const test_dir = android::base::GetExecutableDirectory();
-  auto query = get_storage_file_version(test_dir + "/tests/tmp.ro.package.map");
-  ASSERT_EQ(query.error_message, std::string());
-  ASSERT_TRUE(query.query_success);
-  ASSERT_EQ(query.version_number, 1);
-  query = get_storage_file_version(test_dir + "/tests/tmp.ro.flag.map");
-  ASSERT_EQ(query.error_message, std::string());
-  ASSERT_TRUE(query.query_success);
-  ASSERT_EQ(query.version_number, 1);
-  query = get_storage_file_version(test_dir + "/tests/tmp.ro.flag.val");
-  ASSERT_EQ(query.error_message, std::string());
-  ASSERT_TRUE(query.query_success);
-  ASSERT_EQ(query.version_number, 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(AconfigStorageTest, test_package_offset_query) {
-  auto pb_file = std::string("/tmp/test_package_offset_query.pb");
-  write_storage_location_pb_to_file(pb_file);
+/// Negative test to lock down the error when mapping a writeable storage file
+TEST_F(AconfigStorageTest, test_writable_storage_file_mapping) {
+  ASSERT_TRUE(chmod(package_map.c_str(), 0666) != -1);
+  auto mapped_file = private_api::get_mapped_file_impl(
+      storage_record_pb, "system", api::StorageFileType::package_map);
+  ASSERT_FALSE(mapped_file.ok());
+  ASSERT_EQ(mapped_file.error().message(), "cannot map writeable 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);
+  ASSERT_TRUE(chmod(flag_map.c_str(), 0666) != -1);
+  mapped_file = private_api::get_mapped_file_impl(
+      storage_record_pb, "system", api::StorageFileType::flag_map);
+  ASSERT_FALSE(mapped_file.ok());
+  ASSERT_EQ(mapped_file.error().message(), "cannot map writeable file");
 
-  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);
+  ASSERT_TRUE(chmod(flag_val.c_str(), 0666) != -1);
+  mapped_file = private_api::get_mapped_file_impl(
+      storage_record_pb, "system", api::StorageFileType::flag_val);
+  ASSERT_FALSE(mapped_file.ok());
+  ASSERT_EQ(mapped_file.error().message(), "cannot map writeable file");
 }
 
-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);
+/// 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, "system", api::StorageFileType::package_map);
+  ASSERT_TRUE(mapped_file.ok());
 
-  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);
+  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);
 
-  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);
+  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(AconfigStorageTest, test_flag_offset_query) {
-  auto pb_file = std::string("/tmp/test_package_offset_query.pb");
-  write_storage_location_pb_to_file(pb_file);
+/// 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, "system", 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, "system", api::StorageFileType::flag_map);
+  ASSERT_TRUE(mapped_file.ok());
 
   auto baseline = std::vector<std::tuple<int, std::string, int>>{
     {0, "enabled_ro", 1},
@@ -123,56 +191,49 @@
     {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);
+    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(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);
+/// 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, "system", api::StorageFileType::flag_map);
+  ASSERT_TRUE(mapped_file.ok());
 
-  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);
+  auto offset = api::get_flag_offset(*mapped_file, 0, "none_exist");
+  ASSERT_TRUE(offset.ok());
+  ASSERT_FALSE(offset->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);
+  offset = api::get_flag_offset(*mapped_file, 3, "enabled_ro");
+  ASSERT_TRUE(offset.ok());
+  ASSERT_FALSE(offset->flag_exists);
 }
 
-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);
+/// 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, "system", api::StorageFileType::flag_val);
+  ASSERT_TRUE(mapped_file.ok());
+
   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);
+    auto value = api::get_boolean_flag_value(*mapped_file, offset);
+    ASSERT_TRUE(value.ok());
+    ASSERT_FALSE(*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);
+/// 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, "system", api::StorageFileType::flag_val);
+  ASSERT_TRUE(mapped_file.ok());
 
-  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,
+  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.)"));
-  ASSERT_FALSE(query.query_success);
 }
diff --git a/tools/aconfig/aconfig_storage_read_api/tests/storage_read_api_test.rs b/tools/aconfig/aconfig_storage_read_api/tests/storage_read_api_test.rs
index 4a65876..eb4d54d 100644
--- a/tools/aconfig/aconfig_storage_read_api/tests/storage_read_api_test.rs
+++ b/tools/aconfig/aconfig_storage_read_api/tests/storage_read_api_test.rs
@@ -1,96 +1,54 @@
 #[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_impl, get_flag_offset_impl, get_package_offset_impl,
-        get_storage_file_version, PackageOffset, ProtoStorageFiles,
+        get_boolean_flag_value, get_flag_offset, get_package_offset, get_storage_file_version,
+        mapped_file::get_mapped_file, PackageOffset,
     };
-    use protobuf::Message;
-    use std::io::Write;
+    use std::fs;
     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();
+    pub fn copy_to_ro_temp_file(source_file: &str) -> NamedTempFile {
+        let file = NamedTempFile::new().unwrap();
+        fs::copy(source_file, file.path()).unwrap();
+        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();
         file
     }
 
-    #[test]
-    fn test_package_offset_query() {
-        let file = write_storage_location_file();
-        let file_full_path = file.path().display().to_string();
+    fn create_test_storage_files() -> [NamedTempFile; 4] {
+        let package_map = copy_to_ro_temp_file("./package.map");
+        let flag_map = copy_to_ro_temp_file("./flag.map");
+        let flag_val = copy_to_ro_temp_file("./flag.val");
 
-        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);
+        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_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();
+    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();
+        let err =
+            get_mapped_file(&pb_file_path, "vendor", StorageFileType::PackageMap).unwrap_err();
         assert_eq!(
             format!("{:?}", err),
             "StorageFileNotFound(Storage file does not exist for vendor)"
@@ -98,9 +56,51 @@
     }
 
     #[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();
+        let package_mapped_file =
+            get_mapped_file(&pb_file_path, "system", 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();
+        let package_mapped_file =
+            get_mapped_file(&pb_file_path, "system", 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 file = write_storage_location_file();
-        let file_full_path = file.path().display().to_string();
+        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 =
+            get_mapped_file(&pb_file_path, "system", StorageFileType::FlagMap).unwrap();
 
         let baseline = vec![
             (0, "enabled_ro", 1u16),
@@ -114,58 +114,47 @@
         ];
         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();
+                get_flag_offset(&flag_mapped_file, 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();
+    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();
+        let flag_mapped_file =
+            get_mapped_file(&pb_file_path, "system", StorageFileType::FlagMap).unwrap();
 
-        let flag_offset_option =
-            get_flag_offset_impl(&file_full_path, "system", 0, "none_exist").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_impl(&file_full_path, "system", 3, "enabled_ro").unwrap();
+        let flag_offset_option = get_flag_offset(&flag_mapped_file, 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 [_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 =
+            get_mapped_file(&pb_file_path, "system", StorageFileType::FlagVal).unwrap();
 
         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();
+            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 file = write_storage_location_file();
-        let file_full_path = file.path().display().to_string();
+        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 =
+            get_mapped_file(&pb_file_path, "system", StorageFileType::FlagVal).unwrap();
 
-        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();
+        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.)"
@@ -174,8 +163,8 @@
 
     #[test]
     fn test_storage_version_query() {
-        assert_eq!(get_storage_file_version("./tests/tmp.ro.package.map").unwrap(), 1);
-        assert_eq!(get_storage_file_version("./tests/tmp.ro.flag.map").unwrap(), 1);
-        assert_eq!(get_storage_file_version("./tests/tmp.ro.flag.val").unwrap(), 1);
+        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..391b305
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_write_api/aconfig_storage_write_api.cpp
@@ -0,0 +1,124 @@
+
+#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 Error() << "fstat 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 Error() << "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 Error() << "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();
+  }
+  return map_storage_file(*file_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..c2375dd
--- /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.as_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().as_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.as_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..75b8564
--- /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..3a1c5de
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_write_api/tests/storage_write_api_test.cpp
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#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("system");
+    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, "system");
+  ASSERT_FALSE(mapped_file_result.ok());
+  ASSERT_EQ(mapped_file_result.error().message(), "cannot map nonwriteable file");
+}
+
+/// 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, "system");
+  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, "system");
+  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..f6c1bbc
--- /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: "system"
+    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, "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!(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..b36aa34 100644
--- a/tools/aconfig/aflags/Android.bp
+++ b/tools/aconfig/aflags/Android.bp
@@ -12,6 +12,7 @@
         "libaconfig_protos",
         "libanyhow",
         "libclap",
+        "libnix",
         "libprotobuf",
         "libregex",
     ],
diff --git a/tools/aconfig/aflags/Cargo.toml b/tools/aconfig/aflags/Cargo.toml
index 3350a6cd..6a08da6 100644
--- a/tools/aconfig/aflags/Cargo.toml
+++ b/tools/aconfig/aflags/Cargo.toml
@@ -10,3 +10,4 @@
 protobuf = "3.2.0"
 regex = "1.10.3"
 aconfig_protos = { path = "../aconfig_protos" }
+nix = { version = "0.28.0", features = ["user"] }
diff --git a/tools/aconfig/aflags/src/device_config_source.rs b/tools/aconfig/aflags/src/device_config_source.rs
index 1cea6ed..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,
     }
@@ -86,14 +86,16 @@
     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)
 }
@@ -103,38 +105,75 @@
     if !output.status.success() {
         let reason = match output.status.code() {
             Some(code) => {
-                format!("exit code {}, output was {}", code, str::from_utf8(&output.stdout)?)
+                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>> {
+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()
 }
@@ -143,8 +182,9 @@
     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)
     }
 
@@ -167,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 ef0195f..808ffa0 100644
--- a/tools/aconfig/aflags/src/main.rs
+++ b/tools/aconfig/aflags/src/main.rs
@@ -16,13 +16,13 @@
 
 //! `aflags` is a device binary to read and write aconfig flags.
 
-use anyhow::{anyhow, Result};
+use anyhow::{anyhow, ensure, Result};
 use clap::Parser;
 
 mod device_config_source;
 use device_config_source::DeviceConfigSource;
 
-#[derive(Clone)]
+#[derive(Clone, PartialEq, Debug)]
 enum FlagPermission {
     ReadOnly,
     ReadWrite,
@@ -37,7 +37,7 @@
     }
 }
 
-#[derive(Clone)]
+#[derive(Clone, Debug)]
 enum ValuePickedFrom {
     Default,
     Server,
@@ -52,13 +52,41 @@
     }
 }
 
-#[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,
 }
@@ -67,6 +95,13 @@
     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 {
@@ -83,6 +118,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.
@@ -116,22 +155,22 @@
 }
 
 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;
@@ -141,20 +180,21 @@
 
     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 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?"),
     )?;
 
-    if let FlagPermission::ReadOnly = flag.permission {
-        return Err(anyhow!(
-            "could not write flag '{qualified_name}', it is read-only for the current release configuration.",
-        ));
-    }
+    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)?;
 
@@ -164,9 +204,13 @@
 fn list() -> Result<String> {
     let flags = DeviceConfigSource::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())
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/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/sign_target_files_apks.py b/tools/releasetools/sign_target_files_apks.py
index 52d95d8..5d92ede 100755
--- a/tools/releasetools/sign_target_files_apks.py
+++ b/tools/releasetools/sign_target_files_apks.py
@@ -822,6 +822,52 @@
   # 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):
@@ -830,51 +876,7 @@
       avbtool, 'info_image',
       '--image', image_name
     ])
-
-    # 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
+    return ParseAvbInfo(info_raw)
 
   # Get hashtree descriptor from info
   def GetAvbHashtreeDescriptor(avb_info):
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) {