Merge changes from topic "merge_cc_logtags" into main

* changes:
  Add LOCAL_SOONG_LOGTAGS_FILES
  Add python Soong modules for logtags
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 e563873..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
diff --git a/core/android_soong_config_vars.mk b/core/android_soong_config_vars.mk
index 56da574..18d245c 100644
--- a/core/android_soong_config_vars.mk
+++ b/core/android_soong_config_vars.mk
@@ -59,17 +59,6 @@
   MODULE_BUILD_FROM_SOURCE := $(BRANCH_DEFAULT_MODULE_BUILD_FROM_SOURCE)
 endif
 
-ifneq (,$(ART_MODULE_BUILD_FROM_SOURCE))
-  # Keep an explicit setting.
-else ifneq (,$(findstring .android.art,$(TARGET_BUILD_APPS)))
-  # Build ART modules from source if they are listed in TARGET_BUILD_APPS.
-  ART_MODULE_BUILD_FROM_SOURCE := true
-else
-  # Do the same as other modules by default.
-  ART_MODULE_BUILD_FROM_SOURCE := $(MODULE_BUILD_FROM_SOURCE)
-endif
-
-$(call soong_config_set,art_module,source_build,$(ART_MODULE_BUILD_FROM_SOURCE))
 ifdef ART_DEBUG_OPT_FLAG
 $(call soong_config_set,art_module,art_debug_opt_flag,$(ART_DEBUG_OPT_FLAG))
 endif
diff --git a/core/binary.mk b/core/binary.mk
index b17ab00..f86b5a4 100644
--- a/core/binary.mk
+++ b/core/binary.mk
@@ -1196,6 +1196,17 @@
 endif
 
 ###################################################################
+## When compiling a memtag_stack enabled target, use the .memtag_stack variant
+## of any static dependencies (where they exist).
+##################################################################
+ifneq ($(filter memtag_stack,$(my_sanitize)),)
+  my_whole_static_libraries := $(call use_soong_sanitized_static_libraries,\
+    $(my_whole_static_libraries),memtag_stack)
+  my_static_libraries := $(call use_soong_sanitized_static_libraries,\
+    $(my_static_libraries),memtag_stack)
+endif
+
+###################################################################
 ## When compiling against API imported module, use API import stub
 ## libraries.
 ##################################################################
diff --git a/core/config.mk b/core/config.mk
index 5842594..aaf8117 100644
--- a/core/config.mk
+++ b/core/config.mk
@@ -509,7 +509,6 @@
 
 ifeq ($(CALLED_FROM_SETUP),true)
 include $(BUILD_SYSTEM)/ccache.mk
-include $(BUILD_SYSTEM)/goma.mk
 include $(BUILD_SYSTEM)/rbe.mk
 endif
 
diff --git a/core/goma.mk b/core/goma.mk
deleted file mode 100644
index 2b51d8b..0000000
--- a/core/goma.mk
+++ /dev/null
@@ -1,34 +0,0 @@
-#
-# Copyright (C) 2015 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.
-#
-
-# Notice: this works only with Google's Goma build infrastructure.
-ifneq ($(filter-out false,$(USE_GOMA)),)
-  ifdef GOMA_DIR
-    goma_dir := $(GOMA_DIR)
-  else
-    goma_dir := $(HOME)/goma
-  endif
-  GOMA_CC := $(goma_dir)/gomacc
-
-  # Append gomacc to existing *_WRAPPER variables so it's possible to
-  # use both ccache and gomacc.
-  CC_WRAPPER := $(strip $(CC_WRAPPER) $(GOMA_CC))
-  CXX_WRAPPER := $(strip $(CXX_WRAPPER) $(GOMA_CC))
-  # b/143658984: goma can't handle the --system argument to javac
-  #JAVAC_WRAPPER := $(strip $(JAVAC_WRAPPER) $(GOMA_CC))
-
-  goma_dir :=
-endif
diff --git a/core/product_config.mk b/core/product_config.mk
index 4eeac95..f21c1c4 100644
--- a/core/product_config.mk
+++ b/core/product_config.mk
@@ -314,6 +314,14 @@
 ifeq (true,$(PRODUCT_MODULE_BUILD_FROM_SOURCE))
   ignore_apex_contributions := true
 endif
+ifneq ($(EMMA_INSTRUMENT)$(EMMA_INSTRUMENT_STATIC)$(EMMA_INSTRUMENT_FRAMEWORK)$(CLANG_COVERAGE)$(NATIVE_COVERAGE_PATHS),)
+# Coverage builds for TARGET_RELEASE=foo should always build from source,
+# even if TARGET_RELEASE=foo uses prebuilt mainline modules.
+# This is necessary because the checked-in prebuilts were generated with
+# instrumentation turned off.
+  ignore_apex_contributions := true
+endif
+
 ifeq (true, $(ignore_apex_contributions))
 PRODUCT_BUILD_IGNORE_APEX_CONTRIBUTION_CONTENTS := true
 endif
diff --git a/core/product_config.rbc b/core/product_config.rbc
index 921f068..59e2c95 100644
--- a/core/product_config.rbc
+++ b/core/product_config.rbc
@@ -351,6 +351,7 @@
                 if cfg.get(attr, "") == "":
                     cfg[attr] = value
                     percolated_attrs[attr] = True
+                    child_cfg.pop(attr)
 
     for attr in _options.trace_variables:
         if attr in percolated_attrs:
@@ -360,7 +361,7 @@
     value = from_cfg.get(attr, [])
     if value:
         to_list.extend(value)
-        from_cfg[attr] = []
+        from_cfg.pop(attr)
 
 def _indirect(pcm_name):
     """Returns configuration item for the inherited module."""
diff --git a/core/proguard/kotlin.flags b/core/proguard/kotlin.flags
index 70dbaa7..ef6bf0e 100644
--- a/core/proguard/kotlin.flags
+++ b/core/proguard/kotlin.flags
@@ -10,7 +10,9 @@
 
 # Kotlin DebugMetadata has no value in release builds, these two rules, will
 # allow AppReduce to strip out DebutMetadata.
--checkdiscard interface kotlin.coroutines.jvm.internal.DebugMetadata
+# TODO(b/302383328): Restore the below checkdiscard after resolving transitive
+# inclusion of kotlin-stdlib from androidx.annotation library deps.
+# -checkdiscard interface kotlin.coroutines.jvm.internal.DebugMetadata
 -assumenosideeffects class kotlin.coroutines.jvm.internal.DebugMetadataKt {
   *** getDebugMetadataAnnotation(...);
 }
diff --git a/core/release_config.mk b/core/release_config.mk
index 3e51af5..bb51980 100644
--- a/core/release_config.mk
+++ b/core/release_config.mk
@@ -41,6 +41,7 @@
 # which has OWNERS control.  If it isn't let others define their own.
 # TODO: Remove wildcard for build/release one when all branch manifests
 # have updated.
+_must_protobuf :=
 config_map_files := $(wildcard build/release/release_config_map.mk) \
     $(wildcard vendor/google_shared/build/release/release_config_map.mk) \
     $(if $(wildcard vendor/google/release/release_config_map.mk), \
@@ -53,13 +54,85 @@
         ) \
     )
 
+protobuf_map_files := $(wildcard build/release/release_config_map.textproto) \
+    $(wildcard vendor/google_shared/build/release/release_config_map.textproto) \
+    $(if $(wildcard vendor/google/release/release_config_map.textproto), \
+        vendor/google/release/release_config_map.textproto, \
+        $(sort \
+            $(wildcard device/*/release/release_config_map.textproto) \
+            $(wildcard device/*/*/release/release_config_map.textproto) \
+            $(wildcard vendor/*/release/release_config_map.textproto) \
+            $(wildcard vendor/*/*/release/release_config_map.textproto) \
+        ) \
+    )
+
 # PRODUCT_RELEASE_CONFIG_MAPS is set by Soong using an initial run of product
 # config to capture only the list of config maps needed by the build.
 # Keep them in the order provided, but remove duplicates.
+# Treat .mk and .textproto as equal for duplicate elimination, but force
+# protobuf if any PRODUCT_RELEASE_CONFIG_MAPS specify .textproto.
 $(foreach map,$(PRODUCT_RELEASE_CONFIG_MAPS), \
-    $(if $(filter $(map),$(config_map_files)),,$(eval config_map_files += $(map))) \
+    $(if $(filter $(basename $(map)),$(basename $(config_map_files))),, \
+        $(eval config_map_files += $(map))) \
+    $(if $(filter $(basename $(map)).textproto,$(map)),$(eval _must_protobuf := true)) \
 )
 
+
+# If we are missing the textproto version of any of $(config_map_files), we cannot use protobuf.
+_can_protobuf := true
+$(foreach map,$(config_map_files), \
+    $(if $(wildcard $(basename $(map)).textproto),,$(eval _can_protobuf :=)) \
+)
+# If we are missing the mk version of any of $(protobuf_map_files), we must use protobuf.
+$(foreach map,$(protobuf_map_files), \
+    $(if $(wildcard $(basename $(map)).mk),,$(eval _must_protobuf := true)) \
+)
+
+ifneq (,$(_must_protobuf))
+    ifeq (,$(_can_protobuf))
+	# We must use protobuf, but we cannot use protobuf.
+        $(error release config is a mixture of .scl and .textproto)
+    endif
+endif
+
+_use_protobuf :=
+ifneq (,$(_must_protobuf))
+    _use_protobuf := true
+else
+    ifneq ($(_can_protobuf),)
+        # Determine the default
+        $(foreach map,$(config_map_files), \
+            $(if $(wildcard $(dir $(map))/build_config/DEFAULT=proto),$(eval _use_protobuf := true)) \
+            $(if $(wildcard $(dir $(map))/build_config/DEFAULT=make),$(eval _use_protobuf := )) \
+        )
+        # Update for this specific release config only (no inheritance).
+        $(foreach map,$(config_map_files), \
+            $(if $(wildcard $(dir $(map))/build_config/$(TARGET_RELEASE)=proto),$(eval _use_protobuf := true)) \
+            $(if $(wildcard $(dir $(map))/build_config/$(TARGET_RELEASE)=make),$(eval _use_protobuf := )) \
+        )
+    endif
+endif
+
+ifneq (,$(_use_protobuf))
+    # The .textproto files are the canonical source of truth.
+    _args := $(foreach map,$(config_map_files), --map $(map) )
+    ifneq (,$(_must_protobuf))
+        # Disable the build flag in release-config.
+        _args += --guard=false
+    endif
+    $(KATI_shell_no_rerun $(OUT_DIR)/release-config $(_args) >$(OUT_DIR)/release-config.out && touch -t 200001010000 $(OUT_DIR)/release-config.out)
+    $(if $(filter-out 0,$(.SHELLSTATUS)),$(error release-config failed to run))
+    # This will also set _all_release_configs for us.
+    $(eval include $(OUT_DIR)/soong/release-config/release_config-$(TARGET_PRODUCT)-$(TARGET_RELEASE).mk)
+    $(KATI_extra_file_deps $(OUT_DIR)/release-config $(config_map_files))
+    ifeq (,$(_must_protobuf)$(RELEASE_BUILD_FLAGS_IN_PROTOBUF))
+        _use_protobuf :=
+    endif
+endif
+ifeq (,$(_use_protobuf))
+    # The .mk files are the canonical source of truth.
+
+
 # Declare an alias release-config
 #
 # This should be used to declare a release as an alias of another, meaning no
