Merge "Revert^2 "16k: core: Enable TARGET_NO_BIONIC_PAGE_SIZE_MACRO by default on Android V targets"" into main
diff --git a/core/Makefile b/core/Makefile
index 9d77ec1..b245d32 100644
--- a/core/Makefile
+++ b/core/Makefile
@@ -1469,8 +1469,13 @@
 boototapackage_4k: $(BUILT_BOOT_OTA_PACKAGE_4K)
 .PHONY: boototapackage_4k
 
+ifeq ($(BOARD_16K_OTA_MOVE_VENDOR),true)
+$(eval $(call copy-one-file,$(BUILT_BOOT_OTA_PACKAGE_4K),$(TARGET_OUT_VENDOR)/boot_otas/boot_ota_4k.zip))
+$(eval $(call copy-one-file,$(BUILT_BOOT_OTA_PACKAGE_16K),$(TARGET_OUT_VENDOR)/boot_otas/boot_ota_16k.zip))
+else
 $(eval $(call copy-one-file,$(BUILT_BOOT_OTA_PACKAGE_4K),$(TARGET_OUT)/boot_otas/boot_ota_4k.zip))
 $(eval $(call copy-one-file,$(BUILT_BOOT_OTA_PACKAGE_16K),$(TARGET_OUT)/boot_otas/boot_ota_16k.zip))
+endif # BOARD_16K_OTA_MOVE_VENDOR == true
 
 ALL_DEFAULT_INSTALLED_MODULES += $(TARGET_OUT)/boot_otas/boot_ota_4k.zip
 ALL_DEFAULT_INSTALLED_MODULES += $(TARGET_OUT)/boot_otas/boot_ota_16k.zip
diff --git a/core/OWNERS b/core/OWNERS
index 36951a9..35ea83d 100644
--- a/core/OWNERS
+++ b/core/OWNERS
@@ -10,3 +10,4 @@
 
 # For Ravenwood test configs
 per-file ravenwood_test_config_template.xml = jsharkey@google.com,omakoto@google.com
+
diff --git a/core/android_soong_config_vars.mk b/core/android_soong_config_vars.mk
index ca87417..70b3744 100644
--- a/core/android_soong_config_vars.mk
+++ b/core/android_soong_config_vars.mk
@@ -44,59 +44,9 @@
   BRANCH_DEFAULT_MODULE_BUILD_FROM_SOURCE := false
 endif
 
-ifneq ($(SANITIZE_TARGET)$(EMMA_INSTRUMENT_FRAMEWORK),)
-  # Always use sources when building the framework with Java coverage or
-  # sanitized builds as they both require purpose built prebuilts which we do
-  # not provide.
-  BRANCH_DEFAULT_MODULE_BUILD_FROM_SOURCE := true
-endif
-
-ifneq ($(CLANG_COVERAGE)$(NATIVE_COVERAGE_PATHS),)
-  # Always use sources when building with clang coverage and native coverage.
-  # It is possible that there are certain situations when building with coverage
-  # would work with prebuilts, e.g. when the coverage is not being applied to
-  # modules for which we provide prebuilts. Unfortunately, determining that
-  # would require embedding knowledge of which coverage paths affect which
-  # modules here. That would duplicate a lot of information, add yet another
-  # location  module authors have to update and complicate the logic here.
-  # For nowe we will just always build from sources when doing coverage builds.
-  BRANCH_DEFAULT_MODULE_BUILD_FROM_SOURCE := true
-endif
-
-# ART does not provide linux_bionic variants needed for products that
-# set HOST_CROSS_OS=linux_bionic.
-ifeq (linux_bionic,${HOST_CROSS_OS})
-  BRANCH_DEFAULT_MODULE_BUILD_FROM_SOURCE := true
-endif
-
-# ART does not provide host side arm64 variants needed for products that
-# set HOST_CROSS_ARCH=arm64.
-ifeq (arm64,${HOST_CROSS_ARCH})
-  BRANCH_DEFAULT_MODULE_BUILD_FROM_SOURCE := true
-endif
-
-# TV based devices do not seem to work with prebuilts, so build from source
-# for now and fix in a follow up.
-ifneq (,$(filter tv,$(subst $(comma),$(space),${PRODUCT_CHARACTERISTICS})))
-  BRANCH_DEFAULT_MODULE_BUILD_FROM_SOURCE := true
-endif
-
-# ATV based devices do not seem to work with prebuilts, so build from source
-# for now and fix in a follow up.
-ifneq (,${PRODUCT_IS_ATV})
-  BRANCH_DEFAULT_MODULE_BUILD_FROM_SOURCE := true
-endif
-
 ifneq (,$(MODULE_BUILD_FROM_SOURCE))
   # Keep an explicit setting.
-else ifeq (,$(filter docs sdk win_sdk sdk_addon,$(MAKECMDGOALS))$(findstring com.google.android.conscrypt,$(PRODUCT_PACKAGES))$(findstring com.google.android.go.conscrypt,$(PRODUCT_PACKAGES)))
-  # Prebuilt module SDKs require prebuilt modules to work, and currently
-  # prebuilt modules are only provided for com.google.android(.go)?.xxx. If we can't
-  # find one of them in PRODUCT_PACKAGES then assume com.android.xxx are in use,
-  # and disable prebuilt SDKs. In particular this applies to AOSP builds.
-  #
-  # However, docs/sdk/win_sdk/sdk_addon builds might not include com.google.android.xxx
-  # packages, so for those we respect the default behavior.
+else ifeq (,$(filter docs sdk win_sdk sdk_addon,$(MAKECMDGOALS)))
   MODULE_BUILD_FROM_SOURCE := true
 else ifneq (,$(PRODUCT_MODULE_BUILD_FROM_SOURCE))
   # Let products override the branch default.
@@ -194,6 +144,8 @@
 
 $(call add_soong_config_var_value,ANDROID,release_binder_death_recipient_weak_from_jni,$(RELEASE_BINDER_DEATH_RECIPIENT_WEAK_FROM_JNI))
 
+$(call add_soong_config_var_value,ANDROID,release_package_libandroid_runtime_punch_holes,$(RELEASE_PACKAGE_LIBANDROID_RUNTIME_PUNCH_HOLES))
+
 $(call add_soong_config_var_value,ANDROID,release_selinux_data_data_ignore,$(RELEASE_SELINUX_DATA_DATA_IGNORE))
 
 # Enable system_server optimizations by default unless explicitly set or if
diff --git a/core/board_config.mk b/core/board_config.mk
index 4424517..633303f 100644
--- a/core/board_config.mk
+++ b/core/board_config.mk
@@ -992,7 +992,7 @@
 # BOARD_API_LEVEL for vendor API surface
 ifdef RELEASE_BOARD_API_LEVEL
   ifdef BOARD_API_LEVEL
-    $(error BOARD_API_LEVEL must not set manully. The build system automatically sets this value.)
+    $(error BOARD_API_LEVEL must not be set manually. The build system automatically sets this value.)
   endif
   BOARD_API_LEVEL := $(RELEASE_BOARD_API_LEVEL)
   .KATI_READONLY := BOARD_API_LEVEL
diff --git a/core/envsetup.mk b/core/envsetup.mk
index 7f9cbad..1c3a1b3 100644
--- a/core/envsetup.mk
+++ b/core/envsetup.mk
@@ -57,18 +57,6 @@
   KEEP_VNDK ?= true
 endif
 
-ifeq ($(KEEP_VNDK),true)
-  # Starting in Android U, non-VNDK devices not supported
-  # WARNING: DO NOT CHANGE: if you are downstream of AOSP, and you change this, without
-  # letting upstream know it's important to you, we may do cleanup which breaks this
-  # significantly. Please let us know if you are changing this.
-  ifndef BOARD_VNDK_VERSION
-  # READ WARNING - DO NOT CHANGE
-  BOARD_VNDK_VERSION := current
-  # READ WARNING - DO NOT CHANGE
-  endif
-endif
-
 # ---------------------------------------------------------------
 # Set up version information
 include $(BUILD_SYSTEM)/version_util.mk
diff --git a/core/main.mk b/core/main.mk
index bc8adde..b798b49 100644
--- a/core/main.mk
+++ b/core/main.mk
@@ -1222,8 +1222,7 @@
 # Returns modules included automatically as a result of certain BoardConfig
 # variables being set.
 define auto-included-modules
-  $(if $(and $(BOARD_VNDK_VERSION),$(filter true,$(KEEP_VNDK))),vndk_package) \
-  $(if $(filter true,$(KEEP_VNDK)),,llndk_in_system) \
+  llndk_in_system \
   $(if $(DEVICE_MANIFEST_FILE),vendor_manifest.xml) \
   $(if $(DEVICE_MANIFEST_SKUS),$(foreach sku, $(DEVICE_MANIFEST_SKUS),vendor_manifest_$(sku).xml)) \
   $(if $(ODM_MANIFEST_FILES),odm_manifest.xml) \
diff --git a/core/product.mk b/core/product.mk
index 01b5ead..9752f32 100644
--- a/core/product.mk
+++ b/core/product.mk
@@ -325,6 +325,13 @@
 # set this variable to prevent OTA failures.
 _product_list_vars += PRODUCT_OTA_ENFORCE_VINTF_KERNEL_REQUIREMENTS
 
+# If set to true, this product forces HIDL to be enabled by declaring android.hidl.manager
+# and android.hidl.token in the framework manifest. The product will also need to add the
+# 'hwservicemanager' service to PRODUCT_PACKAGES if its SHIPPING_API_LEVEL is greater than 34.
+# This should only be used during bringup for devices that are targeting FCM 202404 and still
+# have partner-owned HIDL interfaces that are being converted to AIDL.
+_product_single_value_vars += PRODUCT_HIDL_ENABLED
+
 # If set to true, this product builds a generic OTA package, which installs generic system images
 # onto matching devices. The product may only build a subset of system images (e.g. only
 # system.img), so devices need to install the package in a system-only OTA manner.
@@ -456,7 +463,7 @@
 
 _product_single_value_vars += PRODUCT_BUILD_FROM_SOURCE_STUB
 
-_product_list_vars += PRODUCT_BUILD_IGNORE_APEX_CONTRIBUTION_CONTENTS
+_product_single_value_vars += PRODUCT_BUILD_IGNORE_APEX_CONTRIBUTION_CONTENTS
 
 _product_single_value_vars += PRODUCT_HIDDEN_API_EXPORTABLE_STUBS
 
diff --git a/core/product_config.mk b/core/product_config.mk
index 4525423..4eeac95 100644
--- a/core/product_config.mk
+++ b/core/product_config.mk
@@ -301,6 +301,23 @@
 PRODUCT_INCLUDE_TAGS += com.android.mainline mainline_module_prebuilt_nightly
 endif
 
+# AOSP and Google products currently share the same `apex_contributions` in next.
+# This causes issues when building <aosp_product>-next-userdebug in main.
+# Create a temporary allowlist to ignore the google apexes listed in `contents` of apex_contributions of `next`
+# *for aosp products*.
+# TODO(b/308187268): Remove this denylist mechanism
+# Use PRODUCT_PACKAGES to determine if this is an aosp product. aosp products do not use google signed apexes.
+ignore_apex_contributions :=
+ifeq (,$(findstring com.google.android.conscrypt,$(PRODUCT_PACKAGES))$(findstring com.google.android.go.conscrypt,$(PRODUCT_PACKAGES)))
+  ignore_apex_contributions := true
+endif
+ifeq (true,$(PRODUCT_MODULE_BUILD_FROM_SOURCE))
+  ignore_apex_contributions := true
+endif
+ifeq (true, $(ignore_apex_contributions))
+PRODUCT_BUILD_IGNORE_APEX_CONTRIBUTION_CONTENTS := true
+endif
+
 #############################################################################
 
 # Quick check and assign default values
@@ -552,20 +569,27 @@
       $(PRODUCT_ENFORCE_RRO_EXEMPTED_TARGETS))
 endif
 
-# Get the board API level.
-board_api_level := $(PLATFORM_SDK_VERSION)
-ifdef BOARD_API_LEVEL
-  board_api_level := $(BOARD_API_LEVEL)
-else ifdef BOARD_SHIPPING_API_LEVEL
-  # Vendors with GRF must define BOARD_SHIPPING_API_LEVEL for the vendor API level.
-  board_api_level := $(BOARD_SHIPPING_API_LEVEL)
-endif
+# This table maps sdk version 35 to vendor api level 202404 and assumes yearly
+# release for the same month.
+define sdk-to-vendor-api-level
+  $(if $(call math_lt_or_eq,$(1),34),$(1),20$(call int_subtract,$(1),11)04)
+endef
 
-# Calculate the VSR vendor API level.
-VSR_VENDOR_API_LEVEL := $(board_api_level)
-
-ifdef PRODUCT_SHIPPING_API_LEVEL
-  VSR_VENDOR_API_LEVEL := $(call math_min,$(PRODUCT_SHIPPING_API_LEVEL),$(board_api_level))
+ifdef PRODUCT_SHIPPING_VENDOR_API_LEVEL
+# Follow the version that is set manually.
+  VSR_VENDOR_API_LEVEL := $(PRODUCT_SHIPPING_VENDOR_API_LEVEL)
+else
+  # VSR API level is the vendor api level of the product shipping API level.
+  VSR_VENDOR_API_LEVEL := $(call sdk-to-vendor-api-level,$(PLATFORM_SDK_VERSION))
+  ifdef PRODUCT_SHIPPING_API_LEVEL
+    VSR_VENDOR_API_LEVEL := $(call sdk-to-vendor-api-level,$(PRODUCT_SHIPPING_API_LEVEL))
+  endif
+  ifdef BOARD_SHIPPING_API_LEVEL
+    # Vendors with GRF must define BOARD_SHIPPING_API_LEVEL for the vendor API level.
+    # In this case, the VSR API level is the minimum of the PRODUCT_SHIPPING_API_LEVEL
+    # and RELEASE_BOARD_API_LEVEL
+    VSR_VENDOR_API_LEVEL := $(call math_min,$(VSR_VENDOR_API_LEVEL),$(RELEASE_BOARD_API_LEVEL))
+  endif
 endif
 .KATI_READONLY := VSR_VENDOR_API_LEVEL
 
@@ -580,7 +604,7 @@
 
 # Boolean variable determining if selinux labels of /dev are enforced
 CHECK_DEV_TYPE_VIOLATIONS := false
-ifneq ($(call math_gt,$(VSR_VENDOR_API_LEVEL),35),)
+ifneq ($(call math_gt,$(VSR_VENDOR_API_LEVEL),202404),)
   CHECK_DEV_TYPE_VIOLATIONS := true
 else ifneq ($(PRODUCT_CHECK_DEV_TYPE_VIOLATIONS),)
   CHECK_DEV_TYPE_VIOLATIONS := $(PRODUCT_CHECK_DEV_TYPE_VIOLATIONS)
diff --git a/core/release_config.mk b/core/release_config.mk
index a7b5b3f..3e51af5 100644
--- a/core/release_config.mk
+++ b/core/release_config.mk
@@ -41,8 +41,7 @@
 # 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/trunk_release/release_config_map.mk) \
-    $(wildcard build/release/release_config_map.mk) \
+config_map_files := $(wildcard build/release/release_config_map.mk) \
     $(wildcard vendor/google_shared/build/release/release_config_map.mk) \
     $(if $(wildcard vendor/google/release/release_config_map.mk), \
         vendor/google/release/release_config_map.mk, \
diff --git a/core/soong_config.mk b/core/soong_config.mk
index 1e419f3..3cffef2 100644
--- a/core/soong_config.mk
+++ b/core/soong_config.mk
@@ -152,10 +152,6 @@
 $(call add_json_str,  BtConfigIncludeDir,                $(BOARD_BLUETOOTH_BDROID_BUILDCFG_INCLUDE_DIR))
 $(call add_json_list, DeviceKernelHeaders,               $(TARGET_DEVICE_KERNEL_HEADERS) $(TARGET_BOARD_KERNEL_HEADERS) $(TARGET_PRODUCT_KERNEL_HEADERS))
 $(call add_json_str,  VendorApiLevel,                    $(BOARD_API_LEVEL))
-ifeq ($(KEEP_VNDK),true)
-$(call add_json_str,  DeviceVndkVersion,                 $(BOARD_VNDK_VERSION))
-$(call add_json_str,  Platform_vndk_version,             $(PLATFORM_VNDK_VERSION))
-endif
 $(call add_json_list, ExtraVndkVersions,                 $(PRODUCT_EXTRA_VNDK_VERSIONS))
 $(call add_json_list, DeviceSystemSdkVersions,           $(BOARD_SYSTEMSDK_VERSIONS))
 $(call add_json_str,  RecoverySnapshotVersion,           $(RECOVERY_SNAPSHOT_VERSION))
@@ -339,7 +335,7 @@
 
 $(call add_json_bool, CheckVendorSeappViolations, $(filter true,$(CHECK_VENDOR_SEAPP_VIOLATIONS)))
 
-$(call add_json_list, BuildIgnoreApexContributionContents, $(sort $(PRODUCT_BUILD_IGNORE_APEX_CONTRIBUTION_CONTENTS)))
+$(call add_json_bool, BuildIgnoreApexContributionContents, $(PRODUCT_BUILD_IGNORE_APEX_CONTRIBUTION_CONTENTS))
 
 $(call add_json_map, PartitionVarsForBazelMigrationOnlyDoNotUse)
   $(call add_json_str,  ProductDirectory,    $(dir $(INTERNAL_PRODUCT)))
diff --git a/core/tasks/automotive-sdv-tests.mk b/core/tasks/automotive-sdv-tests.mk
new file mode 100644
index 0000000..12706ce
--- /dev/null
+++ b/core/tasks/automotive-sdv-tests.mk
@@ -0,0 +1,61 @@
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+.PHONY: automotive-sdv-tests
+
+automotive-sdv-tests-zip := $(PRODUCT_OUT)/automotive-sdv-tests.zip
+# Create an artifact to include a list of test config files in automotive-sdv-tests.
+automotive-sdv-tests-list-zip := $(PRODUCT_OUT)/automotive-sdv-tests_list.zip
+# Create an artifact to include all test config files in automotive-sdv-tests.
+automotive-sdv-tests-configs-zip := $(PRODUCT_OUT)/automotive-sdv-tests_configs.zip
+my_host_shared_lib_for_automotive_sdv_tests := $(call copy-many-files,$(COMPATIBILITY.automotive-sdv-tests.HOST_SHARED_LIBRARY.FILES))
+automotive_sdv_tests_host_shared_libs_zip := $(PRODUCT_OUT)/automotive-sdv-tests_host-shared-libs.zip
+
+$(automotive-sdv-tests-zip) : .KATI_IMPLICIT_OUTPUTS := $(automotive-sdv-tests-list-zip) $(automotive-sdv-tests-configs-zip) $(automotive_sdv_tests_host_shared_libs_zip)
+$(automotive-sdv-tests-zip) : PRIVATE_automotive_sdv_tests_list := $(PRODUCT_OUT)/automotive-sdv-tests_list
+$(automotive-sdv-tests-zip) : PRIVATE_HOST_SHARED_LIBS := $(my_host_shared_lib_for_automotive_sdv_tests)
+$(automotive-sdv-tests-zip) : PRIVATE_automotive_host_shared_libs_zip := $(automotive_sdv_tests_host_shared_libs_zip)
+$(automotive-sdv-tests-zip) : $(COMPATIBILITY.automotive-sdv-tests.FILES) $(my_host_shared_lib_for_automotive_sdv_tests) $(SOONG_ZIP)
+	rm -f $@-shared-libs.list
+	echo $(sort $(COMPATIBILITY.automotive-sdv-tests.FILES)) | tr " " "\n" > $@.list
+	grep $(HOST_OUT_TESTCASES) $@.list > $@-host.list || true
+	grep -e .*\\.config$$ $@-host.list > $@-host-test-configs.list || true
+	$(hide) for shared_lib in $(PRIVATE_HOST_SHARED_LIBS); do \
+	  echo $$shared_lib >> $@-host.list; \
+	  echo $$shared_lib >> $@-shared-libs.list; \
+	done
+	grep $(HOST_OUT_TESTCASES) $@-shared-libs.list > $@-host-shared-libs.list || true
+	grep $(TARGET_OUT_TESTCASES) $@.list > $@-target.list || true
+	grep -e .*\\.config$$ $@-target.list > $@-target-test-configs.list || true
+	$(hide) $(SOONG_ZIP) -d -o $@ -P host -C $(HOST_OUT) -l $@-host.list -P target -C $(PRODUCT_OUT) -l $@-target.list
+	$(hide) $(SOONG_ZIP) -d -o $(automotive-sdv-tests-configs-zip) \
+	  -P host -C $(HOST_OUT) -l $@-host-test-configs.list \
+	  -P target -C $(PRODUCT_OUT) -l $@-target-test-configs.list
+	$(SOONG_ZIP) -d -o $(PRIVATE_automotive_host_shared_libs_zip) \
+	  -P host -C $(HOST_OUT) -l $@-host-shared-libs.list
+	rm -f $(PRIVATE_automotive_sdv_tests_list)
+	$(hide) grep -e .*\\.config$$ $@-host.list | sed s%$(HOST_OUT)%host%g > $(PRIVATE_automotive_sdv_tests_list)
+	$(hide) grep -e .*\\.config$$ $@-target.list | sed s%$(PRODUCT_OUT)%target%g >> $(PRIVATE_automotive_sdv_tests_list)
+	$(hide) $(SOONG_ZIP) -d -o $(automotive-sdv-tests-list-zip) -C $(dir $@) -f $(PRIVATE_automotive_sdv_tests_list)
+	rm -f $@.list $@-host.list $@-target.list $@-host-test-configs.list $@-target-test-configs.list \
+	  $@-shared-libs.list $@-host-shared-libs.list $(PRIVATE_automotive_sdv_tests_list)
+
+automotive-sdv-tests: $(automotive-sdv-tests-zip)
+$(call dist-for-goals, automotive-sdv-tests, $(automotive-sdv-tests-zip) $(automotive-sdv-tests-list-zip) $(automotive-sdv-tests-configs-zip) $(automotive_sdv_tests_host_shared_libs_zip))
+
+$(call declare-1p-container,$(automotive-sdv-tests-zip),)
+$(call declare-container-license-deps,$(automotive-sdv-tests-zip),$(COMPATIBILITY.automotive-sdv-tests.FILES) $(my_host_shared_lib_for_automotive_sdv_tests),$(PRODUCT_OUT)/:/)
+
+tests: automotive-sdv-tests
diff --git a/core/tasks/berberis_test.mk b/core/tasks/berberis_test.mk
new file mode 100644
index 0000000..8604709
--- /dev/null
+++ b/core/tasks/berberis_test.mk
@@ -0,0 +1,25 @@
+#
+# Copyright (C) 2023 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+BERBERIS_DIR := frameworks/libs/binary_translation
+
+# Berberis includes some components which may conflict with other packages.
+# Only build it when requested explicitly.
+ifeq ($(BUILD_BERBERIS),true)
+
+include $(BERBERIS_DIR)/tests/run_host_tests.mk
+
+endif  # BUILD_BERBERIS
diff --git a/core/tasks/meta-lic.mk b/core/tasks/meta-lic.mk
index dea4892..0348844 100644
--- a/core/tasks/meta-lic.mk
+++ b/core/tasks/meta-lic.mk
@@ -14,6 +14,9 @@
 
 # Declare license metadata for non-module files released with products.
 
+# Moved here from device/sample/Android.mk
+$(eval $(call declare-1p-copy-files,device/sample,))
+
 # Moved here from frameworks/av/media/Android.mk
 $(eval $(call declare-1p-copy-files,frameworks/av/media/libeffects,audio_effects.conf))
 $(eval $(call declare-1p-copy-files,frameworks/av/media/libeffects,audio_effects.xml))
diff --git a/core/tasks/module-info.mk b/core/tasks/module-info.mk
index 8546828..aa695eb 100644
--- a/core/tasks/module-info.mk
+++ b/core/tasks/module-info.mk
@@ -39,7 +39,7 @@
 			$(call write-optional-json-list, "srcjars", $(sort $(ALL_MODULES.$(m).SRCJARS))) \
 			$(call write-optional-json-list, "classes_jar", $(sort $(ALL_MODULES.$(m).CLASSES_JAR))) \
 			$(call write-optional-json-list, "test_mainline_modules", $(sort $(ALL_MODULES.$(m).TEST_MAINLINE_MODULES))) \
-			$(call write-optional-json-bool, $(ALL_MODULES.$(m).IS_UNIT_TEST)) \
+			$(call write-optional-json-bool, "is_unit_test", $(ALL_MODULES.$(m).IS_UNIT_TEST)) \
 			$(call write-optional-json-list, "test_options_tags", $(sort $(ALL_MODULES.$(m).TEST_OPTIONS_TAGS))) \
 			$(call write-optional-json-list, "data", $(sort $(ALL_MODULES.$(m).TEST_DATA))) \
 			$(call write-optional-json-list, "runtime_dependencies", $(sort $(ALL_MODULES.$(m).LOCAL_RUNTIME_LIBRARIES))) \
diff --git a/target/board/BoardConfigMainlineCommon.mk b/target/board/BoardConfigMainlineCommon.mk
index 2b17349..b5e3dc2 100644
--- a/target/board/BoardConfigMainlineCommon.mk
+++ b/target/board/BoardConfigMainlineCommon.mk
@@ -24,11 +24,6 @@
 # the devices with metadata parition
 BOARD_USES_METADATA_PARTITION := true
 
-ifeq ($(KEEP_VNDK),true)
-# Default is current, but allow devices to override vndk version if needed.
-BOARD_VNDK_VERSION ?= current
-endif
-
 # 64 bit mediadrmserver
 TARGET_ENABLE_MEDIADRM_64 := true
 
diff --git a/target/product/aosp_x86_64.mk b/target/product/aosp_x86_64.mk
index 3040dd3..4344f50 100644
--- a/target/product/aosp_x86_64.mk
+++ b/target/product/aosp_x86_64.mk
@@ -74,3 +74,5 @@
 PRODUCT_DEVICE := generic_x86_64
 PRODUCT_BRAND := Android
 PRODUCT_MODEL := AOSP on x86_64
+
+PRODUCT_NO_BIONIC_PAGE_SIZE_MACRO := true
diff --git a/target/product/gsi/Android.mk b/target/product/gsi/Android.mk
index 3bb65ac..fc6cc68 100644
--- a/target/product/gsi/Android.mk
+++ b/target/product/gsi/Android.mk
@@ -1,95 +1,18 @@
 LOCAL_PATH:= $(call my-dir)
 
 #####################################################################
-# list of vndk libraries from the source code.
-INTERNAL_VNDK_LIB_LIST := $(SOONG_VNDK_LIBRARIES_FILE)
-
-#####################################################################
 # Check the generate list against the latest list stored in the
 # source tree
-.PHONY: check-vndk-list
+.PHONY: check-abi-dump-list
 
 # Check if vndk list is changed
-droidcore: check-vndk-list
+droidcore: check-abi-dump-list
 
-check-vndk-list-timestamp := $(call intermediates-dir-for,PACKAGING,vndk)/check-list-timestamp
 check-vndk-abi-dump-list-timestamp := $(call intermediates-dir-for,PACKAGING,vndk)/check-abi-dump-list-timestamp
 
-ifeq ($(TARGET_IS_64_BIT)|$(TARGET_2ND_ARCH),true|)
-# TODO(b/110429754) remove this condition when we support 64-bit-only device
-check-vndk-list: ;
-else ifeq ($(TARGET_SKIP_CURRENT_VNDK),true)
-check-vndk-list: ;
-else ifeq ($(BOARD_VNDK_VERSION),)
-check-vndk-list: ;
-else
-check-vndk-list: $(check-vndk-list-timestamp)
 ifneq ($(SKIP_ABI_CHECKS),true)
-check-vndk-list: $(check-vndk-abi-dump-list-timestamp)
+check-abi-dump-list: $(check-abi-dump-list-timestamp)
 endif
-endif
-
-_vndk_check_failure_message := " error: VNDK library list has been changed.\n"
-ifeq (REL,$(PLATFORM_VERSION_CODENAME))
-_vndk_check_failure_message += "       Changing the VNDK library list is not allowed in API locked branches."
-else
-_vndk_check_failure_message += "       Run \`update-vndk-list.sh\` to update $(LATEST_VNDK_LIB_LIST)"
-endif
-
-# The *-ndk_platform.so libraries no longer exist and are removed from the VNDK set. However, they
-# can exist if NEED_AIDL_NDK_PLATFORM_BACKEND is set to true for legacy devices. Don't be bothered
-# with the extraneous libraries.
-ifeq ($(NEED_AIDL_NDK_PLATFORM_BACKEND),true)
-	_READ_INTERNAL_VNDK_LIB_LIST := sed /ndk_platform.so/d $(INTERNAL_VNDK_LIB_LIST)
-else
-	_READ_INTERNAL_VNDK_LIB_LIST := cat $(INTERNAL_VNDK_LIB_LIST)
-endif
-
-$(check-vndk-list-timestamp): $(INTERNAL_VNDK_LIB_LIST) $(LATEST_VNDK_LIB_LIST) $(HOST_OUT_EXECUTABLES)/update-vndk-list.sh
-	$(hide) ($(_READ_INTERNAL_VNDK_LIB_LIST) | sort | \
-	diff --old-line-format="Removed %L" \
-	  --new-line-format="Added %L" \
-	  --unchanged-line-format="" \
-	  <(cat $(LATEST_VNDK_LIB_LIST) | sort) - \
-	  || ( echo -e $(_vndk_check_failure_message); exit 1 ))
-	$(hide) mkdir -p $(dir $@)
-	$(hide) touch $@
-
-#####################################################################
-# Script to update the latest VNDK lib list
-include $(CLEAR_VARS)
-LOCAL_MODULE := update-vndk-list.sh
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := build/soong/licenses/LICENSE
-LOCAL_MODULE_CLASS := EXECUTABLES
-LOCAL_MODULE_STEM := $(LOCAL_MODULE)
-LOCAL_IS_HOST_MODULE := true
-include $(BUILD_SYSTEM)/base_rules.mk
-$(LOCAL_BUILT_MODULE): PRIVATE_INTERNAL_VNDK_LIB_LIST := $(INTERNAL_VNDK_LIB_LIST)
-$(LOCAL_BUILT_MODULE): PRIVATE_LATEST_VNDK_LIB_LIST := $(LATEST_VNDK_LIB_LIST)
-$(LOCAL_BUILT_MODULE):
-	@echo "Generate: $@"
-	@mkdir -p $(dir $@)
-	@rm -f $@
-	$(hide) echo "#!/bin/bash" > $@
-ifeq (REL,$(PLATFORM_VERSION_CODENAME))
-	$(hide) echo "echo Updating VNDK library list is NOT allowed in API locked branches." >> $@; \
-	        echo "exit 1" >> $@
-else
-	$(hide) echo "if [ -z \"\$${ANDROID_BUILD_TOP}\" ]; then" >> $@; \
-	        echo "  echo Run lunch or choosecombo first" >> $@; \
-	        echo "  exit 1" >> $@; \
-	        echo "fi" >> $@; \
-	        echo "cd \$${ANDROID_BUILD_TOP}" >> $@
-ifeq ($(NEED_AIDL_NDK_PLATFORM_BACKEND),true)
-	$(hide) echo "sed /ndk_platform.so/d $(PRIVATE_INTERNAL_VNDK_LIB_LIST) > $(PRIVATE_LATEST_VNDK_LIB_LIST)" >> $@
-else
-	$(hide) echo "cp $(PRIVATE_INTERNAL_VNDK_LIB_LIST) $(PRIVATE_LATEST_VNDK_LIB_LIST)" >> $@
-endif
-	$(hide) echo "echo $(PRIVATE_LATEST_VNDK_LIB_LIST) updated." >> $@
-endif
-	@chmod a+x $@
 
 #####################################################################
 # ABI reference dumps.
@@ -142,15 +65,13 @@
 $(notdir $(call filter-abi-dump-paths,$(1),$(2)))
 endef
 
-
+VNDK_ABI_DUMP_DIR := prebuilts/abi-dumps/vndk/$(RELEASE_BOARD_API_LEVEL)
 ifeq (REL,$(PLATFORM_VERSION_CODENAME))
-    NDK_ABI_DUMP_DIR := prebuilts/abi-dumps/ndk/$(PLATFORM_SDK_VERSION)
     PLATFORM_ABI_DUMP_DIR := prebuilts/abi-dumps/platform/$(PLATFORM_SDK_VERSION)
 else
-    NDK_ABI_DUMP_DIR := prebuilts/abi-dumps/ndk/current
     PLATFORM_ABI_DUMP_DIR := prebuilts/abi-dumps/platform/current
 endif
-NDK_ABI_DUMPS := $(call find-abi-dump-paths,$(NDK_ABI_DUMP_DIR))
+VNDK_ABI_DUMPS := $(call find-abi-dump-paths,$(VNDK_ABI_DUMP_DIR))
 PLATFORM_ABI_DUMPS := $(call find-abi-dump-paths,$(PLATFORM_ABI_DUMP_DIR))
 
 # Check for superfluous lsdump files. Since LSDUMP_PATHS only covers the
@@ -158,22 +79,15 @@
 # Mainline modules may be in use, we also allow the libs in STUB_LIBRARIES for
 # NDK and platform ABIs.
 
-$(check-vndk-abi-dump-list-timestamp): PRIVATE_LSDUMP_PATHS := $(LSDUMP_PATHS)
-$(check-vndk-abi-dump-list-timestamp): PRIVATE_STUB_LIBRARIES := $(STUB_LIBRARIES)
-$(check-vndk-abi-dump-list-timestamp):
+$(check-abi-dump-list-timestamp): PRIVATE_LSDUMP_PATHS := $(LSDUMP_PATHS)
+$(check-abi-dump-list-timestamp): PRIVATE_STUB_LIBRARIES := $(STUB_LIBRARIES)
+$(check-abi-dump-list-timestamp):
 	$(eval added_vndk_abi_dumps := $(strip $(sort $(filter-out \
-	  $(call filter-abi-dump-names,LLNDK VNDK-SP VNDK-core,$(PRIVATE_LSDUMP_PATHS)), \
+	  $(call filter-abi-dump-names,LLNDK,$(PRIVATE_LSDUMP_PATHS)), \
 	  $(notdir $(VNDK_ABI_DUMPS))))))
 	$(if $(added_vndk_abi_dumps), \
 	  echo -e "Found unexpected ABI reference dump files under $(VNDK_ABI_DUMP_DIR). It is caused by mismatch between Android.bp and the dump files. Run \`find \$${ANDROID_BUILD_TOP}/$(VNDK_ABI_DUMP_DIR) '(' -name $(subst $(space), -or -name ,$(added_vndk_abi_dumps)) ')' -delete\` to delete the dump files.")
 
-	$(eval added_ndk_abi_dumps := $(strip $(sort $(filter-out \
-	  $(call filter-abi-dump-names,NDK,$(PRIVATE_LSDUMP_PATHS)) \
-	  $(addsuffix .lsdump,$(PRIVATE_STUB_LIBRARIES)), \
-	  $(notdir $(NDK_ABI_DUMPS))))))
-	$(if $(added_ndk_abi_dumps), \
-	  echo -e "Found unexpected ABI reference dump files under $(NDK_ABI_DUMP_DIR). It is caused by mismatch between Android.bp and the dump files. Run \`find \$${ANDROID_BUILD_TOP}/$(NDK_ABI_DUMP_DIR) '(' -name $(subst $(space), -or -name ,$(added_ndk_abi_dumps)) ')' -delete\` to delete the dump files.")
-
 	# TODO(b/314010764): Remove LLNDK tag after PLATFORM_SDK_VERSION is upgraded to 35.
 	$(eval added_platform_abi_dumps := $(strip $(sort $(filter-out \
 	  $(call filter-abi-dump-names,LLNDK PLATFORM,$(PRIVATE_LSDUMP_PATHS)) \
@@ -182,7 +96,7 @@
 	$(if $(added_platform_abi_dumps), \
 	  echo -e "Found unexpected ABI reference dump files under $(PLATFORM_ABI_DUMP_DIR). It is caused by mismatch between Android.bp and the dump files. Run \`find \$${ANDROID_BUILD_TOP}/$(PLATFORM_ABI_DUMP_DIR) '(' -name $(subst $(space), -or -name ,$(added_platform_abi_dumps)) ')' -delete\` to delete the dump files.")
 
-	$(if $(added_vndk_abi_dumps)$(added_ndk_abi_dumps)$(added_platform_abi_dumps),exit 1)
+	$(if $(added_vndk_abi_dumps)$(added_platform_abi_dumps),exit 1)
 	$(hide) mkdir -p $(dir $@)
 	$(hide) touch $@
 
@@ -190,27 +104,6 @@
 # VNDK package and snapshot.
 
 include $(CLEAR_VARS)
-LOCAL_MODULE := vndk_package
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := build/soong/licenses/LICENSE
-# Filter LLNDK libs moved to APEX to avoid pulling them into /system/LIB
-LOCAL_REQUIRED_MODULES := llndk_in_system
-
-ifneq ($(TARGET_SKIP_CURRENT_VNDK),true)
-LOCAL_REQUIRED_MODULES += \
-    vndkcorevariant.libraries.txt \
-    $(addsuffix .vendor,$(VNDK_CORE_LIBRARIES)) \
-    $(addsuffix .vendor,$(VNDK_SAMEPROCESS_LIBRARIES)) \
-    $(VNDK_USING_CORE_VARIANT_LIBRARIES)
-
-LOCAL_ADDITIONAL_DEPENDENCIES += $(call module-built-files,\
-    $(addsuffix .vendor,$(VNDK_CORE_LIBRARIES) $(VNDK_SAMEPROCESS_LIBRARIES)))
-
-endif
-include $(BUILD_PHONY_PACKAGE)
-
-include $(CLEAR_VARS)
 
 LOCAL_MODULE := vndk_apex_snapshot_package
 LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
diff --git a/target/product/handheld_system_ext.mk b/target/product/handheld_system_ext.mk
index 1218f7a..187b627 100644
--- a/target/product/handheld_system_ext.mk
+++ b/target/product/handheld_system_ext.mk
@@ -29,8 +29,3 @@
     StorageManager \
     SystemUI \
     WallpaperCropper \
-
-# Base modules when shipping api level is less than or equal to 34
-PRODUCT_PACKAGES_SHIPPING_API_LEVEL_34 += \
-    hwservicemanager \
-    android.hidl.allocator@1.0-service \
diff --git a/target/product/sdk.mk b/target/product/sdk.mk
index 04649a2..1a07363 100644
--- a/target/product/sdk.mk
+++ b/target/product/sdk.mk
@@ -34,6 +34,9 @@
 
 PRODUCT_BUILD_FROM_SOURCE_STUB := true
 
+# Use sources of mainline modules
+PRODUCT_MODULE_BUILD_FROM_SOURCE := true
+
 ifeq ($(WITHOUT_CHECK_API),true)
   $(error WITHOUT_CHECK_API cannot be set to true for SDK product builds)
 endif
diff --git a/teams/Android.bp b/teams/Android.bp
index 8f83e71..a02a573 100644
--- a/teams/Android.bp
+++ b/teams/Android.bp
@@ -732,7 +732,7 @@
 }
 
 team {
-    name: "trendy_team_deprecated_systemui_gfx",
+    name: "trendy_team_ailabs",
 
     // go/trendy/manage/engineers/6673470538285056
     trendy_team_id: "6673470538285056",
diff --git a/tools/aconfig/TEST_MAPPING b/tools/aconfig/TEST_MAPPING
index 0ea8fea..638b92a 100644
--- a/tools/aconfig/TEST_MAPPING
+++ b/tools/aconfig/TEST_MAPPING
@@ -20,7 +20,7 @@
       // aconfig C++ integration tests (test mode auto-generated code)
       "name": "aconfig.test.cpp.test_mode"
     },
-    // TODO(327420679): Enable export mode for native flag library
+    // TODO(b/327420679): Enable export mode for native flag library
     // {
     //   // aconfig C++ integration tests (exported mode auto-generated code)
     //   "name": "aconfig.test.cpp.exported_mode"
@@ -33,12 +33,16 @@
       // aconfig Rust integration tests (test mode auto-generated code)
       "name": "aconfig.test_mode.test.rust"
     },
-    // TODO(327420679): Enable export mode for native flag library
+    // TODO(b/327420679): Enable export mode for native flag library
     // {
     //   // aconfig Rust integration tests (exported mode auto-generated code)
     //   "name": "aconfig.exported_mode.test.rust"
     // },
     {
+      // aflags CLI unit tests
+      "name": "aflags.test"
+    },
+    {
       // printflags unit tests
       "name": "printflags.test"
     },
@@ -66,9 +70,7 @@
       // test testing filtering logic. Breakage on this test means all tests
       // that using the flag macros to do filtering will get affected.
       "name": "FlagMacrosTests"
-    }
-  ],
-  "postsubmit": [
+    },
     {
       // aconfig_storage_write_api unit tests
       "name": "aconfig_storage_write_api.test"
@@ -82,21 +84,18 @@
       "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"
     },
     {
       // aconfig_storage read api cpp integration tests
       "name": "aconfig_storage_read_api.test.cpp"
-    },
+    }
+  ],
+  "postsubmit": [
     {
-      // aflags CLI unit tests
-      // TODO(b/326062088): add to presubmit once proven in postsubmit.
-      "name": "aflags.test"
+      // aconfig_storage write api cpp integration tests
+      "name": "aconfig_storage_write_api.test.cpp"
     }
   ]
 }
