Merge "Add tool to determine if products can be lunched" into main
diff --git a/cogsetup.sh b/cogsetup.sh
index 44538f2..ef1485d 100644
--- a/cogsetup.sh
+++ b/cogsetup.sh
@@ -52,7 +52,9 @@
   # it with this function. If the user is running repo within a Cog workspace,
   # we'll fail with an error, otherwise, we run the original repo command with
   # the given args.
-  ORIG_REPO_PATH=`which repo`
+  if ! ORIG_REPO_PATH=`which repo`; then
+    return 0
+  fi
   function repo {
     if [[ "${PWD}" == /google/cog/* ]]; then
       echo "\e[01;31mERROR:\e[0mrepo command is disallowed within Cog workspaces."
diff --git a/core/Makefile b/core/Makefile
index e6a5bc0..00577ed 100644
--- a/core/Makefile
+++ b/core/Makefile
@@ -1477,13 +1477,14 @@
 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))
+ALL_DEFAULT_INSTALLED_MODULES += $(TARGET_OUT_VENDOR)/boot_otas/boot_ota_4k.zip
+ALL_DEFAULT_INSTALLED_MODULES += $(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
+endif # BOARD_16K_OTA_MOVE_VENDOR == true
 
 
 endif
@@ -6919,6 +6920,7 @@
         --verbose \
         --path $(HOST_OUT) \
         $(if $(OEM_OTA_CONFIG), --oem_settings $(OEM_OTA_CONFIG)) \
+        $(if $(BOOT_VAR_OTA_CONFIG), --boot_variable_file $(BOOT_VAR_OTA_CONFIG)) \
         $(2) \
         $(patsubst %.zip,%,$(BUILT_TARGET_FILES_PACKAGE)) $(1)
 endef
diff --git a/core/dex_preopt.mk b/core/dex_preopt.mk
index 08311ca..26b8b17 100644
--- a/core/dex_preopt.mk
+++ b/core/dex_preopt.mk
@@ -123,22 +123,28 @@
 
 $(call dist-for-goals, droidcore, $(boot_zip))
 
-ifneq (,$(filter true,$(ART_MODULE_BUILD_FROM_SOURCE) $(MODULE_BUILD_FROM_SOURCE)))
 # Build the system_server.zip which contains the Apex system server jars and standalone system server jars
+system_server_dex2oat_dir := $(SOONG_OUT_DIR)/system_server_dexjars
 system_server_zip := $(PRODUCT_OUT)/system_server.zip
+# non_updatable_system_server_jars contains jars in /system and /system_ext that are not part of an apex.
+non_updatable_system_server_jars := \
+  $(foreach m,$(PRODUCT_SYSTEM_SERVER_JARS),\
+    $(system_server_dex2oat_dir)/$(call word-colon,2,$(m)).jar)
+
 apex_system_server_jars := \
   $(foreach m,$(PRODUCT_APEX_SYSTEM_SERVER_JARS),\
-    $(PRODUCT_OUT)/apex/$(call word-colon,1,$(m))/javalib/$(call word-colon,2,$(m)).jar)
+    $(system_server_dex2oat_dir)/$(call word-colon,2,$(m)).jar)
 
 apex_standalone_system_server_jars := \
   $(foreach m,$(PRODUCT_APEX_STANDALONE_SYSTEM_SERVER_JARS),\
-    $(PRODUCT_OUT)/apex/$(call word-colon,1,$(m))/javalib/$(call word-colon,2,$(m)).jar)
+    $(system_server_dex2oat_dir)/$(call word-colon,2,$(m)).jar)
 
 standalone_system_server_jars := \
   $(foreach m,$(PRODUCT_STANDALONE_SYSTEM_SERVER_JARS),\
-    $(PRODUCT_OUT)/apex/$(call word-colon,1,$(m))/javalib/$(call word-colon,2,$(m)).jar)
+    $(system_server_dex2oat_dir)/$(call word-colon,2,$(m)).jar)
 
-$(system_server_zip): PRIVATE_SYSTEM_SERVER_JARS := $(system_server_jars)
+$(system_server_zip): PRIVATE_SYSTEM_SERVER_DEX2OAT_DIR := $(system_server_dex2oat_dir)
+$(system_server_zip): PRIVATE_SYSTEM_SERVER_JARS := $(non_updatable_system_server_jars)
 $(system_server_zip): PRIVATE_APEX_SYSTEM_SERVER_JARS := $(apex_system_server_jars)
 $(system_server_zip): PRIVATE_APEX_STANDALONE_SYSTEM_SERVER_JARS := $(apex_standalone_system_server_jars)
 $(system_server_zip): PRIVATE_STANDALONE_SYSTEM_SERVER_JARS := $(standalone_system_server_jars)
@@ -146,14 +152,13 @@
 	@echo "Create system server package: $@"
 	rm -f $@
 	$(SOONG_ZIP) -o $@ \
-	  -C $(PRODUCT_OUT) $(addprefix -f ,$(PRIVATE_SYSTEM_SERVER_JARS)) \
-	  -C $(PRODUCT_OUT) $(addprefix -f ,$(PRIVATE_APEX_SYSTEM_SERVER_JARS)) \
-          -C $(PRODUCT_OUT) $(addprefix -f ,$(PRIVATE_APEX_STANDALONE_SYSTEM_SERVER_JARS)) \
-	  -C $(PRODUCT_OUT) $(addprefix -f ,$(PRIVATE_STANDALONE_SYSTEM_SERVER_JARS))
+	  -C $(PRIVATE_SYSTEM_SERVER_DEX2OAT_DIR) $(addprefix -f ,$(PRIVATE_SYSTEM_SERVER_JARS)) \
+	  -C $(PRIVATE_SYSTEM_SERVER_DEX2OAT_DIR) $(addprefix -f ,$(PRIVATE_APEX_SYSTEM_SERVER_JARS)) \
+	  -C $(PRIVATE_SYSTEM_SERVER_DEX2OAT_DIR) $(addprefix -f ,$(PRIVATE_APEX_STANDALONE_SYSTEM_SERVER_JARS)) \
+	  -C $(PRIVATE_SYSTEM_SERVER_DEX2OAT_DIR) $(addprefix -f ,$(PRIVATE_STANDALONE_SYSTEM_SERVER_JARS))
 
 $(call dist-for-goals, droidcore, $(system_server_zip))
 
-endif  #ART_MODULE_BUILD_FROM_SOURCE || MODULE_BUILD_FROM_SOURCE
 endif  #PRODUCT_USES_DEFAULT_ART_CONFIG
 endif  #WITH_DEXPREOPT_ART_BOOT_IMG_ONLY
 endif  #WITH_DEXPREOPT
diff --git a/core/tasks/meta-lic.mk b/core/tasks/meta-lic.mk
index 99ecd83..c630bcc 100644
--- a/core/tasks/meta-lic.mk
+++ b/core/tasks/meta-lic.mk
@@ -49,6 +49,41 @@
 $(eval $(call declare-1p-copy-files,device/google/coral,audio_policy_configuration.xml))
 $(eval $(call declare-1p-copy-files,device/google/coral,display_19260504575090817.xml))
 
+# Moved here from device/google/gs101/Android.mk
+$(eval $(call declare-copy-files-license-metadata,device/google/gs101,default-permissions.xml,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
+$(eval $(call declare-copy-files-license-metadata,device/google/gs101,libnfc-nci.conf,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
+$(eval $(call declare-copy-files-license-metadata,device/google/gs101,fstab.postinstall,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
+$(eval $(call declare-copy-files-license-metadata,device/google/gs101,ueventd.rc,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
+$(eval $(call declare-copy-files-license-metadata,device/google/gs101,wpa_supplicant.conf,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
+$(eval $(call declare-copy-files-license-metadata,device/google/gs101,hals.conf,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
+$(eval $(call declare-copy-files-license-metadata,device/google/gs101,media_profiles_V1_0.xml,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
+$(eval $(call declare-copy-files-license-metadata,device/google/gs101,media_codecs_performance.xml,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
+$(eval $(call declare-copy-files-license-metadata,device/google/gs101,device_state_configuration.xml,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
+$(eval $(call declare-copy-files-license-metadata,device/google/gs101,task_profiles.json,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
+$(eval $(call declare-copy-files-license-metadata,device/google/gs101,p2p_supplicant_overlay.conf,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
+$(eval $(call declare-copy-files-license-metadata,device/google/gs101,wpa_supplicant.conf,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
+$(eval $(call declare-copy-files-license-metadata,device/google/gs101,wpa_supplicant_overlay.conf,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
+
+$(eval $(call declare-1p-copy-files,device/google/gs101,audio_policy_configuration.xml))
+
+# Move here from device/google/raviole/Android.mk
+$(eval $(call declare-copy-files-license-metadata,device/google/raviole,default-permissions.xml,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
+$(eval $(call declare-copy-files-license-metadata,device/google/raviole,libnfc-nci-raven.conf,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
+$(eval $(call declare-copy-files-license-metadata,device/google/raviole,libnfc-nci.conf,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
+$(eval $(call declare-copy-files-license-metadata,device/google/raviole,fstab.postinstall,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
+$(eval $(call declare-copy-files-license-metadata,device/google/raviole,ueventd.rc,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
+$(eval $(call declare-copy-files-license-metadata,device/google/raviole,wpa_supplicant.conf,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
+$(eval $(call declare-copy-files-license-metadata,device/google/raviole,hals.conf,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
+$(eval $(call declare-copy-files-license-metadata,device/google/raviole,media_profiles_V1_0.xml,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
+$(eval $(call declare-copy-files-license-metadata,device/google/raviole,media_codecs_performance.xml,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
+$(eval $(call declare-copy-files-license-metadata,device/google/raviole,device_state_configuration.xml,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
+$(eval $(call declare-copy-files-license-metadata,device/google/raviole,task_profiles.json,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
+$(eval $(call declare-copy-files-license-metadata,device/google/raviole,p2p_supplicant.conf,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
+$(eval $(call declare-copy-files-license-metadata,device/google/raviole,wpa_supplicant.conf,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
+$(eval $(call declare-copy-files-license-metadata,device/google/raviole,wpa_supplicant_overlay.conf,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
+
+$(eval $(call declare-1p-copy-files,device/google/raviole,audio_policy_configuration.xml))
+
 # Moved here from device/sample/Android.mk
 $(eval $(call declare-1p-copy-files,device/sample,))
 
diff --git a/target/product/handheld_system.mk b/target/product/handheld_system.mk
index bf9aa41..3c401f3 100644
--- a/target/product/handheld_system.mk
+++ b/target/product/handheld_system.mk
@@ -82,8 +82,9 @@
     KeyChain \
     Telecom \
 
+PRODUCT_PACKAGES += framework-audio_effects.xml
+
 PRODUCT_COPY_FILES += \
-    frameworks/av/media/libeffects/data/audio_effects.xml:system/etc/audio_effects.xml \
     frameworks/native/data/etc/android.software.window_magnification.xml:$(TARGET_COPY_OUT_SYSTEM)/etc/permissions/android.software.window_magnification.xml \
 
 PRODUCT_VENDOR_PROPERTIES += \
diff --git a/teams/Android.bp b/teams/Android.bp
index a02a573..78efa61 100644
--- a/teams/Android.bp
+++ b/teams/Android.bp
@@ -4371,3 +4371,10 @@
   // go/trendy/manage/engineers/5810097836621824
   trendy_team_id: "5810097836621824",
 }
+
+team {
+    name: "trendy_team_adte",
+
+    // go/trendy/manage/engineers/5551098528825344
+    trendy_team_id: "5551098528825344",
+}
diff --git a/tools/aconfig/aconfig/src/codegen/cpp.rs b/tools/aconfig/aconfig/src/codegen/cpp.rs
index cd71b10..e743b2f 100644
--- a/tools/aconfig/aconfig/src/codegen/cpp.rs
+++ b/tools/aconfig/aconfig/src/codegen/cpp.rs
@@ -16,6 +16,7 @@
 
 use anyhow::{ensure, Result};
 use serde::Serialize;
+use std::collections::HashMap;
 use std::path::PathBuf;
 use tinytemplate::TinyTemplate;
 
@@ -29,13 +30,15 @@
     package: &str,
     parsed_flags_iter: I,
     codegen_mode: CodegenMode,
+    flag_ids: HashMap<String, u16>,
+    allow_instrumentation: bool,
 ) -> Result<Vec<OutputFile>>
 where
     I: Iterator<Item = ProtoParsedFlag>,
 {
     let mut readwrite_count = 0;
     let class_elements: Vec<ClassElement> = parsed_flags_iter
-        .map(|pf| create_class_element(package, &pf, &mut readwrite_count))
+        .map(|pf| create_class_element(package, &pf, flag_ids.clone(), &mut readwrite_count))
         .collect();
     let readwrite = readwrite_count > 0;
     let has_fixed_read_only = class_elements.iter().any(|item| item.is_fixed_read_only);
@@ -53,6 +56,7 @@
         readwrite_count,
         is_test_mode: codegen_mode == CodegenMode::Test,
         class_elements,
+        allow_instrumentation,
     };
 
     let files = [
@@ -96,6 +100,7 @@
     pub readwrite_count: i32,
     pub is_test_mode: bool,
     pub class_elements: Vec<ClassElement>,
+    pub allow_instrumentation: bool,
 }
 
 #[derive(Serialize)]
@@ -106,11 +111,18 @@
     pub default_value: String,
     pub flag_name: String,
     pub flag_macro: String,
+    pub flag_offset: u16,
     pub device_config_namespace: String,
     pub device_config_flag: String,
+    pub container: String,
 }
 
-fn create_class_element(package: &str, pf: &ProtoParsedFlag, rw_count: &mut i32) -> ClassElement {
+fn create_class_element(
+    package: &str,
+    pf: &ProtoParsedFlag,
+    flag_ids: HashMap<String, u16>,
+    rw_count: &mut i32,
+) -> ClassElement {
     ClassElement {
         readwrite_idx: if pf.permission() == ProtoFlagPermission::READ_WRITE {
             let index = *rw_count;
@@ -128,9 +140,11 @@
         },
         flag_name: pf.name().to_string(),
         flag_macro: pf.name().to_uppercase(),
+        flag_offset: *flag_ids.get(pf.name()).expect("values checked at flag parse time"),
         device_config_namespace: pf.namespace().to_string(),
         device_config_flag: codegen::create_device_config_ident(package, pf.name())
             .expect("values checked at flag parse time"),
+        container: pf.container().to_string(),
     }
 }
 
@@ -1162,18 +1176,27 @@
     return true;
 }
 "#;
+    use crate::commands::assign_flag_ids;
 
     fn test_generate_cpp_code(
         parsed_flags: ProtoParsedFlags,
         mode: CodegenMode,
         expected_header: &str,
         expected_src: &str,
+        allow_instrumentation: bool,
     ) {
         let modified_parsed_flags =
             crate::commands::modify_parsed_flags_based_on_mode(parsed_flags, mode).unwrap();
-        let generated =
-            generate_cpp_code(crate::test::TEST_PACKAGE, modified_parsed_flags.into_iter(), mode)
-                .unwrap();
+        let flag_ids =
+            assign_flag_ids(crate::test::TEST_PACKAGE, modified_parsed_flags.iter()).unwrap();
+        let generated = generate_cpp_code(
+            crate::test::TEST_PACKAGE,
+            modified_parsed_flags.into_iter(),
+            mode,
+            flag_ids,
+            allow_instrumentation,
+        )
+        .unwrap();
         let mut generated_files_map = HashMap::new();
         for file in generated {
             generated_files_map.insert(
@@ -1211,6 +1234,7 @@
             CodegenMode::Production,
             EXPORTED_PROD_HEADER_EXPECTED,
             PROD_SOURCE_FILE_EXPECTED,
+            false,
         );
     }
 
@@ -1222,6 +1246,7 @@
             CodegenMode::Test,
             EXPORTED_TEST_HEADER_EXPECTED,
             TEST_SOURCE_FILE_EXPECTED,
+            false,
         );
     }
 
@@ -1233,6 +1258,7 @@
             CodegenMode::Exported,
             EXPORTED_EXPORTED_HEADER_EXPECTED,
             EXPORTED_SOURCE_FILE_EXPECTED,
+            false,
         );
     }
 
@@ -1244,6 +1270,7 @@
             CodegenMode::ForceReadOnly,
             EXPORTED_FORCE_READ_ONLY_HEADER_EXPECTED,
             FORCE_READ_ONLY_SOURCE_FILE_EXPECTED,
+            false,
         );
     }
 
@@ -1255,6 +1282,7 @@
             CodegenMode::Production,
             READ_ONLY_EXPORTED_PROD_HEADER_EXPECTED,
             READ_ONLY_PROD_SOURCE_FILE_EXPECTED,
+            false,
         );
     }
 }
diff --git a/tools/aconfig/aconfig/src/codegen/java.rs b/tools/aconfig/aconfig/src/codegen/java.rs
index 18a4be5..3360ddd 100644
--- a/tools/aconfig/aconfig/src/codegen/java.rs
+++ b/tools/aconfig/aconfig/src/codegen/java.rs
@@ -64,20 +64,27 @@
         include_str!("../../templates/FeatureFlags.java.template"),
     )?;
     template.add_template(
+        "CustomFeatureFlags.java",
+        include_str!("../../templates/CustomFeatureFlags.java.template"),
+    )?;
+    template.add_template(
         "FakeFeatureFlagsImpl.java",
         include_str!("../../templates/FakeFeatureFlagsImpl.java.template"),
     )?;
 
     let path: PathBuf = package.split('.').collect();
-    ["Flags.java", "FeatureFlags.java", "FeatureFlagsImpl.java", "FakeFeatureFlagsImpl.java"]
-        .iter()
-        .map(|file| {
-            Ok(OutputFile {
-                contents: template.render(file, &context)?.into(),
-                path: path.join(file),
-            })
-        })
-        .collect::<Result<Vec<OutputFile>>>()
+    [
+        "Flags.java",
+        "FeatureFlags.java",
+        "FeatureFlagsImpl.java",
+        "CustomFeatureFlags.java",
+        "FakeFeatureFlagsImpl.java",
+    ]
+    .iter()
+    .map(|file| {
+        Ok(OutputFile { contents: template.render(file, &context)?.into(), path: path.join(file) })
+    })
+    .collect::<Result<Vec<OutputFile>>>()
 }
 
 fn gen_flags_by_namespace(flags: &[FlagElement]) -> Vec<NamespaceFlags> {
@@ -292,76 +299,82 @@
         }
     "#;
 
-    const EXPECTED_FAKEFEATUREFLAGSIMPL_CONTENT: &str = r#"
+    const EXPECTED_CUSTOMFEATUREFLAGS_CONTENT: &str = r#"
     package com.android.aconfig.test;
+
     // TODO(b/303773055): Remove the annotation after access issue is resolved.
     import android.compat.annotation.UnsupportedAppUsage;
     import java.util.Arrays;
-    import java.util.HashMap;
     import java.util.HashSet;
-    import java.util.Map;
+    import java.util.List;
     import java.util.Set;
+    import java.util.function.BiPredicate;
+    import java.util.function.Predicate;
+
     /** @hide */
-    public class FakeFeatureFlagsImpl implements FeatureFlags {
-        public FakeFeatureFlagsImpl() {
-            resetAll();
+    public class CustomFeatureFlags implements FeatureFlags {
+
+        private BiPredicate<String, Predicate<FeatureFlags>> mGetValueImpl;
+
+        public CustomFeatureFlags(BiPredicate<String, Predicate<FeatureFlags>> getValueImpl) {
+            mGetValueImpl = getValueImpl;
         }
+
         @Override
         @UnsupportedAppUsage
         public boolean disabledRo() {
-            return getValue(Flags.FLAG_DISABLED_RO);
+            return getValue(Flags.FLAG_DISABLED_RO,
+                    FeatureFlags::disabledRo);
         }
         @Override
         @UnsupportedAppUsage
         public boolean disabledRw() {
-            return getValue(Flags.FLAG_DISABLED_RW);
+            return getValue(Flags.FLAG_DISABLED_RW,
+                FeatureFlags::disabledRw);
         }
         @Override
         @UnsupportedAppUsage
         public boolean disabledRwExported() {
-            return getValue(Flags.FLAG_DISABLED_RW_EXPORTED);
+            return getValue(Flags.FLAG_DISABLED_RW_EXPORTED,
+                FeatureFlags::disabledRwExported);
         }
         @Override
         @UnsupportedAppUsage
         public boolean disabledRwInOtherNamespace() {
-            return getValue(Flags.FLAG_DISABLED_RW_IN_OTHER_NAMESPACE);
+            return getValue(Flags.FLAG_DISABLED_RW_IN_OTHER_NAMESPACE,
+                FeatureFlags::disabledRwInOtherNamespace);
         }
         @Override
         @UnsupportedAppUsage
         public boolean enabledFixedRo() {
-            return getValue(Flags.FLAG_ENABLED_FIXED_RO);
+            return getValue(Flags.FLAG_ENABLED_FIXED_RO,
+                FeatureFlags::enabledFixedRo);
         }
         @Override
         @UnsupportedAppUsage
         public boolean enabledFixedRoExported() {
-            return getValue(Flags.FLAG_ENABLED_FIXED_RO_EXPORTED);
+            return getValue(Flags.FLAG_ENABLED_FIXED_RO_EXPORTED,
+                FeatureFlags::enabledFixedRoExported);
         }
         @Override
         @UnsupportedAppUsage
         public boolean enabledRo() {
-            return getValue(Flags.FLAG_ENABLED_RO);
+            return getValue(Flags.FLAG_ENABLED_RO,
+                FeatureFlags::enabledRo);
         }
         @Override
         @UnsupportedAppUsage
         public boolean enabledRoExported() {
-            return getValue(Flags.FLAG_ENABLED_RO_EXPORTED);
+            return getValue(Flags.FLAG_ENABLED_RO_EXPORTED,
+                FeatureFlags::enabledRoExported);
         }
         @Override
         @UnsupportedAppUsage
         public boolean enabledRw() {
-            return getValue(Flags.FLAG_ENABLED_RW);
+            return getValue(Flags.FLAG_ENABLED_RW,
+                FeatureFlags::enabledRw);
         }
-        public void setFlag(String flagName, boolean value) {
-            if (!this.mFlagMap.containsKey(flagName)) {
-                throw new IllegalArgumentException("no such flag " + flagName);
-            }
-            this.mFlagMap.put(flagName, value);
-        }
-        public void resetAll() {
-            for (Map.Entry entry : mFlagMap.entrySet()) {
-                entry.setValue(null);
-            }
-        }
+
         public boolean isFlagReadOnlyOptimized(String flagName) {
             if (mReadOnlyFlagsSet.contains(flagName) &&
                 isOptimizationEnabled()) {
@@ -369,30 +382,30 @@
             }
             return false;
         }
+
         @com.android.aconfig.annotations.AssumeTrueForR8
         private boolean isOptimizationEnabled() {
             return false;
         }
-        private boolean getValue(String flagName) {
-            Boolean value = this.mFlagMap.get(flagName);
-            if (value == null) {
-                throw new IllegalArgumentException(flagName + " is not set");
-            }
-            return value;
+
+        protected boolean getValue(String flagName, Predicate<FeatureFlags> getter) {
+            return mGetValueImpl.test(flagName, getter);
         }
-        private Map<String, Boolean> mFlagMap = new HashMap<>(
-            Map.ofEntries(
-                Map.entry(Flags.FLAG_DISABLED_RO, false),
-                Map.entry(Flags.FLAG_DISABLED_RW, false),
-                Map.entry(Flags.FLAG_DISABLED_RW_EXPORTED, false),
-                Map.entry(Flags.FLAG_DISABLED_RW_IN_OTHER_NAMESPACE, false),
-                Map.entry(Flags.FLAG_ENABLED_FIXED_RO, false),
-                Map.entry(Flags.FLAG_ENABLED_FIXED_RO_EXPORTED, false),
-                Map.entry(Flags.FLAG_ENABLED_RO, false),
-                Map.entry(Flags.FLAG_ENABLED_RO_EXPORTED, false),
-                Map.entry(Flags.FLAG_ENABLED_RW, false)
-            )
-        );
+
+        public List<String> getFlagNames() {
+            return Arrays.asList(
+                Flags.FLAG_DISABLED_RO,
+                Flags.FLAG_DISABLED_RW,
+                Flags.FLAG_DISABLED_RW_EXPORTED,
+                Flags.FLAG_DISABLED_RW_IN_OTHER_NAMESPACE,
+                Flags.FLAG_ENABLED_FIXED_RO,
+                Flags.FLAG_ENABLED_FIXED_RO_EXPORTED,
+                Flags.FLAG_ENABLED_RO,
+                Flags.FLAG_ENABLED_RO_EXPORTED,
+                Flags.FLAG_ENABLED_RW
+            );
+        }
+
         private Set<String> mReadOnlyFlagsSet = new HashSet<>(
             Arrays.asList(
                 Flags.FLAG_DISABLED_RO,
@@ -406,6 +419,58 @@
     }
     "#;
 
+    const EXPECTED_FAKEFEATUREFLAGSIMPL_CONTENT: &str = r#"
+    package com.android.aconfig.test;
+
+    import java.util.HashMap;
+    import java.util.Map;
+    import java.util.function.Predicate;
+
+    /** @hide */
+    public class FakeFeatureFlagsImpl extends CustomFeatureFlags {
+        private final Map<String, Boolean> mFlagMap = new HashMap<>();
+        private final FeatureFlags mDefaults;
+
+        public FakeFeatureFlagsImpl() {
+            this(null);
+        }
+
+        public FakeFeatureFlagsImpl(FeatureFlags defaults) {
+            super(null);
+            mDefaults = defaults;
+            // Initialize the map with null values
+            for (String flagName : getFlagNames()) {
+                mFlagMap.put(flagName, null);
+            }
+        }
+
+        @Override
+        protected boolean getValue(String flagName, Predicate<FeatureFlags> getter) {
+            Boolean value = this.mFlagMap.get(flagName);
+            if (value != null) {
+                return value;
+            }
+            if (mDefaults != null) {
+                return getter.test(mDefaults);
+            }
+            throw new IllegalArgumentException(flagName + " is not set");
+        }
+
+        public void setFlag(String flagName, boolean value) {
+            if (!this.mFlagMap.containsKey(flagName)) {
+                throw new IllegalArgumentException("no such flag " + flagName);
+            }
+            this.mFlagMap.put(flagName, value);
+        }
+
+        public void resetAll() {
+            for (Map.Entry entry : mFlagMap.entrySet()) {
+                entry.setValue(null);
+            }
+        }
+    }
+    "#;
+
     #[test]
     fn test_generate_java_code_production() {
         let parsed_flags = crate::test::parse_test_flags();
@@ -549,6 +614,10 @@
             ("com/android/aconfig/test/FeatureFlagsImpl.java", expect_featureflagsimpl_content),
             ("com/android/aconfig/test/FeatureFlags.java", EXPECTED_FEATUREFLAGS_COMMON_CONTENT),
             (
+                "com/android/aconfig/test/CustomFeatureFlags.java",
+                EXPECTED_CUSTOMFEATUREFLAGS_CONTENT,
+            ),
+            (
                 "com/android/aconfig/test/FakeFeatureFlagsImpl.java",
                 EXPECTED_FAKEFEATUREFLAGSIMPL_CONTENT,
             ),
@@ -671,55 +740,53 @@
             }
         }"#;
 
-        let expect_fake_feature_flags_impl_content = r#"
+        let expect_custom_feature_flags_content = r#"
         package com.android.aconfig.test;
+
         import java.util.Arrays;
-        import java.util.HashMap;
         import java.util.HashSet;
-        import java.util.Map;
+        import java.util.List;
         import java.util.Set;
+        import java.util.function.BiPredicate;
+        import java.util.function.Predicate;
+
         /** @hide */
-        public class FakeFeatureFlagsImpl implements FeatureFlags {
-            public FakeFeatureFlagsImpl() {
-                resetAll();
+        public class CustomFeatureFlags implements FeatureFlags {
+
+            private BiPredicate<String, Predicate<FeatureFlags>> mGetValueImpl;
+
+            public CustomFeatureFlags(BiPredicate<String, Predicate<FeatureFlags>> getValueImpl) {
+                mGetValueImpl = getValueImpl;
             }
+
             @Override
             public boolean disabledRwExported() {
-                return getValue(Flags.FLAG_DISABLED_RW_EXPORTED);
+                return getValue(Flags.FLAG_DISABLED_RW_EXPORTED,
+                    FeatureFlags::disabledRwExported);
             }
             @Override
             public boolean enabledFixedRoExported() {
-                return getValue(Flags.FLAG_ENABLED_FIXED_RO_EXPORTED);
+                return getValue(Flags.FLAG_ENABLED_FIXED_RO_EXPORTED,
+                    FeatureFlags::enabledFixedRoExported);
             }
             @Override
             public boolean enabledRoExported() {
-                return getValue(Flags.FLAG_ENABLED_RO_EXPORTED);
+                return getValue(Flags.FLAG_ENABLED_RO_EXPORTED,
+                    FeatureFlags::enabledRoExported);
             }
-            public void setFlag(String flagName, boolean value) {
-                if (!this.mFlagMap.containsKey(flagName)) {
-                    throw new IllegalArgumentException("no such flag " + flagName);
-                }
-                this.mFlagMap.put(flagName, value);
+
+            protected boolean getValue(String flagName, Predicate<FeatureFlags> getter) {
+                return mGetValueImpl.test(flagName, getter);
             }
-            public void resetAll() {
-                for (Map.Entry entry : mFlagMap.entrySet()) {
-                    entry.setValue(null);
-                }
+
+            public List<String> getFlagNames() {
+                return Arrays.asList(
+                    Flags.FLAG_DISABLED_RW_EXPORTED,
+                    Flags.FLAG_ENABLED_FIXED_RO_EXPORTED,
+                    Flags.FLAG_ENABLED_RO_EXPORTED
+                );
             }
-            private boolean getValue(String flagName) {
-                Boolean value = this.mFlagMap.get(flagName);
-                if (value == null) {
-                    throw new IllegalArgumentException(flagName + " is not set");
-                }
-                return value;
-            }
-            private Map<String, Boolean> mFlagMap = new HashMap<>(
-                Map.ofEntries(
-                    Map.entry(Flags.FLAG_DISABLED_RW_EXPORTED, false),
-                    Map.entry(Flags.FLAG_ENABLED_FIXED_RO_EXPORTED, false),
-                    Map.entry(Flags.FLAG_ENABLED_RO_EXPORTED, false)
-                )
-            );
+
             private Set<String> mReadOnlyFlagsSet = new HashSet<>(
                 Arrays.asList(
                     ""
@@ -733,8 +800,12 @@
             ("com/android/aconfig/test/FeatureFlags.java", expect_feature_flags_content),
             ("com/android/aconfig/test/FeatureFlagsImpl.java", expect_feature_flags_impl_content),
             (
+                "com/android/aconfig/test/CustomFeatureFlags.java",
+                expect_custom_feature_flags_content,
+            ),
+            (
                 "com/android/aconfig/test/FakeFeatureFlagsImpl.java",
-                expect_fake_feature_flags_impl_content,
+                EXPECTED_FAKEFEATUREFLAGSIMPL_CONTENT,
             ),
         ]);
 
@@ -854,6 +925,10 @@
             ("com/android/aconfig/test/FeatureFlags.java", EXPECTED_FEATUREFLAGS_COMMON_CONTENT),
             ("com/android/aconfig/test/FeatureFlagsImpl.java", expect_featureflagsimpl_content),
             (
+                "com/android/aconfig/test/CustomFeatureFlags.java",
+                EXPECTED_CUSTOMFEATUREFLAGS_CONTENT,
+            ),
+            (
                 "com/android/aconfig/test/FakeFeatureFlagsImpl.java",
                 EXPECTED_FAKEFEATUREFLAGSIMPL_CONTENT,
             ),
@@ -1020,61 +1095,64 @@
             private static FeatureFlags FEATURE_FLAGS = new FeatureFlagsImpl();
         }"#;
 
-        let expect_fakefeatureflags_content = r#"
+        let expect_customfeatureflags_content = r#"
         package com.android.aconfig.test;
+
         // TODO(b/303773055): Remove the annotation after access issue is resolved.
         import android.compat.annotation.UnsupportedAppUsage;
         import java.util.Arrays;
-        import java.util.HashMap;
         import java.util.HashSet;
-        import java.util.Map;
+        import java.util.List;
         import java.util.Set;
+        import java.util.function.BiPredicate;
+        import java.util.function.Predicate;
+
         /** @hide */
-        public class FakeFeatureFlagsImpl implements FeatureFlags {
-            public FakeFeatureFlagsImpl() {
-                resetAll();
+        public class CustomFeatureFlags implements FeatureFlags {
+
+            private BiPredicate<String, Predicate<FeatureFlags>> mGetValueImpl;
+
+            public CustomFeatureFlags(BiPredicate<String, Predicate<FeatureFlags>> getValueImpl) {
+                mGetValueImpl = getValueImpl;
             }
+
             @Override
             @UnsupportedAppUsage
             public boolean disabledRo() {
-                return getValue(Flags.FLAG_DISABLED_RO);
+                return getValue(Flags.FLAG_DISABLED_RO,
+                        FeatureFlags::disabledRo);
             }
             @Override
             @UnsupportedAppUsage
             public boolean disabledRw() {
-                return getValue(Flags.FLAG_DISABLED_RW);
+                return getValue(Flags.FLAG_DISABLED_RW,
+                    FeatureFlags::disabledRw);
             }
             @Override
             @UnsupportedAppUsage
             public boolean disabledRwInOtherNamespace() {
-                return getValue(Flags.FLAG_DISABLED_RW_IN_OTHER_NAMESPACE);
+                return getValue(Flags.FLAG_DISABLED_RW_IN_OTHER_NAMESPACE,
+                    FeatureFlags::disabledRwInOtherNamespace);
             }
             @Override
             @UnsupportedAppUsage
             public boolean enabledFixedRo() {
-                return getValue(Flags.FLAG_ENABLED_FIXED_RO);
+                return getValue(Flags.FLAG_ENABLED_FIXED_RO,
+                    FeatureFlags::enabledFixedRo);
             }
             @Override
             @UnsupportedAppUsage
             public boolean enabledRo() {
-                return getValue(Flags.FLAG_ENABLED_RO);
+                return getValue(Flags.FLAG_ENABLED_RO,
+                    FeatureFlags::enabledRo);
             }
             @Override
             @UnsupportedAppUsage
             public boolean enabledRw() {
-                return getValue(Flags.FLAG_ENABLED_RW);
+                return getValue(Flags.FLAG_ENABLED_RW,
+                    FeatureFlags::enabledRw);
             }
-            public void setFlag(String flagName, boolean value) {
-                if (!this.mFlagMap.containsKey(flagName)) {
-                    throw new IllegalArgumentException("no such flag " + flagName);
-                }
-                this.mFlagMap.put(flagName, value);
-            }
-            public void resetAll() {
-                for (Map.Entry entry : mFlagMap.entrySet()) {
-                    entry.setValue(null);
-                }
-            }
+
             public boolean isFlagReadOnlyOptimized(String flagName) {
                 if (mReadOnlyFlagsSet.contains(flagName) &&
                     isOptimizationEnabled()) {
@@ -1082,27 +1160,27 @@
                 }
                 return false;
             }
+
             @com.android.aconfig.annotations.AssumeTrueForR8
             private boolean isOptimizationEnabled() {
                 return false;
             }
-            private boolean getValue(String flagName) {
-                Boolean value = this.mFlagMap.get(flagName);
-                if (value == null) {
-                    throw new IllegalArgumentException(flagName + " is not set");
-                }
-                return value;
+
+            protected boolean getValue(String flagName, Predicate<FeatureFlags> getter) {
+                return mGetValueImpl.test(flagName, getter);
             }
-            private Map<String, Boolean> mFlagMap = new HashMap<>(
-                Map.ofEntries(
-                    Map.entry(Flags.FLAG_DISABLED_RO, false),
-                    Map.entry(Flags.FLAG_DISABLED_RW, false),
-                    Map.entry(Flags.FLAG_DISABLED_RW_IN_OTHER_NAMESPACE, false),
-                    Map.entry(Flags.FLAG_ENABLED_FIXED_RO, false),
-                    Map.entry(Flags.FLAG_ENABLED_RO, false),
-                    Map.entry(Flags.FLAG_ENABLED_RW, false)
-                )
-            );
+
+            public List<String> getFlagNames() {
+                return Arrays.asList(
+                    Flags.FLAG_DISABLED_RO,
+                    Flags.FLAG_DISABLED_RW,
+                    Flags.FLAG_DISABLED_RW_IN_OTHER_NAMESPACE,
+                    Flags.FLAG_ENABLED_FIXED_RO,
+                    Flags.FLAG_ENABLED_RO,
+                    Flags.FLAG_ENABLED_RW
+                );
+            }
+
             private Set<String> mReadOnlyFlagsSet = new HashSet<>(
                 Arrays.asList(
                     Flags.FLAG_DISABLED_RO,
@@ -1116,11 +1194,16 @@
             );
         }
         "#;
+
         let mut file_set = HashMap::from([
             ("com/android/aconfig/test/Flags.java", expect_flags_content),
             ("com/android/aconfig/test/FeatureFlagsImpl.java", expect_featureflagsimpl_content),
             ("com/android/aconfig/test/FeatureFlags.java", expect_featureflags_content),
-            ("com/android/aconfig/test/FakeFeatureFlagsImpl.java", expect_fakefeatureflags_content),
+            ("com/android/aconfig/test/CustomFeatureFlags.java", expect_customfeatureflags_content),
+            (
+                "com/android/aconfig/test/FakeFeatureFlagsImpl.java",
+                EXPECTED_FAKEFEATUREFLAGSIMPL_CONTENT,
+            ),
         ]);
 
         for file in generated_files {
diff --git a/tools/aconfig/aconfig/src/commands.rs b/tools/aconfig/aconfig/src/commands.rs
index 9cb40bf..6945fd4 100644
--- a/tools/aconfig/aconfig/src/commands.rs
+++ b/tools/aconfig/aconfig/src/commands.rs
@@ -202,7 +202,11 @@
     generate_java_code(&package, modified_parsed_flags.into_iter(), codegen_mode)
 }
 
-pub fn create_cpp_lib(mut input: Input, codegen_mode: CodegenMode) -> Result<Vec<OutputFile>> {
+pub fn create_cpp_lib(
+    mut input: Input,
+    codegen_mode: CodegenMode,
+    allow_instrumentation: bool,
+) -> Result<Vec<OutputFile>> {
     // TODO(327420679): Enable export mode for native flag library
     ensure!(
         codegen_mode != CodegenMode::Exported,
@@ -214,8 +218,14 @@
         bail!("no parsed flags, or the parsed flags use different packages");
     };
     let package = package.to_string();
-    let _flag_ids = assign_flag_ids(&package, modified_parsed_flags.iter())?;
-    generate_cpp_code(&package, modified_parsed_flags.into_iter(), codegen_mode)
+    let flag_ids = assign_flag_ids(&package, modified_parsed_flags.iter())?;
+    generate_cpp_code(
+        &package,
+        modified_parsed_flags.into_iter(),
+        codegen_mode,
+        flag_ids,
+        allow_instrumentation,
+    )
 }
 
 pub fn create_rust_lib(mut input: Input, codegen_mode: CodegenMode) -> Result<OutputFile> {
@@ -239,10 +249,8 @@
     container: &str,
     file: &StorageFileType,
 ) -> Result<Vec<u8>> {
-    let parsed_flags_vec: Vec<ProtoParsedFlags> = caches
-        .into_iter()
-        .map(|mut input| input.try_parse_flags())
-        .collect::<Result<Vec<_>>>()?;
+    let parsed_flags_vec: Vec<ProtoParsedFlags> =
+        caches.into_iter().map(|mut input| input.try_parse_flags()).collect::<Result<Vec<_>>>()?;
     generate_storage_file(container, parsed_flags_vec.iter(), file)
 }
 
diff --git a/tools/aconfig/aconfig/src/main.rs b/tools/aconfig/aconfig/src/main.rs
index 69f5458..72be1c9 100644
--- a/tools/aconfig/aconfig/src/main.rs
+++ b/tools/aconfig/aconfig/src/main.rs
@@ -83,6 +83,12 @@
                         .long("mode")
                         .value_parser(EnumValueParser::<CodegenMode>::new())
                         .default_value("production"),
+                )
+                .arg(
+                    Arg::new("allow-instrumentation")
+                        .long("allow-instrumentation")
+                        .value_parser(clap::value_parser!(bool))
+                        .default_value("false"),
                 ),
         )
         .subcommand(
@@ -241,8 +247,10 @@
         Some(("create-cpp-lib", sub_matches)) => {
             let cache = open_single_file(sub_matches, "cache")?;
             let mode = get_required_arg::<CodegenMode>(sub_matches, "mode")?;
-            let generated_files =
-                commands::create_cpp_lib(cache, *mode).context("failed to create cpp lib")?;
+            let allow_instrumentation =
+                get_required_arg::<bool>(sub_matches, "allow-instrumentation")?;
+            let generated_files = commands::create_cpp_lib(cache, *mode, *allow_instrumentation)
+                .context("failed to create cpp lib")?;
             let dir = PathBuf::from(get_required_arg::<String>(sub_matches, "out")?);
             generated_files
                 .iter()
diff --git a/tools/aconfig/aconfig/src/storage/mod.rs b/tools/aconfig/aconfig/src/storage/mod.rs
index 855ed02..73339f2 100644
--- a/tools/aconfig/aconfig/src/storage/mod.rs
+++ b/tools/aconfig/aconfig/src/storage/mod.rs
@@ -189,14 +189,14 @@
         assert_eq!(packages[1].package_id, 1);
         assert_eq!(packages[1].flag_names.len(), 3);
         assert!(packages[1].flag_names.contains("enabled_ro"));
-        assert!(packages[1].flag_names.contains("disabled_ro"));
+        assert!(packages[1].flag_names.contains("disabled_rw"));
         assert!(packages[1].flag_names.contains("enabled_fixed_ro"));
         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_rw"));
         assert!(packages[2].flag_names.contains("enabled_fixed_ro"));
         assert_eq!(packages[2].boolean_start_index, 6);
     }
diff --git a/tools/aconfig/aconfig/templates/CustomFeatureFlags.java.template b/tools/aconfig/aconfig/templates/CustomFeatureFlags.java.template
new file mode 100644
index 0000000..b82b9cb
--- /dev/null
+++ b/tools/aconfig/aconfig/templates/CustomFeatureFlags.java.template
@@ -0,0 +1,70 @@
+package {package_name};
+
+{{ if not library_exported- }}
+// TODO(b/303773055): Remove the annotation after access issue is resolved.
+import android.compat.annotation.UnsupportedAppUsage;
+{{ -endif }}
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.function.BiPredicate;
+import java.util.function.Predicate;
+
+/** @hide */
+public class CustomFeatureFlags implements FeatureFlags \{
+
+    private BiPredicate<String, Predicate<FeatureFlags>> mGetValueImpl;
+
+    public CustomFeatureFlags(BiPredicate<String, Predicate<FeatureFlags>> getValueImpl) \{
+        mGetValueImpl = getValueImpl;
+    }
+
+{{ -for item in flag_elements}}
+    @Override
+{{ if not library_exported }}    @UnsupportedAppUsage{{ -endif }}
+    public boolean {item.method_name}() \{
+        return getValue(Flags.FLAG_{item.flag_name_constant_suffix},
+            FeatureFlags::{item.method_name});
+    }
+{{ endfor }}
+
+{{ -if not library_exported }}
+    public boolean isFlagReadOnlyOptimized(String flagName) \{
+        if (mReadOnlyFlagsSet.contains(flagName) &&
+            isOptimizationEnabled()) \{
+                return true;
+        }
+        return false;
+    }
+
+    @com.android.aconfig.annotations.AssumeTrueForR8
+    private boolean isOptimizationEnabled() \{
+        return false;
+    }
+{{ -endif }}
+
+    protected boolean getValue(String flagName, Predicate<FeatureFlags> getter) \{
+        return mGetValueImpl.test(flagName, getter);
+    }
+
+    public List<String> getFlagNames() \{
+        return Arrays.asList(
+            {{ -for item in flag_elements }}
+            Flags.FLAG_{item.flag_name_constant_suffix}
+            {{ -if not @last }},{{ endif }}
+            {{ -endfor }}
+        );
+    }
+
+    private Set<String> mReadOnlyFlagsSet = new HashSet<>(
+        Arrays.asList(
+            {{ -for item in flag_elements }}
+            {{ -if not item.is_read_write }}
+            Flags.FLAG_{item.flag_name_constant_suffix},
+            {{ -endif }}
+            {{ -endfor }}
+            ""{# The empty string here is to resolve the ending comma #}
+        )
+    );
+}
diff --git a/tools/aconfig/aconfig/templates/FakeFeatureFlagsImpl.java.template b/tools/aconfig/aconfig/templates/FakeFeatureFlagsImpl.java.template
index 177e711..290d2c4 100644
--- a/tools/aconfig/aconfig/templates/FakeFeatureFlagsImpl.java.template
+++ b/tools/aconfig/aconfig/templates/FakeFeatureFlagsImpl.java.template
@@ -1,27 +1,39 @@
 package {package_name};
-{{ if not library_exported- }}
-// TODO(b/303773055): Remove the annotation after access issue is resolved.
-import android.compat.annotation.UnsupportedAppUsage;
-{{ -endif }}
-import java.util.Arrays;
+
 import java.util.HashMap;
-import java.util.HashSet;
 import java.util.Map;
-import java.util.Set;
+import java.util.function.Predicate;
 
 /** @hide */
-public class FakeFeatureFlagsImpl implements FeatureFlags \{
+public class FakeFeatureFlagsImpl extends CustomFeatureFlags \{
+    private final Map<String, Boolean> mFlagMap = new HashMap<>();
+    private final FeatureFlags mDefaults;
+
     public FakeFeatureFlagsImpl() \{
-        resetAll();
+        this(null);
     }
 
-{{ for item in flag_elements}}
-    @Override
-{{ if not library_exported }}    @UnsupportedAppUsage{{ -endif }}
-    public boolean {item.method_name}() \{
-        return getValue(Flags.FLAG_{item.flag_name_constant_suffix});
+    public FakeFeatureFlagsImpl(FeatureFlags defaults) \{
+        super(null);
+        mDefaults = defaults;
+        // Initialize the map with null values
+        for (String flagName : getFlagNames()) \{
+            mFlagMap.put(flagName, null);
+        }
     }
-{{ endfor}}
+
+    @Override
+    protected boolean getValue(String flagName, Predicate<FeatureFlags> getter) \{
+        Boolean value = this.mFlagMap.get(flagName);
+        if (value != null) \{
+            return value;
+        }
+        if (mDefaults != null) \{
+            return getter.test(mDefaults);
+        }
+        throw new IllegalArgumentException(flagName + " is not set");
+    }
+
     public void setFlag(String flagName, boolean value) \{
         if (!this.mFlagMap.containsKey(flagName)) \{
             throw new IllegalArgumentException("no such flag " + flagName);
@@ -34,46 +46,4 @@
             entry.setValue(null);
         }
     }
-{{ if not library_exported }}
-    public boolean isFlagReadOnlyOptimized(String flagName) \{
-        if (mReadOnlyFlagsSet.contains(flagName) &&
-            isOptimizationEnabled()) \{
-                return true;
-        }
-        return false;
-    }
-
-    @com.android.aconfig.annotations.AssumeTrueForR8
-    private boolean isOptimizationEnabled() \{
-        return false;
-    }
-{{ -endif }}
-    private boolean getValue(String flagName) \{
-        Boolean value = this.mFlagMap.get(flagName);
-        if (value == null) \{
-            throw new IllegalArgumentException(flagName + " is not set");
-        }
-        return value;
-    }
-
-
-    private Map<String, Boolean> mFlagMap = new HashMap<>(
-        Map.ofEntries(
-            {{ -for item in flag_elements }}
-            Map.entry(Flags.FLAG_{item.flag_name_constant_suffix}, false)
-            {{ -if not @last }},{{ endif }}
-            {{ -endfor }}
-        )
-    );
-
-    private Set<String> mReadOnlyFlagsSet = new HashSet<>(
-        Arrays.asList(
-            {{ -for item in flag_elements }}
-            {{ -if not item.is_read_write }}
-            Flags.FLAG_{item.flag_name_constant_suffix},
-            {{ -endif }}
-            {{ -endfor }}
-            ""{# The empty string here is to resolve the ending comma #}
-        )
-    );
 }
diff --git a/tools/aconfig/aconfig/templates/cpp_source_file.template b/tools/aconfig/aconfig/templates/cpp_source_file.template
index 4bcd1b7..7646015 100644
--- a/tools/aconfig/aconfig/templates/cpp_source_file.template
+++ b/tools/aconfig/aconfig/templates/cpp_source_file.template
@@ -1,5 +1,16 @@
 #include "{header}.h"
 
+{{ if allow_instrumentation }}
+#include <sys/stat.h>
+#include "aconfig_storage/aconfig_storage_read_api.hpp"
+#include <protos/aconfig_storage_metadata.pb.h>
+#include <android/log.h>
+
+#define ALOGI(msg, ...)                                                        \
+  __android_log_print(ANDROID_LOG_INFO, "AconfigTestMission1", (msg), __VA_ARGS__)
+
+{{ endif }}
+
 {{ if readwrite- }}
 #include <server_configurable_flags/get_flags.h>
 {{ endif }}
@@ -97,6 +108,58 @@
     {{ -if item.readwrite }}
     return {cpp_namespace}::{item.flag_name}();
     {{ -else }}
+    {{ if allow_instrumentation }}
+    auto result =
+        {{ if item.is_fixed_read_only }}
+	    {package_macro}_{item.flag_macro}
+	{{ else }}
+	    {item.default_value}
+	{{ endif }};
+
+    struct stat buffer;
+    if (stat("/metadata/aconfig_test_missions/mission_1", &buffer) != 0) \{
+        return result;
+    }
+
+    auto package_map_file = aconfig_storage::get_mapped_file(
+        "{item.container}",
+        aconfig_storage::StorageFileType::package_map);
+    if (!package_map_file.ok()) \{
+        ALOGI("error: failed to get package map file: %s", package_map_file.error().message().c_str());
+        return result;
+    }
+
+    auto package_read_context = aconfig_storage::get_package_read_context(
+        *package_map_file, "{package}");
+    if (!package_read_context.ok()) \{
+        ALOGI("error: failed to get package read context: %s", package_map_file.error().message().c_str());
+        return result;
+    }
+
+    auto flag_val_map = aconfig_storage::get_mapped_file(
+        "{item.container}",
+        aconfig_storage::StorageFileType::flag_val);
+    if (!flag_val_map.ok()) \{
+        ALOGI("error: failed to get flag val map: %s", package_map_file.error().message().c_str());
+        return result;
+    }
+
+    auto value = aconfig_storage::get_boolean_flag_value(
+        *flag_val_map,
+        package_read_context->package_id + {item.flag_offset});
+    if (!value.ok()) \{
+        ALOGI("error: failed to get flag val: %s", package_map_file.error().message().c_str());
+        return result;
+    }
+
+    if (*value != result) \{
+        ALOGI("error: new storage value '%d' does not match current value '%d'", *value, result);
+    } else \{
+        ALOGI("success: new storage value was '%d, legacy storage was '%d'", *value, result);
+    }
+
+    return result;
+    {{ else }}
     {{ -if item.is_fixed_read_only }}
     return {package_macro}_{item.flag_macro};
     {{ -else }}
@@ -104,6 +167,7 @@
     {{ -endif }}
     {{ -endif }}
     {{ -endif }}
+    {{ -endif }}
 }
 
 {{ -if is_test_mode }}
@@ -119,3 +183,4 @@
 }
 {{ -endif }}
 
+
diff --git a/tools/aconfig/aconfig/tests/storage_test_2.aconfig b/tools/aconfig/aconfig/tests/storage_test_2.aconfig
index bb14fd1..db77f7a 100644
--- a/tools/aconfig/aconfig/tests/storage_test_2.aconfig
+++ b/tools/aconfig/aconfig/tests/storage_test_2.aconfig
@@ -9,7 +9,7 @@
 }
 
 flag {
-    name: "disabled_ro"
+    name: "disabled_rw"
     namespace: "aconfig_test"
     description: "This flag is DISABLED + READ_ONLY"
     bug: "123"
diff --git a/tools/aconfig/aconfig/tests/storage_test_2.values b/tools/aconfig/aconfig/tests/storage_test_2.values
index a7bb0b1..b650721 100644
--- a/tools/aconfig/aconfig/tests/storage_test_2.values
+++ b/tools/aconfig/aconfig/tests/storage_test_2.values
@@ -6,9 +6,9 @@
 }
 flag_value {
     package: "com.android.aconfig.storage.test_2"
-    name: "disabled_ro"
+    name: "disabled_rw"
     state: DISABLED
-    permission: READ_ONLY
+    permission: READ_WRITE
 }
 flag_value {
     package: "com.android.aconfig.storage.test_2"
diff --git a/tools/aconfig/aconfig/tests/storage_test_4.aconfig b/tools/aconfig/aconfig/tests/storage_test_4.aconfig
index 333fe09..5802a73 100644
--- a/tools/aconfig/aconfig/tests/storage_test_4.aconfig
+++ b/tools/aconfig/aconfig/tests/storage_test_4.aconfig
@@ -2,7 +2,7 @@
 container: "system"
 
 flag {
-    name: "enabled_ro"
+    name: "enabled_rw"
     namespace: "aconfig_test"
     description: "This flag is ENABLED + READ_ONLY"
     bug: "abc"
diff --git a/tools/aconfig/aconfig/tests/storage_test_4.values b/tools/aconfig/aconfig/tests/storage_test_4.values
index fa21317..784b744 100644
--- a/tools/aconfig/aconfig/tests/storage_test_4.values
+++ b/tools/aconfig/aconfig/tests/storage_test_4.values
@@ -1,8 +1,8 @@
 flag_value {
     package: "com.android.aconfig.storage.test_4"
-    name: "enabled_ro"
+    name: "enabled_rw"
     state: ENABLED
-    permission: READ_ONLY
+    permission: READ_WRITE
 }
 flag_value {
     package: "com.android.aconfig.storage.test_4"
diff --git a/tools/aconfig/aconfig_storage_file/src/lib.rs b/tools/aconfig/aconfig_storage_file/src/lib.rs
index 070a3cf..2acfc7d 100644
--- a/tools/aconfig/aconfig_storage_file/src/lib.rs
+++ b/tools/aconfig/aconfig_storage_file/src/lib.rs
@@ -357,8 +357,8 @@
             ),
             (
                 String::from("com.android.aconfig.storage.test_2"),
-                String::from("disabled_ro"),
-                StoredFlagType::ReadOnlyBoolean,
+                String::from("disabled_rw"),
+                StoredFlagType::ReadWriteBoolean,
                 false,
             ),
             (
@@ -381,8 +381,8 @@
             ),
             (
                 String::from("com.android.aconfig.storage.test_4"),
-                String::from("enabled_ro"),
-                StoredFlagType::ReadOnlyBoolean,
+                String::from("enabled_rw"),
+                StoredFlagType::ReadWriteBoolean,
                 true,
             ),
         ];
diff --git a/tools/aconfig/aconfig_storage_file/src/test_utils.rs b/tools/aconfig/aconfig_storage_file/src/test_utils.rs
index 608563c..106666c 100644
--- a/tools/aconfig/aconfig_storage_file/src/test_utils.rs
+++ b/tools/aconfig/aconfig_storage_file/src/test_utils.rs
@@ -92,8 +92,8 @@
         None,
         None,
         None,
-        Some(178),
         None,
+        Some(177),
         Some(204),
         None,
         Some(262),
@@ -108,8 +108,8 @@
     let nodes = vec![
         FlagTableNode::new_expected(0, "enabled_ro", 1, 1, None),
         FlagTableNode::new_expected(0, "enabled_rw", 0, 2, Some(151)),
-        FlagTableNode::new_expected(1, "disabled_ro", 1, 0, None),
-        FlagTableNode::new_expected(2, "enabled_ro", 1, 1, None),
+        FlagTableNode::new_expected(2, "enabled_rw", 0, 1, None),
+        FlagTableNode::new_expected(1, "disabled_rw", 0, 0, None),
         FlagTableNode::new_expected(1, "enabled_fixed_ro", 2, 1, Some(236)),
         FlagTableNode::new_expected(1, "enabled_ro", 1, 2, None),
         FlagTableNode::new_expected(2, "enabled_fixed_ro", 2, 0, None),
@@ -140,7 +140,7 @@
         num_flags: 8,
         boolean_flag_offset: 27,
     };
-    let is_flag_rw = [true, false, true, false, false, false, false, false];
+    let is_flag_rw = [true, false, true, true, false, false, false, true];
     let nodes = is_flag_rw.iter().map(|&rw| FlagInfoNode::create(rw)).collect();
     FlagInfoList { header, nodes }
 }
diff --git a/tools/aconfig/aconfig_storage_read_api/Android.bp b/tools/aconfig/aconfig_storage_read_api/Android.bp
index 3fb6a53..c89107f 100644
--- a/tools/aconfig/aconfig_storage_read_api/Android.bp
+++ b/tools/aconfig/aconfig_storage_read_api/Android.bp
@@ -102,5 +102,10 @@
         "//apex_available:anyapex",
     ],
     min_sdk_version: "29",
+    target: {
+        linux: {
+            version_script: "libaconfig_storage_read_api_cc.map",
+        },
+    },
     double_loadable: true,
 }
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 ff2f38e..8fe42ce 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
@@ -65,8 +65,24 @@
   return Error() << "Unable to find storage files for container " << container;;
 }
 
+namespace private_internal_api {
+
+/// Get mapped file implementation.
+Result<MappedStorageFile> get_mapped_file_impl(
+    std::string const& pb_file,
+    std::string const& container,
+    StorageFileType file_type) {
+  auto file_result = find_storage_file(pb_file, container, file_type);
+  if (!file_result.ok()) {
+    return Error() << file_result.error();
+  }
+  return map_storage_file(*file_result);
+}
+
+} // namespace private internal api
+
 /// Map a storage file
-static Result<MappedStorageFile> map_storage_file(std::string const& file) {
+Result<MappedStorageFile> map_storage_file(std::string const& file) {
   int fd = open(file.c_str(), O_CLOEXEC | O_NOFOLLOW | O_RDONLY);
   if (fd == -1) {
     return ErrnoError() << "failed to open " << file;
@@ -90,22 +106,6 @@
   return mapped_file;
 }
 
-namespace private_internal_api {
-
-/// Get mapped file implementation.
-Result<MappedStorageFile> get_mapped_file_impl(
-    std::string const& pb_file,
-    std::string const& container,
-    StorageFileType file_type) {
-  auto file_result = find_storage_file(pb_file, container, file_type);
-  if (!file_result.ok()) {
-    return Error() << file_result.error();
-  }
-  return map_storage_file(*file_result);
-}
-
-} // namespace private internal api
-
 /// Map from StoredFlagType to FlagValueType
 android::base::Result<FlagValueType> map_to_flag_value_type(
     StoredFlagType stored_type) {
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 0a4d5b4..2bd84fc 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
@@ -67,6 +67,11 @@
 
 } // namespace private_internal_api
 
+/// Map a storage file
+android::base::Result<MappedStorageFile> map_storage_file(
+    std::string const& file);
+
+
 /// Map from StoredFlagType to FlagValueType
 /// \input stored_type: stored flag type in the storage file
 /// \returns the flag value type enum
diff --git a/tools/aconfig/aconfig_storage_read_api/libaconfig_storage_read_api_cc.map b/tools/aconfig/aconfig_storage_read_api/libaconfig_storage_read_api_cc.map
new file mode 100644
index 0000000..7d47e0b
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_read_api/libaconfig_storage_read_api_cc.map
@@ -0,0 +1,11 @@
+LIBACONFIG_STORAGE_READ_API_CC {
+  # Export everything in the aconfig_storage namespace. This includes both the
+  # public API and library internals.
+  global:
+    extern "C++" {
+        aconfig_storage::*;
+    };
+  # Hide everything else.
+  local:
+    *;
+};
diff --git a/tools/aconfig/aconfig_storage_read_api/src/flag_info_query.rs b/tools/aconfig/aconfig_storage_read_api/src/flag_info_query.rs
index 0087feb..6d03377 100644
--- a/tools/aconfig/aconfig_storage_read_api/src/flag_info_query.rs
+++ b/tools/aconfig/aconfig_storage_read_api/src/flag_info_query.rs
@@ -70,7 +70,7 @@
     // 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];
+        let baseline: Vec<bool> = vec![true, false, true, true, false, false, false, true];
         for offset in 0..8 {
             let attribute =
                 find_flag_attribute(&flag_info_list[..], FlagValueType::Boolean, offset).unwrap();
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 55fdcb7..a1a4793 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
@@ -82,8 +82,8 @@
         let baseline = vec![
             (0, "enabled_ro", StoredFlagType::ReadOnlyBoolean, 1u16),
             (0, "enabled_rw", StoredFlagType::ReadWriteBoolean, 2u16),
-            (1, "disabled_ro", StoredFlagType::ReadOnlyBoolean, 0u16),
-            (2, "enabled_ro", StoredFlagType::ReadOnlyBoolean, 1u16),
+            (2, "enabled_rw", StoredFlagType::ReadWriteBoolean, 1u16),
+            (1, "disabled_rw", StoredFlagType::ReadWriteBoolean, 0u16),
             (1, "enabled_fixed_ro", StoredFlagType::FixedReadOnlyBoolean, 1u16),
             (1, "enabled_ro", StoredFlagType::ReadOnlyBoolean, 2u16),
             (2, "enabled_fixed_ro", StoredFlagType::FixedReadOnlyBoolean, 0u16),
diff --git a/tools/aconfig/aconfig_storage_read_api/src/lib.rs b/tools/aconfig/aconfig_storage_read_api/src/lib.rs
index fa8dad6..e65dcfb 100644
--- a/tools/aconfig/aconfig_storage_read_api/src/lib.rs
+++ b/tools/aconfig/aconfig_storage_read_api/src/lib.rs
@@ -468,8 +468,8 @@
         let baseline = vec![
             (0, "enabled_ro", StoredFlagType::ReadOnlyBoolean, 1u16),
             (0, "enabled_rw", StoredFlagType::ReadWriteBoolean, 2u16),
-            (1, "disabled_ro", StoredFlagType::ReadOnlyBoolean, 0u16),
-            (2, "enabled_ro", StoredFlagType::ReadOnlyBoolean, 1u16),
+            (2, "enabled_rw", StoredFlagType::ReadWriteBoolean, 1u16),
+            (1, "disabled_rw", StoredFlagType::ReadWriteBoolean, 0u16),
             (1, "enabled_fixed_ro", StoredFlagType::FixedReadOnlyBoolean, 1u16),
             (1, "enabled_ro", StoredFlagType::ReadOnlyBoolean, 2u16),
             (2, "enabled_fixed_ro", StoredFlagType::FixedReadOnlyBoolean, 0u16),
diff --git a/tools/aconfig/aconfig_storage_read_api/tests/flag.map b/tools/aconfig/aconfig_storage_read_api/tests/flag.map
index d26e00f..e868f53 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/storage_read_api_test.cpp b/tools/aconfig/aconfig_storage_read_api/tests/storage_read_api_test.cpp
index 5b4d32c..b499c1c 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
@@ -167,8 +167,8 @@
   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},
+    {2, "enabled_rw", api::StoredFlagType::ReadWriteBoolean, 1},
+    {1, "disabled_rw", api::StoredFlagType::ReadWriteBoolean, 0},
     {1, "enabled_fixed_ro", api::StoredFlagType::FixedReadOnlyBoolean, 1},
     {1, "enabled_ro", api::StoredFlagType::ReadOnlyBoolean, 2},
     {2, "enabled_fixed_ro", api::StoredFlagType::FixedReadOnlyBoolean, 0},
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 1a8af75..ce9c018 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
@@ -118,8 +118,8 @@
         let baseline = vec![
             (0, "enabled_ro", StoredFlagType::ReadOnlyBoolean, 1u16),
             (0, "enabled_rw", StoredFlagType::ReadWriteBoolean, 2u16),
-            (1, "disabled_ro", StoredFlagType::ReadOnlyBoolean, 0u16),
-            (2, "enabled_ro", StoredFlagType::ReadOnlyBoolean, 1u16),
+            (2, "enabled_rw", StoredFlagType::ReadWriteBoolean, 1u16),
+            (1, "disabled_rw", StoredFlagType::ReadWriteBoolean, 0u16),
             (1, "enabled_fixed_ro", StoredFlagType::FixedReadOnlyBoolean, 1u16),
             (1, "enabled_ro", StoredFlagType::ReadOnlyBoolean, 2u16),
             (2, "enabled_fixed_ro", StoredFlagType::FixedReadOnlyBoolean, 0u16),
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 f031d01..cd57f4f 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
@@ -17,78 +17,6 @@
 
 namespace aconfig_storage {
 
-/// Storage location pb file
-static constexpr char kPersistStorageRecordsPb[] =
-    "/metadata/aconfig/persistent_storage_file_records.pb";
-
-/// Read aconfig storage records pb file
-static Result<storage_records_pb> read_storage_records_pb(std::string const& pb_file) {
-  auto records = storage_records_pb();
-  auto content = std::string();
-  if (!ReadFileToString(pb_file, &content)) {
-    return ErrnoError() << "ReadFileToString failed";
-  }
-
-  if (!records.ParseFromString(content)) {
-    return ErrnoError() << "Unable to parse persistent storage records protobuf";
-  }
-  return records;
-}
-
-/// Get storage file path
-static Result<std::string> find_storage_file(
-    std::string const& pb_file,
-    std::string const& container,
-    StorageFileType file_type) {
-  auto records_pb = read_storage_records_pb(pb_file);
-  if (!records_pb.ok()) {
-    return Error() << "Unable to read storage records from " << pb_file
-                   << " : " << records_pb.error();
-  }
-
-  for (auto& entry : records_pb->files()) {
-    if (entry.container() == container) {
-      switch(file_type) {
-        case StorageFileType::package_map:
-          return entry.package_map();
-        case StorageFileType::flag_map:
-          return entry.flag_map();
-        case StorageFileType::flag_val:
-          return entry.flag_val();
-        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;
-}
-
-
-namespace private_internal_api {
-
-/// Get mutable mapped file implementation.
-Result<MutableMappedStorageFile> get_mutable_mapped_file_impl(
-    std::string const& pb_file,
-    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_mutable_storage_file(*file_result);
-}
-
-} // namespace private internal api
-
 /// Map a storage file
 Result<MutableMappedStorageFile> map_mutable_storage_file(std::string const& file) {
   struct stat file_stat;
@@ -120,14 +48,6 @@
   return mapped_file;
 }
 
-/// 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 MutableMappedStorageFile& file,
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 12b0ecc..7148396 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
@@ -16,25 +16,10 @@
   size_t file_size;
 };
 
-/// DO NOT USE APIS IN THE FOLLOWING NAMESPACE DIRECTLY
-namespace private_internal_api {
-
-Result<MutableMappedStorageFile> get_mutable_mapped_file_impl(
-    std::string const& pb_file,
-    std::string const& container,
-    StorageFileType file_type);
-
-} // namespace private_internal_api
-
 /// Map a storage file
 Result<MutableMappedStorageFile> map_mutable_storage_file(
     std::string const& file);
 
-/// 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 MutableMappedStorageFile& file,
diff --git a/tools/aconfig/aconfig_storage_write_api/src/lib.rs b/tools/aconfig/aconfig_storage_write_api/src/lib.rs
index bcddce1..aec28de 100644
--- a/tools/aconfig/aconfig_storage_write_api/src/lib.rs
+++ b/tools/aconfig/aconfig_storage_write_api/src/lib.rs
@@ -34,15 +34,9 @@
 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 read write mapped storage files.
 ///
-/// \input container: the flag package container
-/// \input file_type: storage file type enum
-/// \return a result of read write mapped file
-///
+/// \input file_path: path to the storage file
 ///
 /// # Safety
 ///
@@ -50,11 +44,8 @@
 /// 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_storage_file(
-    container: &str,
-    file_type: StorageFileType,
-) -> Result<MmapMut, AconfigStorageError> {
-    unsafe { crate::mapped_file::get_mapped_file(STORAGE_LOCATION_FILE, container, file_type) }
+pub unsafe fn map_mutable_storage_file(file_path: &str) -> Result<MmapMut, AconfigStorageError> {
+    crate::mapped_file::map_file(file_path)
 }
 
 /// Set boolean flag value thru mapped file and flush the change to file
@@ -344,7 +335,6 @@
 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,
@@ -367,33 +357,12 @@
     fn test_set_boolean_flag_value() {
         let flag_value_file = copy_to_temp_file("./tests/flag.val", false).unwrap();
         let flag_value_path = flag_value_file.path().display().to_string();
-        let text_proto = format!(
-            r#"
-files {{
-    version: 0
-    container: "system"
-    package_map: "some_package.map"
-    flag_map: "some_flag.map"
-    flag_val: "{}"
-    flag_info: "some_flag.info"
-    timestamp: 12345
-}}
-"#,
-            flag_value_path
-        );
-        let record_pb_file = write_proto_to_temp_file(&text_proto).unwrap();
-        let record_pb_path = record_pb_file.path().display().to_string();
 
         // SAFETY:
         // The safety here is guaranteed as only this single threaded test process will
         // write to this file
         unsafe {
-            let mut file = crate::mapped_file::get_mapped_file(
-                &record_pb_path,
-                "system",
-                StorageFileType::FlagVal,
-            )
-            .unwrap();
+            let mut file = map_mutable_storage_file(&flag_value_path).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);
@@ -417,33 +386,12 @@
     fn test_set_flag_has_server_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();
+            let mut file = map_mutable_storage_file(&flag_info_path).unwrap();
             for i in 0..8 {
                 set_flag_has_server_override(&mut file, FlagValueType::Boolean, i, true).unwrap();
                 let attribute =
@@ -461,33 +409,12 @@
     fn test_set_flag_has_local_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();
+            let mut file = map_mutable_storage_file(&flag_info_path).unwrap();
             for i in 0..8 {
                 set_flag_has_local_override(&mut file, FlagValueType::Boolean, i, true).unwrap();
                 let attribute =
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 ea9ac19..401d6b7 100644
--- a/tools/aconfig/aconfig_storage_write_api/src/mapped_file.rs
+++ b/tools/aconfig/aconfig_storage_write_api/src/mapped_file.rs
@@ -17,11 +17,8 @@
 use anyhow::anyhow;
 use memmap2::MmapMut;
 use std::fs::{self, OpenOptions};
-use std::io::Read;
 
 use aconfig_storage_file::AconfigStorageError::{self, FileReadFail, MapFileFail};
-use aconfig_storage_file::StorageFileType;
-use aconfig_storage_read_api::mapped_file::find_container_storage_location;
 
 /// Get the mutable memory mapping of a storage file
 ///
@@ -31,7 +28,7 @@
 /// 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> {
+pub(crate) 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() {
@@ -51,57 +48,18 @@
     }
 }
 
-/// Get a mapped storage file given the container and file type
-///
-/// # Safety
-///
-/// The memory mapped file may have undefined behavior if there are writes to this
-/// file not thru this memory mapped file or there are concurrent writes to this
-/// memory mapped file. Ensure all writes to the underlying file are thru this memory
-/// mapped file and there are no concurrent writes.
-pub unsafe fn get_mapped_file(
-    location_pb_file: &str,
-    container: &str,
-    file_type: StorageFileType,
-) -> Result<MmapMut, AconfigStorageError> {
-    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
-        ))),
-    }
-}
-
 #[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 std::io::Read;
 
     #[test]
     fn test_mapped_file_contents() {
         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 {{
-    version: 0
-    container: "system"
-    package_map: "some_package.map"
-    flag_map: "some_flag.map"
-    flag_val: "{}"
-    flag_info: "{}"
-    timestamp: 12345
-}}
-"#,
-            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 flag_val = rw_val_file.path().display().to_string();
+        let flag_info = rw_info_file.path().display().to_string();
 
         let mut content = Vec::new();
         rw_val_file.read_to_end(&mut content).unwrap();
@@ -109,9 +67,7 @@
         // 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::FlagVal)
-                    .unwrap();
+            let mmaped_file = map_file(&flag_val).unwrap();
             assert_eq!(mmaped_file[..], content[..]);
         }
 
@@ -121,90 +77,23 @@
         // 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();
+            let mmaped_file = map_file(&flag_info).unwrap();
             assert_eq!(mmaped_file[..], content[..]);
         }
     }
 
     #[test]
     fn test_mapped_read_only_file() {
-        let ro_file = copy_to_temp_file("./tests/flag.val", true).unwrap();
-        let text_proto = format!(
-            r#"
-files {{
-    version: 0
-    container: "system"
-    package_map: "some_package.map"
-    flag_map: "some_flag.map"
-    flag_val: "{}"
-    flag_info: "some_flag.info"
-    timestamp: 12345
-}}
-"#,
-            ro_file.path().display().to_string()
-        );
-        let storage_record_file = write_proto_to_temp_file(&text_proto).unwrap();
-        let storage_record_file_path = storage_record_file.path().display().to_string();
+        let ro_val_file = copy_to_temp_file("./tests/flag.val", true).unwrap();
+        let flag_val = ro_val_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::FlagVal)
-                    .unwrap_err();
+            let error = map_file(&flag_val).unwrap_err();
             assert_eq!(
                 format!("{:?}", error),
-                format!(
-                    "MapFileFail(fail to map non read write storage file {})",
-                    ro_file.path().display().to_string()
-                )
-            );
-        }
-    }
-
-    #[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
-                )
+                format!("MapFileFail(fail to map non read write storage file {})", flag_val)
             );
         }
     }
diff --git a/tools/aconfig/aconfig_storage_write_api/tests/flag.info b/tools/aconfig/aconfig_storage_write_api/tests/flag.info
index 820d839..6223edf 100644
--- a/tools/aconfig/aconfig_storage_write_api/tests/flag.info
+++ b/tools/aconfig/aconfig_storage_write_api/tests/flag.info
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 ab5f0a7..bd39e9e 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,88 +50,36 @@
     return temp_file;
   }
 
-  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("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();
-    proto.SerializeToString(&content);
-    if (!WriteStringToFile(content, temp_file)) {
-      return Error() << "failed to write storage records pb file";
-    }
-    return temp_file;
-  }
-
   void SetUp() override {
     auto const test_dir = android::base::GetExecutableDirectory();
     flag_val = *copy_to_rw_temp_file(test_dir + "/flag.val");
     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_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");
-}
-
 /// 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_mutable_mapped_file_impl(
-      storage_record_pb, "mockup", api::StorageFileType::flag_val);
+  auto mapped_file_result = api::map_mutable_storage_file(flag_val);
   ASSERT_FALSE(mapped_file_result.ok());
   auto it = mapped_file_result.error().message().find("cannot map nonwriteable file");
   ASSERT_TRUE(it != std::string::npos) << mapped_file_result.error().message();
 }
 
-/// 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_mutable_mapped_file_impl(
-      storage_record_pb, "mockup", api::StorageFileType::flag_val);
+  auto mapped_file_result = api::map_mutable_storage_file(flag_val);
   ASSERT_TRUE(mapped_file_result.ok());
-  auto mapped_file = *mapped_file_result;
 
+  auto mapped_file = *mapped_file_result;
   for (int offset = 0; offset < 8; ++offset) {
     auto update_result = api::set_boolean_flag_value(mapped_file, offset, true);
     ASSERT_TRUE(update_result.ok());
@@ -146,10 +94,10 @@
 
 /// 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_mutable_mapped_file_impl(
-      storage_record_pb, "mockup", api::StorageFileType::flag_val);
+  auto mapped_file_result = api::map_mutable_storage_file(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);
   ASSERT_FALSE(update_result.ok());
   ASSERT_EQ(update_result.error().message(),
@@ -158,15 +106,14 @@
 
 /// Test to lock down storage flag has server override update api
 TEST_F(AconfigStorageTest, test_flag_has_server_override_update) {
-  auto mapped_file_result = private_api::get_mutable_mapped_file_impl(
-      storage_record_pb, "mockup", api::StorageFileType::flag_info);
+  auto mapped_file_result = api::map_mutable_storage_file(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_server_override(
         mapped_file, api::FlagValueType::Boolean, offset, true);
-    ASSERT_TRUE(update_result.ok());
+    ASSERT_TRUE(update_result.ok()) << update_result.error();
     auto ro_mapped_file = api::MappedStorageFile();
     ro_mapped_file.file_ptr = mapped_file.file_ptr;
     ro_mapped_file.file_size = mapped_file.file_size;
@@ -189,8 +136,7 @@
 
 /// Test to lock down storage flag has local override update api
 TEST_F(AconfigStorageTest, test_flag_has_local_override_update) {
-  auto mapped_file_result = private_api::get_mutable_mapped_file_impl(
-      storage_record_pb, "mockup", api::StorageFileType::flag_info);
+  auto mapped_file_result = api::map_mutable_storage_file(flag_info);
   ASSERT_TRUE(mapped_file_result.ok());
   auto mapped_file = *mapped_file_result;
 
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 a087c1a..367569d 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,44 +1,17 @@
 #[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_file::{FlagInfoBit, FlagValueType};
     use aconfig_storage_read_api::flag_info_query::find_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, set_flag_has_local_override,
+        map_mutable_storage_file, set_boolean_flag_value, set_flag_has_local_override,
         set_flag_has_server_override,
     };
 
-    use protobuf::Message;
     use std::fs::{self, File};
-    use std::io::{Read, Write};
+    use std::io::Read;
     use tempfile::NamedTempFile;
 
-    /// Write storage location record pb to a temp file
-    fn write_storage_record_file(flag_val: &str, flag_info: &str) -> NamedTempFile {
-        let text_proto = format!(
-            r#"
-files {{
-    version: 0
-    container: "mockup"
-    package_map: "some_package_map"
-    flag_map: "some_flag_map"
-    flag_val: "{}"
-    flag_info: "{}"
-    timestamp: 12345
-}}
-"#,
-            flag_val, flag_info
-        );
-        let storage_files: ProtoStorageFiles =
-            protobuf::text_format::parse_from_str(&text_proto).unwrap();
-        let mut binary_proto_bytes = Vec::new();
-        storage_files.write_to_vec(&mut binary_proto_bytes).unwrap();
-        let mut file = NamedTempFile::new().unwrap();
-        file.write_all(&binary_proto_bytes).unwrap();
-        file
-    }
-
     /// Create temp file copy
     fn copy_to_temp_rw_file(source_file: &str) -> NamedTempFile {
         let file = NamedTempFile::new().unwrap();
@@ -66,18 +39,12 @@
     /// 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 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::FlagVal).unwrap()
-        };
+        let mut file = unsafe { map_mutable_storage_file(&flag_value_path).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);
@@ -92,19 +59,13 @@
     #[test]
     /// Test to lock down flag has server override update api
     fn test_set_flag_has_server_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()
-        };
+        let mut file = unsafe { map_mutable_storage_file(&flag_info_path).unwrap() };
         for i in 0..8 {
             set_flag_has_server_override(&mut file, FlagValueType::Boolean, i, true).unwrap();
             let attribute =
@@ -120,19 +81,13 @@
     #[test]
     /// Test to lock down flag has local override update api
     fn test_set_flag_has_local_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()
-        };
+        let mut file = unsafe { map_mutable_storage_file(&flag_info_path).unwrap() };
         for i in 0..8 {
             set_flag_has_local_override(&mut file, FlagValueType::Boolean, i, true).unwrap();
             let attribute =
diff --git a/tools/check-flagged-apis/src/com/android/checkflaggedapis/CheckFlaggedApisTest.kt b/tools/check-flagged-apis/src/com/android/checkflaggedapis/CheckFlaggedApisTest.kt
index 5d87a4c..0569bfd 100644
--- a/tools/check-flagged-apis/src/com/android/checkflaggedapis/CheckFlaggedApisTest.kt
+++ b/tools/check-flagged-apis/src/com/android/checkflaggedapis/CheckFlaggedApisTest.kt
@@ -16,6 +16,8 @@
 package com.android.checkflaggedapis
 
 import android.aconfig.Aconfig
+import android.aconfig.Aconfig.flag_state.DISABLED
+import android.aconfig.Aconfig.flag_state.ENABLED
 import java.io.ByteArrayInputStream
 import java.io.ByteArrayOutputStream
 import java.io.InputStream
@@ -28,9 +30,12 @@
     """
       // Signature format: 2.0
       package android {
-        public final class Clazz {
-          ctor public Clazz();
+        @FlaggedApi("android.flag.foo") public final class Clazz {
+          ctor @FlaggedApi("android.flag.foo") public Clazz();
           field @FlaggedApi("android.flag.foo") public static final int FOO = 1; // 0x1
+          method @FlaggedApi("android.flag.foo") public int getErrorCode();
+        }
+        @FlaggedApi("android.flag.bar") public static class Clazz.Builder {
         }
       }
 """
@@ -43,13 +48,19 @@
         <class name="android/Clazz" since="1">
           <method name="&lt;init>()V"/>
           <field name="FOO"/>
+          <method name="getErrorCode()I"/>
+        </class>
+        <class name="android/Clazz${"$"}Builder" since="2">
         </class>
       </api>
 """
         .trim()
 
-private fun generateFlagsProto(fooState: Aconfig.flag_state): InputStream {
-  val parsed_flag =
+private fun generateFlagsProto(
+    fooState: Aconfig.flag_state,
+    barState: Aconfig.flag_state
+): InputStream {
+  val fooFlag =
       Aconfig.parsed_flag
           .newBuilder()
           .setPackage("android.flag")
@@ -57,9 +68,18 @@
           .setState(fooState)
           .setPermission(Aconfig.flag_permission.READ_ONLY)
           .build()
-  val parsed_flags = Aconfig.parsed_flags.newBuilder().addParsedFlag(parsed_flag).build()
+  val barFlag =
+      Aconfig.parsed_flag
+          .newBuilder()
+          .setPackage("android.flag")
+          .setName("bar")
+          .setState(barState)
+          .setPermission(Aconfig.flag_permission.READ_ONLY)
+          .build()
+  val flags =
+      Aconfig.parsed_flags.newBuilder().addParsedFlag(fooFlag).addParsedFlag(barFlag).build()
   val binaryProto = ByteArrayOutputStream()
-  parsed_flags.writeTo(binaryProto)
+  flags.writeTo(binaryProto)
   return ByteArrayInputStream(binaryProto.toByteArray())
 }
 
@@ -67,21 +87,36 @@
 class CheckFlaggedApisTest {
   @Test
   fun testParseApiSignature() {
-    val expected = setOf(Pair(Symbol("android.Clazz.FOO"), Flag("android.flag.foo")))
+    val expected =
+        setOf(
+            Pair(Symbol("android.Clazz"), Flag("android.flag.foo")),
+            Pair(Symbol("android.Clazz.Clazz()"), Flag("android.flag.foo")),
+            Pair(Symbol("android.Clazz.FOO"), Flag("android.flag.foo")),
+            Pair(Symbol("android.Clazz.getErrorCode()"), Flag("android.flag.foo")),
+            Pair(Symbol("android.Clazz.Builder"), Flag("android.flag.bar")),
+        )
     val actual = parseApiSignature("in-memory", API_SIGNATURE.byteInputStream())
     assertEquals(expected, actual)
   }
 
   @Test
   fun testParseFlagValues() {
-    val expected: Map<Flag, Boolean> = mapOf(Flag("android.flag.foo") to true)
-    val actual = parseFlagValues(generateFlagsProto(Aconfig.flag_state.ENABLED))
+    val expected: Map<Flag, Boolean> =
+        mapOf(Flag("android.flag.foo") to true, Flag("android.flag.bar") to true)
+    val actual = parseFlagValues(generateFlagsProto(ENABLED, ENABLED))
     assertEquals(expected, actual)
   }
 
   @Test
   fun testParseApiVersions() {
-    val expected: Set<Symbol> = setOf(Symbol("android.Clazz.FOO"))
+    val expected: Set<Symbol> =
+        setOf(
+            Symbol("android.Clazz"),
+            Symbol("android.Clazz.Clazz()"),
+            Symbol("android.Clazz.FOO"),
+            Symbol("android.Clazz.getErrorCode()"),
+            Symbol("android.Clazz.Builder"),
+        )
     val actual = parseApiVersions(API_VERSIONS.byteInputStream())
     assertEquals(expected, actual)
   }
@@ -92,7 +127,7 @@
     val actual =
         findErrors(
             parseApiSignature("in-memory", API_SIGNATURE.byteInputStream()),
-            parseFlagValues(generateFlagsProto(Aconfig.flag_state.ENABLED)),
+            parseFlagValues(generateFlagsProto(ENABLED, ENABLED)),
             parseApiVersions(API_VERSIONS.byteInputStream()))
     assertEquals(expected, actual)
   }
@@ -101,11 +136,19 @@
   fun testFindErrorsDisabledFlaggedApiIsPresent() {
     val expected =
         setOf<ApiError>(
-            DisabledFlaggedApiIsPresentError(Symbol("android.Clazz.FOO"), Flag("android.flag.foo")))
+            DisabledFlaggedApiIsPresentError(Symbol("android.Clazz"), Flag("android.flag.foo")),
+            DisabledFlaggedApiIsPresentError(
+                Symbol("android.Clazz.Clazz()"), Flag("android.flag.foo")),
+            DisabledFlaggedApiIsPresentError(Symbol("android.Clazz.FOO"), Flag("android.flag.foo")),
+            DisabledFlaggedApiIsPresentError(
+                Symbol("android.Clazz.getErrorCode()"), Flag("android.flag.foo")),
+            DisabledFlaggedApiIsPresentError(
+                Symbol("android.Clazz.Builder"), Flag("android.flag.bar")),
+        )
     val actual =
         findErrors(
             parseApiSignature("in-memory", API_SIGNATURE.byteInputStream()),
-            parseFlagValues(generateFlagsProto(Aconfig.flag_state.DISABLED)),
+            parseFlagValues(generateFlagsProto(DISABLED, DISABLED)),
             parseApiVersions(API_VERSIONS.byteInputStream()))
     assertEquals(expected, actual)
   }
diff --git a/tools/check-flagged-apis/src/com/android/checkflaggedapis/Main.kt b/tools/check-flagged-apis/src/com/android/checkflaggedapis/Main.kt
index 84564ba..0c078a0 100644
--- a/tools/check-flagged-apis/src/com/android/checkflaggedapis/Main.kt
+++ b/tools/check-flagged-apis/src/com/android/checkflaggedapis/Main.kt
@@ -19,7 +19,10 @@
 
 import android.aconfig.Aconfig
 import com.android.tools.metalava.model.BaseItemVisitor
+import com.android.tools.metalava.model.ClassItem
 import com.android.tools.metalava.model.FieldItem
+import com.android.tools.metalava.model.Item
+import com.android.tools.metalava.model.MethodItem
 import com.android.tools.metalava.model.text.ApiFile
 import com.github.ajalt.clikt.core.CliktCommand
 import com.github.ajalt.clikt.core.ProgramResult
@@ -46,7 +49,7 @@
 @JvmInline
 internal value class Symbol(val name: String) {
   companion object {
-    private val FORBIDDEN_CHARS = listOf('/', '#', '$')
+    private val FORBIDDEN_CHARS = listOf('#', '$')
 
     /** Create a new Symbol from a String that may include delimiters other than dot. */
     fun create(name: String): Symbol {
@@ -167,22 +170,50 @@
 }
 
 internal fun parseApiSignature(path: String, input: InputStream): Set<Pair<Symbol, Flag>> {
-  // TODO(334870672): add support for classes and metods
+  // TODO(334870672): add support for metods
   val output = mutableSetOf<Pair<Symbol, Flag>>()
   val visitor =
       object : BaseItemVisitor() {
-        override fun visitField(field: FieldItem) {
-          val flag =
-              field.modifiers
-                  .findAnnotation("android.annotation.FlaggedApi")
-                  ?.findAttribute("value")
-                  ?.value
-                  ?.value() as? String
-          if (flag != null) {
-            val symbol = Symbol.create(field.baselineElementId())
-            output.add(Pair(symbol, Flag(flag)))
+        override fun visitClass(cls: ClassItem) {
+          getFlagOrNull(cls)?.let { flag ->
+            val symbol = Symbol.create(cls.baselineElementId())
+            output.add(Pair(symbol, flag))
           }
         }
+
+        override fun visitField(field: FieldItem) {
+          getFlagOrNull(field)?.let { flag ->
+            val symbol = Symbol.create(field.baselineElementId())
+            output.add(Pair(symbol, flag))
+          }
+        }
+
+        override fun visitMethod(method: MethodItem) {
+          getFlagOrNull(method)?.let { flag ->
+            val name = buildString {
+              append(method.containingClass().qualifiedName())
+              append(".")
+              append(method.name())
+              append("(")
+              // TODO(334870672): replace this early return with proper parsing of the command line
+              // arguments, followed by translation to Lname/of/class; + III format
+              if (!method.parameters().isEmpty()) {
+                return
+              }
+              append(")")
+            }
+            val symbol = Symbol.create(name)
+            output.add(Pair(symbol, flag))
+          }
+        }
+
+        private fun getFlagOrNull(item: Item): Flag? {
+          return item.modifiers
+              .findAnnotation("android.annotation.FlaggedApi")
+              ?.findAttribute("value")
+              ?.value
+              ?.let { Flag(it.value() as String) }
+        }
       }
   val codebase = ApiFile.parseApi(path, input)
   codebase.accept(visitor)
@@ -203,16 +234,54 @@
   val factory = DocumentBuilderFactory.newInstance()
   val parser = factory.newDocumentBuilder()
   val document = parser.parse(input)
+
+  val classes = document.getElementsByTagName("class")
+  // ktfmt doesn't understand the `..<` range syntax; explicitly call .rangeUntil instead
+  for (i in 0.rangeUntil(classes.getLength())) {
+    val cls = classes.item(i)
+    val className =
+        requireNotNull(cls.getAttribute("name")) {
+          "Bad XML: <class> element without name attribute"
+        }
+    output.add(Symbol.create(className.replace("/", ".")))
+  }
+
   val fields = document.getElementsByTagName("field")
   // ktfmt doesn't understand the `..<` range syntax; explicitly call .rangeUntil instead
   for (i in 0.rangeUntil(fields.getLength())) {
     val field = fields.item(i)
-    val fieldName = field.getAttribute("name")
+    val fieldName =
+        requireNotNull(field.getAttribute("name")) {
+          "Bad XML: <field> element without name attribute"
+        }
     val className =
-        requireNotNull(field.getParentNode()) { "Bad XML: top level <field> element" }
-            .getAttribute("name")
-    output.add(Symbol.create("$className.$fieldName"))
+        requireNotNull(field.getParentNode()?.getAttribute("name")) { "Bad XML: top level <field> element" }
+    output.add(Symbol.create("${className.replace("/", ".")}.$fieldName"))
   }
+
+  val methods = document.getElementsByTagName("method")
+  // ktfmt doesn't understand the `..<` range syntax; explicitly call .rangeUntil instead
+  for (i in 0.rangeUntil(methods.getLength())) {
+    val method = methods.item(i)
+    val methodSignature =
+        requireNotNull(method.getAttribute("name")) {
+          "Bad XML: <method> element without name attribute"
+        }
+    val methodSignatureParts = methodSignature.split(Regex("\\(|\\)"))
+    if (methodSignatureParts.size != 3) {
+      throw Exception("Bad XML: method signature '$methodSignature'")
+    }
+    var (methodName, methodArgs, methodReturnValue) = methodSignatureParts
+    val packageAndClassName =
+        requireNotNull(method.getParentNode()?.getAttribute("name")) {
+          "Bad XML: top level <method> element, or <class> element missing name attribute"
+        }
+    if (methodName == "<init>") {
+      methodName = packageAndClassName.split("/").last()
+    }
+    output.add(Symbol.create("${packageAndClassName.replace("/", ".")}.$methodName($methodArgs)"))
+  }
+
   return output
 }
 
diff --git a/tools/releasetools/ota_from_target_files.py b/tools/releasetools/ota_from_target_files.py
index 23762bb..432ea19 100755
--- a/tools/releasetools/ota_from_target_files.py
+++ b/tools/releasetools/ota_from_target_files.py
@@ -195,6 +195,8 @@
       ro.product.* properties are overridden by the 'import' statement.
       The file expects one property per line, and each line has the following
       format: 'prop_name=value1,value2'. e.g. 'ro.boot.product.sku=std,pro'
+      The path specified can either be relative to the current working directory
+      or the path to a file inside of input_target_files.
 
   --skip_postinstall
       Skip the postinstall hooks when generating an A/B OTA package (default:
@@ -1048,6 +1050,10 @@
   from check_target_files_vintf import CheckVintfIfTrebleEnabled
   CheckVintfIfTrebleEnabled(target_file, target_info)
 
+  # Allow boot_variable_file to also exist in target-files
+  if OPTIONS.boot_variable_file:
+    if not os.path.isfile(OPTIONS.boot_variable_file):
+      OPTIONS.boot_variable_file = os.path.join(target_file, OPTIONS.boot_variable_file)
   # Metadata to comply with Android OTA package format.
   metadata = GetPackageMetadata(target_info, source_info)
   # Generate payload.
diff --git a/tools/tool_event_logger/Android.bp b/tools/tool_event_logger/Android.bp
new file mode 100644
index 0000000..7a1d2aa
--- /dev/null
+++ b/tools/tool_event_logger/Android.bp
@@ -0,0 +1,67 @@
+// Copyright 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Set of error prone rules to ensure code quality
+// PackageLocation check requires the androidCompatible=false otherwise it does not do anything.
+
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+    default_team: "trendy_team_adte",
+}
+
+python_library_host {
+    name: "tool_event_proto",
+    srcs: [
+        "proto/tool_event.proto",
+    ],
+    proto: {
+        canonical_path_from_root: false,
+    },
+}
+
+python_binary_host {
+    name: "tool_event_logger",
+    pkg_path: "tool_event_logger",
+    srcs: [
+        "tool_event_logger.py",
+    ],
+    libs: [
+        "asuite_cc_client",
+        "tool_event_proto",
+    ],
+    main: "tool_event_logger.py",
+}
+
+python_test_host {
+    name: "tool_event_logger_test",
+    main: "tool_event_logger_test.py",
+    pkg_path: "tool_event_logger",
+    srcs: [
+        "tool_event_logger.py",
+        "tool_event_logger_test.py",
+    ],
+    test_options: {
+        unit_test: true,
+    },
+    libs: [
+        "asuite_cc_client",
+        "tool_event_proto",
+    ],
+    version: {
+        py3: {
+            embedded_launcher: true,
+            enabled: true,
+        },
+    },
+}
diff --git a/tools/tool_event_logger/OWNERS b/tools/tool_event_logger/OWNERS
new file mode 100644
index 0000000..b692c9e
--- /dev/null
+++ b/tools/tool_event_logger/OWNERS
@@ -0,0 +1,4 @@
+include platform/tools/asuite:/OWNERS
+
+zhuoyao@google.com
+hzalek@google.com
\ No newline at end of file
diff --git a/tools/tool_event_logger/proto/tool_event.proto b/tools/tool_event_logger/proto/tool_event.proto
new file mode 100644
index 0000000..61e28a2
--- /dev/null
+++ b/tools/tool_event_logger/proto/tool_event.proto
@@ -0,0 +1,35 @@
+syntax = "proto3";
+
+package tools.asuite.tool_event_logger;
+
+message ToolEvent {
+  // Occurs immediately upon execution of the tool.
+  message InvocationStarted {
+    string command_args = 1;
+    string cwd = 2;
+    string os = 3;
+  }
+
+  // Occurs when tool exits for any reason.
+  message InvocationStopped {
+    int32 exit_code = 2;
+    string exit_log = 3;
+  }
+
+  // ------------------------
+  // FIELDS FOR ToolEvent
+  // ------------------------
+  // Random string generated to identify the invocation.
+  string invocation_id = 1;
+  // Internal user name.
+  string user_name = 2;
+  // The root of Android source.
+  string source_root = 3;
+  // Name of the tool used.
+  string tool_tag = 6;
+
+  oneof event {
+    InvocationStarted invocation_started = 4;
+    InvocationStopped invocation_stopped = 5;
+  }
+}
diff --git a/tools/tool_event_logger/tool_event_logger.py b/tools/tool_event_logger/tool_event_logger.py
new file mode 100644
index 0000000..65a9696
--- /dev/null
+++ b/tools/tool_event_logger/tool_event_logger.py
@@ -0,0 +1,229 @@
+# Copyright 2024, The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+import argparse
+import datetime
+import getpass
+import logging
+import os
+import platform
+import sys
+import tempfile
+import uuid
+
+from atest.metrics import clearcut_client
+from atest.proto import clientanalytics_pb2
+from proto import tool_event_pb2
+
+LOG_SOURCE = 2395
+
+
+class ToolEventLogger:
+  """Logs tool events to Sawmill through Clearcut."""
+
+  def __init__(
+      self,
+      tool_tag: str,
+      invocation_id: str,
+      user_name: str,
+      source_root: str,
+      platform_version: str,
+      python_version: str,
+      client: clearcut_client.Clearcut,
+  ):
+    self.tool_tag = tool_tag
+    self.invocation_id = invocation_id
+    self.user_name = user_name
+    self.source_root = source_root
+    self.platform_version = platform_version
+    self.python_version = python_version
+    self._clearcut_client = client
+
+  @classmethod
+  def create(cls, tool_tag: str):
+    return ToolEventLogger(
+        tool_tag=tool_tag,
+        invocation_id=str(uuid.uuid4()),
+        user_name=getpass.getuser(),
+        source_root=os.environ.get('ANDROID_BUILD_TOP', ''),
+        platform_version=platform.platform(),
+        python_version=platform.python_version(),
+        client=clearcut_client.Clearcut(LOG_SOURCE),
+    )
+
+  def __enter__(self):
+    return self
+
+  def __exit__(self, exc_type, exc_val, exc_tb):
+    self.flush()
+
+  def log_invocation_started(self, event_time: datetime, command_args: str):
+    """Creates an event log with invocation started info."""
+    event = self._create_tool_event()
+    event.invocation_started.CopyFrom(
+        tool_event_pb2.ToolEvent.InvocationStarted(
+            command_args=command_args,
+            os=f'{self.platform_version}:{self.python_version}',
+        )
+    )
+
+    logging.debug('Log invocation_started: %s', event)
+    self._log_clearcut_event(event, event_time)
+
+  def log_invocation_stopped(
+      self,
+      event_time: datetime,
+      exit_code: int,
+      exit_log: str,
+  ):
+    """Creates an event log with invocation stopped info."""
+    event = self._create_tool_event()
+    event.invocation_stopped.CopyFrom(
+        tool_event_pb2.ToolEvent.InvocationStopped(
+            exit_code=exit_code,
+            exit_log=exit_log,
+        )
+    )
+
+    logging.debug('Log invocation_stopped: %s', event)
+    self._log_clearcut_event(event, event_time)
+
+  def flush(self):
+    """Sends all batched events to Clearcut."""
+    logging.debug('Sending events to Clearcut.')
+    self._clearcut_client.flush_events()
+
+  def _create_tool_event(self):
+    return tool_event_pb2.ToolEvent(
+        tool_tag=self.tool_tag,
+        invocation_id=self.invocation_id,
+        user_name=self.user_name,
+        source_root=self.source_root,
+    )
+
+  def _log_clearcut_event(
+      self, tool_event: tool_event_pb2.ToolEvent, event_time: datetime
+  ):
+    log_event = clientanalytics_pb2.LogEvent(
+        event_time_ms=int(event_time.timestamp() * 1000),
+        source_extension=tool_event.SerializeToString(),
+    )
+    self._clearcut_client.log(log_event)
+
+
+class ArgumentParserWithLogging(argparse.ArgumentParser):
+
+  def error(self, message):
+    logging.error('Failed to parse args with error: %s', message)
+    super().error(message)
+
+
+def create_arg_parser():
+  """Creates an instance of the default ToolEventLogger arg parser."""
+
+  parser = ArgumentParserWithLogging(
+      description='Build and upload logs for Android dev tools',
+      add_help=True,
+      formatter_class=argparse.RawDescriptionHelpFormatter,
+  )
+
+  parser.add_argument(
+      '--tool_tag',
+      type=str,
+      required=True,
+      help='Name of the tool.',
+  )
+
+  parser.add_argument(
+      '--start_timestamp',
+      type=lambda ts: datetime.datetime.fromtimestamp(float(ts)),
+      required=True,
+      help=(
+          'Timestamp when the tool starts. The timestamp should have the format'
+          '%s.%N which represents the seconds elapses since epoch.'
+      ),
+  )
+
+  parser.add_argument(
+      '--end_timestamp',
+      type=lambda ts: datetime.datetime.fromtimestamp(float(ts)),
+      required=True,
+      help=(
+          'Timestamp when the tool exits. The timestamp should have the format'
+          '%s.%N which represents the seconds elapses since epoch.'
+      ),
+  )
+
+  parser.add_argument(
+      '--tool_args',
+      type=str,
+      help='Parameters that are passed to the tool.',
+  )
+
+  parser.add_argument(
+      '--exit_code',
+      type=int,
+      required=True,
+      help='Tool exit code.',
+  )
+
+  parser.add_argument(
+      '--exit_log',
+      type=str,
+      help='Logs when tool exits.',
+  )
+
+  parser.add_argument(
+      '--dry_run',
+      action='store_true',
+      help='Dry run the tool event logger if set.',
+  )
+
+  return parser
+
+
+def configure_logging():
+  root_logging_dir = tempfile.mkdtemp(prefix='tool_event_logger_')
+
+  log_fmt = '%(asctime)s %(filename)s:%(lineno)s:%(levelname)s: %(message)s'
+  date_fmt = '%Y-%m-%d %H:%M:%S'
+  _, log_path = tempfile.mkstemp(dir=root_logging_dir, suffix='.log')
+
+  logging.basicConfig(
+      filename=log_path, level=logging.DEBUG, format=log_fmt, datefmt=date_fmt
+  )
+
+
+def main(argv: list[str]):
+  args = create_arg_parser().parse_args(argv[1:])
+
+  if args.dry_run:
+    logging.debug('This is a dry run.')
+    return
+
+  try:
+    with ToolEventLogger.create(args.tool_tag) as logger:
+      logger.log_invocation_started(args.start_timestamp, args.tool_args)
+      logger.log_invocation_stopped(
+          args.end_timestamp, args.exit_code, args.exit_log
+      )
+  except Exception as e:
+    logging.error('Log failed with unexpected error: %s', e)
+    raise
+
+
+if __name__ == '__main__':
+  configure_logging()
+  main(sys.argv)
diff --git a/tools/tool_event_logger/tool_event_logger_test.py b/tools/tool_event_logger/tool_event_logger_test.py
new file mode 100644
index 0000000..34b6c35
--- /dev/null
+++ b/tools/tool_event_logger/tool_event_logger_test.py
@@ -0,0 +1,209 @@
+# Copyright 2024, The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Unittests for ToolEventLogger."""
+
+import datetime
+import logging
+import unittest
+from unittest import mock
+
+from atest.metrics import clearcut_client
+from proto import tool_event_pb2
+from tool_event_logger import tool_event_logger
+
+TEST_INVOCATION_ID = 'test_invocation_id'
+TEST_USER_NAME = 'test_user'
+TEST_TOOL_TAG = 'test_tool'
+TEST_SOURCE_ROOT = 'test_source_root'
+TEST_PLATFORM_VERSION = 'test_platform_version'
+TEST_PYTHON_VERSION = 'test_python_version'
+TEST_EVENT_TIMESTAMP = datetime.datetime.now()
+
+
+class ToolEventLoggerTest(unittest.TestCase):
+
+  def setUp(self):
+    super().setUp()
+    self.clearcut_client = FakeClearcutClient()
+    self.logger = tool_event_logger.ToolEventLogger(
+        TEST_TOOL_TAG,
+        TEST_INVOCATION_ID,
+        TEST_USER_NAME,
+        TEST_SOURCE_ROOT,
+        TEST_PLATFORM_VERSION,
+        TEST_PYTHON_VERSION,
+        client=self.clearcut_client,
+    )
+
+  def test_log_event_timestamp(self):
+    with self.logger:
+      self.logger.log_invocation_started(
+          datetime.datetime.fromtimestamp(100.101), 'test_command'
+      )
+
+    self.assertEqual(
+        self.clearcut_client.get_last_sent_event().event_time_ms, 100101
+    )
+
+  def test_log_event_basic_information(self):
+    with self.logger:
+      self.logger.log_invocation_started(TEST_EVENT_TIMESTAMP, 'test_command')
+
+    sent_event = self.clearcut_client.get_last_sent_event()
+    log_event = tool_event_pb2.ToolEvent.FromString(sent_event.source_extension)
+    self.assertEqual(log_event.invocation_id, TEST_INVOCATION_ID)
+    self.assertEqual(log_event.user_name, TEST_USER_NAME)
+    self.assertEqual(log_event.tool_tag, TEST_TOOL_TAG)
+    self.assertEqual(log_event.source_root, TEST_SOURCE_ROOT)
+
+  def test_log_invocation_started(self):
+    expected_invocation_started = tool_event_pb2.ToolEvent.InvocationStarted(
+        command_args='test_command',
+        os=TEST_PLATFORM_VERSION + ':' + TEST_PYTHON_VERSION,
+    )
+
+    with self.logger:
+      self.logger.log_invocation_started(TEST_EVENT_TIMESTAMP, 'test_command')
+
+    self.assertEqual(self.clearcut_client.get_number_of_sent_events(), 1)
+    sent_event = self.clearcut_client.get_last_sent_event()
+    self.assertEqual(
+        expected_invocation_started,
+        tool_event_pb2.ToolEvent.FromString(
+            sent_event.source_extension
+        ).invocation_started,
+    )
+
+  def test_log_invocation_stopped(self):
+    expected_invocation_stopped = tool_event_pb2.ToolEvent.InvocationStopped(
+        exit_code=0,
+        exit_log='exit_log',
+    )
+
+    with self.logger:
+      self.logger.log_invocation_stopped(TEST_EVENT_TIMESTAMP, 0, 'exit_log')
+
+    self.assertEqual(self.clearcut_client.get_number_of_sent_events(), 1)
+    sent_event = self.clearcut_client.get_last_sent_event()
+    self.assertEqual(
+        expected_invocation_stopped,
+        tool_event_pb2.ToolEvent.FromString(
+            sent_event.source_extension
+        ).invocation_stopped,
+    )
+
+  def test_log_multiple_events(self):
+    with self.logger:
+      self.logger.log_invocation_started(TEST_EVENT_TIMESTAMP, 'test_command')
+      self.logger.log_invocation_stopped(TEST_EVENT_TIMESTAMP, 0, 'exit_log')
+
+    self.assertEqual(self.clearcut_client.get_number_of_sent_events(), 2)
+
+
+class MainTest(unittest.TestCase):
+
+  REQUIRED_ARGS = [
+      '',
+      '--tool_tag',
+      'test_tool',
+      '--start_timestamp',
+      '1',
+      '--end_timestamp',
+      '2',
+      '--exit_code',
+      '0',
+  ]
+
+  def test_log_and_exit_with_missing_required_args(self):
+    with self.assertLogs() as logs:
+      with self.assertRaises(SystemExit) as ex:
+        tool_event_logger.main(['', '--tool_tag', 'test_tool'])
+
+    with self.subTest('Verify exception code'):
+      self.assertEqual(ex.exception.code, 2)
+
+    with self.subTest('Verify log messages'):
+      self.assertIn(
+          'the following arguments are required',
+          '\n'.join(logs.output),
+      )
+
+  def test_log_and_exit_with_invalid_args(self):
+    with self.assertLogs() as logs:
+      with self.assertRaises(SystemExit) as ex:
+        tool_event_logger.main(['', '--start_timestamp', 'test'])
+
+    with self.subTest('Verify exception code'):
+      self.assertEqual(ex.exception.code, 2)
+
+    with self.subTest('Verify log messages'):
+      self.assertIn(
+          '--start_timestamp: invalid',
+          '\n'.join(logs.output),
+      )
+
+  def test_log_and_exit_with_dry_run(self):
+    with self.assertLogs(level=logging.DEBUG) as logs:
+      tool_event_logger.main(self.REQUIRED_ARGS + ['--dry_run'])
+
+    with self.subTest('Verify log messages'):
+      self.assertIn('dry run', '\n'.join(logs.output))
+
+  @mock.patch.object(clearcut_client, 'Clearcut')
+  def test_log_and_exit_with_unexpected_exception(self, mock_cc):
+    mock_cc.return_value = FakeClearcutClient(raise_log_exception=True)
+
+    with self.assertLogs() as logs:
+      with self.assertRaises(Exception) as ex:
+        tool_event_logger.main(self.REQUIRED_ARGS)
+
+    with self.subTest('Verify log messages'):
+      self.assertIn('unexpected error', '\n'.join(logs.output))
+
+  @mock.patch.object(clearcut_client, 'Clearcut')
+  def test_success(self, mock_cc):
+    mock_clear_cut_client = FakeClearcutClient()
+    mock_cc.return_value = mock_clear_cut_client
+
+    tool_event_logger.main(self.REQUIRED_ARGS)
+
+    self.assertEqual(mock_clear_cut_client.get_number_of_sent_events(), 2)
+
+
+class FakeClearcutClient:
+
+  def __init__(self, raise_log_exception=False):
+    self.pending_log_events = []
+    self.sent_log_events = []
+    self.raise_log_exception = raise_log_exception
+
+  def log(self, log_event):
+    if self.raise_log_exception:
+      raise Exception('unknown exception')
+    self.pending_log_events.append(log_event)
+
+  def flush_events(self):
+    self.sent_log_events.extend(self.pending_log_events)
+    self.pending_log_events.clear()
+
+  def get_number_of_sent_events(self):
+    return len(self.sent_log_events)
+
+  def get_last_sent_event(self):
+    return self.sent_log_events[-1]
+
+
+if __name__ == '__main__':
+  unittest.main()