@@ -144,6 +217,9 @@
     $(error Alias release config "$(r)" may not specify release config files $(_all_release_configs.$(r).FILES))\
 )))
 
+# Use makefiles
+endif
+
 ifeq ($(TARGET_RELEASE),)
     # We allow some internal paths to explicitly set TARGET_RELEASE to the
     # empty string.  For the most part, 'make' treats unset and empty string as
@@ -167,6 +243,7 @@
     endif
 endif
 
+ifeq (,$(_use_protobuf))
 # Choose flag files
 # Don't sort this, use it in the order they gave us.
 # Do allow duplicate entries, retaining only the first usage.
@@ -196,6 +273,9 @@
 $(error invalid use of apply-release-config-overrides)
 endef
 
+# use makefiles
+endif
+
 # TODO: Remove this check after enough people have sourced lunch that we don't
 # need to worry about it trying to do get_build_vars TARGET_RELEASE. Maybe after ~9/2023
 ifneq ($(CALLED_FROM_SETUP),true)
@@ -207,15 +287,20 @@
 endif
 .KATI_READONLY := TARGET_RELEASE
 
+ifeq (,$(_use_protobuf))
 $(foreach config, $(_all_release_configs), \
     $(eval _all_release_configs.$(config).DECLARED_IN:= ) \
     $(eval _all_release_configs.$(config).FILES:= ) \
 )
+applied_releases:=
+# use makefiles
+endif
 _all_release_configs:=
 config_map_files:=
-applied_releases:=
+protobuf_map_files:=
 
 
+ifeq (,$(_use_protobuf))
 # -----------------------------------------------------------------
 # Flag declarations and values
 # -----------------------------------------------------------------
@@ -252,3 +337,8 @@
 # outside of the source tree.
 $(call run-starlark,$(OUT_DIR)/release_config_entrypoint.scl,$(OUT_DIR)/release_config_entrypoint.scl,--allow_external_entrypoint)
 
+# use makefiles
+endif
+_can_protobuf :=
+_must_protobuf :=
+_use_protobuf :=
diff --git a/core/tasks/meta-lic.mk b/core/tasks/meta-lic.mk
index 1094726..9a8dfff 100644
--- a/core/tasks/meta-lic.mk
+++ b/core/tasks/meta-lic.mk
@@ -66,9 +66,30 @@
 
 $(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,))
 
+# Moved here from device/google/trout/Android.mk
+$(eval $(call declare-1p-copy-files,device/google/trout,))
+
 # Moved here from frameworks/av/media/Android.mk
 $(eval $(call declare-1p-copy-files,frameworks/av/media/libeffects,audio_effects.conf))
 $(eval $(call declare-1p-copy-files,frameworks/av/media/libeffects,audio_effects.xml))
diff --git a/core/tasks/sdk-addon.mk b/core/tasks/sdk-addon.mk
index 7acac72..2fd4ce9 100644
--- a/core/tasks/sdk-addon.mk
+++ b/core/tasks/sdk-addon.mk
@@ -126,7 +126,7 @@
 $(full_target_img): $(full_target) $(addon_img_source_prop) | $(SOONG_ZIP)
 	@echo Packaging SDK Addon System-Image: $@
 	$(hide) mkdir -p $(dir $@)
-	cp -R $(PRODUCT_OUT)/data $(PRIVATE_STAGING_DIR)/data
+	cp -R $(PRODUCT_OUT)/data $(PRIVATE_STAGING_DIR)
 	$(hide) $(SOONG_ZIP) -o $@ -C $(dir $(PRIVATE_STAGING_DIR)) -D $(PRIVATE_STAGING_DIR)
 
 
diff --git a/envsetup.sh b/envsetup.sh
index ab43ada..1ef9a54 100644
--- a/envsetup.sh
+++ b/envsetup.sh
@@ -1134,8 +1134,9 @@
     --tool_tag "${tool_tag}" \
     --start_timestamp "${start_time}" \
     --end_timestamp "$(date +%s.%N)" \
-    --tool_args \""${@}"\" \
+    --tool_args "$*" \
     --exit_code "${exit_code}" \
+    ${ANDROID_TOOL_LOGGER_EXTRA_ARGS} \
     > /dev/null 2>&1 &
   exit ${exit_code}
   ' SIGINT SIGTERM SIGQUIT EXIT
diff --git a/target/product/aosp_arm64.mk b/target/product/aosp_arm64.mk
index d944615..364fed4 100644
--- a/target/product/aosp_arm64.mk
+++ b/target/product/aosp_arm64.mk
@@ -55,7 +55,8 @@
 # All components inherited here go to vendor or vendor_boot image
 #
 $(call inherit-product, $(SRC_TARGET_DIR)/board/generic_arm64/device.mk)
-$(call inherit-product, $(SRC_TARGET_DIR)/product/non_ab_device.mk)
+AB_OTA_UPDATER := true
+AB_OTA_PARTITIONS ?= system
 
 #
 # Special settings for GSI releasing
diff --git a/target/product/aosp_x86_64.mk b/target/product/aosp_x86_64.mk
index 4344f50..595940d 100644
--- a/target/product/aosp_x86_64.mk
+++ b/target/product/aosp_x86_64.mk
@@ -57,7 +57,8 @@
 # All components inherited here go to vendor image
 #
 $(call inherit-product, $(SRC_TARGET_DIR)/board/generic_x86_64/device.mk)
-$(call inherit-product, $(SRC_TARGET_DIR)/product/non_ab_device.mk)
+AB_OTA_UPDATER := true
+AB_OTA_PARTITIONS ?= system
 
 #
 # Special settings for GSI releasing
diff --git a/target/product/base_system.mk b/target/product/base_system.mk
index 57e8275..22284b1 100644
--- a/target/product/base_system.mk
+++ b/target/product/base_system.mk
@@ -486,6 +486,11 @@
 # Enable dirty image object binning to reduce dirty pages in the image.
 PRODUCT_PACKAGES += dirty-image-objects
 
+# Enable go/perfetto-persistent-tracing for eng builds
+ifneq (,$(filter eng, $(TARGET_BUILD_VARIANT)))
+    PRODUCT_PRODUCT_PROPERTIES += persist.debug.perfetto.persistent_sysui_tracing_for_bugreport=1
+endif
+
 $(call inherit-product, $(SRC_TARGET_DIR)/product/runtime_libart.mk)
 
 # Ensure all trunk-stable flags are available.
diff --git a/target/product/go_defaults.mk b/target/product/go_defaults.mk
index b717486..a10cfa8 100644
--- a/target/product/go_defaults.mk
+++ b/target/product/go_defaults.mk
@@ -17,6 +17,8 @@
 # Inherit common Android Go defaults.
 $(call inherit-product, build/make/target/product/go_defaults_common.mk)
 
+PRODUCT_RELEASE_CONFIG_MAPS += $(wildcard vendor/google/release/go_devices/release_config_map.mk)
+
 # Add the system properties.
 TARGET_SYSTEM_PROP += \
     build/make/target/board/go_defaults.prop