diff --git a/tools/aconfig/aconfig/src/codegen/java.rs b/tools/aconfig/aconfig/src/codegen/java.rs
index fab2fa3..18a4be5 100644
--- a/tools/aconfig/aconfig/src/codegen/java.rs
+++ b/tools/aconfig/aconfig/src/codegen/java.rs
@@ -181,26 +181,35 @@
     /** @hide */
     public interface FeatureFlags {
         @com.android.aconfig.annotations.AssumeFalseForR8
+        @com.android.aconfig.annotations.AconfigFlagAccessor
         @UnsupportedAppUsage
         boolean disabledRo();
+        @com.android.aconfig.annotations.AconfigFlagAccessor
         @UnsupportedAppUsage
         boolean disabledRw();
+        @com.android.aconfig.annotations.AconfigFlagAccessor
         @UnsupportedAppUsage
         boolean disabledRwExported();
+        @com.android.aconfig.annotations.AconfigFlagAccessor
         @UnsupportedAppUsage
         boolean disabledRwInOtherNamespace();
         @com.android.aconfig.annotations.AssumeTrueForR8
+        @com.android.aconfig.annotations.AconfigFlagAccessor
         @UnsupportedAppUsage
         boolean enabledFixedRo();
         @com.android.aconfig.annotations.AssumeTrueForR8
+        @com.android.aconfig.annotations.AconfigFlagAccessor
         @UnsupportedAppUsage
         boolean enabledFixedRoExported();
         @com.android.aconfig.annotations.AssumeTrueForR8
+        @com.android.aconfig.annotations.AconfigFlagAccessor
         @UnsupportedAppUsage
         boolean enabledRo();
         @com.android.aconfig.annotations.AssumeTrueForR8
+        @com.android.aconfig.annotations.AconfigFlagAccessor
         @UnsupportedAppUsage
         boolean enabledRoExported();
+        @com.android.aconfig.annotations.AconfigFlagAccessor
         @UnsupportedAppUsage
         boolean enabledRw();
     }
@@ -232,42 +241,51 @@
         public static final String FLAG_ENABLED_RW = "com.android.aconfig.test.enabled_rw";
 
         @com.android.aconfig.annotations.AssumeFalseForR8
+        @com.android.aconfig.annotations.AconfigFlagAccessor
         @UnsupportedAppUsage
         public static boolean disabledRo() {
             return FEATURE_FLAGS.disabledRo();
         }
+        @com.android.aconfig.annotations.AconfigFlagAccessor
         @UnsupportedAppUsage
         public static boolean disabledRw() {
             return FEATURE_FLAGS.disabledRw();
         }
+        @com.android.aconfig.annotations.AconfigFlagAccessor
         @UnsupportedAppUsage
         public static boolean disabledRwExported() {
             return FEATURE_FLAGS.disabledRwExported();
         }
+        @com.android.aconfig.annotations.AconfigFlagAccessor
         @UnsupportedAppUsage
         public static boolean disabledRwInOtherNamespace() {
             return FEATURE_FLAGS.disabledRwInOtherNamespace();
         }
         @com.android.aconfig.annotations.AssumeTrueForR8
+        @com.android.aconfig.annotations.AconfigFlagAccessor
         @UnsupportedAppUsage
         public static boolean enabledFixedRo() {
             return FEATURE_FLAGS.enabledFixedRo();
         }
         @com.android.aconfig.annotations.AssumeTrueForR8
+        @com.android.aconfig.annotations.AconfigFlagAccessor
         @UnsupportedAppUsage
         public static boolean enabledFixedRoExported() {
             return FEATURE_FLAGS.enabledFixedRoExported();
         }
         @com.android.aconfig.annotations.AssumeTrueForR8
+        @com.android.aconfig.annotations.AconfigFlagAccessor
         @UnsupportedAppUsage
         public static boolean enabledRo() {
             return FEATURE_FLAGS.enabledRo();
         }
         @com.android.aconfig.annotations.AssumeTrueForR8
+        @com.android.aconfig.annotations.AconfigFlagAccessor
         @UnsupportedAppUsage
         public static boolean enabledRoExported() {
             return FEATURE_FLAGS.enabledRoExported();
         }
+        @com.android.aconfig.annotations.AconfigFlagAccessor
         @UnsupportedAppUsage
         public static boolean enabledRw() {
             return FEATURE_FLAGS.enabledRw();
@@ -458,13 +476,14 @@
                 other_namespace_is_cached = true;
             }
 
-
             @Override
+            @com.android.aconfig.annotations.AconfigFlagAccessor
             @UnsupportedAppUsage
             public boolean disabledRo() {
                 return false;
             }
             @Override