diff --git a/teams/Android.bp b/teams/Android.bp
index 78efa61..e5886bd 100644
--- a/teams/Android.bp
+++ b/teams/Android.bp
@@ -4351,6 +4351,13 @@
 }
 
 team {
+  name: "trendy_team_pixel_pearl",
+
+  // go/trendy/manage/engineers/6326219602231296
+  trendy_team_id: "6326219602231296",
+}
+
+team {
   name: "trendy_team_ar_sensors_context_hub",
 
   // go/trendy/manage/engineers/4776371090259968
diff --git a/tests/Android.bp b/tests/Android.bp
index d3964e5..39debf5 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -29,6 +29,7 @@
     },
     data: [
         ":envsetup_minimum.zip",
+        ":tool_event_logger",
     ],
     test_suites: [
         "general-tests",
diff --git a/tests/run.rbc b/tests/run.rbc
index 85d6c09..221b40f 100644
--- a/tests/run.rbc
+++ b/tests/run.rbc
@@ -26,6 +26,7 @@
 load(":board.rbc", board_init = "init")
 load(":board_input_vars.rbc", board_input_vars_init = "init")
 load("//build/make/tests/single_value_inheritance:test.rbc", test_single_value_inheritance = "test")
+load("//build/make/tests/single_value_inheritance_2:test.rbc", test_single_value_inheritance_2 = "test")
 load("//build/make/tests/artifact_path_requirements:test.rbc", test_artifact_path_requirements = "test")
 load("//build/make/tests/prefixed_sort_order:test.rbc", test_prefixed_sort_order = "test")
 load("//build/make/tests/inherits_in_regular_variables:test.rbc", test_inherits_in_regular_variables = "test")
@@ -181,6 +182,7 @@
 assert_eq("", g.get("NEWVAR"))
 
 test_single_value_inheritance()
+test_single_value_inheritance_2()
 test_artifact_path_requirements()
 test_prefixed_sort_order()
 test_inherits_in_regular_variables()
diff --git a/tests/run_tool_with_logging_test.py b/tests/run_tool_with_logging_test.py
index 1eb78f1..215d992 100644
--- a/tests/run_tool_with_logging_test.py
+++ b/tests/run_tool_with_logging_test.py
@@ -13,20 +13,22 @@
 # limitations under the License.
 
 import dataclasses
+import glob
 from importlib import resources
 import logging
 import os
 from pathlib import Path
 import re
+import shutil
 import signal
 import stat
 import subprocess
+import sys
 import tempfile
 import textwrap
 import time
 import unittest
 import zipfile
-import sys
 
 EXII_RETURN_CODE = 0
 INTERRUPTED_RETURN_CODE = 130
@@ -40,7 +42,7 @@
     # Configure to print logging to stdout.
     logging.basicConfig(filename=None, level=logging.DEBUG)
     console = logging.StreamHandler(sys.stdout)
-    logging.getLogger('').addHandler(console)
+    logging.getLogger("").addHandler(console)
 
   def setUp(self):
     super().setUp()
@@ -49,7 +51,7 @@
     os.chdir(self.working_dir.name)
     # Extract envsetup.zip which contains the envsetup.sh and other dependent
     # scripts required to set up the build environments.
-    with resources.files("testdata").joinpath("envsetup.zip").open('rb') as p:
+    with resources.files("testdata").joinpath("envsetup.zip").open("rb") as p:
       with zipfile.ZipFile(p, "r") as zip_f:
         zip_f.extractall()
 
@@ -118,7 +120,7 @@
     test_tool.assert_called_once_with_args("arg1 arg2")
     expected_logger_args = (
         "--tool_tag FAKE_TOOL --start_timestamp \d+\.\d+ --end_timestamp"
-        ' \d+\.\d+ --tool_args "arg1 arg2" --exit_code 0'
+        " \d+\.\d+ --tool_args arg1 arg2 --exit_code 0"
     )
     test_logger.assert_called_once_with_args(expected_logger_args)
 
@@ -196,7 +198,7 @@
 
     expected_logger_args = (
         "--tool_tag FAKE_TOOL --start_timestamp \d+\.\d+ --end_timestamp"
-        ' \d+\.\d+ --tool_args "arg1 arg2" --exit_code 130'
+        " \d+\.\d+ --tool_args arg1 arg2 --exit_code 130"
     )
     test_logger.assert_called_once_with_args(expected_logger_args)
 
@@ -226,6 +228,37 @@
 
     test_logger.assert_not_called()
 
+  def test_integration_tool_event_logger_dry_run(self):
+    test_tool = TestScript.create(self.working_dir)
+    logger_path = self._import_logger()
+
+    self._run_script_and_wait(f"""
+      TMPDIR="{self.working_dir.name}"
+      ANDROID_ENABLE_TOOL_LOGGING=true
+      ANDROID_TOOL_LOGGER="{logger_path}"
+      ANDROID_TOOL_LOGGER_EXTRA_ARGS="--dry_run"
+      run_tool_with_logging "FAKE_TOOL" {test_tool.executable} arg1 arg2
+    """)
+
+    self._assert_logger_dry_run()
+
+  def _import_logger(self) -> Path:
+    logger = "tool_event_logger"
+    logger_path = Path(self.working_dir.name).joinpath(logger)
+    with resources.as_file(resources.files("testdata").joinpath(logger)) as p:
+      shutil.copy(p, logger_path)
+    Path.chmod(logger_path, 0o755)
+    return logger_path
+
+  def _assert_logger_dry_run(self):
+    log_files = glob.glob(self.working_dir.name + "/tool_event_logger_*/*.log")
+    self.assertEqual(len(log_files), 1)
+
+    with open(log_files[0], "r") as f:
+      lines = f.readlines()
+      self.assertEqual(len(lines), 1)
+      self.assertIn("dry run", lines[0])
+
   def _create_build_env_script(self) -> str:
     return f"""
       source {Path(self.working_dir.name).joinpath("build/make/envsetup.sh")}
@@ -248,7 +281,7 @@
         stderr=subprocess.PIPE,
         text=True,
         start_new_session=True,
-        executable='/bin/bash'
+        executable="/bin/bash",
         )
 
   def _wait_for_process(
@@ -301,7 +334,7 @@
       """)
       f.write(executable_contents.encode("utf-8"))
 
-    os.chmod(f.name, os.stat(f.name).st_mode | stat.S_IEXEC)
+    Path.chmod(f.name, os.stat(f.name).st_mode | stat.S_IEXEC)
 
     return TestScript(executable, output_file)
 
diff --git a/tests/single_value_inheritance_2/a.rbc b/tests/single_value_inheritance_2/a.rbc
new file mode 100644
index 0000000..fe186c7
--- /dev/null
+++ b/tests/single_value_inheritance_2/a.rbc
@@ -0,0 +1,20 @@
+# Copyright 2024 Google LLC
+#
+# 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
+#
+#      https://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.
+
+load("//build/make/core:product_config.rbc", "rblf")
+
+def init(g, handle):
+  cfg = rblf.cfg(handle)
+
+  cfg["PRODUCT_ENABLE_UFFD_GC"] = "true"
diff --git a/tests/single_value_inheritance_2/b.rbc b/tests/single_value_inheritance_2/b.rbc
new file mode 100644
index 0000000..7d95749
--- /dev/null
+++ b/tests/single_value_inheritance_2/b.rbc
@@ -0,0 +1,20 @@
+# Copyright 2024 Google LLC
+#
+# 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
+#
+#      https://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.
+
+load("//build/make/core:product_config.rbc", "rblf")
+
+def init(g, handle):
+  cfg = rblf.cfg(handle)
+
+  cfg["PRODUCT_ENABLE_UFFD_GC"] = "default"
diff --git a/tests/single_value_inheritance_2/c.rbc b/tests/single_value_inheritance_2/c.rbc
new file mode 100644
index 0000000..e90e37d
--- /dev/null
+++ b/tests/single_value_inheritance_2/c.rbc
@@ -0,0 +1,21 @@
+# Copyright 2024 Google LLC
+#
+# 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
+#
+#      https://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.
+
+load("//build/make/core:product_config.rbc", "rblf")
+load(":b.rbc", _b_init = "init")
+
+def init(g, handle):
+  cfg = rblf.cfg(handle)
+
+  rblf.inherit(handle, "test/b", _b_init)
diff --git a/tests/single_value_inheritance_2/d.rbc b/tests/single_value_inheritance_2/d.rbc
new file mode 100644
index 0000000..3a88c2c
--- /dev/null
+++ b/tests/single_value_inheritance_2/d.rbc
@@ -0,0 +1,23 @@
+# Copyright 2024 Google LLC
+#
+# 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
+#
+#      https://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.
+
+load("//build/make/core:product_config.rbc", "rblf")
+load(":c.rbc", _c_init = "init")
+load(":a.rbc", _a_init = "init")
+
+def init(g, handle):
+  cfg = rblf.cfg(handle)
+
+  rblf.inherit(handle, "test/a", _a_init)
+  rblf.inherit(handle, "test/c", _c_init)
diff --git a/tests/single_value_inheritance_2/product.rbc b/tests/single_value_inheritance_2/product.rbc
new file mode 100644
index 0000000..c47664d
--- /dev/null
+++ b/tests/single_value_inheritance_2/product.rbc
@@ -0,0 +1,23 @@
+# Copyright 2024 Google LLC
+#
+# 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
+#
+#      https://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.
+
+load("//build/make/core:product_config.rbc", "rblf")
+load(":b.rbc", _b_init = "init")
+load(":d.rbc", _d_init = "init")
+
+def init(g, handle):
+  cfg = rblf.cfg(handle)
+
+  rblf.inherit(handle, "test/b", _b_init)
+  rblf.inherit(handle, "test/d", _d_init)
diff --git a/tests/single_value_inheritance_2/test.rbc b/tests/single_value_inheritance_2/test.rbc
new file mode 100644
index 0000000..fa93aaa
--- /dev/null
+++ b/tests/single_value_inheritance_2/test.rbc
@@ -0,0 +1,40 @@
+# Copyright 2024 Google LLC
+#
+# 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
+#
+#      https://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.
+
+load("//build/make/core:product_config.rbc", "rblf")
+load("//build/make/tests/input_variables.rbc", input_variables_init = "init")
+load(":product.rbc", "init")
+
+
+def assert_eq(expected, actual):
+    if expected != actual:
+        fail("Expected '%s', got '%s'" % (expected, actual))
+
+# This test is testing that single value variables are "stolen" when processing the inheritance
+# graph. i.e. if you have a graph like this:
+#
+#   B   A
+#   |\  |
+#   | C |
+#    \ \|
+#     \ D
+#      \|
+#       E
+#
+# The same variable is defined in both A and B. In D, the value from A is chosen because it comes
+# alphabetically before C. But then in E, the value from D is chosen instead of the value from B,
+# because the value of B was "stolen" and sucked into C, leaving B with no value set.
+def test():
+    (globals, globals_base) = rblf.product_configuration("test/device", init, input_variables_init)
+    assert_eq("true", globals["PRODUCTS.test/device.mk.PRODUCT_ENABLE_UFFD_GC"])
diff --git a/tools/aconfig/TEST_MAPPING b/tools/aconfig/TEST_MAPPING
index b7ff8ef..421e94a 100644
--- a/tools/aconfig/TEST_MAPPING
+++ b/tools/aconfig/TEST_MAPPING
@@ -97,5 +97,9 @@
     }
   ],
   "postsubmit": [
+    {
+      // aconfig_storage file cpp integration tests
+      "name": "aconfig_storage_file.test.cpp"
+    }
   ]
 }
diff --git a/tools/aconfig/aconfig/Android.bp b/tools/aconfig/aconfig/Android.bp
index 00a6fee..68521af 100644
--- a/tools/aconfig/aconfig/Android.bp
+++ b/tools/aconfig/aconfig/Android.bp
@@ -161,6 +161,9 @@
     shared_libs: [
         "server_configurable_flags",
     ],
+    defaults: [
+        "aconfig_lib_cc_static_link.defaults",
+    ],
     test_suites: ["general-tests"],
 }
 
@@ -176,6 +179,9 @@
     shared_libs: [
         "server_configurable_flags",
     ],
+    defaults: [
+        "aconfig_lib_cc_static_link.defaults",
+    ],
     test_suites: ["general-tests"],
 }
 
@@ -199,6 +205,9 @@
     shared_libs: [
         "server_configurable_flags",
     ],
+    defaults: [
+        "aconfig_lib_cc_static_link.defaults",
+    ],
     test_suites: ["general-tests"],
 }
 */
@@ -215,6 +224,9 @@
     shared_libs: [
         "server_configurable_flags",
     ],
+    defaults: [
+        "aconfig_lib_cc_static_link.defaults",
+    ],
     test_suites: ["general-tests"],
 }
 
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 9abc892..3360ddd 100644
--- a/tools/aconfig/aconfig/src/codegen/java.rs
+++ b/tools/aconfig/aconfig/src/codegen/java.rs
@@ -428,10 +428,16 @@
 
     /** @hide */
     public class FakeFeatureFlagsImpl extends CustomFeatureFlags {
-        private Map<String, Boolean> mFlagMap = new HashMap<>();
+        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);
@@ -441,10 +447,13 @@
         @Override
         protected boolean getValue(String flagName, Predicate<FeatureFlags> getter) {
             Boolean value = this.mFlagMap.get(flagName);
-            if (value == null) {
-                throw new IllegalArgumentException(flagName + " is not set");
+            if (value != null) {
+                return value;
             }
-            return value;
+            if (mDefaults != null) {
+                return getter.test(mDefaults);
+            }
+            throw new IllegalArgumentException(flagName + " is not set");
         }
 
         public void setFlag(String flagName, boolean value) {
diff --git a/tools/aconfig/aconfig/src/commands.rs b/tools/aconfig/aconfig/src/commands.rs
index ad96bb8..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> {
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/templates/FakeFeatureFlagsImpl.java.template b/tools/aconfig/aconfig/templates/FakeFeatureFlagsImpl.java.template
index c20d3c5..290d2c4 100644
--- a/tools/aconfig/aconfig/templates/FakeFeatureFlagsImpl.java.template
+++ b/tools/aconfig/aconfig/templates/FakeFeatureFlagsImpl.java.template
@@ -6,10 +6,16 @@
 
 /** @hide */
 public class FakeFeatureFlagsImpl extends CustomFeatureFlags \{
-    private Map<String, Boolean> mFlagMap = new HashMap<>();
+    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);
@@ -19,10 +25,13 @@
     @Override
     protected boolean getValue(String flagName, Predicate<FeatureFlags> getter) \{
         Boolean value = this.mFlagMap.get(flagName);
-        if (value == null) \{
-            throw new IllegalArgumentException(flagName + " is not set");
+        if (value != null) \{
+            return value;
         }
-        return value;
+        if (mDefaults != null) \{
+            return getter.test(mDefaults);
+        }
+        throw new IllegalArgumentException(flagName + " is not set");
     }
 
     public void setFlag(String flagName, boolean value) \{
diff --git a/tools/aconfig/aconfig/templates/cpp_source_file.template b/tools/aconfig/aconfig/templates/cpp_source_file.template
index 4bcd1b7..62664bc 100644
--- a/tools/aconfig/aconfig/templates/cpp_source_file.template
+++ b/tools/aconfig/aconfig/templates/cpp_source_file.template
@@ -1,5 +1,15 @@
 #include "{header}.h"
 
+{{ if allow_instrumentation }}
+#include <sys/stat.h>
+#include "aconfig_storage/aconfig_storage_read_api.hpp"
+#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 +107,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 +166,7 @@
     {{ -endif }}
     {{ -endif }}
     {{ -endif }}
+    {{ -endif }}
 }
 
 {{ -if is_test_mode }}
@@ -119,3 +182,4 @@
 }
 {{ -endif }}
 
+
diff --git a/tools/aconfig/aconfig_storage_file/Android.bp b/tools/aconfig/aconfig_storage_file/Android.bp
index 08c00b0..e066e31 100644
--- a/tools/aconfig/aconfig_storage_file/Android.bp
+++ b/tools/aconfig/aconfig_storage_file/Android.bp
@@ -12,6 +12,7 @@
         "libtempfile",
         "libprotobuf",
         "libclap",
+        "libcxx",
         "libaconfig_storage_protos",
     ],
 }
@@ -77,3 +78,62 @@
     product_available: true,
     double_loadable: true,
 }
+
+// cxx source codegen from rust api
+genrule {
+    name: "libcxx_aconfig_storage_file_bridge_code",
+    tools: ["cxxbridge"],
+    cmd: "$(location cxxbridge) $(in) > $(out)",
+    srcs: ["src/lib.rs"],
+    out: ["aconfig_storage/lib.rs.cc"],
+}
+
+// cxx header codegen from rust api
+genrule {
+    name: "libcxx_aconfig_storage_file_bridge_header",
+    tools: ["cxxbridge"],
+    cmd: "$(location cxxbridge) $(in) --header > $(out)",
+    srcs: ["src/lib.rs"],
+    out: ["aconfig_storage/lib.rs.h"],
+}
+
+// a static cc lib based on generated code
+rust_ffi_static {
+    name: "libaconfig_storage_file_cxx_bridge",
+    crate_name: "aconfig_storage_file_cxx_bridge",
+    host_supported: true,
+    vendor_available: true,
+    product_available: true,
+    srcs: ["src/lib.rs"],
+    defaults: ["aconfig_storage_file.defaults"],
+    apex_available: [
+        "//apex_available:platform",
+        "//apex_available:anyapex",
+    ],
+    min_sdk_version: "29",
+}
+
+// storage file parse api cc interface
+cc_library {
+    name: "libaconfig_storage_file_cc",
+    srcs: ["aconfig_storage_file.cpp"],
+    generated_headers: [
+        "cxx-bridge-header",
+        "libcxx_aconfig_storage_file_bridge_header",
+    ],
+    generated_sources: ["libcxx_aconfig_storage_file_bridge_code"],
+    whole_static_libs: ["libaconfig_storage_file_cxx_bridge"],
+    export_include_dirs: ["include"],
+    host_supported: true,
+    vendor_available: true,
+    product_available: true,
+    shared_libs: [
+        "libbase",
+    ],
+    apex_available: [
+        "//apex_available:platform",
+        "//apex_available:anyapex",
+    ],
+    min_sdk_version: "29",
+    double_loadable: true,
+}
diff --git a/tools/aconfig/aconfig_storage_file/Cargo.toml b/tools/aconfig/aconfig_storage_file/Cargo.toml
index 641f481..192dfad 100644
--- a/tools/aconfig/aconfig_storage_file/Cargo.toml
+++ b/tools/aconfig/aconfig_storage_file/Cargo.toml
@@ -13,6 +13,7 @@
 tempfile = "3.9.0"
 thiserror = "1.0.56"
 clap = { version = "4.1.8", features = ["derive"] }
+cxx = "1.0"
 
 [[bin]]
 name = "aconfig-storage"
diff --git a/tools/aconfig/aconfig_storage_file/aconfig_storage_file.cpp b/tools/aconfig/aconfig_storage_file/aconfig_storage_file.cpp
new file mode 100644
index 0000000..548078f
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_file/aconfig_storage_file.cpp
@@ -0,0 +1,38 @@
+#include "rust/cxx.h"
+#include "aconfig_storage/lib.rs.h"
+
+#include "aconfig_storage/aconfig_storage_file.hpp"
+
+using namespace android::base;
+
+namespace aconfig_storage {
+
+Result<std::vector<FlagValueAndInfoSummary>> list_flags_with_info(
+    const std::string& package_map,
+    const std::string& flag_map,
+    const std::string& flag_val,
+    const std::string& flag_info) {
+  auto flag_list_cxx = list_flags_with_info_cxx(rust::Str(package_map.c_str()),
+                                                rust::Str(flag_map.c_str()),
+                                                rust::Str(flag_val.c_str()),
+                                                rust::Str(flag_info.c_str()));
+  if (flag_list_cxx.query_success) {
+    auto flag_list = std::vector<FlagValueAndInfoSummary>();
+    for (const auto& flag_cxx : flag_list_cxx.flags) {
+      auto flag = FlagValueAndInfoSummary();
+      flag.package_name = std::string(flag_cxx.package_name);
+      flag.flag_name = std::string(flag_cxx.flag_name);
+      flag.flag_value = std::string(flag_cxx.flag_value);
+      flag.value_type = std::string(flag_cxx.value_type);
+      flag.is_readwrite = flag_cxx.is_readwrite;
+      flag.has_server_override = flag_cxx.has_server_override;
+      flag.has_local_override = flag_cxx.has_local_override;
+      flag_list.push_back(flag);
+    }
+    return flag_list;
+  } else {
+    return Error() << flag_list_cxx.error_message;
+  }
+}
+
+} // namespace aconfig_storage
diff --git a/tools/aconfig/aconfig_storage_file/build.rs b/tools/aconfig/aconfig_storage_file/build.rs
index 1feeb60..e0ade2a 100644
--- a/tools/aconfig/aconfig_storage_file/build.rs
+++ b/tools/aconfig/aconfig_storage_file/build.rs
@@ -14,4 +14,6 @@
         .inputs(proto_files)
         .cargo_out_dir("aconfig_storage_protos")
         .run_from_script();
+
+    let _ = cxx_build::bridge("src/lib.rs");
 }
diff --git a/tools/aconfig/aconfig_storage_file/include/aconfig_storage/aconfig_storage_file.hpp b/tools/aconfig/aconfig_storage_file/include/aconfig_storage/aconfig_storage_file.hpp
new file mode 100644
index 0000000..5044a4d
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_file/include/aconfig_storage/aconfig_storage_file.hpp
@@ -0,0 +1,31 @@
+#pragma once
+
+#include <vector>
+#include <string>
+#include <android-base/result.h>
+
+namespace aconfig_storage {
+
+/// Flag value and info summary for a flag
+struct FlagValueAndInfoSummary {
+  std::string package_name;
+  std::string flag_name;
+  std::string flag_value;
+  std::string value_type;
+  bool is_readwrite;
+  bool has_server_override;
+  bool has_local_override;
+};
+
+/// List all flag values with their flag info
+/// \input package_map: package map file
+/// \input flag_map: flag map file
+/// \input flag_val: flag value file
+/// \input flag_info: flag info file
+android::base::Result<std::vector<FlagValueAndInfoSummary>> list_flags_with_info(
+    const std::string& package_map,
+    const std::string& flag_map,
+    const std::string& flag_val,
+    const std::string& flag_info);
+
+}// namespace aconfig_storage
diff --git a/tools/aconfig/aconfig_storage_file/src/lib.rs b/tools/aconfig/aconfig_storage_file/src/lib.rs
index 2acfc7d..80602bb 100644
--- a/tools/aconfig/aconfig_storage_file/src/lib.rs
+++ b/tools/aconfig/aconfig_storage_file/src/lib.rs
@@ -278,12 +278,21 @@
     Ok(buffer)
 }
 