+            @com.android.aconfig.annotations.AconfigFlagAccessor
             @UnsupportedAppUsage
             public boolean disabledRw() {
                 if (!aconfig_test_is_cached) {
@@ -473,6 +492,7 @@
                 return disabledRw;
             }
             @Override
+            @com.android.aconfig.annotations.AconfigFlagAccessor
             @UnsupportedAppUsage
             public boolean disabledRwExported() {
                 if (!aconfig_test_is_cached) {
@@ -481,6 +501,7 @@
                 return disabledRwExported;
             }
             @Override
+            @com.android.aconfig.annotations.AconfigFlagAccessor
             @UnsupportedAppUsage
             public boolean disabledRwInOtherNamespace() {
                 if (!other_namespace_is_cached) {
@@ -489,26 +510,31 @@
                 return disabledRwInOtherNamespace;
             }
             @Override
+            @com.android.aconfig.annotations.AconfigFlagAccessor
             @UnsupportedAppUsage
             public boolean enabledFixedRo() {
                 return true;
             }
             @Override
+            @com.android.aconfig.annotations.AconfigFlagAccessor
             @UnsupportedAppUsage
             public boolean enabledFixedRoExported() {
                 return true;
             }
             @Override
+            @com.android.aconfig.annotations.AconfigFlagAccessor
             @UnsupportedAppUsage
             public boolean enabledRo() {
                 return true;
             }
             @Override
+            @com.android.aconfig.annotations.AconfigFlagAccessor
             @UnsupportedAppUsage
             public boolean enabledRoExported() {
                 return true;
             }
             @Override
+            @com.android.aconfig.annotations.AconfigFlagAccessor
             @UnsupportedAppUsage
             public boolean enabledRw() {
                 if (!aconfig_test_is_cached) {
@@ -566,7 +592,6 @@
             public static final String FLAG_ENABLED_FIXED_RO_EXPORTED = "com.android.aconfig.test.enabled_fixed_ro_exported";
             /** @hide */
             public static final String FLAG_ENABLED_RO_EXPORTED = "com.android.aconfig.test.enabled_ro_exported";
-
             public static boolean disabledRwExported() {
                 return FEATURE_FLAGS.disabledRwExported();
             }
@@ -623,7 +648,6 @@
                 }
                 aconfig_test_is_cached = true;
             }
-
             @Override
             public boolean disabledRwExported() {
                 if (!aconfig_test_is_cached) {
@@ -631,7 +655,6 @@
                 }
                 return disabledRwExported;
             }
-
             @Override
             public boolean enabledFixedRoExported() {
                 if (!aconfig_test_is_cached) {
@@ -639,7 +662,6 @@
                 }
                 return enabledFixedRoExported;
             }
-
             @Override
             public boolean enabledRoExported() {
                 if (!aconfig_test_is_cached) {
@@ -762,54 +784,63 @@
         /** @hide */
         public final class FeatureFlagsImpl implements FeatureFlags {
             @Override
+            @com.android.aconfig.annotations.AconfigFlagAccessor
             @UnsupportedAppUsage
             public boolean disabledRo() {
                 throw new UnsupportedOperationException(
                     "Method is not implemented.");
             }
             @Override
+            @com.android.aconfig.annotations.AconfigFlagAccessor
             @UnsupportedAppUsage
             public boolean disabledRw() {
                 throw new UnsupportedOperationException(
                     "Method is not implemented.");
             }
             @Override
+            @com.android.aconfig.annotations.AconfigFlagAccessor
             @UnsupportedAppUsage
             public boolean disabledRwExported() {
                 throw new UnsupportedOperationException(
                     "Method is not implemented.");
             }
             @Override
+            @com.android.aconfig.annotations.AconfigFlagAccessor
             @UnsupportedAppUsage
             public boolean disabledRwInOtherNamespace() {
                 throw new UnsupportedOperationException(
                     "Method is not implemented.");
             }
             @Override
+            @com.android.aconfig.annotations.AconfigFlagAccessor
             @UnsupportedAppUsage
             public boolean enabledFixedRo() {
                 throw new UnsupportedOperationException(
                     "Method is not implemented.");
             }
             @Override
+            @com.android.aconfig.annotations.AconfigFlagAccessor
             @UnsupportedAppUsage
             public boolean enabledFixedRoExported() {
                 throw new UnsupportedOperationException(
                     "Method is not implemented.");
             }
             @Override
+            @com.android.aconfig.annotations.AconfigFlagAccessor
             @UnsupportedAppUsage
             public boolean enabledRo() {
                 throw new UnsupportedOperationException(
                     "Method is not implemented.");
             }
             @Override
+            @com.android.aconfig.annotations.AconfigFlagAccessor
             @UnsupportedAppUsage
             public boolean enabledRoExported() {
                 throw new UnsupportedOperationException(
                     "Method is not implemented.");
             }
             @Override
+            @com.android.aconfig.annotations.AconfigFlagAccessor
             @UnsupportedAppUsage
             public boolean enabledRw() {
                 throw new UnsupportedOperationException(
@@ -862,21 +893,27 @@
         /** @hide */
         public interface FeatureFlags {
             @com.android.aconfig.annotations.AssumeFalseForR8
+            @com.android.aconfig.annotations.AconfigFlagAccessor
             @UnsupportedAppUsage
             boolean disabledRo();
             @com.android.aconfig.annotations.AssumeFalseForR8
+            @com.android.aconfig.annotations.AconfigFlagAccessor
             @UnsupportedAppUsage
             boolean disabledRw();
             @com.android.aconfig.annotations.AssumeFalseForR8
+            @com.android.aconfig.annotations.AconfigFlagAccessor
             @UnsupportedAppUsage
             boolean disabledRwInOtherNamespace();
             @com.android.aconfig.annotations.AssumeTrueForR8
+            @com.android.aconfig.annotations.AconfigFlagAccessor
             @UnsupportedAppUsage
             boolean enabledFixedRo();
             @com.android.aconfig.annotations.AssumeTrueForR8
+            @com.android.aconfig.annotations.AconfigFlagAccessor
             @UnsupportedAppUsage
             boolean enabledRo();
             @com.android.aconfig.annotations.AssumeTrueForR8
+            @com.android.aconfig.annotations.AconfigFlagAccessor
             @UnsupportedAppUsage
             boolean enabledRw();
         }"#;
@@ -888,31 +925,37 @@
         /** @hide */
         public final class FeatureFlagsImpl implements FeatureFlags {
             @Override
+            @com.android.aconfig.annotations.AconfigFlagAccessor
             @UnsupportedAppUsage
             public boolean disabledRo() {
                 return false;
             }
             @Override
+            @com.android.aconfig.annotations.AconfigFlagAccessor
             @UnsupportedAppUsage
             public boolean disabledRw() {
                 return false;
             }
             @Override
+            @com.android.aconfig.annotations.AconfigFlagAccessor
             @UnsupportedAppUsage
             public boolean disabledRwInOtherNamespace() {
                 return false;
             }
             @Override
+            @com.android.aconfig.annotations.AconfigFlagAccessor
             @UnsupportedAppUsage
             public boolean enabledFixedRo() {
                 return true;
             }
             @Override
+            @com.android.aconfig.annotations.AconfigFlagAccessor
             @UnsupportedAppUsage
             public boolean enabledRo() {
                 return true;
             }
             @Override
+            @com.android.aconfig.annotations.AconfigFlagAccessor
             @UnsupportedAppUsage
             public boolean enabledRw() {
                 return true;
@@ -938,33 +981,38 @@
             public static final String FLAG_ENABLED_RO = "com.android.aconfig.test.enabled_ro";
             /** @hide */
             public static final String FLAG_ENABLED_RW = "com.android.aconfig.test.enabled_rw";
-
             @com.android.aconfig.annotations.AssumeFalseForR8
+            @com.android.aconfig.annotations.AconfigFlagAccessor
             @UnsupportedAppUsage
             public static boolean disabledRo() {
                 return FEATURE_FLAGS.disabledRo();
             }
             @com.android.aconfig.annotations.AssumeFalseForR8
+            @com.android.aconfig.annotations.AconfigFlagAccessor
             @UnsupportedAppUsage
             public static boolean disabledRw() {
                 return FEATURE_FLAGS.disabledRw();
             }
             @com.android.aconfig.annotations.AssumeFalseForR8
+            @com.android.aconfig.annotations.AconfigFlagAccessor
             @UnsupportedAppUsage
             public static boolean disabledRwInOtherNamespace() {
                 return FEATURE_FLAGS.disabledRwInOtherNamespace();
             }
             @com.android.aconfig.annotations.AssumeTrueForR8
+            @com.android.aconfig.annotations.AconfigFlagAccessor
             @UnsupportedAppUsage
             public static boolean enabledFixedRo() {
                 return FEATURE_FLAGS.enabledFixedRo();
             }
             @com.android.aconfig.annotations.AssumeTrueForR8
+            @com.android.aconfig.annotations.AconfigFlagAccessor
             @UnsupportedAppUsage
             public static boolean enabledRo() {
                 return FEATURE_FLAGS.enabledRo();
             }
             @com.android.aconfig.annotations.AssumeTrueForR8
+            @com.android.aconfig.annotations.AconfigFlagAccessor
             @UnsupportedAppUsage
             public static boolean enabledRw() {
                 return FEATURE_FLAGS.enabledRw();
diff --git a/tools/aconfig/aconfig/src/storage/flag_table.rs b/tools/aconfig/aconfig/src/storage/flag_table.rs
index b861c1f..a971211 100644
--- a/tools/aconfig/aconfig/src/storage/flag_table.rs
+++ b/tools/aconfig/aconfig/src/storage/flag_table.rs
@@ -16,8 +16,10 @@
 
 use crate::commands::assign_flag_ids;
 use crate::storage::FlagPackage;
+use aconfig_protos::ProtoFlagPermission;
 use aconfig_storage_file::{
-    get_table_size, FlagTable, FlagTableHeader, FlagTableNode, FILE_VERSION, StorageFileType
+    get_table_size, FlagTable, FlagTableHeader, FlagTableNode, StorageFileType, StoredFlagType,
+    FILE_VERSION,
 };
 use anyhow::{anyhow, Result};
 
@@ -45,8 +47,8 @@
     fn new(
         package_id: u32,
         flag_name: &str,
-        flag_type: u16,
-        flag_id: u16,
+        flag_type: StoredFlagType,
+        flag_index: u16,
         num_buckets: u32,
     ) -> Self {
         let bucket_index = FlagTableNode::find_bucket_index(package_id, flag_name, num_buckets);
@@ -54,7 +56,7 @@
             package_id,
             flag_name: flag_name.to_string(),
             flag_type,
-            flag_id,
+            flag_index,
             next_offset: None,
         };
         Self { node, bucket_index }
@@ -70,11 +72,14 @@
                 let fid = flag_ids
                     .get(pf.name())
                     .ok_or(anyhow!(format!("missing flag id for {}", pf.name())))?;
-                // all flags are boolean value at the moment, thus using the last bit.
-                // When more flag value types are supported, flag value type information
-                // should come from the parsed flag, and we will set the flag_type bit
-                // mask properly.
-                let flag_type = 1;
+                let flag_type = if pf.is_fixed_read_only() {
+                    StoredFlagType::FixedReadOnlyBoolean
+                } else {
+                    match pf.permission() {
+                        ProtoFlagPermission::READ_WRITE => StoredFlagType::ReadWriteBoolean,
+                        ProtoFlagPermission::READ_ONLY => StoredFlagType::ReadOnlyBoolean,
+                    }
+                };
                 Ok(Self::new(package.package_id, pf.name(), flag_type, *fid, num_buckets))
             })
             .collect::<Result<Vec<_>>>()
@@ -95,10 +100,10 @@
         .concat();
 
     // initialize all header fields
-    header.bucket_offset = header.as_bytes().len() as u32;
+    header.bucket_offset = header.into_bytes().len() as u32;
     header.node_offset = header.bucket_offset + num_buckets * 4;
     header.file_size = header.node_offset
-        + node_wrappers.iter().map(|x| x.node.as_bytes().len()).sum::<usize>() as u32;
+        + node_wrappers.iter().map(|x| x.node.into_bytes().len()).sum::<usize>() as u32;
 
     // sort nodes by bucket index for efficiency
     node_wrappers.sort_by(|a, b| a.bucket_index.cmp(&b.bucket_index));
@@ -116,7 +121,7 @@
         if buckets[node_bucket_idx as usize].is_none() {
             buckets[node_bucket_idx as usize] = Some(offset);
         }
-        offset += node_wrappers[i].node.as_bytes().len() as u32;
+        offset += node_wrappers[i].node.into_bytes().len() as u32;
 
         if let Some(index) = next_node_bucket_idx {
             if index == node_bucket_idx {
@@ -136,79 +141,18 @@
     use super::*;
     use crate::storage::{group_flags_by_package, tests::parse_all_test_flags};
 
-    // create test baseline, syntactic sugar
-    fn new_expected_node(
-        package_id: u32,
-        flag_name: &str,
-        flag_type: u16,
-        flag_id: u16,
-        next_offset: Option<u32>,
-    ) -> FlagTableNode {
-        FlagTableNode {
-            package_id,
-            flag_name: flag_name.to_string(),
-            flag_type,
-            flag_id,
-            next_offset,
-        }
-    }
-
-    fn create_test_flag_table() -> Result<FlagTable> {
+    fn create_test_flag_table_from_source() -> Result<FlagTable> {
         let caches = parse_all_test_flags();
         let packages = group_flags_by_package(caches.iter());
-        create_flag_table("system", &packages)
+        create_flag_table("mockup", &packages)
     }
 
     #[test]
     // this test point locks down the table creation and each field
     fn test_table_contents() {
-        let flag_table = create_test_flag_table();
+        let flag_table = create_test_flag_table_from_source();
         assert!(flag_table.is_ok());
-
-        let header: &FlagTableHeader = &flag_table.as_ref().unwrap().header;
-        let expected_header = FlagTableHeader {
-            version: FILE_VERSION,
-            container: String::from("system"),
-            file_type: StorageFileType::FlagMap as u8,
-            file_size: 321,
-            num_flags: 8,
-            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(99),
-            Some(125),
-            None,
-            None,
-            None,
-            Some(178),
-            None,
-            Some(204),
-            None,
-            Some(262),
-            None,
-            None,
-            None,
-            None,
-            None,
-            Some(294),
-            None,
-        ];
-        assert_eq!(buckets, &expected_bucket);
-
-        let nodes: &Vec<FlagTableNode> = &flag_table.as_ref().unwrap().nodes;
-        assert_eq!(nodes.len(), 8);
-
-        assert_eq!(nodes[0], new_expected_node(0, "enabled_ro", 1, 1, None));
-        assert_eq!(nodes[1], new_expected_node(0, "enabled_rw", 1, 2, Some(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(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));
+        let expected_flag_table = aconfig_storage_file::test_utils::create_test_flag_table();
+        assert_eq!(flag_table.unwrap(), expected_flag_table);
     }
 }
diff --git a/tools/aconfig/aconfig/src/storage/flag_value.rs b/tools/aconfig/aconfig/src/storage/flag_value.rs
index e40bbc1..c15ba54 100644
--- a/tools/aconfig/aconfig/src/storage/flag_value.rs
+++ b/tools/aconfig/aconfig/src/storage/flag_value.rs
@@ -17,7 +17,7 @@
 use crate::commands::assign_flag_ids;
 use crate::storage::FlagPackage;
 use aconfig_protos::ProtoFlagState;
-use aconfig_storage_file::{FlagValueHeader, FlagValueList, FILE_VERSION, StorageFileType};
+use aconfig_storage_file::{FlagValueHeader, FlagValueList, StorageFileType, FILE_VERSION};
 use anyhow::{anyhow, Result};
 
 fn new_header(container: &str, num_flags: u32) -> FlagValueHeader {
@@ -41,19 +41,19 @@
     };
 
     for pkg in packages.iter() {
-        let start_offset = pkg.boolean_offset as usize;
+        let start_index = pkg.boolean_start_index as usize;
         let flag_ids = assign_flag_ids(pkg.package_name, pkg.boolean_flags.iter().copied())?;
         for pf in pkg.boolean_flags.iter() {
             let fid = flag_ids
                 .get(pf.name())
                 .ok_or(anyhow!(format!("missing flag id for {}", pf.name())))?;
 
-            list.booleans[start_offset + (*fid as usize)] = pf.state() == ProtoFlagState::ENABLED;
+            list.booleans[start_index + (*fid as usize)] = pf.state() == ProtoFlagState::ENABLED;
         }
     }
 
     // initialize all header fields
-    list.header.boolean_value_offset = list.header.as_bytes().len() as u32;
+    list.header.boolean_value_offset = list.header.into_bytes().len() as u32;
     list.header.file_size = list.header.boolean_value_offset + num_flags;
 
     Ok(list)
@@ -64,31 +64,19 @@
     use super::*;
     use crate::storage::{group_flags_by_package, tests::parse_all_test_flags};
 
-    pub fn create_test_flag_value_list() -> Result<FlagValueList> {
+    pub fn create_test_flag_value_list_from_source() -> Result<FlagValueList> {
         let caches = parse_all_test_flags();
         let packages = group_flags_by_package(caches.iter());
-        create_flag_value("system", &packages)
+        create_flag_value("mockup", &packages)
     }
 
     #[test]
     // this test point locks down the flag value creation and each field
     fn test_list_contents() {
-        let flag_value_list = create_test_flag_value_list();
+        let flag_value_list = create_test_flag_value_list_from_source();
         assert!(flag_value_list.is_ok());
-
-        let header: &FlagValueHeader = &flag_value_list.as_ref().unwrap().header;
-        let expected_header = FlagValueHeader {
-            version: FILE_VERSION,
-            container: String::from("system"),
-            file_type: StorageFileType::FlagVal as u8,
-            file_size: 35,
-            num_flags: 8,
-            boolean_value_offset: 27,
-        };
-        assert_eq!(header, &expected_header);
-
-        let booleans: &Vec<bool> = &flag_value_list.as_ref().unwrap().booleans;
-        let expected_booleans: Vec<bool> = vec![false; header.num_flags as usize];
-        assert_eq!(booleans, &expected_booleans);
+        let expected_flag_value_list =
+            aconfig_storage_file::test_utils::create_test_flag_value_list();
+        assert_eq!(flag_value_list.unwrap(), expected_flag_value_list);
     }
 }
diff --git a/tools/aconfig/aconfig/src/storage/mod.rs b/tools/aconfig/aconfig/src/storage/mod.rs
index c818d79..855ed02 100644
--- a/tools/aconfig/aconfig/src/storage/mod.rs
+++ b/tools/aconfig/aconfig/src/storage/mod.rs
@@ -18,7 +18,7 @@
 pub mod flag_value;
 pub mod package_table;
 
-use anyhow::Result;
+use anyhow::{anyhow, Result};
 use std::collections::{HashMap, HashSet};
 
 use crate::storage::{
@@ -33,9 +33,9 @@
     pub package_id: u32,
     pub flag_names: HashSet<&'a str>,
     pub boolean_flags: Vec<&'a ProtoParsedFlag>,
-    // offset of the first boolean flag in this flag package with respect to the start of
-    // boolean flag value array in the flag value file
-    pub boolean_offset: u32,
+    // The index of the first boolean flag in this aconfig package among all boolean
+    // flags in this container.
+    pub boolean_start_index: u32,
 }
 
 impl<'a> FlagPackage<'a> {
@@ -45,7 +45,7 @@
             package_id,
             flag_names: HashSet::new(),
             boolean_flags: vec![],
-            boolean_offset: 0,
+            boolean_start_index: 0,
         }
     }
 
@@ -73,12 +73,11 @@
         }
     }
 
-    // calculate package flag value start offset, in flag value file, each boolean
-    // is stored as a single byte
-    let mut boolean_offset = 0;
+    // cacluate boolean flag start index for each package
+    let mut boolean_start_index = 0;
     for p in packages.iter_mut() {
-        p.boolean_offset = boolean_offset;
-        boolean_offset += p.boolean_flags.len() as u32;
+        p.boolean_start_index = boolean_start_index;
+        boolean_start_index += p.boolean_flags.len() as u32;
     }
 
     packages
@@ -97,16 +96,17 @@
     match file {
         StorageFileType::PackageMap => {
             let package_table = create_package_table(container, &packages)?;
-            Ok(package_table.as_bytes())
+            Ok(package_table.into_bytes())
         }
         StorageFileType::FlagMap => {
             let flag_table = create_flag_table(container, &packages)?;
-            Ok(flag_table.as_bytes())
+            Ok(flag_table.into_bytes())
         }
         StorageFileType::FlagVal => {
             let flag_value = create_flag_value(container, &packages)?;
-            Ok(flag_value.as_bytes())
+            Ok(flag_value.into_bytes())
         }
+        _ => Err(anyhow!("aconfig does not support the creation of this storage file type")),
     }
 }
 
@@ -121,30 +121,38 @@
                 "com.android.aconfig.storage.test_1",
                 "storage_test_1.aconfig",
                 include_bytes!("../../tests/storage_test_1.aconfig").as_slice(),
+                "storage_test_1.value",
+                include_bytes!("../../tests/storage_test_1.values").as_slice(),
             ),
             (
                 "com.android.aconfig.storage.test_2",
                 "storage_test_2.aconfig",
                 include_bytes!("../../tests/storage_test_2.aconfig").as_slice(),
+                "storage_test_2.value",
+                include_bytes!("../../tests/storage_test_2.values").as_slice(),
             ),
             (
                 "com.android.aconfig.storage.test_4",
                 "storage_test_4.aconfig",
                 include_bytes!("../../tests/storage_test_4.aconfig").as_slice(),
+                "storage_test_4.value",
+                include_bytes!("../../tests/storage_test_4.values").as_slice(),
             ),
         ];
-
         aconfig_files
             .into_iter()
-            .map(|(pkg, file, content)| {
+            .map(|(pkg, aconfig_file, aconfig_content, value_file, value_content)| {
                 let bytes = crate::commands::parse_flags(
                     pkg,
                     Some("system"),
                     vec![Input {
-                        source: format!("tests/{}", file).to_string(),
-                        reader: Box::new(content),
+                        source: format!("tests/{}", aconfig_file).to_string(),
+                        reader: Box::new(aconfig_content),
                     }],
-                    vec![],
+                    vec![Input {
+                        source: format!("tests/{}", value_file).to_string(),
+                        reader: Box::new(value_content),
+                    }],
                     crate::commands::DEFAULT_FLAG_PERMISSION,
                 )
                 .unwrap();
@@ -175,7 +183,7 @@
         assert!(packages[0].flag_names.contains("enabled_rw"));
         assert!(packages[0].flag_names.contains("disabled_rw"));
         assert!(packages[0].flag_names.contains("enabled_ro"));
-        assert_eq!(packages[0].boolean_offset, 0);
+        assert_eq!(packages[0].boolean_start_index, 0);
 
         assert_eq!(packages[1].package_name, "com.android.aconfig.storage.test_2");
         assert_eq!(packages[1].package_id, 1);
@@ -183,13 +191,13 @@
         assert!(packages[1].flag_names.contains("enabled_ro"));
         assert!(packages[1].flag_names.contains("disabled_ro"));
         assert!(packages[1].flag_names.contains("enabled_fixed_ro"));
-        assert_eq!(packages[1].boolean_offset, 3);
+        assert_eq!(packages[1].boolean_start_index, 3);
 
         assert_eq!(packages[2].package_name, "com.android.aconfig.storage.test_4");
         assert_eq!(packages[2].package_id, 2);
         assert_eq!(packages[2].flag_names.len(), 2);
         assert!(packages[2].flag_names.contains("enabled_ro"));
         assert!(packages[2].flag_names.contains("enabled_fixed_ro"));
-        assert_eq!(packages[2].boolean_offset, 6);
+        assert_eq!(packages[2].boolean_start_index, 6);
     }
 }
diff --git a/tools/aconfig/aconfig/src/storage/package_table.rs b/tools/aconfig/aconfig/src/storage/package_table.rs
index bc2da4d..c53602f 100644
--- a/tools/aconfig/aconfig/src/storage/package_table.rs
+++ b/tools/aconfig/aconfig/src/storage/package_table.rs
@@ -17,7 +17,8 @@
 use anyhow::Result;
 
 use aconfig_storage_file::{
-    get_table_size, PackageTable, PackageTableHeader, PackageTableNode, FILE_VERSION, StorageFileType
+    get_table_size, PackageTable, PackageTableHeader, PackageTableNode, StorageFileType,
+    FILE_VERSION,
 };
 
 use crate::storage::FlagPackage;
@@ -47,7 +48,7 @@
         let node = PackageTableNode {
             package_name: String::from(package.package_name),
             package_id: package.package_id,
-            boolean_offset: package.boolean_offset,
+            boolean_start_index: package.boolean_start_index,
             next_offset: None,
         };
         let bucket_index = PackageTableNode::find_bucket_index(package.package_name, num_buckets);
@@ -65,10 +66,10 @@
         packages.iter().map(|pkg| PackageTableNodeWrapper::new(pkg, num_buckets)).collect();
 
     // initialize all header fields
-    header.bucket_offset = header.as_bytes().len() as u32;
+    header.bucket_offset = header.into_bytes().len() as u32;
     header.node_offset = header.bucket_offset + num_buckets * 4;
     header.file_size = header.node_offset
-        + node_wrappers.iter().map(|x| x.node.as_bytes().len()).sum::<usize>() as u32;
+        + node_wrappers.iter().map(|x| x.node.into_bytes().len()).sum::<usize>() as u32;
 
     // sort node_wrappers by bucket index for efficiency
     node_wrappers.sort_by(|a, b| a.bucket_index.cmp(&b.bucket_index));
@@ -86,7 +87,7 @@
         if buckets[node_bucket_idx as usize].is_none() {
             buckets[node_bucket_idx as usize] = Some(offset);
         }
-        offset += node_wrappers[i].node.as_bytes().len() as u32;
+        offset += node_wrappers[i].node.into_bytes().len() as u32;
 
         if let Some(index) = next_node_bucket_idx {
             if index == node_bucket_idx {
@@ -108,56 +109,18 @@
     use super::*;
     use crate::storage::{group_flags_by_package, tests::parse_all_test_flags};
 
-    pub fn create_test_package_table() -> Result<PackageTable> {
+    pub fn create_test_package_table_from_source() -> Result<PackageTable> {
         let caches = parse_all_test_flags();
         let packages = group_flags_by_package(caches.iter());
-        create_package_table("system", &packages)
+        create_package_table("mockup", &packages)
     }
 
     #[test]
     // this test point locks down the table creation and each field
     fn test_table_contents() {
-        let package_table = create_test_package_table();
+        let package_table = create_test_package_table_from_source();
         assert!(package_table.is_ok());
-
-        let header: &PackageTableHeader = &package_table.as_ref().unwrap().header;
-        let expected_header = PackageTableHeader {
-            version: FILE_VERSION,
-            container: String::from("system"),
-            file_type: StorageFileType::PackageMap as u8,
-            file_size: 209,
-            num_packages: 3,
-            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(59), None, None, Some(109), None, None, None];
-        assert_eq!(buckets, &expected);
-
-        let nodes: &Vec<PackageTableNode> = &package_table.as_ref().unwrap().nodes;
-        assert_eq!(nodes.len(), 3);
-        let first_node_expected = PackageTableNode {
-            package_name: String::from("com.android.aconfig.storage.test_2"),
-            package_id: 1,
-            boolean_offset: 3,
-            next_offset: None,
-        };
-        assert_eq!(nodes[0], first_node_expected);
-        let second_node_expected = PackageTableNode {
-            package_name: String::from("com.android.aconfig.storage.test_1"),
-            package_id: 0,
-            boolean_offset: 0,
-            next_offset: Some(159),
-        };
-        assert_eq!(nodes[1], second_node_expected);
-        let third_node_expected = PackageTableNode {
-            package_name: String::from("com.android.aconfig.storage.test_4"),
-            package_id: 2,
-            boolean_offset: 6,
-            next_offset: None,
-        };
-        assert_eq!(nodes[2], third_node_expected);
+        let expected_package_table = aconfig_storage_file::test_utils::create_test_package_table();
+        assert_eq!(package_table.unwrap(), expected_package_table);
     }
 }
diff --git a/tools/aconfig/aconfig/templates/FeatureFlags.java.template b/tools/aconfig/aconfig/templates/FeatureFlags.java.template
index 13edcb4..38c8f13 100644
--- a/tools/aconfig/aconfig/templates/FeatureFlags.java.template
+++ b/tools/aconfig/aconfig/templates/FeatureFlags.java.template
@@ -14,6 +14,7 @@
 {{ -endif- }}
 {{ -endif }}
 {{ -if not library_exported }}
+    @com.android.aconfig.annotations.AconfigFlagAccessor
     @UnsupportedAppUsage
 {{ -endif }}
     boolean {item.method_name}();
diff --git a/tools/aconfig/aconfig/templates/FeatureFlagsImpl.java.template b/tools/aconfig/aconfig/templates/FeatureFlagsImpl.java.template
index 12b2fc1..6235e69 100644
--- a/tools/aconfig/aconfig/templates/FeatureFlagsImpl.java.template
+++ b/tools/aconfig/aconfig/templates/FeatureFlagsImpl.java.template
@@ -47,6 +47,7 @@
 {{ -for flag in flag_elements }}
     @Override
 {{ -if not library_exported }}
+    @com.android.aconfig.annotations.AconfigFlagAccessor
     @UnsupportedAppUsage
 {{ -endif }}
     public boolean {flag.method_name}() \{
@@ -68,6 +69,7 @@
 {{ for flag in flag_elements }}
     @Override
 {{ -if not library_exported }}
+    @com.android.aconfig.annotations.AconfigFlagAccessor
     @UnsupportedAppUsage
 {{ -endif }}
     public boolean {flag.method_name}() \{
diff --git a/tools/aconfig/aconfig/templates/Flags.java.template b/tools/aconfig/aconfig/templates/Flags.java.template
index e105991..e2f70b9 100644
--- a/tools/aconfig/aconfig/templates/Flags.java.template
+++ b/tools/aconfig/aconfig/templates/Flags.java.template
@@ -18,6 +18,7 @@
 {{ -endif }}
 {{ -endif }}
 {{ -if not library_exported }}
+    @com.android.aconfig.annotations.AconfigFlagAccessor
     @UnsupportedAppUsage
 {{ -endif }}
     public static boolean {item.method_name}() \{
diff --git a/tools/aconfig/aconfig/tests/storage_test_1.values b/tools/aconfig/aconfig/tests/storage_test_1.values
new file mode 100644
index 0000000..35548ae
--- /dev/null
+++ b/tools/aconfig/aconfig/tests/storage_test_1.values
@@ -0,0 +1,18 @@
+flag_value {
+    package: "com.android.aconfig.storage.test_1"
+    name: "enabled_rw"
+    state: ENABLED
+    permission: READ_WRITE
+}
+flag_value {
+    package: "com.android.aconfig.storage.test_1"
+    name: "disabled_rw"
+    state: DISABLED
+    permission: READ_WRITE
+}
+flag_value {
+    package: "com.android.aconfig.storage.test_1"
+    name: "enabled_ro"
+    state: ENABLED
+    permission: READ_ONLY
+}
diff --git a/tools/aconfig/aconfig/tests/storage_test_2.values b/tools/aconfig/aconfig/tests/storage_test_2.values
new file mode 100644
index 0000000..a7bb0b1
--- /dev/null
+++ b/tools/aconfig/aconfig/tests/storage_test_2.values
@@ -0,0 +1,18 @@
+flag_value {
+    package: "com.android.aconfig.storage.test_2"
+    name: "enabled_ro"
+    state: ENABLED
+    permission: READ_ONLY
+}
+flag_value {
+    package: "com.android.aconfig.storage.test_2"
+    name: "disabled_ro"
+    state: DISABLED
+    permission: READ_ONLY
+}
+flag_value {
+    package: "com.android.aconfig.storage.test_2"
+    name: "enabled_fixed_ro"
+    state: ENABLED
+    permission: READ_ONLY
+}
diff --git a/tools/aconfig/aconfig/tests/storage_test_4.values b/tools/aconfig/aconfig/tests/storage_test_4.values
new file mode 100644
index 0000000..fa21317
--- /dev/null
+++ b/tools/aconfig/aconfig/tests/storage_test_4.values
@@ -0,0 +1,12 @@
+flag_value {
+    package: "com.android.aconfig.storage.test_4"
+    name: "enabled_ro"
+    state: ENABLED
+    permission: READ_ONLY
+}
+flag_value {
+    package: "com.android.aconfig.storage.test_4"
+    name: "enabled_fixed_ro"
+    state: ENABLED
+    permission: READ_ONLY
+}
diff --git a/tools/aconfig/aconfig_storage_file/protos/aconfig_storage_metadata.proto b/tools/aconfig/aconfig_storage_file/protos/aconfig_storage_metadata.proto
index c6728bd..e1c1c7f 100644
--- a/tools/aconfig/aconfig_storage_file/protos/aconfig_storage_metadata.proto
+++ b/tools/aconfig/aconfig_storage_file/protos/aconfig_storage_metadata.proto
@@ -26,7 +26,8 @@
   optional string package_map = 3;
   optional string flag_map = 4;
   optional string flag_val = 5;
-  optional int64 timestamp = 6;
+  optional string flag_info = 6;
+  optional int64 timestamp = 7;
 }
 
 message storage_files {
diff --git a/tools/aconfig/aconfig_storage_file/src/flag_info.rs b/tools/aconfig/aconfig_storage_file/src/flag_info.rs
new file mode 100644
index 0000000..dc2a8d6
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_file/src/flag_info.rs
@@ -0,0 +1,243 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//! flag info module defines the flag info file format and methods for serialization
+//! and deserialization
+
+use crate::{read_str_from_bytes, read_u32_from_bytes, read_u8_from_bytes};
+use crate::{AconfigStorageError, StorageFileType};
+use anyhow::anyhow;
+use std::fmt;
+
+/// Flag info header struct
+#[derive(PartialEq)]
+pub struct FlagInfoHeader {
+    pub version: u32,
+    pub container: String,
+    pub file_type: u8,
+    pub file_size: u32,
+    pub num_flags: u32,
+    pub boolean_flag_offset: u32,
+}
+
+/// Implement debug print trait for header
+impl fmt::Debug for FlagInfoHeader {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        writeln!(
+            f,
+            "Version: {}, Container: {}, File Type: {:?}, File Size: {}",
+            self.version,
+            self.container,
+            StorageFileType::try_from(self.file_type),
+            self.file_size
+        )?;
+        writeln!(
+            f,
+            "Num of Flags: {}, Boolean Flag Offset:{}",
+            self.num_flags, self.boolean_flag_offset
+        )?;
+        Ok(())
+    }
+}
+
+impl FlagInfoHeader {
+    /// Serialize to bytes
+    pub fn into_bytes(&self) -> Vec<u8> {
+        let mut result = Vec::new();
+        result.extend_from_slice(&self.version.to_le_bytes());
+        let container_bytes = self.container.as_bytes();
+        result.extend_from_slice(&(container_bytes.len() as u32).to_le_bytes());
+        result.extend_from_slice(container_bytes);
+        result.extend_from_slice(&self.file_type.to_le_bytes());
+        result.extend_from_slice(&self.file_size.to_le_bytes());
+        result.extend_from_slice(&self.num_flags.to_le_bytes());
+        result.extend_from_slice(&self.boolean_flag_offset.to_le_bytes());
+        result
+    }
+
+    /// Deserialize from bytes
+    pub fn from_bytes(bytes: &[u8]) -> Result<Self, AconfigStorageError> {
+        let mut head = 0;
+        let list = Self {
+            version: read_u32_from_bytes(bytes, &mut head)?,
+            container: read_str_from_bytes(bytes, &mut head)?,
+            file_type: read_u8_from_bytes(bytes, &mut head)?,
+            file_size: read_u32_from_bytes(bytes, &mut head)?,
+            num_flags: read_u32_from_bytes(bytes, &mut head)?,
+            boolean_flag_offset: read_u32_from_bytes(bytes, &mut head)?,
+        };
+        if list.file_type != StorageFileType::FlagInfo as u8 {
+            return Err(AconfigStorageError::BytesParseFail(anyhow!(
+                "binary file is not a flag info file"
+            )));
+        }
+        Ok(list)
+    }
+}
+
+/// bit field for flag info
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub enum FlagInfoBit {
+    IsSticky = 1 << 0,
+    IsReadWrite = 1 << 1,
+    HasOverride = 1 << 2,
+}
+
+/// Flag info node struct
+#[derive(PartialEq, Clone)]
+pub struct FlagInfoNode {
+    pub attributes: u8,
+}
+
+/// Implement debug print trait for node
+impl fmt::Debug for FlagInfoNode {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        writeln!(
+            f,
+            "sticky: {}, readwrite: {}, override: {}",
+            self.attributes & (FlagInfoBit::IsSticky as u8) != 0,
+            self.attributes & (FlagInfoBit::IsReadWrite as u8) != 0,
+            self.attributes & (FlagInfoBit::HasOverride as u8) != 0,
+        )?;
+        Ok(())
+    }
+}
+
+impl FlagInfoNode {
+    /// Serialize to bytes
+    pub fn into_bytes(&self) -> Vec<u8> {
+        let mut result = Vec::new();
+        result.extend_from_slice(&self.attributes.to_le_bytes());
+        result
+    }
+
+    /// Deserialize from bytes
+    pub fn from_bytes(bytes: &[u8]) -> Result<Self, AconfigStorageError> {
+        let mut head = 0;
+        let node = Self { attributes: read_u8_from_bytes(bytes, &mut head)? };
+        Ok(node)
+    }
+
+    /// Create flag info node
+    pub fn create(is_flag_rw: bool) -> Self {
+        Self { attributes: if is_flag_rw { FlagInfoBit::IsReadWrite as u8 } else { 0u8 } }
+    }
+}
+
+/// Flag info list struct
+#[derive(PartialEq)]
+pub struct FlagInfoList {
+    pub header: FlagInfoHeader,
+    pub nodes: Vec<FlagInfoNode>,
+}
+
+/// Implement debug print trait for flag info list
+impl fmt::Debug for FlagInfoList {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        writeln!(f, "Header:")?;
+        write!(f, "{:?}", self.header)?;
+        writeln!(f, "Nodes:")?;
+        for node in self.nodes.iter() {
+            write!(f, "{:?}", node)?;
+        }
+        Ok(())
+    }
+}
+
+impl FlagInfoList {
+    /// Serialize to bytes
+    pub fn into_bytes(&self) -> Vec<u8> {
+        [
+            self.header.into_bytes(),
+            self.nodes.iter().map(|v| v.into_bytes()).collect::<Vec<_>>().concat(),
+        ]
+        .concat()
+    }
+
+    /// Deserialize from bytes
+    pub fn from_bytes(bytes: &[u8]) -> Result<Self, AconfigStorageError> {
+        let header = FlagInfoHeader::from_bytes(bytes)?;
+        let num_flags = header.num_flags;
+        let mut head = header.into_bytes().len();
+        let nodes = (0..num_flags)
+            .map(|_| {
+                let node = FlagInfoNode::from_bytes(&bytes[head..])?;
+                head += node.into_bytes().len();
+                Ok(node)
+            })
+            .collect::<Result<Vec<_>, AconfigStorageError>>()
+            .map_err(|errmsg| {
+                AconfigStorageError::BytesParseFail(anyhow!(
+                    "fail to parse flag info list: {}",
+                    errmsg
+                ))
+            })?;
+        let list = Self { header, nodes };
+        Ok(list)
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use crate::test_utils::create_test_flag_info_list;
+
+    #[test]
+    // this test point locks down the value list serialization
+    fn test_serialization() {
+        let flag_info_list = create_test_flag_info_list();
+
+        let header: &FlagInfoHeader = &flag_info_list.header;
+        let reinterpreted_header = FlagInfoHeader::from_bytes(&header.into_bytes());
+        assert!(reinterpreted_header.is_ok());
+        assert_eq!(header, &reinterpreted_header.unwrap());
+
+        let nodes: &Vec<FlagInfoNode> = &flag_info_list.nodes;
+        for node in nodes.iter() {
+            let reinterpreted_node = FlagInfoNode::from_bytes(&node.into_bytes()).unwrap();
+            assert_eq!(node, &reinterpreted_node);
+        }
+
+        let flag_info_bytes = flag_info_list.into_bytes();
+        let reinterpreted_info_list = FlagInfoList::from_bytes(&flag_info_bytes);
+        assert!(reinterpreted_info_list.is_ok());
+        assert_eq!(&flag_info_list, &reinterpreted_info_list.unwrap());
+        assert_eq!(flag_info_bytes.len() as u32, header.file_size);
+    }
+
+    #[test]
+    // this test point locks down that version number should be at the top of serialized
+    // bytes
+    fn test_version_number() {
+        let flag_info_list = create_test_flag_info_list();
+        let bytes = &flag_info_list.into_bytes();
+        let mut head = 0;
+        let version = read_u32_from_bytes(bytes, &mut head).unwrap();
+        assert_eq!(version, 1);
+    }
+
+    #[test]
+    // this test point locks down file type check
+    fn test_file_type_check() {
+        let mut flag_info_list = create_test_flag_info_list();
+        flag_info_list.header.file_type = 123u8;
+        let error = FlagInfoList::from_bytes(&flag_info_list.into_bytes()).unwrap_err();
+        assert_eq!(
+            format!("{:?}", error),
+            format!("BytesParseFail(binary file is not a flag info file)")
+        );
+    }
+}
diff --git a/tools/aconfig/aconfig_storage_file/src/flag_table.rs b/tools/aconfig/aconfig_storage_file/src/flag_table.rs
index f9b3158..64b90ea 100644
--- a/tools/aconfig/aconfig_storage_file/src/flag_table.rs
+++ b/tools/aconfig/aconfig_storage_file/src/flag_table.rs
@@ -21,7 +21,7 @@
     get_bucket_index, read_str_from_bytes, read_u16_from_bytes, read_u32_from_bytes,
     read_u8_from_bytes,
 };
-use crate::{AconfigStorageError, StorageFileType};
+use crate::{AconfigStorageError, StorageFileType, StoredFlagType};
 use anyhow::anyhow;
 use std::fmt;
 
@@ -59,7 +59,7 @@
 
 impl FlagTableHeader {
     /// Serialize to bytes
-    pub fn as_bytes(&self) -> Vec<u8> {
+    pub fn into_bytes(&self) -> Vec<u8> {
         let mut result = Vec::new();
         result.extend_from_slice(&self.version.to_le_bytes());
         let container_bytes = self.container.as_bytes();
@@ -99,8 +99,9 @@
 pub struct FlagTableNode {
     pub package_id: u32,
     pub flag_name: String,
-    pub flag_type: u16,
-    pub flag_id: u16,
+    pub flag_type: StoredFlagType,
+    // within package flag index of this flag type
+    pub flag_index: u16,
     pub next_offset: Option<u32>,
 }
 
@@ -109,8 +110,8 @@
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         writeln!(
             f,
-            "Package Id: {}, Flag: {}, Type: {}, Offset: {}, Next: {:?}",
-            self.package_id, self.flag_name, self.flag_type, self.flag_id, self.next_offset
+            "Package Id: {}, Flag: {}, Type: {:?}, Index: {}, Next: {:?}",
+            self.package_id, self.flag_name, self.flag_type, self.flag_index, self.next_offset
         )?;
         Ok(())
     }
@@ -118,14 +119,14 @@
 
 impl FlagTableNode {
     /// Serialize to bytes
-    pub fn as_bytes(&self) -> Vec<u8> {
+    pub fn into_bytes(&self) -> Vec<u8> {
         let mut result = Vec::new();
         result.extend_from_slice(&self.package_id.to_le_bytes());
         let name_bytes = self.flag_name.as_bytes();
         result.extend_from_slice(&(name_bytes.len() as u32).to_le_bytes());
         result.extend_from_slice(name_bytes);
-        result.extend_from_slice(&self.flag_type.to_le_bytes());
-        result.extend_from_slice(&self.flag_id.to_le_bytes());
+        result.extend_from_slice(&(self.flag_type as u16).to_le_bytes());
+        result.extend_from_slice(&self.flag_index.to_le_bytes());
         result.extend_from_slice(&self.next_offset.unwrap_or(0).to_le_bytes());
         result
     }
@@ -136,8 +137,8 @@
         let node = Self {
             package_id: read_u32_from_bytes(bytes, &mut head)?,
             flag_name: read_str_from_bytes(bytes, &mut head)?,
-            flag_type: read_u16_from_bytes(bytes, &mut head)?,
-            flag_id: read_u16_from_bytes(bytes, &mut head)?,
+            flag_type: StoredFlagType::try_from(read_u16_from_bytes(bytes, &mut head)?)?,
+            flag_index: read_u16_from_bytes(bytes, &mut head)?,
             next_offset: match read_u32_from_bytes(bytes, &mut head)? {
                 0 => None,
                 val => Some(val),
@@ -178,11 +179,11 @@
 /// Flag table struct
 impl FlagTable {
     /// Serialize to bytes
-    pub fn as_bytes(&self) -> Vec<u8> {
+    pub fn into_bytes(&self) -> Vec<u8> {
         [
-            self.header.as_bytes(),
+            self.header.into_bytes(),
             self.buckets.iter().map(|v| v.unwrap_or(0).to_le_bytes()).collect::<Vec<_>>().concat(),
-            self.nodes.iter().map(|v| v.as_bytes()).collect::<Vec<_>>().concat(),
+            self.nodes.iter().map(|v| v.into_bytes()).collect::<Vec<_>>().concat(),
         ]
         .concat()
     }
@@ -192,7 +193,7 @@
         let header = FlagTableHeader::from_bytes(bytes)?;
         let num_flags = header.num_flags;
         let num_buckets = crate::get_table_size(num_flags)?;
-        let mut head = header.as_bytes().len();
+        let mut head = header.into_bytes().len();
         let buckets = (0..num_buckets)
             .map(|_| match read_u32_from_bytes(bytes, &mut head).unwrap() {
                 0 => None,
@@ -202,7 +203,7 @@
         let nodes = (0..num_flags)
             .map(|_| {
                 let node = FlagTableNode::from_bytes(&bytes[head..])?;
-                head += node.as_bytes().len();
+                head += node.into_bytes().len();
                 Ok(node)
             })
             .collect::<Result<Vec<_>, AconfigStorageError>>()
@@ -226,17 +227,17 @@
         let flag_table = create_test_flag_table();
 
         let header: &FlagTableHeader = &flag_table.header;
-        let reinterpreted_header = FlagTableHeader::from_bytes(&header.as_bytes());
+        let reinterpreted_header = FlagTableHeader::from_bytes(&header.into_bytes());
         assert!(reinterpreted_header.is_ok());
         assert_eq!(header, &reinterpreted_header.unwrap());
 
         let nodes: &Vec<FlagTableNode> = &flag_table.nodes;
         for node in nodes.iter() {
-            let reinterpreted_node = FlagTableNode::from_bytes(&node.as_bytes()).unwrap();
+            let reinterpreted_node = FlagTableNode::from_bytes(&node.into_bytes()).unwrap();
             assert_eq!(node, &reinterpreted_node);
         }
 
-        let flag_table_bytes = flag_table.as_bytes();
+        let flag_table_bytes = flag_table.into_bytes();
         let reinterpreted_table = FlagTable::from_bytes(&flag_table_bytes);
         assert!(reinterpreted_table.is_ok());
         assert_eq!(&flag_table, &reinterpreted_table.unwrap());
@@ -248,10 +249,10 @@
     // bytes
     fn test_version_number() {
         let flag_table = create_test_flag_table();
-        let bytes = &flag_table.as_bytes();
+        let bytes = &flag_table.into_bytes();
         let mut head = 0;
         let version = read_u32_from_bytes(bytes, &mut head).unwrap();
-        assert_eq!(version, 1234)
+        assert_eq!(version, 1);
     }
 
     #[test]
@@ -259,7 +260,7 @@
     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();
+        let error = FlagTable::from_bytes(&flag_table.into_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 c9d09a1..506924b 100644
--- a/tools/aconfig/aconfig_storage_file/src/flag_value.rs
+++ b/tools/aconfig/aconfig_storage_file/src/flag_value.rs
@@ -55,7 +55,7 @@
 
 impl FlagValueHeader {
     /// Serialize to bytes
-    pub fn as_bytes(&self) -> Vec<u8> {
+    pub fn into_bytes(&self) -> Vec<u8> {
         let mut result = Vec::new();
         result.extend_from_slice(&self.version.to_le_bytes());
         let container_bytes = self.container.as_bytes();
@@ -108,9 +108,9 @@
 
 impl FlagValueList {
     /// Serialize to bytes
-    pub fn as_bytes(&self) -> Vec<u8> {
+    pub fn into_bytes(&self) -> Vec<u8> {
         [
-            self.header.as_bytes(),
+            self.header.into_bytes(),
             self.booleans.iter().map(|&v| u8::from(v).to_le_bytes()).collect::<Vec<_>>().concat(),
         ]
         .concat()
@@ -120,7 +120,7 @@
     pub fn from_bytes(bytes: &[u8]) -> Result<Self, AconfigStorageError> {
         let header = FlagValueHeader::from_bytes(bytes)?;
         let num_flags = header.num_flags;
-        let mut head = header.as_bytes().len();
+        let mut head = header.into_bytes().len();
         let booleans =
             (0..num_flags).map(|_| read_u8_from_bytes(bytes, &mut head).unwrap() == 1).collect();
         let list = Self { header, booleans };
@@ -139,11 +139,11 @@
         let flag_value_list = create_test_flag_value_list();
 
         let header: &FlagValueHeader = &flag_value_list.header;
-        let reinterpreted_header = FlagValueHeader::from_bytes(&header.as_bytes());
+        let reinterpreted_header = FlagValueHeader::from_bytes(&header.into_bytes());
         assert!(reinterpreted_header.is_ok());
         assert_eq!(header, &reinterpreted_header.unwrap());
 
-        let flag_value_bytes = flag_value_list.as_bytes();
+        let flag_value_bytes = flag_value_list.into_bytes();
         let reinterpreted_value_list = FlagValueList::from_bytes(&flag_value_bytes);
         assert!(reinterpreted_value_list.is_ok());
         assert_eq!(&flag_value_list, &reinterpreted_value_list.unwrap());
@@ -155,10 +155,10 @@
     // bytes
     fn test_version_number() {
         let flag_value_list = create_test_flag_value_list();
-        let bytes = &flag_value_list.as_bytes();
+        let bytes = &flag_value_list.into_bytes();
         let mut head = 0;
         let version = read_u32_from_bytes(bytes, &mut head).unwrap();
-        assert_eq!(version, 1234)
+        assert_eq!(version, 1);
     }
 
     #[test]
@@ -166,7 +166,7 @@
     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();
+        let error = FlagValueList::from_bytes(&flag_value_list.into_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 24b16a1..070a3cf 100644
--- a/tools/aconfig/aconfig_storage_file/src/lib.rs
+++ b/tools/aconfig/aconfig_storage_file/src/lib.rs
@@ -32,25 +32,28 @@
 //! apis. DO NOT DIRECTLY USE THESE APIS IN YOUR SOURCE CODE. For auto generated flag apis
 //! please refer to the g3doc go/android-flags
 
+pub mod flag_info;
 pub mod flag_table;
 pub mod flag_value;
 pub mod package_table;
 pub mod protos;
-
-#[cfg(test)]
-mod test_utils;
+pub mod test_utils;
 
 use anyhow::anyhow;
+use std::cmp::Ordering;
 use std::collections::hash_map::DefaultHasher;
 use std::fs::File;
 use std::hash::{Hash, Hasher};
 use std::io::Read;
 
+pub use crate::flag_info::{FlagInfoBit, FlagInfoHeader, FlagInfoList, FlagInfoNode};
 pub use crate::flag_table::{FlagTable, FlagTableHeader, FlagTableNode};
 pub use crate::flag_value::{FlagValueHeader, FlagValueList};
 pub use crate::package_table::{PackageTable, PackageTableHeader, PackageTableNode};
 
-use crate::AconfigStorageError::{BytesParseFail, HashTableSizeLimit};
+use crate::AconfigStorageError::{
+    BytesParseFail, HashTableSizeLimit, InvalidFlagValueType, InvalidStoredFlagType,
+};
 
 /// Storage file version
 pub const FILE_VERSION: u32 = 1;
@@ -68,6 +71,7 @@
     PackageMap = 0,
     FlagMap = 1,
     FlagVal = 2,
+    FlagInfo = 3,
 }
 
 impl TryFrom<&str> for StorageFileType {
@@ -78,8 +82,9 @@
             "package_map" => Ok(Self::PackageMap),
             "flag_map" => Ok(Self::FlagMap),
             "flag_val" => Ok(Self::FlagVal),
+            "flag_info" => Ok(Self::FlagInfo),
             _ => Err(anyhow!(
-                "Invalid storage file type, valid types are package_map|flag_map|flag_val"
+                "Invalid storage file type, valid types are package_map|flag_map|flag_val|flag_info"
             )),
         }
     }
@@ -93,11 +98,108 @@
             x if x == Self::PackageMap as u8 => Ok(Self::PackageMap),
             x if x == Self::FlagMap as u8 => Ok(Self::FlagMap),
             x if x == Self::FlagVal as u8 => Ok(Self::FlagVal),
+            x if x == Self::FlagInfo as u8 => Ok(Self::FlagInfo),
             _ => Err(anyhow!("Invalid storage file type")),
         }
     }
 }
 
+/// Flag type enum as stored by storage file
+/// ONLY APPEND, NEVER REMOVE FOR BACKWARD COMPATIBILITY. THE MAX IS U16.
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+pub enum StoredFlagType {
+    ReadWriteBoolean = 0,
+    ReadOnlyBoolean = 1,
+    FixedReadOnlyBoolean = 2,
+}
+
+impl TryFrom<u16> for StoredFlagType {
+    type Error = AconfigStorageError;
+
+    fn try_from(value: u16) -> Result<Self, Self::Error> {
+        match value {
+            x if x == Self::ReadWriteBoolean as u16 => Ok(Self::ReadWriteBoolean),
+            x if x == Self::ReadOnlyBoolean as u16 => Ok(Self::ReadOnlyBoolean),
+            x if x == Self::FixedReadOnlyBoolean as u16 => Ok(Self::FixedReadOnlyBoolean),
+            _ => Err(InvalidStoredFlagType(anyhow!("Invalid stored flag type"))),
+        }
+    }
+}
+
+/// Flag value type enum, one FlagValueType maps to many StoredFlagType
+/// ONLY APPEND, NEVER REMOVE FOR BACKWARD COMPATIBILITY. THE MAX IS U16
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+pub enum FlagValueType {
+    Boolean = 0,
+}
+
+impl TryFrom<StoredFlagType> for FlagValueType {
+    type Error = AconfigStorageError;
+
+    fn try_from(value: StoredFlagType) -> Result<Self, Self::Error> {
+        match value {
+            StoredFlagType::ReadWriteBoolean => Ok(Self::Boolean),
+            StoredFlagType::ReadOnlyBoolean => Ok(Self::Boolean),
+            StoredFlagType::FixedReadOnlyBoolean => Ok(Self::Boolean),
+        }
+    }
+}
+
+impl TryFrom<u16> for FlagValueType {
+    type Error = AconfigStorageError;
+
+    fn try_from(value: u16) -> Result<Self, Self::Error> {
+        match value {
+            x if x == Self::Boolean as u16 => Ok(Self::Boolean),
+            _ => Err(InvalidFlagValueType(anyhow!("Invalid flag value type"))),
+        }
+    }
+}
+
+/// Storage query api error
+#[non_exhaustive]
+#[derive(thiserror::Error, Debug)]
+pub enum AconfigStorageError {
+    #[error("failed to read the file")]
+    FileReadFail(#[source] anyhow::Error),
+
+    #[error("fail to parse protobuf")]
+    ProtobufParseFail(#[source] anyhow::Error),
+
+    #[error("storage files not found for this container")]
+    StorageFileNotFound(#[source] anyhow::Error),
+
+    #[error("fail to map storage file")]
+    MapFileFail(#[source] anyhow::Error),
+
+    #[error("fail to get mapped file")]
+    ObtainMappedFileFail(#[source] anyhow::Error),
+
+    #[error("fail to flush mapped storage file")]
+    MapFlushFail(#[source] anyhow::Error),
+
+    #[error("number of items in hash table exceed limit")]
+    HashTableSizeLimit(#[source] anyhow::Error),
+
+    #[error("failed to parse bytes into data")]
+    BytesParseFail(#[source] anyhow::Error),
+
+    #[error("cannot parse storage files with a higher version")]
+    HigherStorageFileVersion(#[source] anyhow::Error),
+
+    #[error("invalid storage file byte offset")]
+    InvalidStorageFileOffset(#[source] anyhow::Error),
+
+    #[error("failed to create file")]
+    FileCreationFail(#[source] anyhow::Error),
+
+    #[error("invalid stored flag type")]
+    InvalidStoredFlagType(#[source] anyhow::Error),
+
+    #[error("invalid flag value type")]
+    InvalidFlagValueType(#[source] anyhow::Error),
+}
+
 /// Get the right hash table size given number of entries in the table. Use a
 /// load factor of 0.5 for performance.
 pub fn get_table_size(entries: u32) -> Result<u32, AconfigStorageError> {
@@ -160,44 +262,6 @@
     Ok(val)
 }
 
-/// Storage query api error
-#[non_exhaustive]
-#[derive(thiserror::Error, Debug)]
-pub enum AconfigStorageError {
-    #[error("failed to read the file")]
-    FileReadFail(#[source] anyhow::Error),
-
-    #[error("fail to parse protobuf")]
-    ProtobufParseFail(#[source] anyhow::Error),
-
-    #[error("storage files not found for this container")]
-    StorageFileNotFound(#[source] anyhow::Error),
-
-    #[error("fail to map storage file")]
-    MapFileFail(#[source] anyhow::Error),
-
-    #[error("fail to get mapped file")]
-    ObtainMappedFileFail(#[source] anyhow::Error),
-
-    #[error("fail to flush mapped storage file")]
-    MapFlushFail(#[source] anyhow::Error),
-
-    #[error("number of items in hash table exceed limit")]
-    HashTableSizeLimit(#[source] anyhow::Error),
-
-    #[error("failed to parse bytes into data")]
-    BytesParseFail(#[source] anyhow::Error),
-
-    #[error("cannot parse storage files with a higher version")]
-    HigherStorageFileVersion(#[source] anyhow::Error),
-
-    #[error("invalid storage file byte offset")]
-    InvalidStorageFileOffset(#[source] anyhow::Error),
-
-    #[error("failed to create file")]
-    FileCreationFail(#[source] anyhow::Error),
-}
-
 /// Read in storage file as bytes
 pub fn read_file_to_bytes(file_path: &str) -> Result<Vec<u8>, AconfigStorageError> {
     let mut file = File::open(file_path).map_err(|errmsg| {
@@ -206,7 +270,7 @@
     let mut buffer = Vec::new();
     file.read_to_end(&mut buffer).map_err(|errmsg| {
         AconfigStorageError::FileReadFail(anyhow!(
-            "Failed to read 4 bytes from file {}: {}",
+            "Failed to read bytes from file {}: {}",
             file_path,
             errmsg
         ))
@@ -219,26 +283,33 @@
     package_map: &str,
     flag_map: &str,
     flag_val: &str,
-) -> Result<Vec<(String, bool)>, AconfigStorageError> {
+) -> Result<Vec<(String, String, StoredFlagType, bool)>, AconfigStorageError> {
     let package_table = PackageTable::from_bytes(&read_file_to_bytes(package_map)?)?;
     let flag_table = FlagTable::from_bytes(&read_file_to_bytes(flag_map)?)?;
     let flag_value_list = FlagValueList::from_bytes(&read_file_to_bytes(flag_val)?)?;
 
     let mut package_info = vec![("", 0); package_table.header.num_packages as usize];
     for node in package_table.nodes.iter() {
-        package_info[node.package_id as usize] = (&node.package_name, node.boolean_offset);
+        package_info[node.package_id as usize] = (&node.package_name, node.boolean_start_index);
     }
 
     let mut flags = Vec::new();
     for node in flag_table.nodes.iter() {
         let (package_name, package_offset) = package_info[node.package_id as usize];
-        let full_flag_name = String::from(package_name) + "/" + &node.flag_name;
-        let flag_offset = package_offset + node.flag_id as u32;
+        let flag_offset = package_offset + node.flag_index as u32;
         let flag_value = flag_value_list.booleans[flag_offset as usize];
-        flags.push((full_flag_name, flag_value));
+        flags.push((
+            String::from(package_name),
+            node.flag_name.clone(),
+            node.flag_type,
+            flag_value,
+        ));
     }
 
-    flags.sort_by(|v1, v2| v1.0.cmp(&v2.0));
+    flags.sort_by(|v1, v2| match v1.0.cmp(&v2.0) {
+        Ordering::Equal => v1.1.cmp(&v2.1),
+        other => other,
+    });
     Ok(flags)
 }
 
@@ -254,10 +325,10 @@
     // this test point locks down the flag list api
     fn test_list_flag() {
         let package_table =
-            write_bytes_to_temp_file(&create_test_package_table().as_bytes()).unwrap();
-        let flag_table = write_bytes_to_temp_file(&create_test_flag_table().as_bytes()).unwrap();
+            write_bytes_to_temp_file(&create_test_package_table().into_bytes()).unwrap();
+        let flag_table = write_bytes_to_temp_file(&create_test_flag_table().into_bytes()).unwrap();
         let flag_value_list =
-            write_bytes_to_temp_file(&create_test_flag_value_list().as_bytes()).unwrap();
+            write_bytes_to_temp_file(&create_test_flag_value_list().into_bytes()).unwrap();
 
         let package_table_path = package_table.path().display().to_string();
         let flag_table_path = flag_table.path().display().to_string();
@@ -266,14 +337,54 @@
         let flags =
             list_flags(&package_table_path, &flag_table_path, &flag_value_list_path).unwrap();
         let expected = [
-            (String::from("com.android.aconfig.storage.test_1/disabled_rw"), false),
-            (String::from("com.android.aconfig.storage.test_1/enabled_ro"), true),
-            (String::from("com.android.aconfig.storage.test_1/enabled_rw"), false),
-            (String::from("com.android.aconfig.storage.test_2/disabled_ro"), false),
-            (String::from("com.android.aconfig.storage.test_2/enabled_fixed_ro"), true),
-            (String::from("com.android.aconfig.storage.test_2/enabled_ro"), true),
-            (String::from("com.android.aconfig.storage.test_4/enabled_fixed_ro"), false),
-            (String::from("com.android.aconfig.storage.test_4/enabled_ro"), true),
+            (
+                String::from("com.android.aconfig.storage.test_1"),
+                String::from("disabled_rw"),
+                StoredFlagType::ReadWriteBoolean,
+                false,
+            ),
+            (
+                String::from("com.android.aconfig.storage.test_1"),
+                String::from("enabled_ro"),
+                StoredFlagType::ReadOnlyBoolean,
+                true,
+            ),
+            (
+                String::from("com.android.aconfig.storage.test_1"),
+                String::from("enabled_rw"),
+                StoredFlagType::ReadWriteBoolean,
+                true,
+            ),
+            (
+                String::from("com.android.aconfig.storage.test_2"),
+                String::from("disabled_ro"),
+                StoredFlagType::ReadOnlyBoolean,
+                false,
+            ),
+            (
+                String::from("com.android.aconfig.storage.test_2"),
+                String::from("enabled_fixed_ro"),
+                StoredFlagType::FixedReadOnlyBoolean,
+                true,
+            ),
+            (
+                String::from("com.android.aconfig.storage.test_2"),
+                String::from("enabled_ro"),
+                StoredFlagType::ReadOnlyBoolean,
+                true,
+            ),
+            (
+                String::from("com.android.aconfig.storage.test_4"),
+                String::from("enabled_fixed_ro"),
+                StoredFlagType::FixedReadOnlyBoolean,
+                true,
+            ),
+            (
+                String::from("com.android.aconfig.storage.test_4"),
+                String::from("enabled_ro"),
+                StoredFlagType::ReadOnlyBoolean,
+                true,
+            ),
         ];
         assert_eq!(flags, expected);
     }
diff --git a/tools/aconfig/aconfig_storage_file/src/main.rs b/tools/aconfig/aconfig_storage_file/src/main.rs
index 293d018..b686274 100644
--- a/tools/aconfig/aconfig_storage_file/src/main.rs
+++ b/tools/aconfig/aconfig_storage_file/src/main.rs
@@ -17,8 +17,8 @@
 //! `aconfig-storage` is a debugging tool to parse storage files
 
 use aconfig_storage_file::{
-    list_flags, read_file_to_bytes, AconfigStorageError, FlagTable, FlagValueList, PackageTable,
-    StorageFileType,
+    list_flags, read_file_to_bytes, AconfigStorageError, FlagInfoList, FlagTable, FlagValueList,
+    PackageTable, StorageFileType,
 };
 
 use clap::{builder::ArgAction, Arg, Command};
@@ -67,6 +67,10 @@
             let flag_value = FlagValueList::from_bytes(&bytes)?;
             println!("{:?}", flag_value);
         }
+        StorageFileType::FlagInfo => {
+            let flag_info = FlagInfoList::from_bytes(&bytes)?;
+            println!("{:?}", flag_info);
+        }
     }
     Ok(())
 }
@@ -84,8 +88,8 @@
             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);
+            for (package_name, flag_name, flag_type, flag_value) in flags.iter() {
+                println!("{} {} {:?} {}", package_name, flag_name, flag_type, flag_value);
             }
         }
         _ => unreachable!(),
diff --git a/tools/aconfig/aconfig_storage_file/src/package_table.rs b/tools/aconfig/aconfig_storage_file/src/package_table.rs
index 7cb60eb..b734972 100644
--- a/tools/aconfig/aconfig_storage_file/src/package_table.rs
+++ b/tools/aconfig/aconfig_storage_file/src/package_table.rs
@@ -56,7 +56,7 @@
 
 impl PackageTableHeader {
     /// Serialize to bytes
-    pub fn as_bytes(&self) -> Vec<u8> {
+    pub fn into_bytes(&self) -> Vec<u8> {
         let mut result = Vec::new();
         result.extend_from_slice(&self.version.to_le_bytes());
         let container_bytes = self.container.as_bytes();
@@ -96,9 +96,9 @@
 pub struct PackageTableNode {
     pub package_name: String,
     pub package_id: u32,
-    // offset of the first boolean flag in this flag package with respect to the start of
-    // boolean flag value array in the flag value file
-    pub boolean_offset: u32,
+    // The index of the first boolean flag in this aconfig package among all boolean
+    // flags in this container.
+    pub boolean_start_index: u32,
     pub next_offset: Option<u32>,
 }
 
@@ -107,8 +107,8 @@
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         writeln!(
             f,
-            "Package: {}, Id: {}, Offset: {}, Next: {:?}",
-            self.package_name, self.package_id, self.boolean_offset, self.next_offset
+            "Package: {}, Id: {}, Boolean flag start index: {}, Next: {:?}",
+            self.package_name, self.package_id, self.boolean_start_index, self.next_offset
         )?;
         Ok(())
     }
@@ -116,13 +116,13 @@
 
 impl PackageTableNode {
     /// Serialize to bytes
-    pub fn as_bytes(&self) -> Vec<u8> {
+    pub fn into_bytes(&self) -> Vec<u8> {
         let mut result = Vec::new();
         let name_bytes = self.package_name.as_bytes();
         result.extend_from_slice(&(name_bytes.len() as u32).to_le_bytes());
         result.extend_from_slice(name_bytes);
         result.extend_from_slice(&self.package_id.to_le_bytes());
-        result.extend_from_slice(&self.boolean_offset.to_le_bytes());
+        result.extend_from_slice(&self.boolean_start_index.to_le_bytes());
         result.extend_from_slice(&self.next_offset.unwrap_or(0).to_le_bytes());
         result
     }
@@ -133,7 +133,7 @@
         let node = Self {
             package_name: read_str_from_bytes(bytes, &mut head)?,
             package_id: read_u32_from_bytes(bytes, &mut head)?,
-            boolean_offset: read_u32_from_bytes(bytes, &mut head)?,
+            boolean_start_index: read_u32_from_bytes(bytes, &mut head)?,
             next_offset: match read_u32_from_bytes(bytes, &mut head)? {
                 0 => None,
                 val => Some(val),
@@ -175,11 +175,11 @@
 
 impl PackageTable {
     /// Serialize to bytes
-    pub fn as_bytes(&self) -> Vec<u8> {
+    pub fn into_bytes(&self) -> Vec<u8> {
         [
-            self.header.as_bytes(),
+            self.header.into_bytes(),
             self.buckets.iter().map(|v| v.unwrap_or(0).to_le_bytes()).collect::<Vec<_>>().concat(),
-            self.nodes.iter().map(|v| v.as_bytes()).collect::<Vec<_>>().concat(),
+            self.nodes.iter().map(|v| v.into_bytes()).collect::<Vec<_>>().concat(),
         ]
         .concat()
     }
@@ -189,7 +189,7 @@
         let header = PackageTableHeader::from_bytes(bytes)?;
         let num_packages = header.num_packages;
         let num_buckets = crate::get_table_size(num_packages)?;
-        let mut head = header.as_bytes().len();
+        let mut head = header.into_bytes().len();
         let buckets = (0..num_buckets)
             .map(|_| match read_u32_from_bytes(bytes, &mut head).unwrap() {
                 0 => None,
@@ -199,7 +199,7 @@
         let nodes = (0..num_packages)
             .map(|_| {
                 let node = PackageTableNode::from_bytes(&bytes[head..])?;
-                head += node.as_bytes().len();
+                head += node.into_bytes().len();
                 Ok(node)
             })
             .collect::<Result<Vec<_>, AconfigStorageError>>()
@@ -225,17 +225,17 @@
     fn test_serialization() {
         let package_table = create_test_package_table();
         let header: &PackageTableHeader = &package_table.header;
-        let reinterpreted_header = PackageTableHeader::from_bytes(&header.as_bytes());
+        let reinterpreted_header = PackageTableHeader::from_bytes(&header.into_bytes());
         assert!(reinterpreted_header.is_ok());
         assert_eq!(header, &reinterpreted_header.unwrap());
 
         let nodes: &Vec<PackageTableNode> = &package_table.nodes;
         for node in nodes.iter() {
-            let reinterpreted_node = PackageTableNode::from_bytes(&node.as_bytes()).unwrap();
+            let reinterpreted_node = PackageTableNode::from_bytes(&node.into_bytes()).unwrap();
             assert_eq!(node, &reinterpreted_node);
         }
 
-        let package_table_bytes = package_table.as_bytes();
+        let package_table_bytes = package_table.into_bytes();
         let reinterpreted_table = PackageTable::from_bytes(&package_table_bytes);
         assert!(reinterpreted_table.is_ok());
         assert_eq!(&package_table, &reinterpreted_table.unwrap());
@@ -247,10 +247,10 @@
     // bytes
     fn test_version_number() {
         let package_table = create_test_package_table();
-        let bytes = &package_table.as_bytes();
+        let bytes = &package_table.into_bytes();
         let mut head = 0;
         let version = read_u32_from_bytes(bytes, &mut head).unwrap();
-        assert_eq!(version, 1234)
+        assert_eq!(version, 1);
     }
 
     #[test]
@@ -258,7 +258,7 @@
     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();
+        let error = PackageTable::from_bytes(&package_table.into_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 586bb4c..608563c 100644
--- a/tools/aconfig/aconfig_storage_file/src/test_utils.rs
+++ b/tools/aconfig/aconfig_storage_file/src/test_utils.rs
@@ -14,19 +14,20 @@
  * limitations under the License.
  */
 
+use crate::flag_info::{FlagInfoHeader, FlagInfoList, FlagInfoNode};
 use crate::flag_table::{FlagTable, FlagTableHeader, FlagTableNode};
 use crate::flag_value::{FlagValueHeader, FlagValueList};
 use crate::package_table::{PackageTable, PackageTableHeader, PackageTableNode};
-use crate::{AconfigStorageError, StorageFileType};
+use crate::{AconfigStorageError, StorageFileType, StoredFlagType};
 
 use anyhow::anyhow;
 use std::io::Write;
 use tempfile::NamedTempFile;
 
-pub(crate) fn create_test_package_table() -> PackageTable {
+pub fn create_test_package_table() -> PackageTable {
     let header = PackageTableHeader {
-        version: 1234,
-        container: String::from("system"),
+        version: 1,
+        container: String::from("mockup"),
         file_type: StorageFileType::PackageMap as u8,
         file_size: 209,
         num_packages: 3,
@@ -37,19 +38,19 @@
     let first_node = PackageTableNode {
         package_name: String::from("com.android.aconfig.storage.test_2"),
         package_id: 1,
-        boolean_offset: 3,
+        boolean_start_index: 3,
         next_offset: None,
     };
     let second_node = PackageTableNode {
         package_name: String::from("com.android.aconfig.storage.test_1"),
         package_id: 0,
-        boolean_offset: 0,
+        boolean_start_index: 0,
         next_offset: Some(159),
     };
     let third_node = PackageTableNode {
         package_name: String::from("com.android.aconfig.storage.test_4"),
         package_id: 2,
-        boolean_offset: 6,
+        boolean_start_index: 6,
         next_offset: None,
     };
     let nodes = vec![first_node, second_node, third_node];
@@ -62,17 +63,23 @@
         package_id: u32,
         flag_name: &str,
         flag_type: u16,
-        flag_id: u16,
+        flag_index: u16,
         next_offset: Option<u32>,
     ) -> Self {
-        Self { package_id, flag_name: flag_name.to_string(), flag_type, flag_id, next_offset }
+        Self {
+            package_id,
+            flag_name: flag_name.to_string(),
+            flag_type: StoredFlagType::try_from(flag_type).unwrap(),
+            flag_index,
+            next_offset,
+        }
     }
 }
 
-pub(crate) fn create_test_flag_table() -> FlagTable {
+pub fn create_test_flag_table() -> FlagTable {
     let header = FlagTableHeader {
-        version: 1234,
-        container: String::from("system"),
+        version: 1,
+        container: String::from("mockup"),
         file_type: StorageFileType::FlagMap as u8,
         file_size: 321,
         num_flags: 8,
@@ -100,31 +107,45 @@
     ];
     let nodes = vec![
         FlagTableNode::new_expected(0, "enabled_ro", 1, 1, None),
-        FlagTableNode::new_expected(0, "enabled_rw", 1, 2, Some(151)),
+        FlagTableNode::new_expected(0, "enabled_rw", 0, 2, Some(151)),
         FlagTableNode::new_expected(1, "disabled_ro", 1, 0, None),
         FlagTableNode::new_expected(2, "enabled_ro", 1, 1, None),
-        FlagTableNode::new_expected(1, "enabled_fixed_ro", 1, 1, Some(236)),
+        FlagTableNode::new_expected(1, "enabled_fixed_ro", 2, 1, Some(236)),
         FlagTableNode::new_expected(1, "enabled_ro", 1, 2, None),
-        FlagTableNode::new_expected(2, "enabled_fixed_ro", 1, 0, None),
-        FlagTableNode::new_expected(0, "disabled_rw", 1, 0, None),
+        FlagTableNode::new_expected(2, "enabled_fixed_ro", 2, 0, None),
+        FlagTableNode::new_expected(0, "disabled_rw", 0, 0, None),
     ];
     FlagTable { header, buckets, nodes }
 }
 
-pub(crate) fn create_test_flag_value_list() -> FlagValueList {
+pub fn create_test_flag_value_list() -> FlagValueList {
     let header = FlagValueHeader {
-        version: 1234,
-        container: String::from("system"),
+        version: 1,
+        container: String::from("mockup"),
         file_type: StorageFileType::FlagVal as u8,
         file_size: 35,
         num_flags: 8,
         boolean_value_offset: 27,
     };
-    let booleans: Vec<bool> = vec![false, true, false, false, true, true, false, true];
+    let booleans: Vec<bool> = vec![false, true, true, false, true, true, true, true];
     FlagValueList { header, booleans }
 }
 
-pub(crate) fn write_bytes_to_temp_file(bytes: &[u8]) -> Result<NamedTempFile, AconfigStorageError> {
+pub fn create_test_flag_info_list() -> FlagInfoList {
+    let header = FlagInfoHeader {
+        version: 1,
+        container: String::from("mockup"),
+        file_type: StorageFileType::FlagInfo as u8,
+        file_size: 35,
+        num_flags: 8,
+        boolean_flag_offset: 27,
+    };
+    let is_flag_rw = [true, false, true, false, false, false, false, false];
+    let nodes = is_flag_rw.iter().map(|&rw| FlagInfoNode::create(rw)).collect();
+    FlagInfoList { header, nodes }
+}
+
+pub fn write_bytes_to_temp_file(bytes: &[u8]) -> Result<NamedTempFile, AconfigStorageError> {
     let mut file = NamedTempFile::new().map_err(|_| {
         AconfigStorageError::FileCreationFail(anyhow!("Failed to create temp file"))
     })?;
diff --git a/tools/aconfig/aconfig_storage_read_api/Android.bp b/tools/aconfig/aconfig_storage_read_api/Android.bp
index b252e9d..3746d17 100644
--- a/tools/aconfig/aconfig_storage_read_api/Android.bp
+++ b/tools/aconfig/aconfig_storage_read_api/Android.bp
@@ -38,6 +38,7 @@
         "tests/package.map",
         "tests/flag.map",
         "tests/flag.val",
+        "tests/flag.info",
     ],
 }
 
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 ea756b3..39f1840 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
@@ -54,6 +54,8 @@
           return entry.flag_map();
         case StorageFileType::flag_val:
           return entry.flag_val();
+        case StorageFileType::flag_info:
+          return entry.flag_info();
         default:
           return Error() << "Invalid file type " << file_type;
       }
@@ -67,18 +69,18 @@
 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;
+    return ErrnoError() << "failed to open " << file;
   };
 
   struct stat fd_stat;
   if (fstat(fd, &fd_stat) < 0) {
-    return Error() << "fstat failed";
+    return ErrnoError() << "fstat failed";
   }
   size_t file_size = fd_stat.st_size;
 
   void* const map_result = mmap(nullptr, file_size, PROT_READ, MAP_SHARED, fd, 0);
   if (map_result == MAP_FAILED) {
-    return Error() << "mmap failed";
+    return ErrnoError() << "mmap failed";
   }
 
   auto mapped_file = MappedStorageFile();
@@ -124,49 +126,50 @@
   }
 }
 
-/// Get package offset
-Result<PackageOffset> get_package_offset(
+/// Get package context
+Result<PackageReadContext> get_package_read_context(
     MappedStorageFile const& file,
     std::string const& package) {
   auto content = rust::Slice<const uint8_t>(
       static_cast<uint8_t*>(file.file_ptr), file.file_size);
-  auto offset_cxx = get_package_offset_cxx(content, rust::Str(package.c_str()));
-  if (offset_cxx.query_success) {
-    auto offset = PackageOffset();
-    offset.package_exists = offset_cxx.package_exists;
-    offset.package_id = offset_cxx.package_id;
-    offset.boolean_offset = offset_cxx.boolean_offset;
-    return offset;
+  auto context_cxx = get_package_read_context_cxx(content, rust::Str(package.c_str()));
+  if (context_cxx.query_success) {
+    auto context = PackageReadContext();
+    context.package_exists = context_cxx.package_exists;
+    context.package_id = context_cxx.package_id;
+    context.boolean_start_index = context_cxx.boolean_start_index;
+    return context;
   } else {
-    return Error() << offset_cxx.error_message;
+    return Error() << context_cxx.error_message;
   }
 }
 
-/// Get flag offset
-Result<FlagOffset> get_flag_offset(
+/// Get flag read context
+Result<FlagReadContext> get_flag_read_context(
     MappedStorageFile const& file,
     uint32_t package_id,
     std::string const& flag_name){
   auto content = rust::Slice<const uint8_t>(
       static_cast<uint8_t*>(file.file_ptr), file.file_size);
-  auto offset_cxx = get_flag_offset_cxx(content, package_id, rust::Str(flag_name.c_str()));
-  if (offset_cxx.query_success) {
-    auto offset = FlagOffset();
-    offset.flag_exists = offset_cxx.flag_exists;
-    offset.flag_offset = offset_cxx.flag_offset;
-    return offset;
+  auto context_cxx = get_flag_read_context_cxx(content, package_id, rust::Str(flag_name.c_str()));
+  if (context_cxx.query_success) {
+    auto context = FlagReadContext();
+    context.flag_exists = context_cxx.flag_exists;
+    context.flag_type = static_cast<StoredFlagType>(context_cxx.flag_type);
+    context.flag_index = context_cxx.flag_index;
+    return context;
   } else {
-   return Error() << offset_cxx.error_message;
+   return Error() << context_cxx.error_message;
   }
 }
 
 /// Get boolean flag value
 Result<bool> get_boolean_flag_value(
     MappedStorageFile const& file,
-    uint32_t offset) {
+    uint32_t index) {
   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);
+  auto value_cxx = get_boolean_flag_value_cxx(content, index);
   if (value_cxx.query_success) {
     return value_cxx.flag_value;
   } else {
@@ -174,4 +177,17 @@
   }
 }
 
+/// Get boolean flag attribute
+Result<uint8_t> get_boolean_flag_attribute(
+    MappedStorageFile const& file,
+    uint32_t index) {
+  auto content = rust::Slice<const uint8_t>(
+      static_cast<uint8_t*>(file.file_ptr), file.file_size);
+  auto info_cxx = get_boolean_flag_attribute_cxx(content, index);
+  if (info_cxx.query_success) {
+    return info_cxx.flag_attribute;
+  } else {
+    return Error() << info_cxx.error_message;
+  }
+}
 } // 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 aa90f47..4555207 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
@@ -6,30 +6,56 @@
 
 namespace aconfig_storage {
 
-/// Storage file type enum
+/// Storage file type enum, to be consistent with the one defined in
+/// aconfig_storage_file/src/lib.rs
 enum StorageFileType {
   package_map,
   flag_map,
-  flag_val
+  flag_val,
+  flag_info
 };
 
+/// Flag type enum, to be consistent with the one defined in
+/// aconfig_storage_file/src/lib.rs
+enum StoredFlagType {
+  ReadWriteBoolean = 0,
+  ReadOnlyBoolean = 1,
+  FixedReadOnlyBoolean = 2,
+};
+
+/// Flag value type enum, to be consistent with the one defined in
+/// aconfig_storage_file/src/lib.rs
+enum FlagValueType {
+  Boolean = 0,
+};
+
+/// Flag info enum, to be consistent with the one defined in
+/// aconfig_storage_file/src/flag_info.rs
+enum FlagInfoBit {
+  IsSticky = 1<<0,
+  IsReadWrite = 1<<1,
+  HasOverride = 1<<2,
+};
+
+
 /// Mapped storage file
 struct MappedStorageFile {
   void* file_ptr;
   size_t file_size;
 };
 
-/// Package offset query result
-struct PackageOffset {
+/// Package read context query result
+struct PackageReadContext {
   bool package_exists;
   uint32_t package_id;
-  uint32_t boolean_offset;
+  uint32_t boolean_start_index;
 };
 
-/// Flag offset query result
-struct FlagOffset {
+/// Flag read context query result
+struct FlagReadContext {
   bool flag_exists;
-  uint16_t flag_offset;
+  StoredFlagType flag_type;
+  uint16_t flag_index;
 };
 
 /// DO NOT USE APIS IN THE FOLLOWING NAMESPACE DIRECTLY
@@ -56,30 +82,37 @@
 android::base::Result<uint32_t> get_storage_file_version(
     std::string const& file_path);
 
-/// Get package offset
+/// Get package read context
 /// \input file: mapped storage file
 /// \input package: the flag package name
-/// \returns a package offset
-android::base::Result<PackageOffset> get_package_offset(
+/// \returns a package read context
+android::base::Result<PackageReadContext> get_package_read_context(
     MappedStorageFile const& file,
     std::string const& package);
 
-/// Get flag offset
+/// Get flag read context
 /// \input file: mapped storage file
 /// \input package_id: the flag package id obtained from package offset query
 /// \input flag_name: flag name
-/// \returns the flag offset
-android::base::Result<FlagOffset> get_flag_offset(
+/// \returns the flag read context
+android::base::Result<FlagReadContext> get_flag_read_context(
     MappedStorageFile const& file,
     uint32_t package_id,
     std::string const& flag_name);
 
 /// Get boolean flag value
 /// \input file: mapped storage file
-/// \input offset: the boolean flag value byte offset in the file
+/// \input index: the boolean flag index in the file
 /// \returns the boolean flag value
 android::base::Result<bool> get_boolean_flag_value(
     MappedStorageFile const& file,
-    uint32_t offset);
+    uint32_t index);
 
+/// Get boolean flag attribute
+/// \input file: mapped storage file
+/// \input index: the boolean flag index in the file
+/// \returns the boolean flag attribute
+android::base::Result<uint8_t> get_boolean_flag_attribute(
+    MappedStorageFile const& file,
+    uint32_t index);
 } // namespace aconfig_storage
diff --git a/tools/aconfig/aconfig_storage_read_api/src/flag_info_query.rs b/tools/aconfig/aconfig_storage_read_api/src/flag_info_query.rs
new file mode 100644
index 0000000..807da97
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_read_api/src/flag_info_query.rs
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//! flag value query module defines the flag value file read from mapped bytes
+
+use crate::{AconfigStorageError, FILE_VERSION};
+use aconfig_storage_file::{flag_info::FlagInfoHeader, read_u8_from_bytes};
+use anyhow::anyhow;
+
+/// Get flag attribute bitfield
+pub fn find_boolean_flag_attribute(buf: &[u8], flag_index: u32) -> Result<u8, AconfigStorageError> {
+    let interpreted_header = FlagInfoHeader::from_bytes(buf)?;
+    if interpreted_header.version > crate::FILE_VERSION {
+        return Err(AconfigStorageError::HigherStorageFileVersion(anyhow!(
+            "Cannot read storage file with a higher version of {} with lib version {}",
+            interpreted_header.version,
+            FILE_VERSION
+        )));
+    }
+
+    // Find the byte offset to the flag info, each flag info now takes one byte
+    let mut head = (interpreted_header.boolean_flag_offset + flag_index) as usize;
+    if head >= interpreted_header.file_size as usize {
+        return Err(AconfigStorageError::InvalidStorageFileOffset(anyhow!(
+            "Flag info offset goes beyond the end of the file."
+        )));
+    }
+
+    let val = read_u8_from_bytes(buf, &mut head)?;
+    Ok(val)
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use aconfig_storage_file::{test_utils::create_test_flag_info_list, FlagInfoBit};
+
+    #[test]
+    // this test point locks down query if flag is sticky
+    fn test_is_flag_sticky() {
+        let flag_info_list = create_test_flag_info_list().into_bytes();
+        for offset in 0..8 {
+            let attribute = find_boolean_flag_attribute(&flag_info_list[..], offset).unwrap();
+            assert_eq!((attribute & FlagInfoBit::IsSticky as u8) != 0u8, false);
+        }
+    }
+
+    #[test]
+    // this test point locks down query if flag is readwrite
+    fn test_is_flag_readwrite() {
+        let flag_info_list = create_test_flag_info_list().into_bytes();
+        let baseline: Vec<bool> = vec![true, false, true, false, false, false, false, false];
+        for offset in 0..8 {
+            let attribute = find_boolean_flag_attribute(&flag_info_list[..], offset).unwrap();
+            assert_eq!(
+                (attribute & FlagInfoBit::IsReadWrite as u8) != 0u8,
+                baseline[offset as usize]
+            );
+        }
+    }
+
+    #[test]
+    // this test point locks down query if flag has override
+    fn test_flag_has_override() {
+        let flag_info_list = create_test_flag_info_list().into_bytes();
+        for offset in 0..8 {
+            let attribute = find_boolean_flag_attribute(&flag_info_list[..], offset).unwrap();
+            assert_eq!((attribute & FlagInfoBit::HasOverride as u8) != 0u8, false);
+        }
+    }
+
+    #[test]
+    // this test point locks down query beyond the end of boolean section
+    fn test_boolean_out_of_range() {
+        let flag_info_list = create_test_flag_info_list().into_bytes();
+        let error = find_boolean_flag_attribute(&flag_info_list[..], 8).unwrap_err();
+        assert_eq!(
+            format!("{:?}", error),
+            "InvalidStorageFileOffset(Flag info 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 info_list = create_test_flag_info_list();
+        info_list.header.version = crate::FILE_VERSION + 1;
+        let flag_info = info_list.into_bytes();
+        let error = find_boolean_flag_attribute(&flag_info[..], 4).unwrap_err();
+        assert_eq!(
+            format!("{:?}", error),
+            format!(
+                "HigherStorageFileVersion(Cannot read storage file with a higher version of {} with lib version {})",
+                crate::FILE_VERSION + 1,
+                crate::FILE_VERSION
+            )
+        );
+    }
+}
diff --git a/tools/aconfig/aconfig_storage_read_api/src/flag_table_query.rs b/tools/aconfig/aconfig_storage_read_api/src/flag_table_query.rs
index 43977ee..55fdcb7 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
@@ -18,18 +18,23 @@
 
 use crate::{AconfigStorageError, FILE_VERSION};
 use aconfig_storage_file::{
-    flag_table::FlagTableHeader, flag_table::FlagTableNode, read_u32_from_bytes,
+    flag_table::FlagTableHeader, flag_table::FlagTableNode, read_u32_from_bytes, StoredFlagType,
 };
 use anyhow::anyhow;
 
-pub type FlagOffset = u16;
+/// Flag table query return
+#[derive(PartialEq, Debug)]
+pub struct FlagReadContext {
+    pub flag_type: StoredFlagType,
+    pub flag_index: u16,
+}
 
-/// Query flag within package offset
-pub fn find_flag_offset(
+/// Query flag read context: flag type and within package flag index
+pub fn find_flag_read_context(
     buf: &[u8],
     package_id: u32,
     flag: &str,
-) -> Result<Option<FlagOffset>, AconfigStorageError> {
+) -> Result<Option<FlagReadContext>, AconfigStorageError> {
     let interpreted_header = FlagTableHeader::from_bytes(buf)?;
     if interpreted_header.version > crate::FILE_VERSION {
         return Err(AconfigStorageError::HigherStorageFileVersion(anyhow!(
@@ -53,7 +58,10 @@
     loop {
         let interpreted_node = FlagTableNode::from_bytes(&buf[flag_node_offset..])?;
         if interpreted_node.package_id == package_id && interpreted_node.flag_name == flag {
-            return Ok(Some(interpreted_node.flag_id));
+            return Ok(Some(FlagReadContext {
+                flag_type: interpreted_node.flag_type,
+                flag_index: interpreted_node.flag_index,
+            }));
         }
         match interpreted_node.next_offset {
             Some(offset) => flag_node_offset = offset as usize,
@@ -65,96 +73,38 @@
 #[cfg(test)]
 mod tests {
     use super::*;
-    use aconfig_storage_file::{FlagTable, StorageFileType};
-
-    // create test baseline, syntactic sugar
-    fn new_expected_node(
-        package_id: u32,
-        flag_name: &str,
-        flag_type: u16,
-        flag_id: u16,
-        next_offset: Option<u32>,
-    ) -> FlagTableNode {
-        FlagTableNode {
-            package_id,
-            flag_name: flag_name.to_string(),
-            flag_type,
-            flag_id,
-            next_offset,
-        }
-    }
-
-    pub fn create_test_flag_table() -> FlagTable {
-        let header = FlagTableHeader {
-            version: crate::FILE_VERSION,
-            container: String::from("system"),
-            file_type: StorageFileType::FlagMap as u8,
-            file_size: 321,
-            num_flags: 8,
-            bucket_offset: 31,
-            node_offset: 99,
-        };
-        let buckets: Vec<Option<u32>> = vec![
-            Some(99),
-            Some(125),
-            None,
-            None,
-            None,
-            Some(178),
-            None,
-            Some(204),
-            None,
-            Some(262),
-            None,
-            None,
-            None,
-            None,
-            None,
-            Some(294),
-            None,
-        ];
-        let nodes = vec![
-            new_expected_node(0, "enabled_ro", 1, 1, None),
-            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(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),
-        ];
-        FlagTable { header, buckets, nodes }
-    }
+    use aconfig_storage_file::test_utils::create_test_flag_table;
 
     #[test]
     // this test point locks down table query
     fn test_flag_query() {
-        let flag_table = create_test_flag_table().as_bytes();
+        let flag_table = create_test_flag_table().into_bytes();
         let baseline = vec![
-            (0, "enabled_ro", 1u16),
-            (0, "enabled_rw", 2u16),
-            (1, "disabled_ro", 0u16),
-            (2, "enabled_ro", 1u16),
-            (1, "enabled_fixed_ro", 1u16),
-            (1, "enabled_ro", 2u16),
-            (2, "enabled_fixed_ro", 0u16),
-            (0, "disabled_rw", 0u16),
+            (0, "enabled_ro", StoredFlagType::ReadOnlyBoolean, 1u16),
+            (0, "enabled_rw", StoredFlagType::ReadWriteBoolean, 2u16),
+            (1, "disabled_ro", StoredFlagType::ReadOnlyBoolean, 0u16),
+            (2, "enabled_ro", StoredFlagType::ReadOnlyBoolean, 1u16),
+            (1, "enabled_fixed_ro", StoredFlagType::FixedReadOnlyBoolean, 1u16),
+            (1, "enabled_ro", StoredFlagType::ReadOnlyBoolean, 2u16),
+            (2, "enabled_fixed_ro", StoredFlagType::FixedReadOnlyBoolean, 0u16),
+            (0, "disabled_rw", StoredFlagType::ReadWriteBoolean, 0u16),
         ];
-        for (package_id, flag_name, expected_offset) in baseline.into_iter() {
-            let flag_offset =
-                find_flag_offset(&flag_table[..], package_id, flag_name).unwrap().unwrap();
-            assert_eq!(flag_offset, expected_offset);
+        for (package_id, flag_name, flag_type, flag_index) in baseline.into_iter() {
+            let flag_context =
+                find_flag_read_context(&flag_table[..], package_id, flag_name).unwrap().unwrap();
+            assert_eq!(flag_context.flag_type, flag_type);
+            assert_eq!(flag_context.flag_index, flag_index);
         }
     }
 
     #[test]
     // this test point locks down table query of a non exist flag
     fn test_not_existed_flag_query() {
-        let flag_table = create_test_flag_table().as_bytes();
-        let flag_offset = find_flag_offset(&flag_table[..], 1, "disabled_fixed_ro").unwrap();
-        assert_eq!(flag_offset, None);
-        let flag_offset = find_flag_offset(&flag_table[..], 2, "disabled_rw").unwrap();
-        assert_eq!(flag_offset, None);
+        let flag_table = create_test_flag_table().into_bytes();
+        let flag_context = find_flag_read_context(&flag_table[..], 1, "disabled_fixed_ro").unwrap();
+        assert_eq!(flag_context, None);
+        let flag_context = find_flag_read_context(&flag_table[..], 2, "disabled_rw").unwrap();
+        assert_eq!(flag_context, None);
     }
 
     #[test]
@@ -162,8 +112,8 @@
     fn test_higher_version_storage_file() {
         let mut table = create_test_flag_table();
         table.header.version = crate::FILE_VERSION + 1;
-        let flag_table = table.as_bytes();
-        let error = find_flag_offset(&flag_table[..], 0, "enabled_ro").unwrap_err();
+        let flag_table = table.into_bytes();
+        let error = find_flag_read_context(&flag_table[..], 0, "enabled_ro").unwrap_err();
         assert_eq!(
             format!("{:?}", error),
             format!(
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 88d2397..9d32a16 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
@@ -21,7 +21,7 @@
 use anyhow::anyhow;
 
 /// Query flag value
-pub fn find_boolean_flag_value(buf: &[u8], flag_offset: u32) -> Result<bool, AconfigStorageError> {
+pub fn find_boolean_flag_value(buf: &[u8], flag_index: u32) -> Result<bool, AconfigStorageError> {
     let interpreted_header = FlagValueHeader::from_bytes(buf)?;
     if interpreted_header.version > crate::FILE_VERSION {
         return Err(AconfigStorageError::HigherStorageFileVersion(anyhow!(
@@ -31,10 +31,8 @@
         )));
     }
 
-    let mut head = (interpreted_header.boolean_value_offset + flag_offset) as usize;
-
-    // TODO: right now, there is only boolean flags, with more flag value types added
-    // later, the end of boolean flag value section should be updated (b/322826265).
+    // Find byte offset to the flag value, each boolean flag cost one byte to store
+    let mut head = (interpreted_header.boolean_value_offset + flag_index) as usize;
     if head >= interpreted_header.file_size as usize {
         return Err(AconfigStorageError::InvalidStorageFileOffset(anyhow!(
             "Flag value offset goes beyond the end of the file."
@@ -48,26 +46,13 @@
 #[cfg(test)]
 mod tests {
     use super::*;
-    use aconfig_storage_file::{FlagValueList, StorageFileType};
-
-    pub fn create_test_flag_value_list() -> FlagValueList {
-        let header = FlagValueHeader {
-            version: crate::FILE_VERSION,
-            container: String::from("system"),
-            file_type: StorageFileType::FlagVal as u8,
-            file_size: 35,
-            num_flags: 8,
-            boolean_value_offset: 27,
-        };
-        let booleans: Vec<bool> = vec![false, true, false, false, true, true, false, true];
-        FlagValueList { header, booleans }
-    }
+    use aconfig_storage_file::test_utils::create_test_flag_value_list;
 
     #[test]
     // this test point locks down flag value query
     fn test_flag_value_query() {
-        let flag_value_list = create_test_flag_value_list().as_bytes();
-        let baseline: Vec<bool> = vec![false, true, false, false, true, true, false, true];
+        let flag_value_list = create_test_flag_value_list().into_bytes();
+        let baseline: Vec<bool> = vec![false, true, true, false, true, true, true, true];
         for (offset, expected_value) in baseline.into_iter().enumerate() {
             let flag_value = find_boolean_flag_value(&flag_value_list[..], offset as u32).unwrap();
             assert_eq!(flag_value, expected_value);
@@ -77,7 +62,7 @@
     #[test]
     // this test point locks down query beyond the end of boolean section
     fn test_boolean_out_of_range() {
-        let flag_value_list = create_test_flag_value_list().as_bytes();
+        let flag_value_list = create_test_flag_value_list().into_bytes();
         let error = find_boolean_flag_value(&flag_value_list[..], 8).unwrap_err();
         assert_eq!(
             format!("{:?}", error),
@@ -90,7 +75,7 @@
     fn test_higher_version_storage_file() {
         let mut value_list = create_test_flag_value_list();
         value_list.header.version = crate::FILE_VERSION + 1;
-        let flag_value = value_list.as_bytes();
+        let flag_value = value_list.into_bytes();
         let error = find_boolean_flag_value(&flag_value[..], 4).unwrap_err();
         assert_eq!(
             format!("{:?}", error),
diff --git a/tools/aconfig/aconfig_storage_read_api/src/lib.rs b/tools/aconfig/aconfig_storage_read_api/src/lib.rs
index 8a71480..aa0145f 100644
--- a/tools/aconfig/aconfig_storage_read_api/src/lib.rs
+++ b/tools/aconfig/aconfig_storage_read_api/src/lib.rs
@@ -17,14 +17,16 @@
 //! `aconfig_storage_read_api` is a crate that defines read apis to read flags from storage
 //! files. It provides four apis to interface with storage files:
 //!
-//! 1, function to get package flag value start offset
-//! pub fn get_package_offset(container: &str, package: &str) -> `Result<Option<PackageOffset>>>`
+//! 1, function to get package read context
+//! pub fn get_packager_read_context(container: &str, package: &str)
+//! -> `Result<Option<PackageReadContext>>>`
 //!
-//! 2, function to get flag offset within a specific package
-//! pub fn get_flag_offset(container: &str, package_id: u32, flag: &str) -> `Result<Option<u16>>>`
+//! 2, function to get flag read context
+//! pub fn get_flag_read_context(container: &str, package_id: u32, flag: &str)
+//! -> `Result<Option<FlagReadContext>>>`
 //!
-//! 3, function to get the actual flag value given the global offset (combined package and
-//! flag offset).
+//! 3, function to get the actual flag value given the global index (combined package and
+//! flag index).
 //! pub fn get_boolean_flag_value(container: &str, offset: u32) -> `Result<bool>`
 //!
 //! 4, function to get storage file version without mmapping the file.
@@ -34,6 +36,7 @@
 //! apis. DO NOT DIRECTLY USE THESE APIS IN YOUR SOURCE CODE. For auto generated flag apis
 //! please refer to the g3doc go/android-flags
 
+pub mod flag_info_query;
 pub mod flag_table_query;
 pub mod flag_value_query;
 pub mod mapped_file;
@@ -43,13 +46,14 @@
 mod test_utils;
 
 pub use aconfig_storage_file::{AconfigStorageError, StorageFileType};
-pub use flag_table_query::FlagOffset;
-pub use package_table_query::PackageOffset;
+pub use flag_table_query::FlagReadContext;
+pub use package_table_query::PackageReadContext;
 
 use aconfig_storage_file::{read_u32_from_bytes, FILE_VERSION};
-use flag_table_query::find_flag_offset;
+use flag_info_query::find_boolean_flag_attribute;
+use flag_table_query::find_flag_read_context;
 use flag_value_query::find_boolean_flag_value;
-use package_table_query::find_package_offset;
+use package_table_query::find_package_read_context;
 
 use anyhow::anyhow;
 use memmap2::Mmap;
@@ -77,50 +81,50 @@
     unsafe { crate::mapped_file::get_mapped_file(STORAGE_LOCATION_FILE, container, file_type) }
 }
 
-/// Get package start offset for flags.
+/// Get package read context for a specific package.
 ///
 /// \input file: mapped package file
 /// \input package: package name
 ///
 /// \return
-/// If a package is found, it returns Ok(Some(PackageOffset))
+/// If a package is found, it returns Ok(Some(PackageReadContext))
 /// If a package is not found, it returns Ok(None)
 /// If errors out, it returns an Err(errmsg)
-pub fn get_package_offset(
+pub fn get_package_read_context(
     file: &Mmap,
     package: &str,
-) -> Result<Option<PackageOffset>, AconfigStorageError> {
-    find_package_offset(file, package)
+) -> Result<Option<PackageReadContext>, AconfigStorageError> {
+    find_package_read_context(file, package)
 }
 
-/// Get flag offset within a package given.
+/// Get flag read context for a specific flag.
 ///
 /// \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 found, it returns Ok(Some(FlagReadContext))
 /// If a flag is not found, it returns Ok(None)
 /// If errors out, it returns an Err(errmsg)
-pub fn get_flag_offset(
+pub fn get_flag_read_context(
     file: &Mmap,
     package_id: u32,
     flag: &str,
-) -> Result<Option<FlagOffset>, AconfigStorageError> {
-    find_flag_offset(file, package_id, flag)
+) -> Result<Option<FlagReadContext>, AconfigStorageError> {
+    find_flag_read_context(file, package_id, flag)
 }
 
 /// Get the boolean flag value.
 ///
 /// \input file: mapped flag file
-/// \input offset: flag value offset
+/// \input index: boolean flag offset
 ///
 /// \return
 /// If the provide offset is valid, it returns the boolean flag value, otherwise it
 /// returns the error message.
-pub fn get_boolean_flag_value(file: &Mmap, offset: u32) -> Result<bool, AconfigStorageError> {
-    find_boolean_flag_value(file, offset)
+pub fn get_boolean_flag_value(file: &Mmap, index: u32) -> Result<bool, AconfigStorageError> {
+    find_boolean_flag_value(file, index)
 }
 
 /// Get storage file version number
@@ -145,6 +149,18 @@
     read_u32_from_bytes(&buffer, &mut head)
 }
 
+/// Get the boolean flag attribute.
+///
+/// \input file: mapped flag info file
+/// \input index: boolean flag index
+///
+/// \return
+/// If the provide offset is valid, it returns the boolean flag attribute bitfiled, otherwise it
+/// returns the error message.
+pub fn get_boolean_flag_attribute(file: &Mmap, index: u32) -> Result<u8, AconfigStorageError> {
+    find_boolean_flag_attribute(file, index)
+}
+
 // *************************************** //
 // CC INTERLOP
 // *************************************** //
@@ -160,20 +176,21 @@
     }
 
     // Package table query return for cc interlop
-    pub struct PackageOffsetQueryCXX {
+    pub struct PackageReadContextQueryCXX {
         pub query_success: bool,
         pub error_message: String,
         pub package_exists: bool,
         pub package_id: u32,
-        pub boolean_offset: u32,
+        pub boolean_start_index: u32,
     }
 
     // Flag table query return for cc interlop
-    pub struct FlagOffsetQueryCXX {
+    pub struct FlagReadContextQueryCXX {
         pub query_success: bool,
         pub error_message: String,
         pub flag_exists: bool,
-        pub flag_offset: u16,
+        pub flag_type: u16,
+        pub flag_index: u16,
     }
 
     // Flag value query return for cc interlop
@@ -183,21 +200,42 @@
         pub flag_value: bool,
     }
 
+    // Flag info query return for cc interlop
+    pub struct BooleanFlagAttributeQueryCXX {
+        pub query_success: bool,
+        pub error_message: String,
+        pub flag_attribute: u8,
+    }
+
     // Rust export to c++
     extern "Rust" {
         pub fn get_storage_file_version_cxx(file_path: &str) -> VersionNumberQueryCXX;
 
-        pub fn get_package_offset_cxx(file: &[u8], package: &str) -> PackageOffsetQueryCXX;
+        pub fn get_package_read_context_cxx(
+            file: &[u8],
+            package: &str,
+        ) -> PackageReadContextQueryCXX;
 
-        pub fn get_flag_offset_cxx(file: &[u8], package_id: u32, flag: &str) -> FlagOffsetQueryCXX;
+        pub fn get_flag_read_context_cxx(
+            file: &[u8],
+            package_id: u32,
+            flag: &str,
+        ) -> FlagReadContextQueryCXX;
 
         pub fn get_boolean_flag_value_cxx(file: &[u8], offset: u32) -> BooleanFlagValueQueryCXX;
+
+        pub fn get_boolean_flag_attribute_cxx(
+            file: &[u8],
+            offset: u32,
+        ) -> BooleanFlagAttributeQueryCXX;
     }
 }
 
 /// Implement the package offset interlop return type, create from actual package offset api return type
-impl ffi::PackageOffsetQueryCXX {
-    pub(crate) fn new(offset_result: Result<Option<PackageOffset>, AconfigStorageError>) -> Self {
+impl ffi::PackageReadContextQueryCXX {
+    pub(crate) fn new(
+        offset_result: Result<Option<PackageReadContext>, AconfigStorageError>,
+    ) -> Self {
         match offset_result {
             Ok(offset_opt) => match offset_opt {
                 Some(offset) => Self {
@@ -205,14 +243,14 @@
                     error_message: String::from(""),
                     package_exists: true,
                     package_id: offset.package_id,
-                    boolean_offset: offset.boolean_offset,
+                    boolean_start_index: offset.boolean_start_index,
                 },
                 None => Self {
                     query_success: true,
                     error_message: String::from(""),
                     package_exists: false,
                     package_id: 0,
-                    boolean_offset: 0,
+                    boolean_start_index: 0,
                 },
             },
             Err(errmsg) => Self {
@@ -220,35 +258,38 @@
                 error_message: format!("{:?}", errmsg),
                 package_exists: false,
                 package_id: 0,
-                boolean_offset: 0,
+                boolean_start_index: 0,
             },
         }
     }
 }
 
 /// Implement the flag offset interlop return type, create from actual flag offset api return type
-impl ffi::FlagOffsetQueryCXX {
-    pub(crate) fn new(offset_result: Result<Option<FlagOffset>, AconfigStorageError>) -> Self {
+impl ffi::FlagReadContextQueryCXX {
+    pub(crate) fn new(offset_result: Result<Option<FlagReadContext>, AconfigStorageError>) -> Self {
         match offset_result {
             Ok(offset_opt) => match offset_opt {
                 Some(offset) => Self {
                     query_success: true,
                     error_message: String::from(""),
                     flag_exists: true,
-                    flag_offset: offset,
+                    flag_type: offset.flag_type as u16,
+                    flag_index: offset.flag_index,
                 },
                 None => Self {
                     query_success: true,
                     error_message: String::from(""),
                     flag_exists: false,
-                    flag_offset: 0,
+                    flag_type: 0u16,
+                    flag_index: 0u16,
                 },
             },
             Err(errmsg) => Self {
                 query_success: false,
                 error_message: format!("{:?}", errmsg),
                 flag_exists: false,
-                flag_offset: 0,
+                flag_type: 0u16,
+                flag_index: 0u16,
             },
         }
     }
@@ -270,6 +311,22 @@
     }
 }
 
+/// Implement the flag info interlop return type, create from actual flag info api return type
+impl ffi::BooleanFlagAttributeQueryCXX {
+    pub(crate) fn new(info_result: Result<u8, AconfigStorageError>) -> Self {
+        match info_result {
+            Ok(info) => {
+                Self { query_success: true, error_message: String::from(""), flag_attribute: info }
+            }
+            Err(errmsg) => Self {
+                query_success: false,
+                error_message: format!("{:?}", errmsg),
+                flag_attribute: 0u8,
+            },
+        }
+    }
+}
+
 /// Implement the storage version number interlop return type, create from actual version number
 /// api return type
 impl ffi::VersionNumberQueryCXX {
@@ -289,14 +346,18 @@
     }
 }
 
-/// 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 package read context cc interlop
+pub fn get_package_read_context_cxx(file: &[u8], package: &str) -> ffi::PackageReadContextQueryCXX {
+    ffi::PackageReadContextQueryCXX::new(find_package_read_context(file, package))
 }
 
-/// Get flag start offset cc interlop
-pub fn get_flag_offset_cxx(file: &[u8], package_id: u32, flag: &str) -> ffi::FlagOffsetQueryCXX {
-    ffi::FlagOffsetQueryCXX::new(find_flag_offset(file, package_id, flag))
+/// Get flag read context cc interlop
+pub fn get_flag_read_context_cxx(
+    file: &[u8],
+    package_id: u32,
+    flag: &str,
+) -> ffi::FlagReadContextQueryCXX {
+    ffi::FlagReadContextQueryCXX::new(find_flag_read_context(file, package_id, flag))
 }
 
 /// Get boolean flag value cc interlop
@@ -304,6 +365,14 @@
     ffi::BooleanFlagValueQueryCXX::new(find_boolean_flag_value(file, offset))
 }
 
+/// Get boolean flag attribute cc interlop
+pub fn get_boolean_flag_attribute_cxx(
+    file: &[u8],
+    offset: u32,
+) -> ffi::BooleanFlagAttributeQueryCXX {
+    ffi::BooleanFlagAttributeQueryCXX::new(find_boolean_flag_attribute(file, offset))
+}
+
 /// Get storage version number cc interlop
 pub fn get_storage_file_version_cxx(file_path: &str) -> ffi::VersionNumberQueryCXX {
     ffi::VersionNumberQueryCXX::new(get_storage_file_version(file_path))
@@ -315,96 +384,101 @@
     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 aconfig_storage_file::{FlagInfoBit, StoredFlagType};
     use tempfile::NamedTempFile;
 
-    fn create_test_storage_files() -> [NamedTempFile; 4] {
+    fn create_test_storage_files() -> [NamedTempFile; 5] {
         let package_map = copy_to_temp_file("./tests/package.map").unwrap();
         let flag_map = copy_to_temp_file("./tests/flag.map").unwrap();
         let flag_val = copy_to_temp_file("./tests/flag.val").unwrap();
+        let flag_info = copy_to_temp_file("./tests/flag.info").unwrap();
 
         let text_proto = format!(
             r#"
 files {{
     version: 0
-    container: "system"
+    container: "mockup"
     package_map: "{}"
     flag_map: "{}"
     flag_val: "{}"
+    flag_info: "{}"
     timestamp: 12345
 }}
 "#,
             package_map.path().display(),
             flag_map.path().display(),
-            flag_val.path().display()
+            flag_val.path().display(),
+            flag_info.path().display()
         );
         let pb_file = write_proto_to_temp_file(&text_proto).unwrap();
-        [package_map, flag_map, flag_val, pb_file]
+        [package_map, flag_map, flag_val, flag_info, pb_file]
     }
 
     #[test]
-    // this test point locks down flag package offset query
-    fn test_package_offset_query() {
-        let [_package_map, _flag_map, _flag_val, pb_file] = create_test_storage_files();
+    // this test point locks down flag package read context query
+    fn test_package_context_query() {
+        let [_package_map, _flag_map, _flag_val, _flag_info, pb_file] = create_test_storage_files();
         let pb_file_path = pb_file.path().display().to_string();
         let package_mapped_file = unsafe {
-            get_mapped_file(&pb_file_path, "system", StorageFileType::PackageMap).unwrap()
+            get_mapped_file(&pb_file_path, "mockup", StorageFileType::PackageMap).unwrap()
         };
 
-        let package_offset =
-            get_package_offset(&package_mapped_file, "com.android.aconfig.storage.test_1")
+        let package_context =
+            get_package_read_context(&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 expected_package_context = PackageReadContext { package_id: 0, boolean_start_index: 0 };
+        assert_eq!(package_context, expected_package_context);
 
-        let package_offset =
-            get_package_offset(&package_mapped_file, "com.android.aconfig.storage.test_2")
+        let package_context =
+            get_package_read_context(&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 expected_package_context = PackageReadContext { package_id: 1, boolean_start_index: 3 };
+        assert_eq!(package_context, expected_package_context);
 
-        let package_offset =
-            get_package_offset(&package_mapped_file, "com.android.aconfig.storage.test_4")
+        let package_context =
+            get_package_read_context(&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);
+        let expected_package_context = PackageReadContext { package_id: 2, boolean_start_index: 6 };
+        assert_eq!(package_context, expected_package_context);
     }
 
     #[test]
-    // this test point locks down flag offset query
-    fn test_flag_offset_query() {
-        let [_package_map, _flag_map, _flag_val, pb_file] = create_test_storage_files();
+    // this test point locks down flag read context query
+    fn test_flag_context_query() {
+        let [_package_map, _flag_map, _flag_val, _flag_info, pb_file] = create_test_storage_files();
         let pb_file_path = pb_file.path().display().to_string();
         let flag_mapped_file =
-            unsafe { get_mapped_file(&pb_file_path, "system", StorageFileType::FlagMap).unwrap() };
+            unsafe { get_mapped_file(&pb_file_path, "mockup", StorageFileType::FlagMap).unwrap() };
 
         let baseline = vec![
-            (0, "enabled_ro", 1u16),
-            (0, "enabled_rw", 2u16),
-            (1, "disabled_ro", 0u16),
-            (2, "enabled_ro", 1u16),
-            (1, "enabled_fixed_ro", 1u16),
-            (1, "enabled_ro", 2u16),
-            (2, "enabled_fixed_ro", 0u16),
-            (0, "disabled_rw", 0u16),
+            (0, "enabled_ro", StoredFlagType::ReadOnlyBoolean, 1u16),
+            (0, "enabled_rw", StoredFlagType::ReadWriteBoolean, 2u16),
+            (1, "disabled_ro", StoredFlagType::ReadOnlyBoolean, 0u16),
+            (2, "enabled_ro", StoredFlagType::ReadOnlyBoolean, 1u16),
+            (1, "enabled_fixed_ro", StoredFlagType::FixedReadOnlyBoolean, 1u16),
+            (1, "enabled_ro", StoredFlagType::ReadOnlyBoolean, 2u16),
+            (2, "enabled_fixed_ro", StoredFlagType::FixedReadOnlyBoolean, 0u16),
+            (0, "disabled_rw", StoredFlagType::ReadWriteBoolean, 0u16),
         ];
-        for (package_id, flag_name, expected_offset) in baseline.into_iter() {
-            let flag_offset =
-                get_flag_offset(&flag_mapped_file, package_id, flag_name).unwrap().unwrap();
-            assert_eq!(flag_offset, expected_offset);
+        for (package_id, flag_name, flag_type, flag_index) in baseline.into_iter() {
+            let flag_context =
+                get_flag_read_context(&flag_mapped_file, package_id, flag_name).unwrap().unwrap();
+            assert_eq!(flag_context.flag_type, flag_type);
+            assert_eq!(flag_context.flag_index, flag_index);
         }
     }
 
     #[test]
-    // this test point locks down flag offset query
+    // this test point locks down flag value query
     fn test_flag_value_query() {
-        let [_package_map, _flag_map, _flag_val, pb_file] = create_test_storage_files();
+        let [_package_map, _flag_map, _flag_val, _flag_info, pb_file] = create_test_storage_files();
         let pb_file_path = pb_file.path().display().to_string();
         let flag_value_file =
-            unsafe { get_mapped_file(&pb_file_path, "system", StorageFileType::FlagVal).unwrap() };
-        let baseline: Vec<bool> = vec![false; 8];
+            unsafe { get_mapped_file(&pb_file_path, "mockup", StorageFileType::FlagVal).unwrap() };
+        let baseline: Vec<bool> = vec![false, true, true, false, true, true, true, true];
         for (offset, expected_value) in baseline.into_iter().enumerate() {
             let flag_value = get_boolean_flag_value(&flag_value_file, offset as u32).unwrap();
             assert_eq!(flag_value, expected_value);
@@ -412,10 +486,27 @@
     }
 
     #[test]
+    // this test point locks donw flag info query
+    fn test_flag_info_query() {
+        let [_package_map, _flag_map, _flag_val, _flag_info, pb_file] = create_test_storage_files();
+        let pb_file_path = pb_file.path().display().to_string();
+        let flag_info_file =
+            unsafe { get_mapped_file(&pb_file_path, "mockup", StorageFileType::FlagInfo).unwrap() };
+        let is_rw: Vec<bool> = vec![true, false, true, false, false, false, false, false];
+        for (offset, expected_value) in is_rw.into_iter().enumerate() {
+            let attribute = get_boolean_flag_attribute(&flag_info_file, offset as u32).unwrap();
+            assert!((attribute & FlagInfoBit::IsSticky as u8) == 0u8);
+            assert_eq!((attribute & FlagInfoBit::IsReadWrite as u8) != 0u8, expected_value);
+            assert!((attribute & FlagInfoBit::HasOverride as u8) == 0u8);
+        }
+    }
+
+    #[test]
     // this test point locks down flag storage file version number query api
     fn test_storage_version_query() {
         assert_eq!(get_storage_file_version("./tests/package.map").unwrap(), 1);
         assert_eq!(get_storage_file_version("./tests/flag.map").unwrap(), 1);
         assert_eq!(get_storage_file_version("./tests/flag.val").unwrap(), 1);
+        assert_eq!(get_storage_file_version("./tests/flag.info").unwrap(), 1);
     }
 }
diff --git a/tools/aconfig/aconfig_storage_read_api/src/mapped_file.rs b/tools/aconfig/aconfig_storage_read_api/src/mapped_file.rs
index 86c6a1b..3786443 100644
--- a/tools/aconfig/aconfig_storage_read_api/src/mapped_file.rs
+++ b/tools/aconfig/aconfig_storage_read_api/src/mapped_file.rs
@@ -29,7 +29,7 @@
 };
 
 /// Find where storage files are stored for a particular container
-fn find_container_storage_location(
+pub fn find_container_storage_location(
     location_pb_file: &str,
     container: &str,
 ) -> Result<ProtoStorageFileInfo, AconfigStorageError> {
@@ -91,6 +91,7 @@
         StorageFileType::PackageMap => unsafe { map_file(files_location.package_map()) },
         StorageFileType::FlagMap => unsafe { map_file(files_location.flag_map()) },
         StorageFileType::FlagVal => unsafe { map_file(files_location.flag_val()) },
+        StorageFileType::FlagInfo => unsafe { map_file(files_location.flag_info()) },
     }
 }
 
diff --git a/tools/aconfig/aconfig_storage_read_api/src/package_table_query.rs b/tools/aconfig/aconfig_storage_read_api/src/package_table_query.rs
index 3587e10..2cb854b 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
@@ -24,16 +24,16 @@
 
 /// Package table query return
 #[derive(PartialEq, Debug)]
-pub struct PackageOffset {
+pub struct PackageReadContext {
     pub package_id: u32,
-    pub boolean_offset: u32,
+    pub boolean_start_index: u32,
 }
 
-/// Query package id and start offset
-pub fn find_package_offset(
+/// Query package read context: package id and start index
+pub fn find_package_read_context(
     buf: &[u8],
     package: &str,
-) -> Result<Option<PackageOffset>, AconfigStorageError> {
+) -> Result<Option<PackageReadContext>, AconfigStorageError> {
     let interpreted_header = PackageTableHeader::from_bytes(buf)?;
     if interpreted_header.version > FILE_VERSION {
         return Err(AconfigStorageError::HigherStorageFileVersion(anyhow!(
@@ -57,9 +57,9 @@
     loop {
         let interpreted_node = PackageTableNode::from_bytes(&buf[package_node_offset..])?;
         if interpreted_node.package_name == package {
-            return Ok(Some(PackageOffset {
+            return Ok(Some(PackageReadContext {
                 package_id: interpreted_node.package_id,
-                boolean_offset: interpreted_node.boolean_offset,
+                boolean_start_index: interpreted_node.boolean_start_index,
             }));
         }
         match interpreted_node.next_offset {
@@ -72,77 +72,46 @@
 #[cfg(test)]
 mod tests {
     use super::*;
-    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_type: StorageFileType::PackageMap as u8,
-            file_size: 209,
-            num_packages: 3,
-            bucket_offset: 31,
-            node_offset: 59,
-        };
-        let buckets: Vec<Option<u32>> = vec![Some(59), None, None, Some(109), None, None, None];
-        let first_node = PackageTableNode {
-            package_name: String::from("com.android.aconfig.storage.test_2"),
-            package_id: 1,
-            boolean_offset: 3,
-            next_offset: None,
-        };
-        let second_node = PackageTableNode {
-            package_name: String::from("com.android.aconfig.storage.test_1"),
-            package_id: 0,
-            boolean_offset: 0,
-            next_offset: Some(159),
-        };
-        let third_node = PackageTableNode {
-            package_name: String::from("com.android.aconfig.storage.test_4"),
-            package_id: 2,
-            boolean_offset: 6,
-            next_offset: None,
-        };
-        let nodes = vec![first_node, second_node, third_node];
-        PackageTable { header, buckets, nodes }
-    }
+    use aconfig_storage_file::test_utils::create_test_package_table;
 
     #[test]
     // this test point locks down table query
     fn test_package_query() {
-        let package_table = create_test_package_table().as_bytes();
-        let package_offset =
-            find_package_offset(&package_table[..], "com.android.aconfig.storage.test_1")
+        let package_table = create_test_package_table().into_bytes();
+        let package_context =
+            find_package_read_context(&package_table[..], "com.android.aconfig.storage.test_1")
                 .unwrap()
                 .unwrap();
-        let expected_package_offset = PackageOffset { package_id: 0, boolean_offset: 0 };
-        assert_eq!(package_offset, expected_package_offset);
-        let package_offset =
-            find_package_offset(&package_table[..], "com.android.aconfig.storage.test_2")
+        let expected_package_context = PackageReadContext { package_id: 0, boolean_start_index: 0 };
+        assert_eq!(package_context, expected_package_context);
+        let package_context =
+            find_package_read_context(&package_table[..], "com.android.aconfig.storage.test_2")
                 .unwrap()
                 .unwrap();
-        let expected_package_offset = PackageOffset { package_id: 1, boolean_offset: 3 };
-        assert_eq!(package_offset, expected_package_offset);
-        let package_offset =
-            find_package_offset(&package_table[..], "com.android.aconfig.storage.test_4")
+        let expected_package_context = PackageReadContext { package_id: 1, boolean_start_index: 3 };
+        assert_eq!(package_context, expected_package_context);
+        let package_context =
+            find_package_read_context(&package_table[..], "com.android.aconfig.storage.test_4")
                 .unwrap()
                 .unwrap();
-        let expected_package_offset = PackageOffset { package_id: 2, boolean_offset: 6 };
-        assert_eq!(package_offset, expected_package_offset);
+        let expected_package_context = PackageReadContext { package_id: 2, boolean_start_index: 6 };
+        assert_eq!(package_context, expected_package_context);
     }
 
     #[test]
     // this test point locks down table query of a non exist package
     fn test_not_existed_package_query() {
         // this will land at an empty bucket
-        let package_table = create_test_package_table().as_bytes();
-        let package_offset =
-            find_package_offset(&package_table[..], "com.android.aconfig.storage.test_3").unwrap();
-        assert_eq!(package_offset, None);
+        let package_table = create_test_package_table().into_bytes();
+        let package_context =
+            find_package_read_context(&package_table[..], "com.android.aconfig.storage.test_3")
+                .unwrap();
+        assert_eq!(package_context, None);
         // this will land at the end of a linked list
-        let package_offset =
-            find_package_offset(&package_table[..], "com.android.aconfig.storage.test_5").unwrap();
-        assert_eq!(package_offset, None);
+        let package_context =
+            find_package_read_context(&package_table[..], "com.android.aconfig.storage.test_5")
+                .unwrap();
+        assert_eq!(package_context, None);
     }
 
     #[test]
@@ -150,9 +119,10 @@
     fn test_higher_version_storage_file() {
         let mut table = create_test_package_table();
         table.header.version = crate::FILE_VERSION + 1;
-        let package_table = table.as_bytes();
-        let error = find_package_offset(&package_table[..], "com.android.aconfig.storage.test_1")
-            .unwrap_err();
+        let package_table = table.into_bytes();
+        let error =
+            find_package_read_context(&package_table[..], "com.android.aconfig.storage.test_1")
+                .unwrap_err();
         assert_eq!(
             format!("{:?}", error),
             format!(
diff --git a/tools/aconfig/aconfig_storage_read_api/tests/Android.bp b/tools/aconfig/aconfig_storage_read_api/tests/Android.bp
index d9cf238..6b05ca6 100644
--- a/tools/aconfig/aconfig_storage_read_api/tests/Android.bp
+++ b/tools/aconfig/aconfig_storage_read_api/tests/Android.bp
@@ -14,6 +14,7 @@
         "package.map",
         "flag.map",
         "flag.val",
+        "flag.info",
     ],
     test_suites: ["general-tests"],
 }
@@ -35,6 +36,7 @@
         "package.map",
         "flag.map",
         "flag.val",
+        "flag.info",
     ],
     test_suites: [
         "device-tests",
diff --git a/tools/aconfig/aconfig_storage_read_api/tests/flag.info b/tools/aconfig/aconfig_storage_read_api/tests/flag.info
new file mode 100644
index 0000000..820d839
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_read_api/tests/flag.info
Binary files differ
diff --git a/tools/aconfig/aconfig_storage_read_api/tests/flag.map b/tools/aconfig/aconfig_storage_read_api/tests/flag.map
index 5507894..d26e00f 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 75b8564..ed203d4 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 02267e5..6c46a03 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 1d36aae..8ed3756 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
@@ -47,15 +47,17 @@
 
   Result<std::string> write_storage_location_pb_file(std::string const& package_map,
                                                      std::string const& flag_map,
-                                                     std::string const& flag_val) {
+                                                     std::string const& flag_val,
+                                                     std::string const& flag_info) {
     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_container("mockup");
     info->set_package_map(package_map);
     info->set_flag_map(flag_map);
     info->set_flag_val(flag_val);
+    info->set_flag_info(flag_info);
     info->set_timestamp(12345);
 
     auto content = std::string();
@@ -71,20 +73,23 @@
     package_map = *copy_to_temp_file(test_dir + "/package.map");
     flag_map = *copy_to_temp_file(test_dir + "/flag.map");
     flag_val = *copy_to_temp_file(test_dir + "/flag.val");
+    flag_info = *copy_to_temp_file(test_dir + "/flag.info");
     storage_record_pb = *write_storage_location_pb_file(
-        package_map, flag_map, flag_val);
+        package_map, flag_map, flag_val, flag_info);
   }
 
   void TearDown() override {
     std::remove(package_map.c_str());
     std::remove(flag_map.c_str());
     std::remove(flag_val.c_str());
+    std::remove(flag_info.c_str());
     std::remove(storage_record_pb.c_str());
   }
 
   std::string package_map;
   std::string flag_map;
   std::string flag_val;
+  std::string flag_info;
   std::string storage_record_pb;
 };
 
@@ -99,6 +104,9 @@
   version = api::get_storage_file_version(flag_val);
   ASSERT_TRUE(version.ok());
   ASSERT_EQ(*version, 1);
+  version = api::get_storage_file_version(flag_info);
+  ASSERT_TRUE(version.ok());
+  ASSERT_EQ(*version, 1);
 }
 
 /// Negative test to lock down the error when mapping none exist storage files
@@ -110,102 +118,105 @@
             "Unable to find storage files for container vendor");
 }
 
-/// Test to lock down storage package offset query api
-TEST_F(AconfigStorageTest, test_package_offset_query) {
+/// Test to lock down storage package context query api
+TEST_F(AconfigStorageTest, test_package_context_query) {
   auto mapped_file = private_api::get_mapped_file_impl(
-      storage_record_pb, "system", api::StorageFileType::package_map);
+      storage_record_pb, "mockup", api::StorageFileType::package_map);
   ASSERT_TRUE(mapped_file.ok());
 
-  auto offset = api::get_package_offset(
+  auto context = api::get_package_read_context(
       *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);
+  ASSERT_TRUE(context.ok());
+  ASSERT_TRUE(context->package_exists);
+  ASSERT_EQ(context->package_id, 0);
+  ASSERT_EQ(context->boolean_start_index, 0);
 
-  offset = api::get_package_offset(
+  context = api::get_package_read_context(
       *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);
+  ASSERT_TRUE(context.ok());
+  ASSERT_TRUE(context->package_exists);
+  ASSERT_EQ(context->package_id, 1);
+  ASSERT_EQ(context->boolean_start_index, 3);
 
-  offset = api::get_package_offset(
+  context = api::get_package_read_context(
       *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);
+  ASSERT_TRUE(context.ok());
+  ASSERT_TRUE(context->package_exists);
+  ASSERT_EQ(context->package_id, 2);
+  ASSERT_EQ(context->boolean_start_index, 6);
 }
 
 /// Test to lock down when querying none exist package
-TEST_F(AconfigStorageTest, test_none_existent_package_offset_query) {
+TEST_F(AconfigStorageTest, test_none_existent_package_context_query) {
   auto mapped_file = private_api::get_mapped_file_impl(
-      storage_record_pb, "system", api::StorageFileType::package_map);
+      storage_record_pb, "mockup", api::StorageFileType::package_map);
   ASSERT_TRUE(mapped_file.ok());
 
-  auto offset = api::get_package_offset(
+  auto context = api::get_package_read_context(
       *mapped_file, "com.android.aconfig.storage.test_3");
-  ASSERT_TRUE(offset.ok());
-  ASSERT_FALSE(offset->package_exists);
+  ASSERT_TRUE(context.ok());
+  ASSERT_FALSE(context->package_exists);
 }
 
-/// Test to lock down storage flag offset query api
-TEST_F(AconfigStorageTest, test_flag_offset_query) {
+/// Test to lock down storage flag context query api
+TEST_F(AconfigStorageTest, test_flag_context_query) {
   auto mapped_file = private_api::get_mapped_file_impl(
-      storage_record_pb, "system", api::StorageFileType::flag_map);
+      storage_record_pb, "mockup", api::StorageFileType::flag_map);
   ASSERT_TRUE(mapped_file.ok());
 
-  auto baseline = std::vector<std::tuple<int, std::string, int>>{
-    {0, "enabled_ro", 1},
-    {0, "enabled_rw", 2},
-    {1, "disabled_ro", 0},
-    {2, "enabled_ro", 1},
-    {1, "enabled_fixed_ro", 1},
-    {1, "enabled_ro", 2},
-    {2, "enabled_fixed_ro", 0},
-    {0, "disabled_rw", 0},
+  auto baseline = std::vector<std::tuple<int, std::string, api::StoredFlagType, int>>{
+    {0, "enabled_ro", api::StoredFlagType::ReadOnlyBoolean, 1},
+    {0, "enabled_rw", api::StoredFlagType::ReadWriteBoolean, 2},
+    {1, "disabled_ro", api::StoredFlagType::ReadOnlyBoolean, 0},
+    {2, "enabled_ro", api::StoredFlagType::ReadOnlyBoolean, 1},
+    {1, "enabled_fixed_ro", api::StoredFlagType::FixedReadOnlyBoolean, 1},
+    {1, "enabled_ro", api::StoredFlagType::ReadOnlyBoolean, 2},
+    {2, "enabled_fixed_ro", api::StoredFlagType::FixedReadOnlyBoolean, 0},
+    {0, "disabled_rw", api::StoredFlagType::ReadWriteBoolean, 0},
   };
-  for (auto const&[package_id, flag_name, expected_offset] : baseline) {
-    auto offset = api::get_flag_offset(*mapped_file, package_id, flag_name);
-    ASSERT_TRUE(offset.ok());
-    ASSERT_TRUE(offset->flag_exists);
-    ASSERT_EQ(offset->flag_offset, expected_offset);
+  for (auto const&[package_id, flag_name, flag_type, flag_index] : baseline) {
+    auto context = api::get_flag_read_context(*mapped_file, package_id, flag_name);
+    ASSERT_TRUE(context.ok());
+    ASSERT_TRUE(context->flag_exists);
+    ASSERT_EQ(context->flag_type, flag_type);
+    ASSERT_EQ(context->flag_index, flag_index);
   }
 }
 
 /// Test to lock down when querying none exist flag
-TEST_F(AconfigStorageTest, test_none_existent_flag_offset_query) {
+TEST_F(AconfigStorageTest, test_none_existent_flag_context_query) {
   auto mapped_file = private_api::get_mapped_file_impl(
-      storage_record_pb, "system", api::StorageFileType::flag_map);
+      storage_record_pb, "mockup", api::StorageFileType::flag_map);
   ASSERT_TRUE(mapped_file.ok());
 
-  auto offset = api::get_flag_offset(*mapped_file, 0, "none_exist");
-  ASSERT_TRUE(offset.ok());
-  ASSERT_FALSE(offset->flag_exists);
+  auto context = api::get_flag_read_context(*mapped_file, 0, "none_exist");
+  ASSERT_TRUE(context.ok());
+  ASSERT_FALSE(context->flag_exists);
 
-  offset = api::get_flag_offset(*mapped_file, 3, "enabled_ro");
-  ASSERT_TRUE(offset.ok());
-  ASSERT_FALSE(offset->flag_exists);
+  context = api::get_flag_read_context(*mapped_file, 3, "enabled_ro");
+  ASSERT_TRUE(context.ok());
+  ASSERT_FALSE(context->flag_exists);
 }
 
 /// Test to lock down storage flag value query api
 TEST_F(AconfigStorageTest, test_boolean_flag_value_query) {
   auto mapped_file = private_api::get_mapped_file_impl(
-      storage_record_pb, "system", api::StorageFileType::flag_val);
+      storage_record_pb, "mockup", api::StorageFileType::flag_val);
   ASSERT_TRUE(mapped_file.ok());
 
-  for (int offset = 0; offset < 8; ++offset) {
-    auto value = api::get_boolean_flag_value(*mapped_file, offset);
+  auto expected_value = std::vector<bool>{
+    false, true, true, false, true, true, true, true};
+  for (int index = 0; index < 8; ++index) {
+    auto value = api::get_boolean_flag_value(*mapped_file, index);
     ASSERT_TRUE(value.ok());
-    ASSERT_FALSE(*value);
+    ASSERT_EQ(*value, expected_value[index]);
   }
 }
 
 /// 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);
+      storage_record_pb, "mockup", api::StorageFileType::flag_val);
   ASSERT_TRUE(mapped_file.ok());
 
   auto value = api::get_boolean_flag_value(*mapped_file, 8);
@@ -213,3 +224,33 @@
   ASSERT_EQ(value.error().message(),
             std::string("InvalidStorageFileOffset(Flag value offset goes beyond the end of the file.)"));
 }
+
+/// Test to lock down storage flag info query api
+TEST_F(AconfigStorageTest, test_boolean_flag_info_query) {
+  auto mapped_file = private_api::get_mapped_file_impl(
+      storage_record_pb, "mockup", api::StorageFileType::flag_info);
+  ASSERT_TRUE(mapped_file.ok());
+
+  auto expected_value = std::vector<bool>{
+    true, false, true, false, false, false, false, false};
+  for (int index = 0; index < 8; ++index) {
+    auto attribute = api::get_boolean_flag_attribute(*mapped_file, index);
+    ASSERT_TRUE(attribute.ok());
+    ASSERT_EQ(*attribute & static_cast<uint8_t>(api::FlagInfoBit::IsSticky), 0);
+    ASSERT_EQ((*attribute & static_cast<uint8_t>(api::FlagInfoBit::IsReadWrite)) != 0,
+              expected_value[index]);
+    ASSERT_EQ(*attribute & static_cast<uint8_t>(api::FlagInfoBit::HasOverride), 0);
+  }
+}
+
+/// Negative test to lock down the error when querying flag info out of range
+TEST_F(AconfigStorageTest, test_invalid_boolean_flag_info_query) {
+  auto mapped_file = private_api::get_mapped_file_impl(
+      storage_record_pb, "mockup", api::StorageFileType::flag_info);
+  ASSERT_TRUE(mapped_file.ok());
+
+  auto attribute = api::get_boolean_flag_attribute(*mapped_file, 8);
+  ASSERT_FALSE(attribute.ok());
+  ASSERT_EQ(attribute.error().message(),
+            std::string("InvalidStorageFileOffset(Flag info offset goes beyond the end of the file.)"));
+}
diff --git a/tools/aconfig/aconfig_storage_read_api/tests/storage_read_api_test.rs b/tools/aconfig/aconfig_storage_read_api/tests/storage_read_api_test.rs
index afcd5a7..bddb396 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,10 +1,11 @@
 #[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_file::{FlagInfoBit, StorageFileType, StoredFlagType};
     use aconfig_storage_read_api::{
-        get_boolean_flag_value, get_flag_offset, get_package_offset, get_storage_file_version,
-        mapped_file::get_mapped_file, PackageOffset,
+        get_boolean_flag_attribute, get_boolean_flag_value, get_flag_read_context,
+        get_package_read_context, get_storage_file_version, mapped_file::get_mapped_file,
+        PackageReadContext,
     };
     use std::fs;
     use tempfile::NamedTempFile;
@@ -15,33 +16,36 @@
         file
     }
 
-    fn create_test_storage_files() -> [NamedTempFile; 4] {
+    fn create_test_storage_files() -> [NamedTempFile; 5] {
         let package_map = copy_to_temp_file("./package.map");
         let flag_map = copy_to_temp_file("./flag.map");
         let flag_val = copy_to_temp_file("./flag.val");
+        let flag_info = copy_to_temp_file("./flag.info");
 
         let text_proto = format!(
             r#"
 files {{
     version: 0
-    container: "system"
+    container: "mockup"
     package_map: "{}"
     flag_map: "{}"
     flag_val: "{}"
+    flag_info: "{}"
     timestamp: 12345
 }}
 "#,
             package_map.path().display(),
             flag_map.path().display(),
-            flag_val.path().display()
+            flag_val.path().display(),
+            flag_info.path().display()
         );
         let pb_file = write_proto_to_temp_file(&text_proto).unwrap();
-        [package_map, flag_map, flag_val, pb_file]
+        [package_map, flag_map, flag_val, flag_info, pb_file]
     }
 
     #[test]
     fn test_unavailable_stoarge() {
-        let [_package_map, _flag_map, _flag_val, pb_file] = create_test_storage_files();
+        let [_package_map, _flag_map, _flag_val, _flag_info, pb_file] = create_test_storage_files();
         let pb_file_path = pb_file.path().display().to_string();
         // SAFETY:
         // The safety here is ensured as the test process will not write to temp storage file
@@ -55,102 +59,106 @@
     }
 
     #[test]
-    fn test_package_offset_query() {
-        let [_package_map, _flag_map, _flag_val, pb_file] = create_test_storage_files();
+    fn test_package_context_query() {
+        let [_package_map, _flag_map, _flag_val, _flag_info, pb_file] = create_test_storage_files();
         let pb_file_path = pb_file.path().display().to_string();
         // SAFETY:
         // The safety here is ensured as the test process will not write to temp storage file
         let package_mapped_file = unsafe {
-            get_mapped_file(&pb_file_path, "system", StorageFileType::PackageMap).unwrap()
+            get_mapped_file(&pb_file_path, "mockup", StorageFileType::PackageMap).unwrap()
         };
 
-        let package_offset =
-            get_package_offset(&package_mapped_file, "com.android.aconfig.storage.test_1")
+        let package_context =
+            get_package_read_context(&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 expected_package_context = PackageReadContext { package_id: 0, boolean_start_index: 0 };
+        assert_eq!(package_context, expected_package_context);
 
-        let package_offset =
-            get_package_offset(&package_mapped_file, "com.android.aconfig.storage.test_2")
+        let package_context =
+            get_package_read_context(&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 expected_package_context = PackageReadContext { package_id: 1, boolean_start_index: 3 };
+        assert_eq!(package_context, expected_package_context);
 
-        let package_offset =
-            get_package_offset(&package_mapped_file, "com.android.aconfig.storage.test_4")
+        let package_context =
+            get_package_read_context(&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);
+        let expected_package_context = PackageReadContext { package_id: 2, boolean_start_index: 6 };
+        assert_eq!(package_context, expected_package_context);
     }
 
     #[test]
-    fn test_none_exist_package_offset_query() {
-        let [_package_map, _flag_map, _flag_val, pb_file] = create_test_storage_files();
+    fn test_none_exist_package_context_query() {
+        let [_package_map, _flag_map, _flag_val, _flag_info, pb_file] = create_test_storage_files();
         let pb_file_path = pb_file.path().display().to_string();
         // SAFETY:
         // The safety here is ensured as the test process will not write to temp storage file
         let package_mapped_file = unsafe {
-            get_mapped_file(&pb_file_path, "system", StorageFileType::PackageMap).unwrap()
+            get_mapped_file(&pb_file_path, "mockup", StorageFileType::PackageMap).unwrap()
         };
 
-        let package_offset_option =
-            get_package_offset(&package_mapped_file, "com.android.aconfig.storage.test_3").unwrap();
-        assert_eq!(package_offset_option, None);
+        let package_context_option =
+            get_package_read_context(&package_mapped_file, "com.android.aconfig.storage.test_3")
+                .unwrap();
+        assert_eq!(package_context_option, None);
     }
 
     #[test]
-    fn test_flag_offset_query() {
-        let [_package_map, _flag_map, _flag_val, pb_file] = create_test_storage_files();
+    fn test_flag_context_query() {
+        let [_package_map, _flag_map, _flag_val, _flag_info, pb_file] = create_test_storage_files();
         let pb_file_path = pb_file.path().display().to_string();
         // SAFETY:
         // The safety here is ensured as the test process will not write to temp storage file
         let flag_mapped_file =
-            unsafe { get_mapped_file(&pb_file_path, "system", StorageFileType::FlagMap).unwrap() };
+            unsafe { get_mapped_file(&pb_file_path, "mockup", StorageFileType::FlagMap).unwrap() };
 
         let baseline = vec![
-            (0, "enabled_ro", 1u16),
-            (0, "enabled_rw", 2u16),
-            (1, "disabled_ro", 0u16),
-            (2, "enabled_ro", 1u16),
-            (1, "enabled_fixed_ro", 1u16),
-            (1, "enabled_ro", 2u16),
-            (2, "enabled_fixed_ro", 0u16),
-            (0, "disabled_rw", 0u16),
+            (0, "enabled_ro", StoredFlagType::ReadOnlyBoolean, 1u16),
+            (0, "enabled_rw", StoredFlagType::ReadWriteBoolean, 2u16),
+            (1, "disabled_ro", StoredFlagType::ReadOnlyBoolean, 0u16),
+            (2, "enabled_ro", StoredFlagType::ReadOnlyBoolean, 1u16),
+            (1, "enabled_fixed_ro", StoredFlagType::FixedReadOnlyBoolean, 1u16),
+            (1, "enabled_ro", StoredFlagType::ReadOnlyBoolean, 2u16),
+            (2, "enabled_fixed_ro", StoredFlagType::FixedReadOnlyBoolean, 0u16),
+            (0, "disabled_rw", StoredFlagType::ReadWriteBoolean, 0u16),
         ];
-        for (package_id, flag_name, expected_offset) in baseline.into_iter() {
-            let flag_offset =
-                get_flag_offset(&flag_mapped_file, package_id, flag_name).unwrap().unwrap();
-            assert_eq!(flag_offset, expected_offset);
+        for (package_id, flag_name, flag_type, flag_index) in baseline.into_iter() {
+            let flag_context =
+                get_flag_read_context(&flag_mapped_file, package_id, flag_name).unwrap().unwrap();
+            assert_eq!(flag_context.flag_type, flag_type);
+            assert_eq!(flag_context.flag_index, flag_index);
         }
     }
 
     #[test]
-    fn test_none_exist_flag_offset_query() {
-        let [_package_map, _flag_map, _flag_val, pb_file] = create_test_storage_files();
+    fn test_none_exist_flag_context_query() {
+        let [_package_map, _flag_map, _flag_val, _flag_info, pb_file] = create_test_storage_files();
         let pb_file_path = pb_file.path().display().to_string();
         // SAFETY:
         // The safety here is ensured as the test process will not write to temp storage file
         let flag_mapped_file =
-            unsafe { get_mapped_file(&pb_file_path, "system", StorageFileType::FlagMap).unwrap() };
-        let flag_offset_option = get_flag_offset(&flag_mapped_file, 0, "none_exist").unwrap();
-        assert_eq!(flag_offset_option, None);
+            unsafe { get_mapped_file(&pb_file_path, "mockup", StorageFileType::FlagMap).unwrap() };
+        let flag_context_option =
+            get_flag_read_context(&flag_mapped_file, 0, "none_exist").unwrap();
+        assert_eq!(flag_context_option, None);
 
-        let flag_offset_option = get_flag_offset(&flag_mapped_file, 3, "enabled_ro").unwrap();
-        assert_eq!(flag_offset_option, None);
+        let flag_context_option =
+            get_flag_read_context(&flag_mapped_file, 3, "enabled_ro").unwrap();
+        assert_eq!(flag_context_option, None);
     }
 
     #[test]
     fn test_boolean_flag_value_query() {
-        let [_package_map, _flag_map, _flag_val, pb_file] = create_test_storage_files();
+        let [_package_map, _flag_map, _flag_val, _flag_info, pb_file] = create_test_storage_files();
         let pb_file_path = pb_file.path().display().to_string();
         // SAFETY:
         // The safety here is ensured as the test process will not write to temp storage file
         let flag_value_file =
-            unsafe { get_mapped_file(&pb_file_path, "system", StorageFileType::FlagVal).unwrap() };
-        let baseline: Vec<bool> = vec![false; 8];
+            unsafe { get_mapped_file(&pb_file_path, "mockup", StorageFileType::FlagVal).unwrap() };
+        let baseline: Vec<bool> = vec![false, true, true, false, true, true, true, true];
         for (offset, expected_value) in baseline.into_iter().enumerate() {
             let flag_value = get_boolean_flag_value(&flag_value_file, offset as u32).unwrap();
             assert_eq!(flag_value, expected_value);
@@ -159,12 +167,12 @@
 
     #[test]
     fn test_invalid_boolean_flag_value_query() {
-        let [_package_map, _flag_map, _flag_val, pb_file] = create_test_storage_files();
+        let [_package_map, _flag_map, _flag_val, _flag_info, pb_file] = create_test_storage_files();
         let pb_file_path = pb_file.path().display().to_string();
         // SAFETY:
         // The safety here is ensured as the test process will not write to temp storage file
         let flag_value_file =
-            unsafe { get_mapped_file(&pb_file_path, "system", StorageFileType::FlagVal).unwrap() };
+            unsafe { get_mapped_file(&pb_file_path, "mockup", StorageFileType::FlagVal).unwrap() };
         let err = get_boolean_flag_value(&flag_value_file, 8u32).unwrap_err();
         assert_eq!(
             format!("{:?}", err),
@@ -173,9 +181,42 @@
     }
 
     #[test]
+    fn test_flag_info_query() {
+        let [_package_map, _flag_map, _flag_val, _flag_info, pb_file] = create_test_storage_files();
+        let pb_file_path = pb_file.path().display().to_string();
+        // SAFETY:
+        // The safety here is ensured as the test process will not write to temp storage file
+        let flag_info_file =
+            unsafe { get_mapped_file(&pb_file_path, "mockup", StorageFileType::FlagInfo).unwrap() };
+        let is_rw: Vec<bool> = vec![true, false, true, false, false, false, false, false];
+        for (offset, expected_value) in is_rw.into_iter().enumerate() {
+            let attribute = get_boolean_flag_attribute(&flag_info_file, offset as u32).unwrap();
+            assert!((attribute & FlagInfoBit::IsSticky as u8) == 0u8);
+            assert_eq!((attribute & FlagInfoBit::IsReadWrite as u8) != 0u8, expected_value);
+            assert!((attribute & FlagInfoBit::HasOverride as u8) == 0u8);
+        }
+    }
+
+    #[test]
+    fn test_invalid_boolean_flag_info_query() {
+        let [_package_map, _flag_map, _flag_val, _flag_info, pb_file] = create_test_storage_files();
+        let pb_file_path = pb_file.path().display().to_string();
+        // SAFETY:
+        // The safety here is ensured as the test process will not write to temp storage file
+        let flag_info_file =
+            unsafe { get_mapped_file(&pb_file_path, "mockup", StorageFileType::FlagInfo).unwrap() };
+        let err = get_boolean_flag_attribute(&flag_info_file, 8u32).unwrap_err();
+        assert_eq!(
+            format!("{:?}", err),
+            "InvalidStorageFileOffset(Flag info offset goes beyond the end of the file.)"
+        );
+    }
+
+    #[test]
     fn test_storage_version_query() {
         assert_eq!(get_storage_file_version("./package.map").unwrap(), 1);
         assert_eq!(get_storage_file_version("./flag.map").unwrap(), 1);
         assert_eq!(get_storage_file_version("./flag.val").unwrap(), 1);
+        assert_eq!(get_storage_file_version("./flag.info").unwrap(), 1);
     }
 }
diff --git a/tools/aconfig/aconfig_storage_write_api/Android.bp b/tools/aconfig/aconfig_storage_write_api/Android.bp
index 0f15b9c..4dbdbbf 100644
--- a/tools/aconfig/aconfig_storage_write_api/Android.bp
+++ b/tools/aconfig/aconfig_storage_write_api/Android.bp
@@ -14,6 +14,7 @@
         "libcxx",
         "libthiserror",
         "libaconfig_storage_file",
+        "libaconfig_storage_read_api",
     ],
 }
 
@@ -30,6 +31,7 @@
     defaults: ["aconfig_storage_write_api.defaults"],
     data: [
         "tests/flag.val",
+        "tests/flag.info",
     ],
     rustlibs: [
         "libaconfig_storage_read_api",
@@ -68,12 +70,13 @@
     srcs: ["aconfig_storage_write_api.cpp"],
     generated_headers: [
         "cxx-bridge-header",
-        "libcxx_aconfig_storage_write_api_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_read_api_cc",
         "libaconfig_storage_protos_cc",
         "libprotobuf-cpp-lite",
         "libbase",
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
index 391b305..01785e1 100644
--- a/tools/aconfig/aconfig_storage_write_api/aconfig_storage_write_api.cpp
+++ b/tools/aconfig/aconfig_storage_write_api/aconfig_storage_write_api.cpp
@@ -38,7 +38,8 @@
 /// Get storage file path
 static Result<std::string> find_storage_file(
     std::string const& pb_file,
-    std::string const& container) {
+    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
@@ -47,18 +48,29 @@
 
   for (auto& entry : records_pb->files()) {
     if (entry.container() == container) {
-        return entry.flag_val();
+      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();
+        case StorageFileType::flag_info:
+          return entry.flag_info();
+        default:
+          return Error() << "Invalid file type " << file_type;
+      }
     }
   }
 
-  return Error() << "Unable to find storage files for container " << container;;
+  return Error() << "Unable to find storage files for container " << container;
 }
 
 /// Map a storage file
-static Result<MappedFlagValueFile> map_storage_file(std::string const& file) {
+static Result<MutableMappedStorageFile> map_storage_file(std::string const& file) {
   struct stat file_stat;
   if (stat(file.c_str(), &file_stat) < 0) {
-    return Error() << "fstat failed";
+    return ErrnoError() << "stat failed";
   }
 
   if ((file_stat.st_mode & (S_IWUSR | S_IWGRP | S_IWOTH)) == 0) {
@@ -69,16 +81,16 @@
 
   const int fd = open(file.c_str(), O_RDWR | O_NOFOLLOW | O_CLOEXEC);
   if (fd == -1) {
-    return Error() << "failed to open " << file;
+    return ErrnoError() << "failed to open " << file;
   };
 
   void* const map_result =
       mmap(nullptr, file_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
   if (map_result == MAP_FAILED) {
-    return Error() << "mmap failed";
+    return ErrnoError() << "mmap failed";
   }
 
-  auto mapped_file = MappedFlagValueFile();
+  auto mapped_file = MutableMappedStorageFile();
   mapped_file.file_ptr = map_result;
   mapped_file.file_size = file_size;
 
@@ -87,29 +99,37 @@
 
 namespace private_internal_api {
 
-/// Get mapped file implementation.
-Result<MappedFlagValueFile> get_mapped_flag_value_file_impl(
+/// Get mutable mapped file implementation.
+Result<MutableMappedStorageFile> get_mutable_mapped_file_impl(
     std::string const& pb_file,
-    std::string const& container) {
-  auto file_result = find_storage_file(pb_file, container);
+    std::string const& container,
+    StorageFileType file_type) {
+  if (file_type != StorageFileType::flag_val &&
+      file_type != StorageFileType::flag_info) {
+    return Error() << "Cannot create mutable mapped file for this 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 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);
+/// Get mutable mapped file
+Result<MutableMappedStorageFile> get_mutable_mapped_file(
+    std::string const& container,
+    StorageFileType file_type) {
+  return private_internal_api::get_mutable_mapped_file_impl(
+      kPersistStorageRecordsPb, container, file_type);
 }
 
 /// Set boolean flag value
 Result<void> set_boolean_flag_value(
-    const MappedFlagValueFile& file,
+    const MutableMappedStorageFile& file,
     uint32_t offset,
     bool value) {
   auto content = rust::Slice<uint8_t>(
@@ -121,4 +141,50 @@
   return {};
 }
 
+/// Set if flag is sticky
+Result<void> set_flag_is_sticky(
+    const MutableMappedStorageFile& file,
+    FlagValueType value_type,
+    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_flag_is_sticky_cxx(
+      content, static_cast<uint16_t>(value_type), offset, value);
+  if (!update_cxx.update_success) {
+    return Error() << std::string(update_cxx.error_message.c_str());
+  }
+  return {};
+}
+
+/// Set if flag has override
+Result<void> set_flag_has_override(
+    const MutableMappedStorageFile& file,
+    FlagValueType value_type,
+    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_flag_has_override_cxx(
+      content, static_cast<uint16_t>(value_type), offset, value);
+  if (!update_cxx.update_success) {
+    return Error() << std::string(update_cxx.error_message.c_str());
+  }
+  return {};
+}
+
+Result<void> create_flag_info(
+    std::string const& package_map,
+    std::string const& flag_map,
+    std::string const& flag_info_out) {
+  auto creation_cxx = create_flag_info_cxx(
+      rust::Str(package_map.c_str()),
+      rust::Str(flag_map.c_str()),
+      rust::Str(flag_info_out.c_str()));
+  if (creation_cxx.success) {
+    return {};
+  } else {
+    return android::base::Error() << creation_cxx.error_message;
+  }
+}
 } // namespace aconfig_storage
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
index 9e6332a..8699b88 100644
--- 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
@@ -4,13 +4,14 @@
 #include <string>
 
 #include <android-base/result.h>
+#include <aconfig_storage/aconfig_storage_read_api.hpp>
 
 using namespace android::base;
 
 namespace aconfig_storage {
 
 /// Mapped flag value file
-struct MappedFlagValueFile{
+struct MutableMappedStorageFile{
   void* file_ptr;
   size_t file_size;
 };
@@ -18,20 +19,45 @@
 /// DO NOT USE APIS IN THE FOLLOWING NAMESPACE DIRECTLY
 namespace private_internal_api {
 
-Result<MappedFlagValueFile> get_mapped_flag_value_file_impl(
+Result<MutableMappedStorageFile> get_mutable_mapped_file_impl(
     std::string const& pb_file,
-    std::string const& container);
+    std::string const& container,
+    StorageFileType file_type);
 
 } // namespace private_internal_api
 
-/// Get mapped writeable flag value file
-Result<MappedFlagValueFile> get_mapped_flag_value_file(
-    std::string const& container);
+/// Get mapped writeable storage file
+Result<MutableMappedStorageFile> get_mutable_mapped_file(
+    std::string const& container,
+    StorageFileType file_type);
 
 /// Set boolean flag value
 Result<void> set_boolean_flag_value(
-    const MappedFlagValueFile& file,
+    const MutableMappedStorageFile& file,
     uint32_t offset,
     bool value);
 
+/// Set if flag is sticky
+Result<void> set_flag_is_sticky(
+    const MutableMappedStorageFile& file,
+    FlagValueType value_type,
+    uint32_t offset,
+    bool value);
+
+/// Set if flag has override
+Result<void> set_flag_has_override(
+    const MutableMappedStorageFile& file,
+    FlagValueType value_type,
+    uint32_t offset,
+    bool value);
+
+/// Create flag info file based on package and flag map
+/// \input package_map: package map file
+/// \input flag_map: flag map file
+/// \input flag_info_out: flag info file to be created
+Result<void> create_flag_info(
+    std::string const& package_map,
+    std::string const& flag_map,
+    std::string const& flag_info_out);
+
 } // namespace aconfig_storage
diff --git a/tools/aconfig/aconfig_storage_write_api/src/flag_info_update.rs b/tools/aconfig/aconfig_storage_write_api/src/flag_info_update.rs
new file mode 100644
index 0000000..56e1253
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_write_api/src/flag_info_update.rs
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//! flag info update module defines the flag info file write to mapped bytes
+
+use aconfig_storage_file::{
+    read_u8_from_bytes, AconfigStorageError, FlagInfoBit, FlagInfoHeader, FlagValueType,
+    FILE_VERSION,
+};
+use anyhow::anyhow;
+
+fn get_flag_info_offset(
+    buf: &mut [u8],
+    flag_type: FlagValueType,
+    flag_index: u32,
+) -> Result<usize, AconfigStorageError> {
+    let interpreted_header = FlagInfoHeader::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
+        )));
+    }
+
+    // get byte offset to the flag info
+    let head = match flag_type {
+        FlagValueType::Boolean => (interpreted_header.boolean_flag_offset + flag_index) as usize,
+    };
+
+    if head >= interpreted_header.file_size as usize {
+        return Err(AconfigStorageError::InvalidStorageFileOffset(anyhow!(
+            "Flag value offset goes beyond the end of the file."
+        )));
+    }
+
+    Ok(head)
+}
+
+fn get_flag_attribute_and_offset(
+    buf: &mut [u8],
+    flag_type: FlagValueType,
+    flag_index: u32,
+) -> Result<(u8, usize), AconfigStorageError> {
+    let head = get_flag_info_offset(buf, flag_type, flag_index)?;
+    let mut pos = head;
+    let attribute = read_u8_from_bytes(buf, &mut pos)?;
+    Ok((attribute, head))
+}
+
+/// Set if flag is sticky
+pub fn update_flag_is_sticky(
+    buf: &mut [u8],
+    flag_type: FlagValueType,
+    flag_index: u32,
+    value: bool,
+) -> Result<(), AconfigStorageError> {
+    let (attribute, head) = get_flag_attribute_and_offset(buf, flag_type, flag_index)?;
+    let is_sticky = (attribute & (FlagInfoBit::IsSticky as u8)) != 0;
+    if is_sticky != value {
+        buf[head] = (attribute ^ FlagInfoBit::IsSticky as u8).to_le_bytes()[0];
+    }
+    Ok(())
+}
+
+/// Set if flag has override
+pub fn update_flag_has_override(
+    buf: &mut [u8],
+    flag_type: FlagValueType,
+    flag_index: u32,
+    value: bool,
+) -> Result<(), AconfigStorageError> {
+    let (attribute, head) = get_flag_attribute_and_offset(buf, flag_type, flag_index)?;
+    let has_override = (attribute & (FlagInfoBit::HasOverride as u8)) != 0;
+    if has_override != value {
+        buf[head] = (attribute ^ FlagInfoBit::HasOverride as u8).to_le_bytes()[0];
+    }
+    Ok(())
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use aconfig_storage_file::test_utils::create_test_flag_info_list;
+    use aconfig_storage_read_api::flag_info_query::find_boolean_flag_attribute;
+
+    #[test]
+    // this test point locks down is sticky update
+    fn test_update_flag_is_sticky() {
+        let flag_info_list = create_test_flag_info_list();
+        let mut buf = flag_info_list.into_bytes();
+        for i in 0..flag_info_list.header.num_flags {
+            update_flag_is_sticky(&mut buf, FlagValueType::Boolean, i, true).unwrap();
+            let attribute = find_boolean_flag_attribute(&buf, i).unwrap();
+            assert!((attribute & (FlagInfoBit::IsSticky as u8)) != 0);
+            update_flag_is_sticky(&mut buf, FlagValueType::Boolean, i, false).unwrap();
+            let attribute = find_boolean_flag_attribute(&buf, i).unwrap();
+            assert!((attribute & (FlagInfoBit::IsSticky as u8)) == 0);
+        }
+    }
+
+    #[test]
+    // this test point locks down has override update
+    fn test_update_flag_has_override() {
+        let flag_info_list = create_test_flag_info_list();
+        let mut buf = flag_info_list.into_bytes();
+        for i in 0..flag_info_list.header.num_flags {
+            update_flag_has_override(&mut buf, FlagValueType::Boolean, i, true).unwrap();
+            let attribute = find_boolean_flag_attribute(&buf, i).unwrap();
+            assert!((attribute & (FlagInfoBit::HasOverride as u8)) != 0);
+            update_flag_has_override(&mut buf, FlagValueType::Boolean, i, false).unwrap();
+            let attribute = find_boolean_flag_attribute(&buf, i).unwrap();
+            assert!((attribute & (FlagInfoBit::HasOverride as u8)) == 0);
+        }
+    }
+}
diff --git a/tools/aconfig/aconfig_storage_write_api/src/flag_value_update.rs b/tools/aconfig/aconfig_storage_write_api/src/flag_value_update.rs
index c2375dd..0938715 100644
--- a/tools/aconfig/aconfig_storage_write_api/src/flag_value_update.rs
+++ b/tools/aconfig/aconfig_storage_write_api/src/flag_value_update.rs
@@ -22,7 +22,7 @@
 /// Set flag value
 pub fn update_boolean_flag_value(
     buf: &mut [u8],
-    flag_offset: u32,
+    flag_index: u32,
     flag_value: bool,
 ) -> Result<(), AconfigStorageError> {
     let interpreted_header = FlagValueHeader::from_bytes(buf)?;
@@ -34,10 +34,8 @@
         )));
     }
 
-    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).
+    // get byte offset to the flag
+    let head = (interpreted_header.boolean_value_offset + flag_index) as usize;
     if head >= interpreted_header.file_size as usize {
         return Err(AconfigStorageError::InvalidStorageFileOffset(anyhow!(
             "Flag value offset goes beyond the end of the file."
@@ -51,27 +49,14 @@
 #[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 }
-    }
+    use aconfig_storage_file::test_utils::create_test_flag_value_list;
 
     #[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 mut content = flag_value_list.into_bytes();
         let true_byte = u8::from(true).to_le_bytes()[0];
         let false_byte = u8::from(false).to_le_bytes()[0];
 
@@ -87,7 +72,7 @@
     #[test]
     // this test point locks down update beyond the end of boolean section
     fn test_boolean_out_of_range() {
-        let mut flag_value_list = create_test_flag_value_list().as_bytes();
+        let mut flag_value_list = create_test_flag_value_list().into_bytes();
         let error = update_boolean_flag_value(&mut flag_value_list[..], 8, true).unwrap_err();
         assert_eq!(
             format!("{:?}", error),
@@ -100,7 +85,7 @@
     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 mut flag_value = value_list.into_bytes();
         let error = update_boolean_flag_value(&mut flag_value[..], 4, true).unwrap_err();
         assert_eq!(
             format!("{:?}", error),
diff --git a/tools/aconfig/aconfig_storage_write_api/src/lib.rs b/tools/aconfig/aconfig_storage_write_api/src/lib.rs
index 5562d6a..ee08d16 100644
--- a/tools/aconfig/aconfig_storage_write_api/src/lib.rs
+++ b/tools/aconfig/aconfig_storage_write_api/src/lib.rs
@@ -17,24 +17,31 @@
 //! `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_info_update;
 pub mod flag_value_update;
 pub mod mapped_file;
 
 #[cfg(test)]
 mod test_utils;
 
-use aconfig_storage_file::AconfigStorageError;
+use aconfig_storage_file::{
+    AconfigStorageError, FlagInfoHeader, FlagInfoList, FlagInfoNode, FlagTable, FlagValueType,
+    PackageTable, StorageFileType, StoredFlagType, FILE_VERSION,
+};
 
 use anyhow::anyhow;
 use memmap2::MmapMut;
+use std::fs::File;
+use std::io::{Read, Write};
 
 /// 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
+/// Get read write mapped storage files.
 ///
 /// \input container: the flag package container
-/// \return a result of mapped file
+/// \input file_type: storage file type enum
+/// \return a result of read write mapped file
 ///
 ///
 /// # Safety
@@ -43,28 +50,149 @@
 /// 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) }
+pub unsafe fn get_mapped_storage_file(
+    container: &str,
+    file_type: StorageFileType,
+) -> Result<MmapMut, AconfigStorageError> {
+    unsafe { crate::mapped_file::get_mapped_file(STORAGE_LOCATION_FILE, container, file_type) }
 }
 
 /// 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 index: flag index
 /// \input value: updated flag value
 /// \return a result of ()
 ///
 pub fn set_boolean_flag_value(
     file: &mut MmapMut,
-    offset: u32,
+    index: u32,
     value: bool,
 ) -> Result<(), AconfigStorageError> {
-    crate::flag_value_update::update_boolean_flag_value(file, offset, value)?;
+    crate::flag_value_update::update_boolean_flag_value(file, index, value)?;
     file.flush().map_err(|errmsg| {
         AconfigStorageError::MapFlushFail(anyhow!("fail to flush storage file: {}", errmsg))
     })
 }
 
+/// Set if flag is sticky thru mapped file and flush the change to file
+///
+/// \input mapped_file: the mapped flag info file
+/// \input index: flag index
+/// \input value: updated flag sticky value
+/// \return a result of ()
+///
+pub fn set_flag_is_sticky(
+    file: &mut MmapMut,
+    flag_type: FlagValueType,
+    index: u32,
+    value: bool,
+) -> Result<(), AconfigStorageError> {
+    crate::flag_info_update::update_flag_is_sticky(file, flag_type, index, value)?;
+    file.flush().map_err(|errmsg| {
+        AconfigStorageError::MapFlushFail(anyhow!("fail to flush storage file: {}", errmsg))
+    })
+}
+
+/// Set if flag has override thru mapped file and flush the change to file
+///
+/// \input mapped_file: the mapped flag info file
+/// \input index: flag index
+/// \input value: updated flag has override value
+/// \return a result of ()
+///
+pub fn set_flag_has_override(
+    file: &mut MmapMut,
+    flag_type: FlagValueType,
+    index: u32,
+    value: bool,
+) -> Result<(), AconfigStorageError> {
+    crate::flag_info_update::update_flag_has_override(file, flag_type, index, value)?;
+    file.flush().map_err(|errmsg| {
+        AconfigStorageError::MapFlushFail(anyhow!("fail to flush storage file: {}", errmsg))
+    })
+}
+
+/// Read in storage file as bytes
+fn read_file_to_bytes(file_path: &str) -> Result<Vec<u8>, AconfigStorageError> {
+    let mut file = File::open(file_path).map_err(|errmsg| {
+        AconfigStorageError::FileReadFail(anyhow!("Failed to open file {}: {}", file_path, errmsg))
+    })?;
+    let mut buffer = Vec::new();
+    file.read_to_end(&mut buffer).map_err(|errmsg| {
+        AconfigStorageError::FileReadFail(anyhow!(
+            "Failed to read bytes from file {}: {}",
+            file_path,
+            errmsg
+        ))
+    })?;
+    Ok(buffer)
+}
+
+/// Create flag info file given package map file and flag map file
+/// \input package_map: package map file
+/// \input flag_map: flag map file
+/// \output flag_info_out: created flag info file
+pub fn create_flag_info(
+    package_map: &str,
+    flag_map: &str,
+    flag_info_out: &str,
+) -> Result<(), AconfigStorageError> {
+    let package_table = PackageTable::from_bytes(&read_file_to_bytes(package_map)?)?;
+    let flag_table = FlagTable::from_bytes(&read_file_to_bytes(flag_map)?)?;
+
+    if package_table.header.container != flag_table.header.container {
+        return Err(AconfigStorageError::FileCreationFail(anyhow!(
+            "container for package map {} and flag map {} does not match",
+            package_table.header.container,
+            flag_table.header.container,
+        )));
+    }
+
+    let mut package_start_index = vec![0; package_table.header.num_packages as usize];
+    for node in package_table.nodes.iter() {
+        package_start_index[node.package_id as usize] = node.boolean_start_index;
+    }
+
+    let mut is_flag_rw = vec![false; flag_table.header.num_flags as usize];
+    for node in flag_table.nodes.iter() {
+        let flag_index = package_start_index[node.package_id as usize] + node.flag_index as u32;
+        is_flag_rw[flag_index as usize] = node.flag_type == StoredFlagType::ReadWriteBoolean;
+    }
+
+    let mut list = FlagInfoList {
+        header: FlagInfoHeader {
+            version: FILE_VERSION,
+            container: flag_table.header.container,
+            file_type: StorageFileType::FlagInfo as u8,
+            file_size: 0,
+            num_flags: flag_table.header.num_flags,
+            boolean_flag_offset: 0,
+        },
+        nodes: is_flag_rw.iter().map(|&rw| FlagInfoNode::create(rw)).collect(),
+    };
+
+    list.header.boolean_flag_offset = list.header.into_bytes().len() as u32;
+    list.header.file_size = list.into_bytes().len() as u32;
+
+    let mut file = File::create(flag_info_out).map_err(|errmsg| {
+        AconfigStorageError::FileCreationFail(anyhow!(
+            "fail to create file {}: {}",
+            flag_info_out,
+            errmsg
+        ))
+    })?;
+    file.write_all(&list.into_bytes()).map_err(|errmsg| {
+        AconfigStorageError::FileCreationFail(anyhow!(
+            "fail to write to file {}: {}",
+            flag_info_out,
+            errmsg
+        ))
+    })?;
+
+    Ok(())
+}
+
 // *************************************** //
 // CC INTERLOP
 // *************************************** //
@@ -78,6 +206,24 @@
         pub error_message: String,
     }
 
+    // Flag is sticky update return for cc interlop
+    pub struct FlagIsStickyUpdateCXX {
+        pub update_success: bool,
+        pub error_message: String,
+    }
+
+    // Flag has override update return for cc interlop
+    pub struct FlagHasOverrideUpdateCXX {
+        pub update_success: bool,
+        pub error_message: String,
+    }
+
+    // Flag info file creation return for cc interlop
+    pub struct FlagInfoCreationCXX {
+        pub success: bool,
+        pub error_message: String,
+    }
+
     // Rust export to c++
     extern "Rust" {
         pub fn update_boolean_flag_value_cxx(
@@ -85,6 +231,26 @@
             offset: u32,
             value: bool,
         ) -> BooleanFlagValueUpdateCXX;
+
+        pub fn update_flag_is_sticky_cxx(
+            file: &mut [u8],
+            flag_type: u16,
+            offset: u32,
+            value: bool,
+        ) -> FlagIsStickyUpdateCXX;
+
+        pub fn update_flag_has_override_cxx(
+            file: &mut [u8],
+            flag_type: u16,
+            offset: u32,
+            value: bool,
+        ) -> FlagHasOverrideUpdateCXX;
+
+        pub fn create_flag_info_cxx(
+            package_map: &str,
+            flag_map: &str,
+            flag_info_out: &str,
+        ) -> FlagInfoCreationCXX;
     }
 }
 
@@ -104,14 +270,87 @@
     }
 }
 
+pub(crate) fn update_flag_is_sticky_cxx(
+    file: &mut [u8],
+    flag_type: u16,
+    offset: u32,
+    value: bool,
+) -> ffi::FlagIsStickyUpdateCXX {
+    match FlagValueType::try_from(flag_type) {
+        Ok(value_type) => {
+            match crate::flag_info_update::update_flag_is_sticky(file, value_type, offset, value) {
+                Ok(()) => ffi::FlagIsStickyUpdateCXX {
+                    update_success: true,
+                    error_message: String::from("")
+                },
+                Err(errmsg) => ffi::FlagIsStickyUpdateCXX {
+                    update_success: false,
+                    error_message: format!("{:?}", errmsg),
+                },
+            }
+        }
+        Err(errmsg) => ffi::FlagIsStickyUpdateCXX {
+            update_success: false,
+            error_message: format!("{:?}", errmsg),
+        }
+    }
+}
+
+pub(crate) fn update_flag_has_override_cxx(
+    file: &mut [u8],
+    flag_type: u16,
+    offset: u32,
+    value: bool,
+) -> ffi::FlagHasOverrideUpdateCXX {
+    match FlagValueType::try_from(flag_type) {
+        Ok(value_type) => {
+            match crate::flag_info_update::update_flag_has_override(file, value_type, offset, value) {
+                Ok(()) => ffi::FlagHasOverrideUpdateCXX {
+                    update_success: true,
+                    error_message: String::from("")
+                },
+                Err(errmsg) => ffi::FlagHasOverrideUpdateCXX {
+                    update_success: false,
+                    error_message: format!("{:?}", errmsg),
+                },
+            }
+        }
+        Err(errmsg) => ffi::FlagHasOverrideUpdateCXX {
+            update_success: false,
+            error_message: format!("{:?}", errmsg),
+        }
+    }
+}
+
+/// Create flag info file cc interlop
+pub(crate) fn create_flag_info_cxx(
+    package_map: &str,
+    flag_map: &str,
+    flag_info_out: &str,
+) -> ffi::FlagInfoCreationCXX {
+    match create_flag_info(package_map, flag_map, flag_info_out) {
+        Ok(()) => ffi::FlagInfoCreationCXX { success: true, error_message: String::from("") },
+        Err(errmsg) => {
+            ffi::FlagInfoCreationCXX { 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_file::test_utils::{
+        create_test_flag_info_list, create_test_flag_table, create_test_package_table,
+        write_bytes_to_temp_file,
+    };
+    use aconfig_storage_file::FlagInfoBit;
+    use aconfig_storage_read_api::flag_info_query::find_boolean_flag_attribute;
     use aconfig_storage_read_api::flag_value_query::find_boolean_flag_value;
     use std::fs::File;
     use std::io::Read;
+    use tempfile::NamedTempFile;
 
     fn get_boolean_flag_value_at_offset(file: &str, offset: u32) -> bool {
         let mut f = File::open(&file).unwrap();
@@ -132,6 +371,7 @@
     package_map: "some_package.map"
     flag_map: "some_flag.map"
     flag_val: "{}"
+    flag_info: "some_flag.info"
     timestamp: 12345
 }}
 "#,
@@ -144,7 +384,12 @@
         // 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();
+            let mut file = crate::mapped_file::get_mapped_file(
+                &record_pb_path,
+                "system",
+                StorageFileType::FlagVal,
+            )
+            .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);
@@ -156,4 +401,122 @@
             }
         }
     }
+
+    fn get_flag_attribute_at_offset(file: &str, offset: u32) -> u8 {
+        let mut f = File::open(&file).unwrap();
+        let mut bytes = Vec::new();
+        f.read_to_end(&mut bytes).unwrap();
+        find_boolean_flag_attribute(&bytes, offset).unwrap()
+    }
+
+    #[test]
+    fn test_set_flag_is_sticky() {
+        let flag_info_file = copy_to_temp_file("./tests/flag.info", false).unwrap();
+        let flag_info_path = flag_info_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: "some_flag.val"
+        flag_info: "{}"
+        timestamp: 12345
+    }}
+    "#,
+            flag_info_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",
+                StorageFileType::FlagInfo,
+            )
+            .unwrap();
+            for i in 0..8 {
+                set_flag_is_sticky(&mut file, FlagValueType::Boolean, i, true).unwrap();
+                let attribute = get_flag_attribute_at_offset(&flag_info_path, i);
+                assert!((attribute & (FlagInfoBit::IsSticky as u8)) != 0);
+                set_flag_is_sticky(&mut file, FlagValueType::Boolean, i, false).unwrap();
+                let attribute = get_flag_attribute_at_offset(&flag_info_path, i);
+                assert!((attribute & (FlagInfoBit::IsSticky as u8)) == 0);
+            }
+        }
+    }
+
+    #[test]
+    fn test_set_flag_has_override() {
+        let flag_info_file = copy_to_temp_file("./tests/flag.info", false).unwrap();
+        let flag_info_path = flag_info_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: "some_flag.val"
+        flag_info: "{}"
+        timestamp: 12345
+    }}
+    "#,
+            flag_info_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",
+                StorageFileType::FlagInfo,
+            )
+            .unwrap();
+            for i in 0..8 {
+                set_flag_has_override(&mut file, FlagValueType::Boolean, i, true).unwrap();
+                let attribute = get_flag_attribute_at_offset(&flag_info_path, i);
+                assert!((attribute & (FlagInfoBit::HasOverride as u8)) != 0);
+                set_flag_has_override(&mut file, FlagValueType::Boolean, i, false).unwrap();
+                let attribute = get_flag_attribute_at_offset(&flag_info_path, i);
+                assert!((attribute & (FlagInfoBit::HasOverride as u8)) == 0);
+            }
+        }
+    }
+
+    fn create_empty_temp_file() -> Result<NamedTempFile, AconfigStorageError> {
+        let file = NamedTempFile::new().map_err(|_| {
+            AconfigStorageError::FileCreationFail(anyhow!("Failed to create temp file"))
+        })?;
+        Ok(file)
+    }
+
+    #[test]
+    // this test point locks down the flag info creation
+    fn test_create_flag_info() {
+        let package_table =
+            write_bytes_to_temp_file(&create_test_package_table().into_bytes()).unwrap();
+        let flag_table = write_bytes_to_temp_file(&create_test_flag_table().into_bytes()).unwrap();
+        let flag_info = create_empty_temp_file().unwrap();
+
+        let package_table_path = package_table.path().display().to_string();
+        let flag_table_path = flag_table.path().display().to_string();
+        let flag_info_path = flag_info.path().display().to_string();
+
+        assert!(create_flag_info(&package_table_path, &flag_table_path, &flag_info_path).is_ok());
+
+        let flag_info =
+            FlagInfoList::from_bytes(&read_file_to_bytes(&flag_info_path).unwrap()).unwrap();
+        let expected_flag_info = create_test_flag_info_list();
+        assert_eq!(flag_info, expected_flag_info);
+    }
 }
diff --git a/tools/aconfig/aconfig_storage_write_api/src/mapped_file.rs b/tools/aconfig/aconfig_storage_write_api/src/mapped_file.rs
index 4c98be4..ea9ac19 100644
--- a/tools/aconfig/aconfig_storage_write_api/src/mapped_file.rs
+++ b/tools/aconfig/aconfig_storage_write_api/src/mapped_file.rs
@@ -14,43 +14,41 @@
  * limitations under the License.
  */
 
-use std::fs::{self, File, OpenOptions};
-use std::io::{BufReader, Read};
-
 use anyhow::anyhow;
 use memmap2::MmapMut;
+use std::fs::{self, OpenOptions};
+use std::io::Read;
 
-use aconfig_storage_file::protos::{storage_record_pb::try_from_binary_proto, ProtoStorageFiles};
-use aconfig_storage_file::AconfigStorageError::{
-    self, FileReadFail, MapFileFail, ProtobufParseFail, StorageFileNotFound,
-};
+use aconfig_storage_file::AconfigStorageError::{self, FileReadFail, MapFileFail};
+use aconfig_storage_file::StorageFileType;
+use aconfig_storage_read_api::mapped_file::find_container_storage_location;
 
-/// 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());
-        }
+/// Get the mutable memory mapping of a storage 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.
+unsafe fn map_file(file_path: &str) -> Result<MmapMut, AconfigStorageError> {
+    // 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)));
     }
-    Err(StorageFileNotFound(anyhow!("Persistent flag value file does not exist for {}", container)))
+
+    let file =
+        OpenOptions::new().read(true).write(true).open(file_path).map_err(|errmsg| {
+            FileReadFail(anyhow!("Failed to open file {}: {}", file_path, errmsg))
+        })?;
+
+    unsafe {
+        let mapped_file = MmapMut::map_mut(&file).map_err(|errmsg| {
+            MapFileFail(anyhow!("fail to map storage file {}: {}", file_path, errmsg))
+        })?;
+        Ok(mapped_file)
+    }
 }
 
 /// Get a mapped storage file given the container and file type
@@ -64,24 +62,16 @@
 pub unsafe fn get_mapped_file(
     location_pb_file: &str,
     container: &str,
+    file_type: StorageFileType,
 ) -> 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))
-        })
+    let files_location = find_container_storage_location(location_pb_file, container)?;
+    match file_type {
+        StorageFileType::FlagVal => unsafe { map_file(files_location.flag_val()) },
+        StorageFileType::FlagInfo => unsafe { map_file(files_location.flag_info()) },
+        _ => Err(MapFileFail(anyhow!(
+            "Cannot map file type {:?} as writeable memory mapped files.",
+            file_type
+        ))),
     }
 }
 
@@ -92,41 +82,9 @@
     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 mut rw_val_file = copy_to_temp_file("./tests/flag.val", false).unwrap();
+        let mut rw_info_file = copy_to_temp_file("./tests/flag.info", false).unwrap();
         let text_proto = format!(
             r#"
 files {{
@@ -135,21 +93,37 @@
     package_map: "some_package.map"
     flag_map: "some_flag.map"
     flag_val: "{}"
+    flag_info: "{}"
     timestamp: 12345
 }}
 "#,
-            rw_file.path().display().to_string()
+            rw_val_file.path().display().to_string(),
+            rw_info_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();
+        rw_val_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();
+            let mmaped_file =
+                get_mapped_file(&storage_record_file_path, "system", StorageFileType::FlagVal)
+                    .unwrap();
+            assert_eq!(mmaped_file[..], content[..]);
+        }
+
+        let mut content = Vec::new();
+        rw_info_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", StorageFileType::FlagInfo)
+                    .unwrap();
             assert_eq!(mmaped_file[..], content[..]);
         }
     }
@@ -165,6 +139,7 @@
     package_map: "some_package.map"
     flag_map: "some_flag.map"
     flag_val: "{}"
+    flag_info: "some_flag.info"
     timestamp: 12345
 }}
 "#,
@@ -176,7 +151,9 @@
         // 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();
+            let error =
+                get_mapped_file(&storage_record_file_path, "system", StorageFileType::FlagVal)
+                    .unwrap_err();
             assert_eq!(
                 format!("{:?}", error),
                 format!(
@@ -186,4 +163,49 @@
             );
         }
     }
+
+    #[test]
+    fn test_mapped_not_supported_file() {
+        let text_proto = format!(
+            r#"
+files {{
+    version: 0
+    container: "system"
+    package_map: "some_package.map"
+    flag_map: "some_flag.map"
+    flag_val: "some_flag.val"
+    flag_info: "some_flag.info"
+    timestamp: 12345
+}}
+"#,
+        );
+        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", StorageFileType::PackageMap)
+                    .unwrap_err();
+            assert_eq!(
+                format!("{:?}", error),
+                format!(
+                    "MapFileFail(Cannot map file type {:?} as writeable memory mapped files.)",
+                    StorageFileType::PackageMap
+                )
+            );
+
+            let error =
+                get_mapped_file(&storage_record_file_path, "system", StorageFileType::FlagMap)
+                    .unwrap_err();
+            assert_eq!(
+                format!("{:?}", error),
+                format!(
+                    "MapFileFail(Cannot map file type {:?} as writeable memory mapped files.)",
+                    StorageFileType::FlagMap
+                )
+            );
+        }
+    }
 }
diff --git a/tools/aconfig/aconfig_storage_write_api/tests/Android.bp b/tools/aconfig/aconfig_storage_write_api/tests/Android.bp
index d2a52fe..85568e0 100644
--- a/tools/aconfig/aconfig_storage_write_api/tests/Android.bp
+++ b/tools/aconfig/aconfig_storage_write_api/tests/Android.bp
@@ -1,8 +1,7 @@
-
 rust_test {
     name: "aconfig_storage_write_api.test.rust",
     srcs: [
-        "storage_write_api_test.rs"
+        "storage_write_api_test.rs",
     ],
     rustlibs: [
         "libanyhow",
@@ -14,6 +13,7 @@
     ],
     data: [
         "flag.val",
+        "flag.info",
     ],
     test_suites: ["general-tests"],
 }
@@ -34,9 +34,11 @@
     ],
     data: [
         "flag.val",
+        "flag.info",
     ],
     test_suites: [
         "device-tests",
         "general-tests",
     ],
+    ldflags: ["-Wl,--allow-multiple-definition"],
 }
diff --git a/tools/aconfig/aconfig_storage_write_api/tests/flag.info b/tools/aconfig/aconfig_storage_write_api/tests/flag.info
new file mode 100644
index 0000000..820d839
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_write_api/tests/flag.info
Binary files differ
diff --git a/tools/aconfig/aconfig_storage_write_api/tests/flag.val b/tools/aconfig/aconfig_storage_write_api/tests/flag.val
index 75b8564..ed203d4 100644
--- a/tools/aconfig/aconfig_storage_write_api/tests/flag.val
+++ 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
index 3a1c5de..77664e4 100644
--- 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
@@ -50,15 +50,17 @@
     return temp_file;
   }
 
-  Result<std::string> write_storage_location_pb_file(std::string const& flag_val) {
+  Result<std::string> write_storage_location_pb_file(std::string const& flag_val,
+                                                     std::string const& flag_info) {
     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_container("mockup");
     info->set_package_map("some_package.map");
     info->set_flag_map("some_flag.map");
     info->set_flag_val(flag_val);
+    info->set_flag_info(flag_info);
     info->set_timestamp(12345);
 
     auto content = std::string();
@@ -72,22 +74,25 @@
   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);
+    flag_info = *copy_to_rw_temp_file(test_dir + "/flag.info");
+    storage_record_pb = *write_storage_location_pb_file(flag_val, flag_info);
   }
 
   void TearDown() override {
     std::remove(flag_val.c_str());
+    std::remove(flag_info.c_str());
     std::remove(storage_record_pb.c_str());
   }
 
   std::string flag_val;
+  std::string flag_info;
   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");
+  auto mapped_file_result = private_api::get_mutable_mapped_file_impl(
+      storage_record_pb, "vendor", api::StorageFileType::flag_val);
   ASSERT_FALSE(mapped_file_result.ok());
   ASSERT_EQ(mapped_file_result.error().message(),
             "Unable to find storage files for container vendor");
@@ -96,16 +101,34 @@
 /// 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");
+  auto mapped_file_result = private_api::get_mutable_mapped_file_impl(
+      storage_record_pb, "mockup", api::StorageFileType::flag_val);
   ASSERT_FALSE(mapped_file_result.ok());
-  ASSERT_EQ(mapped_file_result.error().message(), "cannot map nonwriteable file");
+  auto it = mapped_file_result.error().message().find("cannot map nonwriteable file");
+  ASSERT_TRUE(it != std::string::npos) << mapped_file_result.error().message();
+}
+
+/// Negative test to lock down the error when mapping a file type that cannot be modified
+TEST_F(AconfigStorageTest, test_invalid_storage_file_type_mapping) {
+  auto mapped_file_result = private_api::get_mutable_mapped_file_impl(
+      storage_record_pb, "mockup", api::StorageFileType::package_map);
+  ASSERT_FALSE(mapped_file_result.ok());
+  auto it = mapped_file_result.error().message().find(
+      "Cannot create mutable mapped file for this file type");
+  ASSERT_TRUE(it != std::string::npos) << mapped_file_result.error().message();
+
+  mapped_file_result = private_api::get_mutable_mapped_file_impl(
+      storage_record_pb, "mockup", api::StorageFileType::flag_map);
+  ASSERT_FALSE(mapped_file_result.ok());
+  it = mapped_file_result.error().message().find(
+      "Cannot create mutable mapped file for this file type");
+  ASSERT_TRUE(it != std::string::npos) << mapped_file_result.error().message();
 }
 
 /// Test to lock down storage flag value update api
 TEST_F(AconfigStorageTest, test_boolean_flag_value_update) {
-  auto mapped_file_result = private_api::get_mapped_flag_value_file_impl(
-      storage_record_pb, "system");
+  auto mapped_file_result = private_api::get_mutable_mapped_file_impl(
+      storage_record_pb, "mockup", api::StorageFileType::flag_val);
   ASSERT_TRUE(mapped_file_result.ok());
   auto mapped_file = *mapped_file_result;
 
@@ -123,8 +146,8 @@
 
 /// 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");
+  auto mapped_file_result = private_api::get_mutable_mapped_file_impl(
+      storage_record_pb, "mockup", api::StorageFileType::flag_val);
   ASSERT_TRUE(mapped_file_result.ok());
   auto mapped_file = *mapped_file_result;
   auto update_result = api::set_boolean_flag_value(mapped_file, 8, true);
@@ -132,3 +155,61 @@
   ASSERT_EQ(update_result.error().message(),
             std::string("InvalidStorageFileOffset(Flag value offset goes beyond the end of the file.)"));
 }
+
+/// Test to lock down storage flag stickiness update api
+TEST_F(AconfigStorageTest, test_flag_is_sticky_update) {
+  auto mapped_file_result = private_api::get_mutable_mapped_file_impl(
+      storage_record_pb, "mockup", api::StorageFileType::flag_info);
+  ASSERT_TRUE(mapped_file_result.ok());
+  auto mapped_file = *mapped_file_result;
+
+  for (int offset = 0; offset < 8; ++offset) {
+    auto update_result = api::set_flag_is_sticky(
+        mapped_file, api::FlagValueType::Boolean, 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 attribute = api::get_boolean_flag_attribute(ro_mapped_file, offset);
+    ASSERT_TRUE(attribute.ok());
+    ASSERT_TRUE(*attribute & api::FlagInfoBit::IsSticky);
+
+    update_result = api::set_flag_is_sticky(
+        mapped_file, api::FlagValueType::Boolean, offset, false);
+    ASSERT_TRUE(update_result.ok());
+    ro_mapped_file.file_ptr = mapped_file.file_ptr;
+    ro_mapped_file.file_size = mapped_file.file_size;
+    attribute = api::get_boolean_flag_attribute(ro_mapped_file, offset);
+    ASSERT_TRUE(attribute.ok());
+    ASSERT_FALSE(*attribute & api::FlagInfoBit::IsSticky);
+  }
+}
+
+/// Test to lock down storage flag has override update api
+TEST_F(AconfigStorageTest, test_flag_has_override_update) {
+  auto mapped_file_result = private_api::get_mutable_mapped_file_impl(
+      storage_record_pb, "mockup", api::StorageFileType::flag_info);
+  ASSERT_TRUE(mapped_file_result.ok());
+  auto mapped_file = *mapped_file_result;
+
+  for (int offset = 0; offset < 8; ++offset) {
+    auto update_result = api::set_flag_has_override(
+        mapped_file, api::FlagValueType::Boolean, 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 attribute = api::get_boolean_flag_attribute(ro_mapped_file, offset);
+    ASSERT_TRUE(attribute.ok());
+    ASSERT_TRUE(*attribute & api::FlagInfoBit::HasOverride);
+
+    update_result = api::set_flag_has_override(
+        mapped_file, api::FlagValueType::Boolean, offset, false);
+    ASSERT_TRUE(update_result.ok());
+    ro_mapped_file.file_ptr = mapped_file.file_ptr;
+    ro_mapped_file.file_size = mapped_file.file_size;
+    attribute = api::get_boolean_flag_attribute(ro_mapped_file, offset);
+    ASSERT_TRUE(attribute.ok());
+    ASSERT_FALSE(*attribute & api::FlagInfoBit::HasOverride);
+  }
+}
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
index f6c1bbc..f6a9b280 100644
--- 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
@@ -1,8 +1,13 @@
 #[cfg(not(feature = "cargo"))]
 mod aconfig_storage_write_api_test {
     use aconfig_storage_file::protos::ProtoStorageFiles;
+    use aconfig_storage_file::{FlagInfoBit, FlagValueType, StorageFileType};
+    use aconfig_storage_read_api::flag_info_query::find_boolean_flag_attribute;
     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 aconfig_storage_write_api::{
+        mapped_file::get_mapped_file, set_boolean_flag_value, set_flag_has_override,
+        set_flag_is_sticky,
+    };
 
     use protobuf::Message;
     use std::fs::{self, File};
@@ -10,19 +15,20 @@
     use tempfile::NamedTempFile;
 
     /// Write storage location record pb to a temp file
-    fn write_storage_record_file(flag_val: &str) -> NamedTempFile {
+    fn write_storage_record_file(flag_val: &str, flag_info: &str) -> NamedTempFile {
         let text_proto = format!(
             r#"
 files {{
     version: 0
-    container: "system"
+    container: "mockup"
     package_map: "some_package_map"
     flag_map: "some_flag_map"
     flag_val: "{}"
+    flag_info: "{}"
     timestamp: 12345
 }}
 "#,
-            flag_val
+            flag_val, flag_info
         );
         let storage_files: ProtoStorageFiles =
             protobuf::text_format::parse_from_str(&text_proto).unwrap();
@@ -48,18 +54,30 @@
         find_boolean_flag_value(&bytes, offset).unwrap()
     }
 
+    /// Get flag attribute at offset
+    fn get_flag_attribute_at_offset(file: &str, offset: u32) -> u8 {
+        let mut f = File::open(file).unwrap();
+        let mut bytes = Vec::new();
+        f.read_to_end(&mut bytes).unwrap();
+        find_boolean_flag_attribute(&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_info_file = copy_to_temp_rw_file("./flag.info");
         let flag_value_path = flag_value_file.path().display().to_string();
-        let record_pb_file = write_storage_record_file(&flag_value_path);
+        let flag_info_path = flag_info_file.path().display().to_string();
+        let record_pb_file = write_storage_record_file(&flag_value_path, &flag_info_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() };
+        let mut file = unsafe {
+            get_mapped_file(&record_pb_path, "mockup", StorageFileType::FlagVal).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);
@@ -70,4 +88,56 @@
             assert!(!value);
         }
     }
+
+    #[test]
+    /// Test to lock down flag is sticky update api
+    fn test_set_flag_is_sticky() {
+        let flag_value_file = copy_to_temp_rw_file("./flag.val");
+        let flag_info_file = copy_to_temp_rw_file("./flag.info");
+        let flag_value_path = flag_value_file.path().display().to_string();
+        let flag_info_path = flag_info_file.path().display().to_string();
+        let record_pb_file = write_storage_record_file(&flag_value_path, &flag_info_path);
+        let record_pb_path = record_pb_file.path().display().to_string();
+
+        // SAFETY:
+        // The safety here is ensured as only this single threaded test process will
+        // write to this file
+        let mut file = unsafe {
+            get_mapped_file(&record_pb_path, "mockup", StorageFileType::FlagInfo).unwrap()
+        };
+        for i in 0..8 {
+            set_flag_is_sticky(&mut file, FlagValueType::Boolean, i, true).unwrap();
+            let attribute = get_flag_attribute_at_offset(&flag_info_path, i);
+            assert!((attribute & (FlagInfoBit::IsSticky as u8)) != 0);
+            set_flag_is_sticky(&mut file, FlagValueType::Boolean, i, false).unwrap();
+            let attribute = get_flag_attribute_at_offset(&flag_info_path, i);
+            assert!((attribute & (FlagInfoBit::IsSticky as u8)) == 0);
+        }
+    }
+
+    #[test]
+    /// Test to lock down flag is sticky update api
+    fn test_set_flag_has_override() {
+        let flag_value_file = copy_to_temp_rw_file("./flag.val");
+        let flag_info_file = copy_to_temp_rw_file("./flag.info");
+        let flag_value_path = flag_value_file.path().display().to_string();
+        let flag_info_path = flag_info_file.path().display().to_string();
+        let record_pb_file = write_storage_record_file(&flag_value_path, &flag_info_path);
+        let record_pb_path = record_pb_file.path().display().to_string();
+
+        // SAFETY:
+        // The safety here is ensured as only this single threaded test process will
+        // write to this file
+        let mut file = unsafe {
+            get_mapped_file(&record_pb_path, "mockup", StorageFileType::FlagInfo).unwrap()
+        };
+        for i in 0..8 {
+            set_flag_has_override(&mut file, FlagValueType::Boolean, i, true).unwrap();
+            let attribute = get_flag_attribute_at_offset(&flag_info_path, i);
+            assert!((attribute & (FlagInfoBit::HasOverride as u8)) != 0);
+            set_flag_has_override(&mut file, FlagValueType::Boolean, i, false).unwrap();
+            let attribute = get_flag_attribute_at_offset(&flag_info_path, i);
+            assert!((attribute & (FlagInfoBit::HasOverride as u8)) == 0);
+        }
+    }
 }
diff --git a/tools/aconfig/aflags/Android.bp b/tools/aconfig/aflags/Android.bp
index b36aa34..4920a6f 100644
--- a/tools/aconfig/aflags/Android.bp
+++ b/tools/aconfig/aflags/Android.bp
@@ -10,6 +10,8 @@
     srcs: ["src/main.rs"],
     rustlibs: [
         "libaconfig_protos",
+        "libaconfig_storage_read_api",
+        "libaconfig_storage_file",
         "libanyhow",
         "libclap",
         "libnix",
diff --git a/tools/aconfig/aflags/Cargo.toml b/tools/aconfig/aflags/Cargo.toml
index 6a08da6..cce7f9d 100644
--- a/tools/aconfig/aflags/Cargo.toml
+++ b/tools/aconfig/aflags/Cargo.toml
@@ -6,8 +6,10 @@
 [dependencies]
 anyhow = "1.0.69"
 paste = "1.0.11"
-clap = { version = "4", features = ["derive"] }
 protobuf = "3.2.0"
 regex = "1.10.3"
 aconfig_protos = { path = "../aconfig_protos" }
 nix = { version = "0.28.0", features = ["user"] }
+aconfig_storage_file = { version = "0.1.0", path = "../aconfig_storage_file" }
+aconfig_storage_read_api = { version = "0.1.0", path = "../aconfig_storage_read_api" }
+clap = {version = "4.5.2" }
diff --git a/tools/aconfig/aflags/src/aconfig_storage_source.rs b/tools/aconfig/aflags/src/aconfig_storage_source.rs
new file mode 100644
index 0000000..a3ca221
--- /dev/null
+++ b/tools/aconfig/aflags/src/aconfig_storage_source.rs
@@ -0,0 +1,56 @@
+use crate::{Flag, FlagPermission, FlagSource, FlagValue, ValuePickedFrom};
+use anyhow::{anyhow, Result};
+
+use std::fs::File;
+use std::io::Read;
+
+pub struct AconfigStorageSource {}
+
+use aconfig_storage_file::protos::ProtoStorageFiles;
+
+static STORAGE_INFO_FILE_PATH: &str = "/metadata/aconfig/persistent_storage_file_records.pb";
+
+impl FlagSource for AconfigStorageSource {
+    fn list_flags() -> Result<Vec<Flag>> {
+        let mut result = Vec::new();
+
+        let mut file = File::open(STORAGE_INFO_FILE_PATH)?;
+        let mut bytes = Vec::new();
+        file.read_to_end(&mut bytes)?;
+        let storage_file_info: ProtoStorageFiles = protobuf::Message::parse_from_bytes(&bytes)?;
+
+        for file_info in storage_file_info.files {
+            let package_map =
+                file_info.package_map.ok_or(anyhow!("storage file is missing package map"))?;
+            let flag_map = file_info.flag_map.ok_or(anyhow!("storage file is missing flag map"))?;
+            let flag_val = file_info.flag_val.ok_or(anyhow!("storage file is missing flag val"))?;
+            let container =
+                file_info.container.ok_or(anyhow!("storage file is missing container"))?;
+
+            for (package, name, _flag_type, val) in
+                aconfig_storage_file::list_flags(&package_map, &flag_map, &flag_val)?
+            {
+                result.push(Flag {
+                    name: name.to_string(),
+                    package: package.to_string(),
+                    value: FlagValue::try_from(val.to_string().as_str())?,
+                    container: container.to_string(),
+
+                    // TODO(b/324436145): delete namespace field once DeviceConfig isn't in CLI.
+                    namespace: "-".to_string(),
+
+                    // TODO(b/324436145): Populate with real values once API is available.
+                    staged_value: None,
+                    permission: FlagPermission::ReadOnly,
+                    value_picked_from: ValuePickedFrom::Default,
+                });
+            }
+        }
+
+        Ok(result)
+    }
+
+    fn override_flag(_namespace: &str, _qualified_name: &str, _value: &str) -> Result<()> {
+        todo!()
+    }
+}
diff --git a/tools/aconfig/aflags/src/main.rs b/tools/aconfig/aflags/src/main.rs
index 808ffa0..1c453c5 100644
--- a/tools/aconfig/aflags/src/main.rs
+++ b/tools/aconfig/aflags/src/main.rs
@@ -22,6 +22,9 @@
 mod device_config_source;
 use device_config_source::DeviceConfigSource;
 
+mod aconfig_storage_source;
+use aconfig_storage_source::AconfigStorageSource;
+
 #[derive(Clone, PartialEq, Debug)]
 enum FlagPermission {
     ReadOnly,
@@ -109,6 +112,11 @@
     fn override_flag(namespace: &str, qualified_name: &str, value: &str) -> Result<()>;
 }
 
+enum FlagSourceType {
+    DeviceConfig,
+    AconfigStorage,
+}
+
 const ABOUT_TEXT: &str = "Tool for reading and writing flags.
 
 Rows in the table from the `list` command follow this format:
@@ -139,7 +147,11 @@
 #[derive(Parser, Debug)]
 enum Command {
     /// List all aconfig flags on this device.
-    List,
+    List {
+        /// Read from the new flag storage.
+        #[clap(long)]
+        use_new_storage: bool,
+    },
 
     /// Enable an aconfig flag on this device, on the next boot.
     Enable {
@@ -201,8 +213,11 @@
     Ok(())
 }
 
-fn list() -> Result<String> {
-    let flags = DeviceConfigSource::list_flags()?;
+fn list(source_type: FlagSourceType) -> Result<String> {
+    let flags = match source_type {
+        FlagSourceType::DeviceConfig => DeviceConfigSource::list_flags()?,
+        FlagSourceType::AconfigStorage => AconfigStorageSource::list_flags()?,
+    };
     let padding_info = PaddingInfo {
         longest_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),
@@ -234,7 +249,8 @@
 fn main() {
     let cli = Cli::parse();
     let output = match cli.command {
-        Command::List => list().map(Some),
+        Command::List { use_new_storage: true } => list(FlagSourceType::AconfigStorage).map(Some),
+        Command::List { use_new_storage: false } => list(FlagSourceType::DeviceConfig).map(Some),
         Command::Enable { qualified_name } => set_flag(&qualified_name, "true").map(|_| None),
         Command::Disable { qualified_name } => set_flag(&qualified_name, "false").map(|_| None),
     };
diff --git a/tools/finalization/OWNERS b/tools/finalization/OWNERS
index 518b60d..b00b774 100644
--- a/tools/finalization/OWNERS
+++ b/tools/finalization/OWNERS
@@ -1,5 +1,7 @@
 include platform/build/soong:/OWNERS
-smoreland@google.com
-alexbuy@google.com
+amhk@google.com
+gurpreetgs@google.com
+michaelwr@google.com
 patb@google.com
+smoreland@google.com
 zyy@google.com
diff --git a/tools/protos/metadata_file.proto b/tools/protos/metadata_file.proto
index 47562c5..5c89618 100644
--- a/tools/protos/metadata_file.proto
+++ b/tools/protos/metadata_file.proto
@@ -282,7 +282,7 @@
   optional string element_id = 3;
 }
 
-// Identifier for a third-package package.
+// Identifier for a third-party package.
 // See go/tp-metadata-id.
 message Identifier {
   // The type of the identifier. Either an "ecosystem" value from
@@ -338,7 +338,19 @@
   //  - "PrebuiltByAlphabet": This type should be used for archives of primarily
   //  Google-owned source code (may contain non-Google-owned dependencies),
   //  which has been built using production Google infrastructure, and copied
-  //  into third_party.
+  //  into Android. The "value" field is the URL of the prebuilt artifact or
+  //  the relative path of the artifact to the root of a package.
+  //  Example:
+  //    identifier {
+  //      type: "PrebuiltByAlphabet",
+  //      version: "1",
+  //      value: "v1/arm84_hdpi.apk",
+  //    }
+  //    identifier {
+  //      type: "PrebuiltByAlphabet",
+  //      version: "2",
+  //      value: "v2/x86_64_xhdpi.apk",
+  //    }
   //
   //  - "LocalSource": The "value" field is the URL identifying where the local
   //  copy of the package source code can be found.
diff --git a/tools/releasetools/Android.bp b/tools/releasetools/Android.bp
index 4941c71..9385f0c 100644
--- a/tools/releasetools/Android.bp
+++ b/tools/releasetools/Android.bp
@@ -244,7 +244,6 @@
         "boot_signer",
         "brotli",
         "bsdiff",
-        "imgdiff",
         "lz4",
         "mkbootfs",
         "signapk",
@@ -308,7 +307,6 @@
         "brotli",
         "bsdiff",
         "deapexer",
-        "imgdiff",
         "lz4",
         "mkbootfs",
         "signapk",
diff --git a/tools/releasetools/add_img_to_target_files.py b/tools/releasetools/add_img_to_target_files.py
index b39a82c..a2c0949 100644
--- a/tools/releasetools/add_img_to_target_files.py
+++ b/tools/releasetools/add_img_to_target_files.py
@@ -1055,6 +1055,20 @@
     for call in add_partition_calls:
       add_partition(*call)
   else:
+    # When calling avbtool make_vbmeta_image, it uses the `partitions`
+    # dictionary to include the options for --include_descriptors_from_image.
+    # The vbmeta image is different if the order of the
+    # --include_descriptors_from_image changes. As the images are generated
+    # parallelly and entries are added on completion of image creation,
+    # this `partitions` dict might be indeterministic as the order of
+    # completion of image creation cannot be predicted.
+    # To address this issue, add keys to the dict `partitions` with null values
+    # in the order they are listed in the variable `add_partition_calls`, and
+    # then the values are updated by `add_partition` keeping the order of the
+    # items. This ensures generated vbmeta.img is the same for the same input.
+    for call in add_partition_calls:
+      if call[1]:
+        partitions[call[0]] = None
     with ThreadPoolExecutor(max_workers=len(add_partition_calls)) as executor:
       for future in [executor.submit(add_partition, *call) for call in add_partition_calls]:
         future.result()
diff --git a/tools/releasetools/common.py b/tools/releasetools/common.py
index 8836248..264c2d8 100644
--- a/tools/releasetools/common.py
+++ b/tools/releasetools/common.py
@@ -955,6 +955,13 @@
   d["build.prop"] = d["system.build.prop"]
 
   if d.get("avb_enable") == "true":
+    build_info = BuildInfo(d, use_legacy_id=True)
+    # Set up the salt for partitions without build.prop
+    if build_info.fingerprint:
+      if "fingerprint" not in d:
+        d["fingerprint"] = build_info.fingerprint
+      if "avb_salt" not in d:
+        d["avb_salt"] = sha256(build_info.fingerprint.encode()).hexdigest()
     # Set the vbmeta digest if exists
     try:
       d["vbmeta_digest"] = read_helper("META/vbmeta_digest.txt").rstrip()
diff --git a/tools/releasetools/ota_from_target_files.py b/tools/releasetools/ota_from_target_files.py
index c0ff5d2..2c5fe0d 100755
--- a/tools/releasetools/ota_from_target_files.py
+++ b/tools/releasetools/ota_from_target_files.py
@@ -872,6 +872,7 @@
   target_info = common.BuildInfo(OPTIONS.info_dict, OPTIONS.oem_dicts)
   if OPTIONS.disable_vabc and target_info.is_release_key:
     raise ValueError("Disabling VABC on release-key builds is not supported.")
+
   ValidateCompressionParam(target_info)
   vabc_compression_param = target_info.vabc_compression_param
 
@@ -907,7 +908,6 @@
       logger.info("Source build and target build use different compression methods {} vs {}, default to source builds parameter {}".format(
           source_info.vabc_compression_param, target_info.vabc_compression_param, source_info.vabc_compression_param))
       vabc_compression_param = source_info.vabc_compression_param
-
     # Virtual AB Cow version 3 is introduced in Android U with improved memory
     # and install time performance. All OTA's with
     # both the source build and target build with VIRTUAL_AB_COW_VERSION = 3
@@ -918,6 +918,7 @@
     elif source_info.vabc_cow_version != target_info.vabc_cow_version:
       logger.info("Source and Target have different cow VABC_COW_VERSION specified, default to minimum version")
       OPTIONS.vabc_cow_version = min(source_info.vabc_cow_version, target_info.vabc_cow_version)
+
     # Virtual AB Compression was introduced in Androd S.
     # Later, we backported VABC to Android R. But verity support was not
     # backported, so if VABC is used and we are on Android R, disable
@@ -930,6 +931,19 @@
     assert "ab_partitions" in OPTIONS.info_dict, \
         "META/ab_partitions.txt is required for ab_update."
     source_info = None
+    if not target_info.vabc_cow_version:
+        OPTIONS.vabc_cow_version = 2
+    elif target_info.vabc_cow_version >= "3" and target_info.vendor_api_level < 35:
+      logger.warning(
+            "This full OTA is configured to use VABC cow version"
+            " 3 which is supported since"
+            " Android API level 35, but device is "
+            "launched with {} . If this full OTA is"
+            " served to a device running old build, OTA might fail due to "
+            "unsupported vabc cow version. For safety, version 2 is used because "
+            "it's supported since day 1.".format(
+                target_info.vendor_api_level))
+      OPTIONS.vabc_cow_version = 2
     if OPTIONS.vabc_compression_param is None and vabc_compression_param:
       minimum_api_level_required = VABC_COMPRESSION_PARAM_SUPPORT[
           vabc_compression_param]
@@ -1274,7 +1288,7 @@
       assert len(words) >= 1 and len(words) <= 2
       OPTIONS.vabc_compression_param = a.lower()
       if len(words) == 2:
-        if not words[1].isdigit():
+        if not words[1].lstrip("-").isdigit():
           raise ValueError("Cannot parse value %r for option $COMPRESSION_LEVEL - only "
                            "integers are allowed." % words[1])
     elif o == "--security_patch_level":