+/// Flag value summary
+#[derive(Debug, PartialEq)]
+pub struct FlagValueSummary {
+    pub package_name: String,
+    pub flag_name: String,
+    pub flag_value: String,
+    pub value_type: StoredFlagType,
+}
+
 /// List flag values from storage files
 pub fn list_flags(
     package_map: &str,
     flag_map: &str,
     flag_val: &str,
-) -> Result<Vec<(String, String, StoredFlagType, bool)>, AconfigStorageError> {
+) -> Result<Vec<FlagValueSummary>, AconfigStorageError> {
     let package_table = PackageTable::from_bytes(&read_file_to_bytes(package_map)?)?;
     let flag_table = FlagTable::from_bytes(&read_file_to_bytes(flag_map)?)?;
     let flag_value_list = FlagValueList::from_bytes(&read_file_to_bytes(flag_val)?)?;
@@ -295,30 +304,155 @@
 
     let mut flags = Vec::new();
     for node in flag_table.nodes.iter() {
-        let (package_name, package_offset) = package_info[node.package_id as usize];
-        let flag_offset = package_offset + node.flag_index as u32;
-        let flag_value = flag_value_list.booleans[flag_offset as usize];
-        flags.push((
-            String::from(package_name),
-            node.flag_name.clone(),
-            node.flag_type,
-            flag_value,
-        ));
+        let (package_name, boolean_start_index) = package_info[node.package_id as usize];
+        let flag_index = boolean_start_index + node.flag_index as u32;
+        let flag_value = flag_value_list.booleans[flag_index as usize];
+        flags.push(FlagValueSummary {
+            package_name: String::from(package_name),
+            flag_name: node.flag_name.clone(),
+            flag_value: flag_value.to_string(),
+            value_type: node.flag_type,
+        });
     }
 
-    flags.sort_by(|v1, v2| match v1.0.cmp(&v2.0) {
-        Ordering::Equal => v1.1.cmp(&v2.1),
+    flags.sort_by(|v1, v2| match v1.package_name.cmp(&v2.package_name) {
+        Ordering::Equal => v1.flag_name.cmp(&v2.flag_name),
         other => other,
     });
     Ok(flags)
 }
 
+/// Flag value and info summary
+#[derive(Debug, PartialEq)]
+pub struct FlagValueAndInfoSummary {
+    pub package_name: String,
+    pub flag_name: String,
+    pub flag_value: String,
+    pub value_type: StoredFlagType,
+    pub is_readwrite: bool,
+    pub has_server_override: bool,
+    pub has_local_override: bool,
+}
+
+/// List flag values and info from storage files
+pub fn list_flags_with_info(
+    package_map: &str,
+    flag_map: &str,
+    flag_val: &str,
+    flag_info: &str,
+) -> Result<Vec<FlagValueAndInfoSummary>, AconfigStorageError> {
+    let package_table = PackageTable::from_bytes(&read_file_to_bytes(package_map)?)?;
+    let flag_table = FlagTable::from_bytes(&read_file_to_bytes(flag_map)?)?;
+    let flag_value_list = FlagValueList::from_bytes(&read_file_to_bytes(flag_val)?)?;
+    let flag_info = FlagInfoList::from_bytes(&read_file_to_bytes(flag_info)?)?;
+
+    let mut package_info = vec![("", 0); package_table.header.num_packages as usize];
+    for node in package_table.nodes.iter() {
+        package_info[node.package_id as usize] = (&node.package_name, node.boolean_start_index);
+    }
+
+    let mut flags = Vec::new();
+    for node in flag_table.nodes.iter() {
+        let (package_name, boolean_start_index) = package_info[node.package_id as usize];
+        let flag_index = boolean_start_index + node.flag_index as u32;
+        let flag_value = flag_value_list.booleans[flag_index as usize];
+        let flag_attribute = flag_info.nodes[flag_index as usize].attributes;
+        flags.push(FlagValueAndInfoSummary {
+            package_name: String::from(package_name),
+            flag_name: node.flag_name.clone(),
+            flag_value: flag_value.to_string(),
+            value_type: node.flag_type,
+            is_readwrite: flag_attribute & (FlagInfoBit::IsReadWrite as u8) != 0,
+            has_server_override: flag_attribute & (FlagInfoBit::HasServerOverride as u8) != 0,
+            has_local_override: flag_attribute & (FlagInfoBit::HasLocalOverride as u8) != 0,
+        });
+    }
+
+    flags.sort_by(|v1, v2| match v1.package_name.cmp(&v2.package_name) {
+        Ordering::Equal => v1.flag_name.cmp(&v2.flag_name),
+        other => other,
+    });
+    Ok(flags)
+}
+
+// *************************************** //
+// CC INTERLOP
+// *************************************** //
+
+// Exported rust data structure and methods, c++ code will be generated
+#[cxx::bridge]
+mod ffi {
+    /// flag value and info summary cxx return
+    pub struct FlagValueAndInfoSummaryCXX {
+        pub package_name: String,
+        pub flag_name: String,
+        pub flag_value: String,
+        pub value_type: String,
+        pub is_readwrite: bool,
+        pub has_server_override: bool,
+        pub has_local_override: bool,
+    }
+
+    /// list flag result cxx return
+    pub struct ListFlagValueAndInfoResultCXX {
+        pub query_success: bool,
+        pub error_message: String,
+        pub flags: Vec<FlagValueAndInfoSummaryCXX>,
+    }
+
+    // Rust export to c++
+    extern "Rust" {
+        pub fn list_flags_with_info_cxx(
+            package_map: &str,
+            flag_map: &str,
+            flag_val: &str,
+            flag_info: &str,
+        ) -> ListFlagValueAndInfoResultCXX;
+    }
+}
+
+/// implement flag value and info summary cxx return type
+impl ffi::FlagValueAndInfoSummaryCXX {
+    pub(crate) fn new(summary: FlagValueAndInfoSummary) -> Self {
+        Self {
+            package_name: summary.package_name,
+            flag_name: summary.flag_name,
+            flag_value: summary.flag_value,
+            value_type: format!("{:?}", summary.value_type),
+            is_readwrite: summary.is_readwrite,
+            has_server_override: summary.has_server_override,
+            has_local_override: summary.has_local_override,
+        }
+    }
+}
+
+/// implement list flag with info cxx interlop
+pub fn list_flags_with_info_cxx(
+    package_map: &str,
+    flag_map: &str,
+    flag_val: &str,
+    flag_info: &str,
+) -> ffi::ListFlagValueAndInfoResultCXX {
+    match list_flags_with_info(package_map, flag_map, flag_val, flag_info) {
+        Ok(summary) => ffi::ListFlagValueAndInfoResultCXX {
+            query_success: true,
+            error_message: String::new(),
+            flags: summary.into_iter().map(ffi::FlagValueAndInfoSummaryCXX::new).collect(),
+        },
+        Err(errmsg) => ffi::ListFlagValueAndInfoResultCXX {
+            query_success: false,
+            error_message: format!("{:?}", errmsg),
+            flags: Vec::new(),
+        },
+    }
+}
+
 #[cfg(test)]
 mod tests {
     use super::*;
     use crate::test_utils::{
-        create_test_flag_table, create_test_flag_value_list, create_test_package_table,
-        write_bytes_to_temp_file,
+        create_test_flag_info_list, create_test_flag_table, create_test_flag_value_list,
+        create_test_package_table, write_bytes_to_temp_file,
     };
 
     #[test]
@@ -337,54 +471,154 @@
         let flags =
             list_flags(&package_table_path, &flag_table_path, &flag_value_list_path).unwrap();
         let expected = [
-            (
-                String::from("com.android.aconfig.storage.test_1"),
-                String::from("disabled_rw"),
-                StoredFlagType::ReadWriteBoolean,
-                false,
-            ),
-            (
-                String::from("com.android.aconfig.storage.test_1"),
-                String::from("enabled_ro"),
-                StoredFlagType::ReadOnlyBoolean,
-                true,
-            ),
-            (
-                String::from("com.android.aconfig.storage.test_1"),
-                String::from("enabled_rw"),
-                StoredFlagType::ReadWriteBoolean,
-                true,
-            ),
-            (
-                String::from("com.android.aconfig.storage.test_2"),
-                String::from("disabled_rw"),
-                StoredFlagType::ReadWriteBoolean,
-                false,
-            ),
-            (
-                String::from("com.android.aconfig.storage.test_2"),
-                String::from("enabled_fixed_ro"),
-                StoredFlagType::FixedReadOnlyBoolean,
-                true,
-            ),
-            (
-                String::from("com.android.aconfig.storage.test_2"),
-                String::from("enabled_ro"),
-                StoredFlagType::ReadOnlyBoolean,
-                true,
-            ),
-            (
-                String::from("com.android.aconfig.storage.test_4"),
-                String::from("enabled_fixed_ro"),
-                StoredFlagType::FixedReadOnlyBoolean,
-                true,
-            ),
-            (
-                String::from("com.android.aconfig.storage.test_4"),
-                String::from("enabled_rw"),
-                StoredFlagType::ReadWriteBoolean,
-                true,
-            ),
+            FlagValueSummary {
+                package_name: String::from("com.android.aconfig.storage.test_1"),
+                flag_name: String::from("disabled_rw"),
+                value_type: StoredFlagType::ReadWriteBoolean,
+                flag_value: String::from("false"),
+            },
+            FlagValueSummary {
+                package_name: String::from("com.android.aconfig.storage.test_1"),
+                flag_name: String::from("enabled_ro"),
+                value_type: StoredFlagType::ReadOnlyBoolean,
+                flag_value: String::from("true"),
+            },
+            FlagValueSummary {
+                package_name: String::from("com.android.aconfig.storage.test_1"),
+                flag_name: String::from("enabled_rw"),
+                value_type: StoredFlagType::ReadWriteBoolean,
+                flag_value: String::from("true"),
+            },
+            FlagValueSummary {
+                package_name: String::from("com.android.aconfig.storage.test_2"),
+                flag_name: String::from("disabled_rw"),
+                value_type: StoredFlagType::ReadWriteBoolean,
+                flag_value: String::from("false"),
+            },
+            FlagValueSummary {
+                package_name: String::from("com.android.aconfig.storage.test_2"),
+                flag_name: String::from("enabled_fixed_ro"),
+                value_type: StoredFlagType::FixedReadOnlyBoolean,
+                flag_value: String::from("true"),
+            },
+            FlagValueSummary {
+                package_name: String::from("com.android.aconfig.storage.test_2"),
+                flag_name: String::from("enabled_ro"),
+                value_type: StoredFlagType::ReadOnlyBoolean,
+                flag_value: String::from("true"),
+            },
+            FlagValueSummary {
+                package_name: String::from("com.android.aconfig.storage.test_4"),
+                flag_name: String::from("enabled_fixed_ro"),
+                value_type: StoredFlagType::FixedReadOnlyBoolean,
+                flag_value: String::from("true"),
+            },
+            FlagValueSummary {
+                package_name: String::from("com.android.aconfig.storage.test_4"),
+                flag_name: String::from("enabled_rw"),
+                value_type: StoredFlagType::ReadWriteBoolean,
+                flag_value: String::from("true"),
+            },
+        ];
+        assert_eq!(flags, expected);
+    }
+
+    #[test]
+    // this test point locks down the flag list with info api
+    fn test_list_flag_with_info() {
+        let package_table =
+            write_bytes_to_temp_file(&create_test_package_table().into_bytes()).unwrap();
+        let flag_table = write_bytes_to_temp_file(&create_test_flag_table().into_bytes()).unwrap();
+        let flag_value_list =
+            write_bytes_to_temp_file(&create_test_flag_value_list().into_bytes()).unwrap();
+        let flag_info_list =
+            write_bytes_to_temp_file(&create_test_flag_info_list().into_bytes()).unwrap();
+
+        let package_table_path = package_table.path().display().to_string();
+        let flag_table_path = flag_table.path().display().to_string();
+        let flag_value_list_path = flag_value_list.path().display().to_string();
+        let flag_info_list_path = flag_info_list.path().display().to_string();
+
+        let flags = list_flags_with_info(
+            &package_table_path,
+            &flag_table_path,
+            &flag_value_list_path,
+            &flag_info_list_path,
+        )
+        .unwrap();
+        let expected = [
+            FlagValueAndInfoSummary {
+                package_name: String::from("com.android.aconfig.storage.test_1"),
+                flag_name: String::from("disabled_rw"),
+                value_type: StoredFlagType::ReadWriteBoolean,
+                flag_value: String::from("false"),
+                is_readwrite: true,
+                has_server_override: false,
+                has_local_override: false,
+            },
+            FlagValueAndInfoSummary {
+                package_name: String::from("com.android.aconfig.storage.test_1"),
+                flag_name: String::from("enabled_ro"),
+                value_type: StoredFlagType::ReadOnlyBoolean,
+                flag_value: String::from("true"),
+                is_readwrite: false,
+                has_server_override: false,
+                has_local_override: false,
+            },
+            FlagValueAndInfoSummary {
+                package_name: String::from("com.android.aconfig.storage.test_1"),
+                flag_name: String::from("enabled_rw"),
+                value_type: StoredFlagType::ReadWriteBoolean,
+                flag_value: String::from("true"),
+                is_readwrite: true,
+                has_server_override: false,
+                has_local_override: false,
+            },
+            FlagValueAndInfoSummary {
+                package_name: String::from("com.android.aconfig.storage.test_2"),
+                flag_name: String::from("disabled_rw"),
+                value_type: StoredFlagType::ReadWriteBoolean,
+                flag_value: String::from("false"),
+                is_readwrite: true,
+                has_server_override: false,
+                has_local_override: false,
+            },
+            FlagValueAndInfoSummary {
+                package_name: String::from("com.android.aconfig.storage.test_2"),
+                flag_name: String::from("enabled_fixed_ro"),
+                value_type: StoredFlagType::FixedReadOnlyBoolean,
+                flag_value: String::from("true"),
+                is_readwrite: false,
+                has_server_override: false,
+                has_local_override: false,
+            },
+            FlagValueAndInfoSummary {
+                package_name: String::from("com.android.aconfig.storage.test_2"),
+                flag_name: String::from("enabled_ro"),
+                value_type: StoredFlagType::ReadOnlyBoolean,
+                flag_value: String::from("true"),
+                is_readwrite: false,
+                has_server_override: false,
+                has_local_override: false,
+            },
+            FlagValueAndInfoSummary {
+                package_name: String::from("com.android.aconfig.storage.test_4"),
+                flag_name: String::from("enabled_fixed_ro"),
+                value_type: StoredFlagType::FixedReadOnlyBoolean,
+                flag_value: String::from("true"),
+                is_readwrite: false,
+                has_server_override: false,
+                has_local_override: false,
+            },
+            FlagValueAndInfoSummary {
+                package_name: String::from("com.android.aconfig.storage.test_4"),
+                flag_name: String::from("enabled_rw"),
+                value_type: StoredFlagType::ReadWriteBoolean,
+                flag_value: String::from("true"),
+                is_readwrite: true,
+                has_server_override: false,
+                has_local_override: false,
+            },
         ];
         assert_eq!(flags, expected);
     }
diff --git a/tools/aconfig/aconfig_storage_file/src/main.rs b/tools/aconfig/aconfig_storage_file/src/main.rs
index b686274..8b9e38d 100644
--- a/tools/aconfig/aconfig_storage_file/src/main.rs
+++ b/tools/aconfig/aconfig_storage_file/src/main.rs
@@ -17,8 +17,8 @@
 //! `aconfig-storage` is a debugging tool to parse storage files
 
 use aconfig_storage_file::{
-    list_flags, read_file_to_bytes, AconfigStorageError, FlagInfoList, FlagTable, FlagValueList,
-    PackageTable, StorageFileType,
+    list_flags, list_flags_with_info, read_file_to_bytes, AconfigStorageError, FlagInfoList,
+    FlagTable, FlagValueList, PackageTable, StorageFileType,
 };
 
 use clap::{builder::ArgAction, Arg, Command};
@@ -45,7 +45,10 @@
                         .action(ArgAction::Set),
                 )
                 .arg(Arg::new("flag-map").long("flag-map").required(true).action(ArgAction::Set))
-                .arg(Arg::new("flag-val").long("flag-val").required(true).action(ArgAction::Set)),
+                .arg(Arg::new("flag-val").long("flag-val").required(true).action(ArgAction::Set))
+                .arg(
+                    Arg::new("flag-info").long("flag-info").required(false).action(ArgAction::Set),
+                ),
         )
 }
 
@@ -87,9 +90,27 @@
             let package_map = sub_matches.get_one::<String>("package-map").unwrap();
             let flag_map = sub_matches.get_one::<String>("flag-map").unwrap();
             let flag_val = sub_matches.get_one::<String>("flag-val").unwrap();
-            let flags = list_flags(package_map, flag_map, flag_val)?;
-            for (package_name, flag_name, flag_type, flag_value) in flags.iter() {
-                println!("{} {} {:?} {}", package_name, flag_name, flag_type, flag_value);
+            let flag_info = sub_matches.get_one::<String>("flag-info");
+            match flag_info {
+                Some(info_file) => {
+                    let flags = list_flags_with_info(package_map, flag_map, flag_val, info_file)?;
+                    for flag in flags.iter() {
+                        println!(
+                            "{} {} {} {:?} IsReadWrite: {}, HasServerOverride: {}, HasLocalOverride: {}",
+                            flag.package_name, flag.flag_name, flag.flag_value, flag.value_type,
+                            flag.is_readwrite, flag.has_server_override, flag.has_local_override,
+                        );
+                    }
+                }
+                None => {
+                    let flags = list_flags(package_map, flag_map, flag_val)?;
+                    for flag in flags.iter() {
+                        println!(
+                            "{} {} {} {:?}",
+                            flag.package_name, flag.flag_name, flag.flag_value, flag.value_type,
+                        );
+                    }
+                }
             }
         }
         _ => unreachable!(),
diff --git a/tools/aconfig/aconfig_storage_file/tests/Android.bp b/tools/aconfig/aconfig_storage_file/tests/Android.bp
new file mode 100644
index 0000000..26b7800
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_file/tests/Android.bp
@@ -0,0 +1,23 @@
+
+cc_test {
+    name: "aconfig_storage_file.test.cpp",
+    team: "trendy_team_android_core_experiments",
+    srcs: [
+        "storage_file_test.cpp",
+    ],
+    static_libs: [
+        "libgmock",
+        "libaconfig_storage_file_cc",
+        "libbase",
+    ],
+    data: [
+        "package.map",
+        "flag.map",
+        "flag.val",
+        "flag.info",
+    ],
+    test_suites: [
+        "device-tests",
+        "general-tests",
+    ],
+}
diff --git a/tools/aconfig/aconfig_storage_file/tests/flag.info b/tools/aconfig/aconfig_storage_file/tests/flag.info
new file mode 100644
index 0000000..6223edf
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_file/tests/flag.info
Binary files differ
diff --git a/tools/aconfig/aconfig_storage_file/tests/flag.map b/tools/aconfig/aconfig_storage_file/tests/flag.map
new file mode 100644
index 0000000..e868f53
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_file/tests/flag.map
Binary files differ
diff --git a/tools/aconfig/aconfig_storage_file/tests/flag.val b/tools/aconfig/aconfig_storage_file/tests/flag.val
new file mode 100644
index 0000000..ed203d4
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_file/tests/flag.val
Binary files differ
diff --git a/tools/aconfig/aconfig_storage_file/tests/package.map b/tools/aconfig/aconfig_storage_file/tests/package.map
new file mode 100644
index 0000000..6c46a03
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_file/tests/package.map
Binary files differ
diff --git a/tools/aconfig/aconfig_storage_file/tests/storage_file_test.cpp b/tools/aconfig/aconfig_storage_file/tests/storage_file_test.cpp
new file mode 100644
index 0000000..eccbca5
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_file/tests/storage_file_test.cpp
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <string>
+#include <vector>
+#include <android-base/file.h>
+#include <android-base/result.h>
+#include <gtest/gtest.h>
+#include "aconfig_storage/aconfig_storage_file.hpp"
+
+using namespace android::base;
+using namespace aconfig_storage;
+
+
+void verify_flag(const FlagValueAndInfoSummary& flag,
+                 const std::string& package_name,
+                 const std::string& flag_name,
+                 const std::string& flag_val,
+                 const std::string& value_type,
+                 bool is_readwrite,
+                 bool has_server_override,
+                 bool has_local_override) {
+  ASSERT_EQ(flag.package_name, package_name);
+  ASSERT_EQ(flag.flag_name, flag_name);
+  ASSERT_EQ(flag.flag_value, flag_val);
+  ASSERT_EQ(flag.value_type, value_type);
+  ASSERT_EQ(flag.is_readwrite, is_readwrite);
+  ASSERT_EQ(flag.has_server_override, has_server_override);
+  ASSERT_EQ(flag.has_local_override, has_local_override);
+}
+
+TEST(AconfigStorageFileTest, test_list_flag_with_info) {
+  auto const test_dir = GetExecutableDirectory();
+  auto const package_map = test_dir + "/package.map";
+  auto const flag_map = test_dir + "/flag.map";
+  auto const flag_val = test_dir + "/flag.val";
+  auto const flag_info = test_dir + "/flag.info";
+  auto flag_list_result = aconfig_storage::list_flags_with_info(
+      package_map, flag_map, flag_val, flag_info);
+  ASSERT_TRUE(flag_list_result.ok());
+
+  auto const& flag_list = *flag_list_result;
+  ASSERT_EQ(flag_list.size(), 8);
+  verify_flag(flag_list[0], "com.android.aconfig.storage.test_1", "disabled_rw",
+              "false", "ReadWriteBoolean", true, false, false);
+  verify_flag(flag_list[1], "com.android.aconfig.storage.test_1", "enabled_ro",
+              "true", "ReadOnlyBoolean", false, false, false);
+  verify_flag(flag_list[2], "com.android.aconfig.storage.test_1", "enabled_rw",
+              "true", "ReadWriteBoolean", true, false, false);
+  verify_flag(flag_list[3], "com.android.aconfig.storage.test_2", "disabled_rw",
+              "false", "ReadWriteBoolean", true, false, false);
+  verify_flag(flag_list[4], "com.android.aconfig.storage.test_2", "enabled_fixed_ro",
+              "true", "FixedReadOnlyBoolean", false, false, false);
+  verify_flag(flag_list[5], "com.android.aconfig.storage.test_2", "enabled_ro",
+              "true", "ReadOnlyBoolean", false, false, false);
+  verify_flag(flag_list[6], "com.android.aconfig.storage.test_4", "enabled_fixed_ro",
+              "true", "FixedReadOnlyBoolean", false, false, false);
+  verify_flag(flag_list[7], "com.android.aconfig.storage.test_4", "enabled_rw",
+              "true", "ReadWriteBoolean", true, false, false);
+}
diff --git a/tools/aconfig/aconfig_storage_read_api/Android.bp b/tools/aconfig/aconfig_storage_read_api/Android.bp
index c89107f..217104b 100644
--- a/tools/aconfig/aconfig_storage_read_api/Android.bp
+++ b/tools/aconfig/aconfig_storage_read_api/Android.bp
@@ -109,3 +109,11 @@
     },
     double_loadable: true,
 }
+
+cc_defaults {
+    name: "aconfig_lib_cc_static_link.defaults",
+    shared_libs: [
+        "libaconfig_storage_read_api_cc",
+        "liblog",
+    ],
+}
diff --git a/tools/aconfig/aconfig_storage_read_api/src/lib.rs b/tools/aconfig/aconfig_storage_read_api/src/lib.rs
index e65dcfb..e419206 100644
--- a/tools/aconfig/aconfig_storage_read_api/src/lib.rs
+++ b/tools/aconfig/aconfig_storage_read_api/src/lib.rs
@@ -504,7 +504,7 @@
         let pb_file_path = pb_file.path().display().to_string();
         let flag_info_file =
             unsafe { get_mapped_file(&pb_file_path, "mockup", StorageFileType::FlagInfo).unwrap() };
-        let is_rw: Vec<bool> = vec![true, false, true, false, false, false, false, false];
+        let is_rw: Vec<bool> = vec![true, false, true, true, false, false, false, true];
         for (offset, expected_value) in is_rw.into_iter().enumerate() {
             let attribute =
                 get_flag_attribute(&flag_info_file, FlagValueType::Boolean, offset as u32).unwrap();
diff --git a/tools/aconfig/aconfig_storage_read_api/tests/flag.info b/tools/aconfig/aconfig_storage_read_api/tests/flag.info
index 820d839..6223edf 100644
--- a/tools/aconfig/aconfig_storage_read_api/tests/flag.info
+++ b/tools/aconfig/aconfig_storage_read_api/tests/flag.info
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 b499c1c..cfd128d 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
@@ -232,7 +232,7 @@
   ASSERT_TRUE(mapped_file.ok());
 
   auto expected_value = std::vector<bool>{
-    true, false, true, false, false, false, false, false};
+    true, false, true, true, false, false, false, true};
   for (int index = 0; index < 8; ++index) {
     auto attribute = api::get_flag_attribute(*mapped_file, api::FlagValueType::Boolean, index);
     ASSERT_TRUE(attribute.ok());
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 ce9c018..ecba573 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
@@ -188,7 +188,7 @@
         // The safety here is ensured as the test process will not write to temp storage file
         let flag_info_file =
             unsafe { get_mapped_file(&pb_file_path, "mockup", StorageFileType::FlagInfo).unwrap() };
-        let is_rw: Vec<bool> = vec![true, false, true, false, false, false, false, false];
+        let is_rw: Vec<bool> = vec![true, false, true, true, false, false, false, true];
         for (offset, expected_value) in is_rw.into_iter().enumerate() {
             let attribute =
                 get_flag_attribute(&flag_info_file, FlagValueType::Boolean, offset as u32).unwrap();
diff --git a/tools/aconfig/aflags/src/aconfig_storage_source.rs b/tools/aconfig/aflags/src/aconfig_storage_source.rs
index a3ca221..c21c542 100644
--- a/tools/aconfig/aflags/src/aconfig_storage_source.rs
+++ b/tools/aconfig/aflags/src/aconfig_storage_source.rs
@@ -27,13 +27,13 @@
             let container =
                 file_info.container.ok_or(anyhow!("storage file is missing container"))?;
 
-            for (package, name, _flag_type, val) in
+            for listed_flag in
                 aconfig_storage_file::list_flags(&package_map, &flag_map, &flag_val)?
             {
                 result.push(Flag {
-                    name: name.to_string(),
-                    package: package.to_string(),
-                    value: FlagValue::try_from(val.to_string().as_str())?,
+                    name: listed_flag.flag_name,
+                    package: listed_flag.package_name,
+                    value: FlagValue::try_from(listed_flag.flag_value.as_str())?,
                     container: container.to_string(),
 
                     // TODO(b/324436145): delete namespace field once DeviceConfig isn't in CLI.
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 62c9cbb..2f76b2a 100644
--- a/tools/check-flagged-apis/src/com/android/checkflaggedapis/CheckFlaggedApisTest.kt
+++ b/tools/check-flagged-apis/src/com/android/checkflaggedapis/CheckFlaggedApisTest.kt
@@ -31,8 +31,12 @@
       // Signature format: 2.0
       package android {
         @FlaggedApi("android.flag.foo") public final class Clazz {
-          ctor public 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();
+          method @FlaggedApi("android.flag.foo") public boolean setData(int, int[][], @NonNull android.util.Utility<T, U>);
+          method @FlaggedApi("android.flag.foo") public boolean setVariableData(int, android.util.Atom...);
+          method @FlaggedApi("android.flag.foo") public boolean innerClassArg(android.Clazz.Builder);
         }
         @FlaggedApi("android.flag.bar") public static class Clazz.Builder {
         }
@@ -47,6 +51,10 @@
         <class name="android/Clazz" since="1">
           <method name="&lt;init>()V"/>
           <field name="FOO"/>
+          <method name="getErrorCode()I"/>
+          <method name="setData(I[[ILandroid/util/Utility;)Z"/>
+          <method name="setVariableData(I[Landroid/util/Atom;)Z"/>
+          <method name="innerClassArg(Landroid/Clazz${"$"}Builder;)"/>
         </class>
         <class name="android/Clazz${"$"}Builder" since="2">
         </class>
@@ -87,9 +95,20 @@
   fun testParseApiSignature() {
     val expected =
         setOf(
-            Pair(Symbol("android.Clazz"), Flag("android.flag.foo")),
-            Pair(Symbol("android.Clazz.FOO"), Flag("android.flag.foo")),
-            Pair(Symbol("android.Clazz.Builder"), Flag("android.flag.bar")),
+            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/setData(I[[ILandroid/util/Utility;)"),
+                Flag("android.flag.foo")),
+            Pair(
+                Symbol("android/Clazz/setVariableData(I[Landroid/util/Atom;)"),
+                Flag("android.flag.foo")),
+            Pair(
+                Symbol("android/Clazz/innerClassArg(Landroid/Clazz/Builder;)"),
+                Flag("android.flag.foo")),
+            Pair(Symbol("android/Clazz/Builder"), Flag("android.flag.bar")),
         )
     val actual = parseApiSignature("in-memory", API_SIGNATURE.byteInputStream())
     assertEquals(expected, actual)
@@ -107,9 +126,14 @@
   fun testParseApiVersions() {
     val expected: Set<Symbol> =
         setOf(
-            Symbol("android.Clazz"),
-            Symbol("android.Clazz.FOO"),
-            Symbol("android.Clazz.Builder"),
+            Symbol("android/Clazz"),
+            Symbol("android/Clazz/Clazz()"),
+            Symbol("android/Clazz/FOO"),
+            Symbol("android/Clazz/getErrorCode()"),
+            Symbol("android/Clazz/setData(I[[ILandroid/util/Utility;)"),
+            Symbol("android/Clazz/setVariableData(I[Landroid/util/Atom;)"),
+            Symbol("android/Clazz/innerClassArg(Landroid/Clazz/Builder;)"),
+            Symbol("android/Clazz/Builder"),
         )
     val actual = parseApiVersions(API_VERSIONS.byteInputStream())
     assertEquals(expected, actual)
@@ -130,10 +154,23 @@
   fun testFindErrorsDisabledFlaggedApiIsPresent() {
     val expected =
         setOf<ApiError>(
-            DisabledFlaggedApiIsPresentError(Symbol("android.Clazz"), Flag("android.flag.foo")),
-            DisabledFlaggedApiIsPresentError(Symbol("android.Clazz.FOO"), Flag("android.flag.foo")),
+            DisabledFlaggedApiIsPresentError(Symbol("android/Clazz"), Flag("android.flag.foo")),
             DisabledFlaggedApiIsPresentError(
-                Symbol("android.Clazz.Builder"), Flag("android.flag.bar")),
+                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/setData(I[[ILandroid/util/Utility;)"),
+                Flag("android.flag.foo")),
+            DisabledFlaggedApiIsPresentError(
+                Symbol("android/Clazz/setVariableData(I[Landroid/util/Atom;)"),
+                Flag("android.flag.foo")),
+            DisabledFlaggedApiIsPresentError(
+                Symbol("android/Clazz/innerClassArg(Landroid/Clazz/Builder;)"),
+                Flag("android.flag.foo")),
+            DisabledFlaggedApiIsPresentError(
+                Symbol("android/Clazz/Builder"), Flag("android.flag.bar")),
         )
     val actual =
         findErrors(
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 918a5d9..e8b1b65 100644
--- a/tools/check-flagged-apis/src/com/android/checkflaggedapis/Main.kt
+++ b/tools/check-flagged-apis/src/com/android/checkflaggedapis/Main.kt
@@ -22,6 +22,7 @@
 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
@@ -40,21 +41,29 @@
  * a Java symbol slightly differently. To keep things consistent, all parsed APIs are converted to
  * Symbols.
  *
- * All parts of the fully qualified name of the Symbol are separated by a dot, e.g.:
+ * Symbols are encoded using the format similar to the one described in section 4.3.2 of the JVM
+ * spec [1], that is, "package.class.inner-class.method(int, int[], android.util.Clazz)" is
+ * represented as
  * <pre>
- *   package.class.inner-class.field
- * </pre>
+ *   package.class.inner-class.method(II[Landroid/util/Clazz;)
+ * <pre>
+ *
+ * Where possible, the format has been simplified (to make translation of the
+ * various input formats easier): for instance, only / is used as delimiter (#
+ * and $ are never used).
+ *
+ * 1. https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.3.2
  */
 @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 {
       var sanitizedName = name
       for (ch in FORBIDDEN_CHARS) {
-        sanitizedName = sanitizedName.replace(ch, '.')
+        sanitizedName = sanitizedName.replace(ch, '/')
       }
       return Symbol(sanitizedName)
     }
@@ -169,7 +178,6 @@
 }
 
 internal fun parseApiSignature(path: String, input: InputStream): Set<Pair<Symbol, Flag>> {
-  // TODO(334870672): add support for metods
   val output = mutableSetOf<Pair<Symbol, Flag>>()
   val visitor =
       object : BaseItemVisitor() {
@@ -187,6 +195,21 @@
           }
         }
 
+        override fun visitMethod(method: MethodItem) {
+          getFlagOrNull(method)?.let { flag ->
+            val name = buildString {
+              append(method.containingClass().qualifiedName())
+              append(".")
+              append(method.name())
+              append("(")
+              method.parameters().joinTo(this, separator = "") { it.type().internalName() }
+              append(")")
+            }
+            val symbol = Symbol.create(name)
+            output.add(Pair(symbol, flag))
+          }
+        }
+
         private fun getFlagOrNull(item: Item): Flag? {
           return item.modifiers
               .findAnnotation("android.annotation.FlaggedApi")
@@ -223,7 +246,7 @@
         requireNotNull(cls.getAttribute("name")) {
           "Bad XML: <class> element without name attribute"
         }
-    output.add(Symbol.create(className))
+    output.add(Symbol.create(className.replace("/", ".")))
   }
 
   val fields = document.getElementsByTagName("field")
@@ -235,9 +258,33 @@
           "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, _) = 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/lunchable b/tools/lunchable
new file mode 100755
index 0000000..fce2c27
--- /dev/null
+++ b/tools/lunchable
@@ -0,0 +1,72 @@
+#!/bin/bash
+
+# TODO: Currently only checks trunk_staging. Should check trunk_staging first,
+#       then use the product-specfic releases. Only applies to -c though.
+
+function Help() {
+cat <<@EOF@
+Usage: lunchable [options]
+
+Lists products that have no functioning lunch combo.
+
+options:
+-c    prints all failing lunch combos for all targets;
+-w    why? Prints the error message after each failed lunch combo. Only
+      works with -c
+
+@EOF@
+}
+
+complete=0
+why=0
+while getopts "cwh" option; do
+  case $option in
+    c)
+      complete=1;;
+    w)
+      why=1;;
+    h)
+      Help
+      exit;;
+  esac
+done
+
+# Getting all named products can fail if we haven't lunched anything
+source $(pwd)/build/envsetup.sh &> /dev/null
+all_named_products=( $(get_build_var all_named_products 2> /dev/null) )
+if [[ $? -ne 0 ]]; then
+  echo "get_build_var all_named_products failed. Lunch something first?" >&2
+  exit 1
+fi
+total_products=${#all_named_products[@]}
+current_product=0
+
+for product in "${all_named_products[@]}"; do
+  (( current_product += 1 ))
+  single_pass=0
+  printf " Checking ${current_product}/${total_products} \r" >&2
+  for release in trunk_staging; do
+    for variant in eng user userdebug; do
+      lunchcombo="${product}-${release}-${variant}"
+      lunch_error="$(lunch $lunchcombo 2>&1 > /dev/null)"
+      if [[ $? -ne 0 ]]; then
+        # Lunch failed
+        if [[ $complete -eq 1 ]]; then
+          echo -e "${product} : ${lunchcombo}"
+          if [[ $why -eq 1 ]]; then
+            echo -e "$(sed 's/^/    /g' <<<$lunch_error)"
+          fi
+        fi
+      elif [[ $complete -ne 1 ]]; then
+        single_pass=1
+        break # skip variant
+      fi
+    done
+    if [[ $single_pass -eq 1 ]]; then
+      break # skip release
+    fi
+  done
+  if [[ $complete -eq 0 ]] && [[ $single_pass -eq 0 ]]; then
+    echo "${product}"
+  fi
+done
diff --git a/tools/releasetools/create_brick_ota.py b/tools/releasetools/create_brick_ota.py
index 9e040a5..bf50f71 100644
--- a/tools/releasetools/create_brick_ota.py
+++ b/tools/releasetools/create_brick_ota.py
@@ -45,10 +45,10 @@
   partitions_to_wipe = PARTITIONS_TO_WIPE
   if extra_wipe_partitions is not None:
     partitions_to_wipe = PARTITIONS_TO_WIPE + extra_wipe_partitions.split(",")
-    ota_metadata = ["ota-type=BRICK", "post-timestamp=9999999999",
-                    "pre-device=" + product_name]
-    if serialno is not None:
-        ota_metadata.append("serialno=" + serialno)
+  ota_metadata = ["ota-type=BRICK", "post-timestamp=9999999999",
+                  "pre-device=" + product_name]
+  if serialno is not None:
+      ota_metadata.append("serialno=" + serialno)
   # recovery requiers product name to be a | separated list
   product_name = product_name.replace(",", "|")
   with zipfile.ZipFile(output_path, "w") as zfp:
diff --git a/tools/whichgit b/tools/whichgit
index 8cf84f5..55c8c6f 100755
--- a/tools/whichgit
+++ b/tools/whichgit
@@ -1,6 +1,7 @@
 #!/usr/bin/env python3
 
 import argparse
+import itertools
 import os
 import subprocess
 import sys
@@ -10,15 +11,34 @@
                         check=True, capture_output=True, text=True).stdout.strip()
 
 
+def get_all_modules():
+  product_out = subprocess.run(["build/soong/soong_ui.bash", "--dumpvar-mode", "--abs", "PRODUCT_OUT"],
+                                check=True, capture_output=True, text=True).stdout.strip()
+  result = subprocess.run(["cat", product_out + "/all_modules.txt"], check=True, capture_output=True, text=True)
+  return result.stdout.strip().split("\n")
+
+
+def batched(iterable, n):
+  # introduced in itertools 3.12, could delete once that's universally available
+  if n < 1:
+    raise ValueError('n must be at least one')
+  it = iter(iterable)
+  while batch := tuple(itertools.islice(it, n)):
+    yield batch
+
+
 def get_sources(modules):
-  result = subprocess.run(["./prebuilts/build-tools/linux-x86/bin/ninja", "-f",
-                           "out/combined-" + os.environ["TARGET_PRODUCT"] + ".ninja",
-                           "-t", "inputs", "-d", ] + modules,
-                          stderr=subprocess.STDOUT, stdout=subprocess.PIPE, check=False, text=True)
-  if result.returncode != 0:
-    sys.stderr.write(result.stdout)
-    sys.exit(1)
-  return set([f for f in result.stdout.split("\n") if not f.startswith("out/")])
+  sources = set()
+  for module_group in batched(modules, 40_000):
+    result = subprocess.run(["./prebuilts/build-tools/linux-x86/bin/ninja", "-f",
+                            "out/combined-" + os.environ["TARGET_PRODUCT"] + ".ninja",
+                            "-t", "inputs", "-d", ] + list(module_group),
+                            stderr=subprocess.STDOUT, stdout=subprocess.PIPE, check=False, text=True)
+    if result.returncode != 0:
+      sys.stderr.write(result.stdout)
+      sys.exit(1)
+    sources.update(set([f for f in result.stdout.split("\n") if not f.startswith("out/")]))
+  return sources
 
 
 def m_nothing():
@@ -57,13 +77,13 @@
   # Argument parsing
   ap = argparse.ArgumentParser(description="List the required git projects for the given modules")
   ap.add_argument("--products", nargs="*",
-                  help="The TARGET_PRODUCT to check. If not provided just uses whatever has"
-                        + " already been built")
+                  help="One or more TARGET_PRODUCT to check, or \"*\" for all. If not provided"
+                        + "just uses whatever has already been built")
   ap.add_argument("--variants", nargs="*",
                   help="The TARGET_BUILD_VARIANTS to check. If not provided just uses whatever has"
                         + " already been built, or eng if --products is supplied")
   ap.add_argument("--modules", nargs="*",
-                  help="The build modules to check, or droid if not supplied")
+                  help="The build modules to check, or \"*\" for all, or droid if not supplied")
   ap.add_argument("--why", nargs="*",
                   help="Also print the input files used in these projects, or \"*\" for all")
   ap.add_argument("--unused", help="List the unused git projects for the given modules rather than"
@@ -72,22 +92,33 @@
 
   modules = args.modules if args.modules else ["droid"]
 
+  match args.products:
+    case ["*"]:
+      products = get_build_var("all_named_products").split(" ")
+    case _:
+      products = args.products
+
   # Get the list of sources for all of the requested build combos
-  if not args.products and not args.variants:
+  if not products and not args.variants:
+    m_nothing()
+    if args.modules == ["*"]:
+      modules = get_all_modules()
     sources = get_sources(modules)
   else:
-    if not args.products:
+    if not products:
       sys.stderr.write("Error: --products must be supplied if --variants is supplied")
       sys.exit(1)
     sources = set()
     build_num = 1
-    for product in args.products:
+    for product in products:
       os.environ["TARGET_PRODUCT"] = product
       variants = args.variants if args.variants else ["user", "userdebug", "eng"]
       for variant in variants:
-        sys.stderr.write(f"Analyzing build {build_num} of {len(args.products)*len(variants)}\r")
+        sys.stderr.write(f"Analyzing build {build_num} of {len(products)*len(variants)}\r")
         os.environ["TARGET_BUILD_VARIANT"] = variant
         m_nothing()
+        if args.modules == ["*"]:
+          modules = get_all_modules()
         sources.update(get_sources(modules))
         build_num += 1
     sys.stderr.write("\n\n")