Merge "Link shared libs to per test module folder in host-unit-tests.zip" into main
diff --git a/CleanSpec.mk b/CleanSpec.mk
index f562279..8c30883 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -791,6 +791,9 @@
 $(call add-clean-step, rm -f $(PRODUCT_OUT)/dexpreopt_config/dexpreopt.config)
 $(call add-clean-step, rm -f $(PRODUCT_OUT)/dexpreopt_config/dexpreopt_soong.config)
 
+# Clear out Soong .intermediates directory regarding removal of hashed subdir
+$(call add-clean-step, rm -rf $(OUT_DIR)/soong/.intermediates)
+
 # ************************************************
 # NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
 # ************************************************
diff --git a/common/math.mk b/common/math.mk
index ecee474..829ceb5 100644
--- a/common/math.mk
+++ b/common/math.mk
@@ -315,8 +315,9 @@
 $(call math-expect,(call numbers_greater_or_equal_to,0,0 2 1 3),0 2 1 3)
 $(call math-expect,(call numbers_greater_or_equal_to,1,0 2 1 3 2),2 1 3 2)
 
-_INT_LIMIT_WORDS := $(foreach a,x x,$(foreach b,x x x x x x x x x x x x x x x x,\
-  $(foreach c,x x x x x x x x x x x x x x x x,x x x x x x x x x x x x x x x x)))
+# 10,001 = 10 ** 4 + 1, contains 10,001 x's, so 1 more than 10,000 (future) API level
+_INT_LIMIT_WORDS := x $(foreach a,0 1 2 3 4 5 6 7 8 9,$(foreach b,0 1 2 3 4 5 6 7 8 9,\
+  $(foreach c,0 1 2 3 4 5 6 7 8 9,x x x x x x x x x x)))
 
 define _int_encode
 $(if $(filter $(words x $(_INT_LIMIT_WORDS)),$(words $(wordlist 1,$(1),x $(_INT_LIMIT_WORDS)))),\
diff --git a/core/Makefile b/core/Makefile
index 61b4cb6..b76a4f8 100644
--- a/core/Makefile
+++ b/core/Makefile
@@ -1052,6 +1052,35 @@
 endif
 endif
 
+
+# -----------------------------------------------------------------
+# dtbo image
+ifdef BOARD_PREBUILT_DTBOIMAGE
+INSTALLED_DTBOIMAGE_TARGET := $(PRODUCT_OUT)/dtbo.img
+
+ifeq ($(BOARD_AVB_ENABLE),true)
+$(INSTALLED_DTBOIMAGE_TARGET): $(BOARD_PREBUILT_DTBOIMAGE) $(AVBTOOL) $(BOARD_AVB_DTBO_KEY_PATH)
+	cp $(BOARD_PREBUILT_DTBOIMAGE) $@
+	chmod +w $@
+	$(AVBTOOL) add_hash_footer \
+	    --image $@ \
+	    $(call get-partition-size-argument,$(BOARD_DTBOIMG_PARTITION_SIZE)) \
+	    --partition_name dtbo $(INTERNAL_AVB_DTBO_SIGNING_ARGS) \
+	    $(BOARD_AVB_DTBO_ADD_HASH_FOOTER_ARGS)
+
+$(call declare-1p-container,$(INSTALLED_DTBOIMAGE_TARGET),)
+$(call declare-container-license-deps,$(INSTALLED_DTBOIMAGE_TARGET),$(BOARD_PREBUILT_DTBOIMAGE),$(PRODUCT_OUT)/:/)
+
+UNMOUNTED_NOTICE_VENDOR_DEPS+= $(INSTALLED_DTBOIMAGE_TARGET)
+else
+$(INSTALLED_DTBOIMAGE_TARGET): $(BOARD_PREBUILT_DTBOIMAGE)
+	cp $(BOARD_PREBUILT_DTBOIMAGE) $@
+endif
+
+endif # BOARD_PREBUILT_DTBOIMAGE
+
+# -----------------------------------------------------------------
+
 # -----------------------------------------------------------------
 # the ramdisk
 INSTALLED_FILES_OUTSIDE_IMAGES := $(filter-out $(TARGET_RAMDISK_OUT)/%, $(INSTALLED_FILES_OUTSIDE_IMAGES))
@@ -1075,6 +1104,16 @@
 
 BUILT_RAMDISK_TARGET := $(PRODUCT_OUT)/ramdisk.img
 
+ifeq ($(BOARD_RAMDISK_USE_LZ4),true)
+# -l enables the legacy format used by the Linux kernel
+COMPRESSION_COMMAND_DEPS := $(LZ4)
+COMPRESSION_COMMAND := $(LZ4) -l -12 --favor-decSpeed
+RAMDISK_EXT := .lz4
+else
+COMPRESSION_COMMAND_DEPS := $(GZIP)
+COMPRESSION_COMMAND := $(GZIP)
+RAMDISK_EXT := .gz
+endif
 
 ifneq ($(BOARD_KERNEL_MODULES_16K),)
 
@@ -1153,6 +1192,33 @@
 
 endif
 
+# -----------------------------------------------------------------
+# 16KB dtbo image
+ifdef BOARD_PREBUILT_DTBOIMAGE_16KB
+INSTALLED_DTBOIMAGE_16KB_TARGET := $(PRODUCT_OUT)/dtbo_16k.img
+
+ifeq ($(BOARD_AVB_ENABLE),true)
+$(INSTALLED_DTBOIMAGE_16KB_TARGET): $(BOARD_PREBUILT_DTBOIMAGE_16KB) $(AVBTOOL) $(BOARD_AVB_DTBO_KEY_PATH)
+	cp $(BOARD_PREBUILT_DTBOIMAGE_16KB) $@
+	chmod +w $@
+	$(AVBTOOL) add_hash_footer \
+	    --image $@ \
+	    $(call get-partition-size-argument,$(BOARD_DTBOIMG_PARTITION_SIZE)) \
+	    --partition_name dtbo $(INTERNAL_AVB_DTBO_SIGNING_ARGS) \
+	    $(BOARD_AVB_DTBO_ADD_HASH_FOOTER_ARGS)
+
+$(call declare-1p-container,$(INSTALLED_DTBOIMAGE_16KB_TARGET),)
+$(call declare-container-license-deps,$(INSTALLED_DTBOIMAGE_16KB_TARGET),$(BOARD_PREBUILT_DTBOIMAGE_16KB),$(PRODUCT_OUT)/:/)
+
+UNMOUNTED_NOTICE_VENDOR_DEPS += $(INSTALLED_DTBOIMAGE_16KB_TARGET)
+else
+$(INSTALLED_DTBOIMAGE_16KB_TARGET): $(BOARD_PREBUILT_DTBOIMAGE_16KB)
+	cp $(BOARD_PREBUILT_DTBOIMAGE_16KB) $@
+endif
+
+endif # BOARD_PREBUILT_DTBOIMAGE_16KB
+
+
 ifneq ($(BOARD_KERNEL_PATH_16K),)
 BUILT_KERNEL_16K_TARGET := $(PRODUCT_OUT)/kernel_16k
 
@@ -1175,16 +1241,26 @@
 .PHONY: bootimage_16k
 
 BUILT_BOOT_OTA_PACKAGE_16K := $(PRODUCT_OUT)/boot_ota_16k.zip
-$(BUILT_BOOT_OTA_PACKAGE_16K): $(OTA_FROM_RAW_IMG) $(BUILT_BOOTIMAGE_16K_TARGET) $(INSTALLED_BOOTIMAGE_TARGET) $(DEFAULT_SYSTEM_DEV_CERTIFICATE).pk8
+$(BUILT_BOOT_OTA_PACKAGE_16K):  $(OTA_FROM_RAW_IMG) \
+                                $(BUILT_BOOTIMAGE_16K_TARGET) \
+                                $(INSTALLED_BOOTIMAGE_TARGET) \
+                                $(DEFAULT_SYSTEM_DEV_CERTIFICATE).pk8 \
+                                $(INSTALLED_DTBOIMAGE_16KB_TARGET) \
+                                $(INSTALLED_DTBOIMAGE_TARGET)
 	$(OTA_FROM_RAW_IMG) --package_key $(DEFAULT_SYSTEM_DEV_CERTIFICATE) \
                       --max_timestamp `cat $(BUILD_DATETIME_FILE)` \
                       --path $(HOST_OUT) \
-                      --partition_name boot \
+                      --partition_name $(if $(and $(INSTALLED_DTBOIMAGE_TARGET),\
+                          $(INSTALLED_DTBOIMAGE_16KB_TARGET)),\
+                        boot$(comma)dtbo,\
+                        boot) \
                       --output $@ \
                       $(if $(BOARD_16K_OTA_USE_INCREMENTAL),\
                         $(INSTALLED_BOOTIMAGE_TARGET):$(BUILT_BOOTIMAGE_16K_TARGET),\
                         $(BUILT_BOOTIMAGE_16K_TARGET)\
-                      )
+                      )\
+                      $(if $(and $(INSTALLED_DTBOIMAGE_TARGET),$(INSTALLED_DTBOIMAGE_16KB_TARGET)),\
+                        $(INSTALLED_DTBOIMAGE_16KB_TARGET))
 
 boototapackage_16k: $(BUILT_BOOT_OTA_PACKAGE_16K)
 .PHONY: boototapackage_16k
@@ -1192,17 +1268,6 @@
 endif
 
 
-ifeq ($(BOARD_RAMDISK_USE_LZ4),true)
-# -l enables the legacy format used by the Linux kernel
-COMPRESSION_COMMAND_DEPS := $(LZ4)
-COMPRESSION_COMMAND := $(LZ4) -l -12 --favor-decSpeed
-RAMDISK_EXT := .lz4
-else
-COMPRESSION_COMMAND_DEPS := $(GZIP)
-COMPRESSION_COMMAND := $(GZIP)
-RAMDISK_EXT := .gz
-endif
-
 # This file contains /dev nodes description added to the generic ramdisk
 RAMDISK_NODE_LIST := $(PRODUCT_OUT)/ramdisk_node_list
 
@@ -1437,16 +1502,26 @@
 
 ifneq ($(BOARD_KERNEL_PATH_16K),)
 BUILT_BOOT_OTA_PACKAGE_4K := $(PRODUCT_OUT)/boot_ota_4k.zip
-$(BUILT_BOOT_OTA_PACKAGE_4K): $(OTA_FROM_RAW_IMG) $(INSTALLED_BOOTIMAGE_TARGET) $(BUILT_BOOTIMAGE_16K_TARGET) $(DEFAULT_SYSTEM_DEV_CERTIFICATE).pk8
+$(BUILT_BOOT_OTA_PACKAGE_4K): $(OTA_FROM_RAW_IMG) \
+                              $(INSTALLED_BOOTIMAGE_TARGET) \
+                              $(BUILT_BOOTIMAGE_16K_TARGET) \
+                              $(DEFAULT_SYSTEM_DEV_CERTIFICATE).pk8 \
+                              $(INSTALLED_DTBOIMAGE_TARGET) \
+                              $(INSTALLED_DTBOIMAGE_16KB_TARGET)
 	$(OTA_FROM_RAW_IMG) --package_key $(DEFAULT_SYSTEM_DEV_CERTIFICATE) \
                       --max_timestamp `cat $(BUILD_DATETIME_FILE)` \
                       --path $(HOST_OUT) \
-                      --partition_name boot \
+                      --partition_name $(if $(and $(INSTALLED_DTBOIMAGE_TARGET),\
+                          $(INSTALLED_DTBOIMAGE_16KB_TARGET)),\
+                        boot$(comma)dtbo,\
+                        boot) \
                       --output $@ \
                       $(if $(BOARD_16K_OTA_USE_INCREMENTAL),\
                         $(BUILT_BOOTIMAGE_16K_TARGET):$(INSTALLED_BOOTIMAGE_TARGET),\
                         $(INSTALLED_BOOTIMAGE_TARGET)\
-                      )
+                      )\
+                      $(if $(and $(INSTALLED_DTBOIMAGE_TARGET),$(INSTALLED_DTBOIMAGE_16KB_TARGET)),\
+                        $(INSTALLED_DTBOIMAGE_TARGET))
 
 boototapackage_4k: $(BUILT_BOOT_OTA_PACKAGE_4K)
 .PHONY: boototapackage_4k
@@ -3482,6 +3557,24 @@
 endef
 
 $(eval $(call write-partition-file-list,$(systemimage_intermediates)/file_list.txt,$(TARGET_OUT),$(FULL_SYSTEMIMAGE_DEPS)))
+
+ifneq ($(PRODUCT_SOONG_DEFINED_SYSTEM_IMAGE),)
+file_list_diff := $(HOST_OUT_EXECUTABLES)/file_list_diff$(HOST_EXECUTABLE_SUFFIX)
+system_file_diff_timestamp := $(systemimage_intermediates)/file_diff.timestamp
+
+$(system_file_diff_timestamp): \
+	    $(systemimage_intermediates)/file_list.txt \
+	    $(ALL_MODULES.$(PRODUCT_SOONG_DEFINED_SYSTEM_IMAGE).FILESYSTEM_FILELIST) \
+	    $(ALL_MODULES.system_image_diff_allowlist.INSTALLED) \
+	    $(file_list_diff)
+	$(file_list_diff) $(systemimage_intermediates)/file_list.txt \
+	  $(ALL_MODULES.$(PRODUCT_SOONG_DEFINED_SYSTEM_IMAGE).FILESYSTEM_FILELIST) \
+	  $(ALL_MODULES.system_image_diff_allowlist.INSTALLED) $(PRODUCT_SOONG_DEFINED_SYSTEM_IMAGE)
+	touch $@
+
+$(BUILT_SYSTEMIMAGE): $(system_file_diff_timestamp)
+endif
+
 # Used by soong sandwich to request the staging dir be built
 $(systemimage_intermediates)/staging_dir.stamp: $(filter $(TARGET_OUT)/%,$(FULL_SYSTEMIMAGE_DEPS))
 	touch $@
@@ -4319,33 +4412,6 @@
 $(eval $(call copy-one-file,$(BOARD_PREBUILT_SYSTEM_DLKMIMAGE),$(INSTALLED_SYSTEM_DLKMIMAGE_TARGET)))
 endif
 
-# -----------------------------------------------------------------
-# dtbo image
-ifdef BOARD_PREBUILT_DTBOIMAGE
-INSTALLED_DTBOIMAGE_TARGET := $(PRODUCT_OUT)/dtbo.img
-
-ifeq ($(BOARD_AVB_ENABLE),true)
-$(INSTALLED_DTBOIMAGE_TARGET): $(BOARD_PREBUILT_DTBOIMAGE) $(AVBTOOL) $(BOARD_AVB_DTBO_KEY_PATH)
-	cp $(BOARD_PREBUILT_DTBOIMAGE) $@
-	chmod +w $@
-	$(AVBTOOL) add_hash_footer \
-	    --image $@ \
-	    $(call get-partition-size-argument,$(BOARD_DTBOIMG_PARTITION_SIZE)) \
-	    --partition_name dtbo $(INTERNAL_AVB_DTBO_SIGNING_ARGS) \
-	    $(BOARD_AVB_DTBO_ADD_HASH_FOOTER_ARGS)
-
-$(call declare-1p-container,$(INSTALLED_DTBOIMAGE_TARGET),)
-$(call declare-container-license-deps,$(INSTALLED_DTBOIMAGE_TARGET),$(BOARD_PREBUILT_DTBOIMAGE),$(PRODUCT_OUT)/:/)
-
-UNMOUNTED_NOTICE_VENDOR_DEPS+= $(INSTALLED_DTBOIMAGE_TARGET)
-else
-$(INSTALLED_DTBOIMAGE_TARGET): $(BOARD_PREBUILT_DTBOIMAGE)
-	cp $(BOARD_PREBUILT_DTBOIMAGE) $@
-endif
-
-endif # BOARD_PREBUILT_DTBOIMAGE
-
-# -----------------------------------------------------------------
 # Protected VM firmware image
 ifeq ($(BOARD_USES_PVMFWIMAGE),true)
 
@@ -6109,12 +6175,6 @@
     $(BUILT_TARGET_FILES_DIR): $(TARGET_OUT_OEM)/$(OSRELEASED_DIRECTORY)/product_version
     $(BUILT_TARGET_FILES_DIR): $(TARGET_OUT_ETC)/$(OSRELEASED_DIRECTORY)/system_version
   endif
-
-  # Not checking in board_config.mk, since AB_OTA_PARTITIONS may be updated in Android.mk (e.g. to
-  # additionally include radio or bootloader partitions).
-  ifeq ($(AB_OTA_PARTITIONS),)
-    $(error AB_OTA_PARTITIONS must be defined when using AB_OTA_UPDATER)
-  endif
 endif
 
 ifneq ($(AB_OTA_PARTITIONS),)
diff --git a/core/android_soong_config_vars.mk b/core/android_soong_config_vars.mk
index 6d4bed2..f2ff286 100644
--- a/core/android_soong_config_vars.mk
+++ b/core/android_soong_config_vars.mk
@@ -27,7 +27,7 @@
 # Add variables to the namespace below:
 
 $(call add_soong_config_var,ANDROID,BOARD_USES_ODMIMAGE)
-$(call add_soong_config_var,ANDROID,BOARD_USES_RECOVERY_AS_BOOT)
+$(call soong_config_set_bool,ANDROID,BOARD_USES_RECOVERY_AS_BOOT,$(BOARD_USES_RECOVERY_AS_BOOT))
 $(call add_soong_config_var,ANDROID,CHECK_DEV_TYPE_VIOLATIONS)
 $(call add_soong_config_var,ANDROID,PLATFORM_SEPOLICY_VERSION)
 $(call add_soong_config_var,ANDROID,PLATFORM_SEPOLICY_COMPAT_VERSIONS)
diff --git a/core/base_rules.mk b/core/base_rules.mk
index 0d72903..a9d6494 100644
--- a/core/base_rules.mk
+++ b/core/base_rules.mk
@@ -717,12 +717,14 @@
 endif
 
 ifeq ($(EXCLUDE_MCTS),true)
+ifeq (,$(filter $(LOCAL_MODULE),$(mcts_whitelist)))
   ifneq (,$(test_config))
     ifneq (,$(filter mcts-%,$(LOCAL_COMPATIBILITY_SUITE)))
       LOCAL_COMPATIBILITY_SUITE := $(filter-out cts,$(LOCAL_COMPATIBILITY_SUITE))
     endif
   endif
 endif
+endif
 
 ifneq (true,$(LOCAL_UNINSTALLABLE_MODULE))
 
@@ -1057,6 +1059,11 @@
       $(ALL_MODULES.$(my_register_name).ACONFIG_FILES) $(LOCAL_ACONFIG_FILES)
 endif
 
+ifdef LOCAL_FILESYSTEM_FILELIST
+  ALL_MODULES.$(my_register_name).FILESYSTEM_FILELIST := \
+      $(ALL_MODULES.$(my_register_name).FILESYSTEM_FILELIST) $(LOCAL_FILESYSTEM_FILELIST)
+endif
+
 ifndef LOCAL_SOONG_MODULE_INFO_JSON
   ALL_MAKE_MODULE_INFO_JSON_MODULES += $(my_register_name)
   ALL_MODULES.$(my_register_name).SHARED_LIBS := \
diff --git a/core/board_config.mk b/core/board_config.mk
index e184601..d3f0493 100644
--- a/core/board_config.mk
+++ b/core/board_config.mk
@@ -923,7 +923,9 @@
 ###########################################
 # Ensure consistency among TARGET_RECOVERY_UPDATER_LIBS, AB_OTA_UPDATER, and PRODUCT_OTA_FORCE_NON_AB_PACKAGE.
 TARGET_RECOVERY_UPDATER_LIBS ?=
-AB_OTA_UPDATER ?=
+ifeq ($(AB_OTA_UPDATER),)
+AB_OTA_UPDATER := true
+endif
 .KATI_READONLY := TARGET_RECOVERY_UPDATER_LIBS AB_OTA_UPDATER
 
 # Ensure that if PRODUCT_OTA_FORCE_NON_AB_PACKAGE == true, then AB_OTA_UPDATER must be true
diff --git a/core/clear_vars.mk b/core/clear_vars.mk
index 6192690..fed19e6 100644
--- a/core/clear_vars.mk
+++ b/core/clear_vars.mk
@@ -87,6 +87,7 @@
 LOCAL_EXTRA_FULL_TEST_CONFIGS:=
 LOCAL_EXTRACT_APK:=
 LOCAL_EXTRACT_DPI_APK:=
+LOCAL_FILESYSTEM_FILELIST:=
 LOCAL_FINDBUGS_FLAGS:=
 LOCAL_FORCE_STATIC_EXECUTABLE:=
 LOCAL_FULL_CLASSES_JACOCO_JAR:=
diff --git a/core/config.mk b/core/config.mk
index 99337fc..ae65944 100644
--- a/core/config.mk
+++ b/core/config.mk
@@ -839,9 +839,6 @@
   else
     min_systemsdk_version := $(PRODUCT_SHIPPING_API_LEVEL)
   endif
-  ifneq ($(call numbers_less_than,$(min_systemsdk_version),$(BOARD_SYSTEMSDK_VERSIONS)),)
-    $(error BOARD_SYSTEMSDK_VERSIONS ($(BOARD_SYSTEMSDK_VERSIONS)) must all be greater than or equal to BOARD_API_LEVEL, BOARD_SHIPPING_API_LEVEL or PRODUCT_SHIPPING_API_LEVEL ($(min_systemsdk_version)))
-  endif
   ifneq ($(call math_gt_or_eq,$(PRODUCT_SHIPPING_API_LEVEL),29),)
     ifneq ($(BOARD_OTA_FRAMEWORK_VBMETA_VERSION_OVERRIDE),)
       $(error When PRODUCT_SHIPPING_API_LEVEL >= 29, BOARD_OTA_FRAMEWORK_VBMETA_VERSION_OVERRIDE cannot be set)
diff --git a/core/definitions.mk b/core/definitions.mk
index 51def29..b30b159 100644
--- a/core/definitions.mk
+++ b/core/definitions.mk
@@ -2584,7 +2584,28 @@
         @$(call emit-line,$(wordlist 98001,98500,$(1)),$(2))
         @$(call emit-line,$(wordlist 98501,99000,$(1)),$(2))
         @$(call emit-line,$(wordlist 99001,99500,$(1)),$(2))
-        @$(if $(wordlist 99501,99502,$(1)),$(error dump-words-to-file: Too many words ($(words $(1)))))
+        @$(call emit-line,$(wordlist 99501,100000,$(1)),$(2))
+        @$(call emit-line,$(wordlist 100001,100500,$(1)),$(2))
+        @$(call emit-line,$(wordlist 100501,101000,$(1)),$(2))
+        @$(call emit-line,$(wordlist 101001,101500,$(1)),$(2))
+        @$(call emit-line,$(wordlist 101501,102000,$(1)),$(2))
+        @$(call emit-line,$(wordlist 102001,102500,$(1)),$(2))
+        @$(call emit-line,$(wordlist 102501,103000,$(1)),$(2))
+        @$(call emit-line,$(wordlist 103001,103500,$(1)),$(2))
+        @$(call emit-line,$(wordlist 103501,104000,$(1)),$(2))
+        @$(call emit-line,$(wordlist 104001,104500,$(1)),$(2))
+        @$(call emit-line,$(wordlist 104501,105000,$(1)),$(2))
+        @$(call emit-line,$(wordlist 105001,105500,$(1)),$(2))
+        @$(call emit-line,$(wordlist 105501,106000,$(1)),$(2))
+        @$(call emit-line,$(wordlist 106001,106500,$(1)),$(2))
+        @$(call emit-line,$(wordlist 106501,107000,$(1)),$(2))
+        @$(call emit-line,$(wordlist 107001,107500,$(1)),$(2))
+        @$(call emit-line,$(wordlist 107501,108000,$(1)),$(2))
+        @$(call emit-line,$(wordlist 108001,108500,$(1)),$(2))
+        @$(call emit-line,$(wordlist 108501,109000,$(1)),$(2))
+        @$(call emit-line,$(wordlist 109001,109500,$(1)),$(2))
+        @$(call emit-line,$(wordlist 109501,110000,$(1)),$(2))
+        @$(if $(wordlist 110001,110002,$(1)),$(error dump-words-to-file: Too many words ($(words $(1)))))
 endef
 # Return jar arguments to compress files in a given directory
 # $(1): directory
diff --git a/core/dex_preopt_odex_install.mk b/core/dex_preopt_odex_install.mk
index 08e2da3..e7086b7 100644
--- a/core/dex_preopt_odex_install.mk
+++ b/core/dex_preopt_odex_install.mk
@@ -504,8 +504,8 @@
   _system_other := $(strip $(if $(strip $(BOARD_USES_SYSTEM_OTHER_ODEX)), \
     $(if $(strip $(SANITIZE_LITE)),, \
       $(if $(filter $(_dexname),$(PRODUCT_DEXPREOPT_SPEED_APPS))$(filter $(_dexname),$(PRODUCT_SYSTEM_SERVER_APPS)),, \
-        $(if $(strip $(foreach myfilter,$(SYSTEM_OTHER_ODEX_FILTER),$(filter system/$(myfilter),$(_dexlocation)))), \
-          system_other/)))))
+        $(if $(strip $(foreach myfilter,$(SYSTEM_OTHER_ODEX_FILTER),$(filter system/$(myfilter),$(_dexlocation))$(filter $(myfilter),$(_dexlocation)))), \
+            system_other/)))))
   # _dexdir has a trailing /
   _dexdir := $(_system_other)$(dir $(_dexlocation))
   my_dexpreopt_zip_contents := $(sort \
diff --git a/core/main.mk b/core/main.mk
index 143c13b..8d73793 100644
--- a/core/main.mk
+++ b/core/main.mk
@@ -1930,13 +1930,13 @@
 	  echo '$(_lib_stem).a,$(_module_path),$(_soong_module_type),,,,,$(_built_file),$(_static_libs),$(_whole_static_libs),$(_is_static_lib)' >> $@; \
 	)
 
-# Create metadata for SBOM generation in Soong
-.PHONY: make-metadata
-make-metadata: \
-    $(SOONG_OUT_DIR)/metadata/$(TARGET_PRODUCT)/make-metadata.csv \
-    $(SOONG_OUT_DIR)/metadata/$(TARGET_PRODUCT)/make-modules.csv
+# Create metadata for compliance support in Soong
+.PHONY: make-compliance-metadata
+make-compliance-metadata: \
+    $(SOONG_OUT_DIR)/compliance-metadata/$(TARGET_PRODUCT)/make-metadata.csv \
+    $(SOONG_OUT_DIR)/compliance-metadata/$(TARGET_PRODUCT)/make-modules.csv
 
-$(SOONG_OUT_DIR)/metadata/$(TARGET_PRODUCT)/make-metadata.csv:
+$(SOONG_OUT_DIR)/compliance-metadata/$(TARGET_PRODUCT)/make-metadata.csv:
 	rm -f $@
 	echo 'installed_file,module_path,is_soong_module,is_prebuilt_make_module,product_copy_files,kernel_module_copy_files,is_platform_generated,static_libs,whole_static_libs,license_text' >> $@
 	$(foreach f,$(installed_files),\
@@ -1967,7 +1967,7 @@
 	  echo '$(_build_output_path),$(_module_path),$(_is_soong_module),$(_is_prebuilt_make_module),$(_product_copy_files),$(_kernel_module_copy_files),$(_is_platform_generated),$(_static_libs),$(_whole_static_libs),$(_license_text)' >> $@; \
 	)
 
-$(SOONG_OUT_DIR)/metadata/$(TARGET_PRODUCT)/make-modules.csv:
+$(SOONG_OUT_DIR)/compliance-metadata/$(TARGET_PRODUCT)/make-modules.csv:
 	rm -f $@
 	echo 'name,module_path,module_class,module_type,static_libs,whole_static_libs,built_files,installed_files' >> $@
 	$(foreach m,$(ALL_MODULES), \
@@ -1985,7 +1985,7 @@
 	  ) \
 	)
 
-$(SOONG_OUT_DIR)/metadata/$(TARGET_PRODUCT)/installed_files.stamp: $(installed_files)
+$(SOONG_OUT_DIR)/compliance-metadata/$(TARGET_PRODUCT)/installed_files.stamp: $(installed_files)
 	touch $@
 
 # (TODO: b/272358583 find another way of always rebuilding sbom.spdx)
diff --git a/core/packaging/flags.mk b/core/packaging/flags.mk
index a96ea8f..a77956b 100644
--- a/core/packaging/flags.mk
+++ b/core/packaging/flags.mk
@@ -27,9 +27,27 @@
 # Create a summary file of build flags for each partition
 # $(1): built aconfig flags file (out)
 # $(2): installed aconfig flags file (out)
-# $(3): input aconfig files for the partition (in)
+# $(3): the partition (in)
+# $(4): input aconfig files for the partition (in)
 define generate-partition-aconfig-flag-file
 $(eval $(strip $(1)): PRIVATE_OUT := $(strip $(1)))
+$(eval $(strip $(1)): PRIVATE_IN := $(strip $(4)))
+$(strip $(1)): $(ACONFIG) $(strip $(4))
+	mkdir -p $$(dir $$(PRIVATE_OUT))
+	$$(if $$(PRIVATE_IN), \
+		$$(ACONFIG) dump --dedup --format protobuf --out $$(PRIVATE_OUT) \
+			--filter container:$$(strip $(3)) $$(addprefix --cache ,$$(PRIVATE_IN)), \
+		echo -n > $$(PRIVATE_OUT) \
+	)
+$(call copy-one-file, $(1), $(2))
+endef
+
+# Create a summary file of build flags for each partition
+# $(1): built aconfig flags file (out)
+# $(2): installed aconfig flags file (out)
+# $(3): input aconfig files for the partition (in)
+define generate-global-aconfig-flag-file
+$(eval $(strip $(1)): PRIVATE_OUT := $(strip $(1)))
 $(eval $(strip $(1)): PRIVATE_IN := $(strip $(3)))
 $(strip $(1)): $(ACONFIG) $(strip $(3))
 	mkdir -p $$(dir $$(PRIVATE_OUT))
@@ -47,6 +65,7 @@
 	$(eval $(call generate-partition-aconfig-flag-file, \
 				$(TARGET_OUT_FLAGS)/$(partition)/aconfig_flags.pb, \
 				$(aconfig_flag_summaries_protobuf.$(partition)), \
+				$(partition), \
 				$(sort $(foreach m,$(call register-names-for-partition, $(partition)), \
 					$(ALL_MODULES.$(m).ACONFIG_FILES) \
 				)), \
@@ -61,7 +80,7 @@
 
 .PHONY: device_aconfig_declarations
 device_aconfig_declarations: $(PRODUCT_OUT)/device_aconfig_declarations.pb
-$(eval $(call generate-partition-aconfig-flag-file, \
+$(eval $(call generate-global-aconfig-flag-file, \
 			$(TARGET_OUT_FLAGS)/device_aconfig_declarations.pb, \
 			$(PRODUCT_OUT)/device_aconfig_declarations.pb, \
 			$(sort $(required_aconfig_flags_files)) \
@@ -121,9 +140,7 @@
 				$(aconfig_storage_package_map.$(partition)), \
 				$(aconfig_storage_flag_map.$(partition)), \
 				$(aconfig_storage_flag_val.$(partition)), \
-				$(sort $(foreach m,$(call register-names-for-partition, $(partition)), \
-					$(ALL_MODULES.$(m).ACONFIG_FILES) \
-				)), \
+				$(aconfig_flag_summaries_protobuf.$(partition)), \
 				$(partition), \
 	)) \
 )
diff --git a/core/product.mk b/core/product.mk
index 5ceccbf..ad80ee4 100644
--- a/core/product.mk
+++ b/core/product.mk
@@ -492,6 +492,9 @@
 # by this flag.
 _product_single_value_vars += PRODUCT_NOT_DEBUGGABLE_IN_USERDEBUG
 
+# If set, build would generate system image from Soong-defined module.
+_product_single_value_vars += PRODUCT_SOONG_DEFINED_SYSTEM_IMAGE
+
 .KATI_READONLY := _product_single_value_vars _product_list_vars
 _product_var_list :=$= $(_product_single_value_vars) $(_product_list_vars)
 
diff --git a/core/product_config.mk b/core/product_config.mk
index f939690..cc2fea9 100644
--- a/core/product_config.mk
+++ b/core/product_config.mk
@@ -405,6 +405,10 @@
   TARGET_AAPT_CHARACTERISTICS := $(PRODUCT_CHARACTERISTICS)
 endif
 
+ifndef PRODUCT_SHIPPING_API_LEVEL
+  PRODUCT_SHIPPING_API_LEVEL := 10000
+endif
+
 ifdef PRODUCT_DEFAULT_DEV_CERTIFICATE
   ifneq (1,$(words $(PRODUCT_DEFAULT_DEV_CERTIFICATE)))
     $(error PRODUCT_DEFAULT_DEV_CERTIFICATE='$(PRODUCT_DEFAULT_DEV_CERTIFICATE)', \
@@ -557,11 +561,26 @@
 endif
 
 # This table maps sdk version 35 to vendor api level 202404 and assumes yearly
-# release for the same month.
+# release for the same month. If 10000 API level or more is used, which usually
+# represents 'current' or 'future' API levels, several zeros are added to
+# preserve ordering. Specifically API level 10,000 is converted to 10,000,000
+# which importantly is greater than 202404 = 202,404. This convention will break
+# in 100,000 CE, which is the closest multiple of 10 that doesn't break earlier
+# than 10,000 as an API level breaks.
 define sdk-to-vendor-api-level
-  $(if $(call math_lt_or_eq,$(1),34),$(1),20$(call int_subtract,$(1),11)04)
+$(if $(call math_lt_or_eq,$(1),34),$(1),$(if $(call math_lt,$(1),10000),20$(call int_subtract,$(1),11)04,$(1)000))
 endef
 
+ifneq ($(call sdk-to-vendor-api-level,34),34)
+$(error sdk-to-vendor-api-level is broken for pre-Trunk-Stable SDKs)
+endif
+ifneq ($(call sdk-to-vendor-api-level,35),202404)
+$(error sdk-to-vendor-api-level is broken for post-Trunk-Stable SDKs)
+endif
+ifneq ($(call sdk-to-vendor-api-level,10000),10000000)
+$(error sdk-to-vendor-api-level is broken for current $(call sdk-to-vendor-api-level,10000))
+endif
+
 ifdef PRODUCT_SHIPPING_VENDOR_API_LEVEL
 # Follow the version that is set manually.
   VSR_VENDOR_API_LEVEL := $(PRODUCT_SHIPPING_VENDOR_API_LEVEL)
diff --git a/core/soong_config.mk b/core/soong_config.mk
index 068034b..12b4135 100644
--- a/core/soong_config.mk
+++ b/core/soong_config.mk
@@ -109,6 +109,8 @@
 $(call add_json_list, AAPTPrebuiltDPI,                   $(PRODUCT_AAPT_PREBUILT_DPI))
 
 $(call add_json_str,  DefaultAppCertificate,             $(PRODUCT_DEFAULT_DEV_CERTIFICATE))
+$(call add_json_list, ExtraOtaKeys,                      $(PRODUCT_EXTRA_OTA_KEYS))
+$(call add_json_list, ExtraOtaRecoveryKeys,              $(PRODUCT_EXTRA_RECOVERY_KEYS))
 $(call add_json_str,  MainlineSepolicyDevCertificates,   $(MAINLINE_SEPOLICY_DEV_CERTIFICATES))
 
 $(call add_json_str,  AppsDefaultVersionName,            $(APPS_DEFAULT_VERSION_NAME))
diff --git a/core/soong_extra_config.mk b/core/soong_extra_config.mk
index c2b4775..e4432d2 100644
--- a/core/soong_extra_config.mk
+++ b/core/soong_extra_config.mk
@@ -91,6 +91,8 @@
 $(call add_json_str, VendorSecurityPatch,       $(VENDOR_SECURITY_PATCH))
 $(call add_json_str, VendorImageFileSystemType, $(BOARD_VENDORIMAGE_FILE_SYSTEM_TYPE))
 
+$(call add_json_list, BuildVersionTags, $(BUILD_VERSION_TAGS))
+
 $(call json_end)
 
 $(shell mkdir -p $(dir $(SOONG_EXTRA_VARIABLES)))
diff --git a/core/sysprop_config.mk b/core/sysprop_config.mk
index 6e3da72..f9b9d1c 100644
--- a/core/sysprop_config.mk
+++ b/core/sysprop_config.mk
@@ -50,6 +50,12 @@
 ADDITIONAL_PRODUCT_PROPERTIES += ro.product.build.16k_page.enabled=false
 endif
 
+ifeq ($(TARGET_BOOTS_16K),true)
+ADDITIONAL_PRODUCT_PROPERTIES += ro.product.page_size=16384
+else
+ADDITIONAL_PRODUCT_PROPERTIES += ro.product.page_size=4096
+endif
+
 # Enable core platform API violation warnings on userdebug and eng builds.
 ifneq ($(TARGET_BUILD_VARIANT),user)
 ADDITIONAL_SYSTEM_PROPERTIES += persist.debug.dalvik.vm.core_platform_api_policy=just-warn
diff --git a/core/tasks/meta-lic.mk b/core/tasks/meta-lic.mk
index 3520a32..85357eb 100644
--- a/core/tasks/meta-lic.mk
+++ b/core/tasks/meta-lic.mk
@@ -188,3 +188,6 @@
 
 # Moved here from packages/services/Car/Android.mk
 $(eval $(call declare-1p-copy-files,packages/services/Car,))
+
+# Moved here from hardware/libhardware_legacy/Android.mk
+$(eval $(call declare-1p-copy-files,hardware/libhardware_legacy,))
diff --git a/target/product/generic_system.mk b/target/product/generic_system.mk
index 4793657..0a09eb1 100644
--- a/target/product/generic_system.mk
+++ b/target/product/generic_system.mk
@@ -120,6 +120,10 @@
     init.zygote64.rc \
     init.zygote64_32.rc
 
+# Support Credential Manager
+PRODUCT_PACKAGES += \
+    android.software.credentials.prebuilt.xml
+
 # Enable dynamic partition size
 PRODUCT_USE_DYNAMIC_PARTITION_SIZE := true
 
diff --git a/target/product/go_defaults.mk b/target/product/go_defaults.mk
index 7fbc09d..4627fde 100644
--- a/target/product/go_defaults.mk
+++ b/target/product/go_defaults.mk
@@ -17,10 +17,7 @@
 # Inherit common Android Go defaults.
 $(call inherit-product, build/make/target/product/go_defaults_common.mk)
 
-PRODUCT_RELEASE_CONFIG_MAPS += $(wildcard vendor/google_shared/build/release/go_devices/release_config_map.mk)
-
-# TODO (b/342265627): Remove v/g/r once all the flags have been moved to v/g_s/b/r
-PRODUCT_RELEASE_CONFIG_MAPS += $(wildcard vendor/google/release/go_devices/release_config_map.mk)
+PRODUCT_RELEASE_CONFIG_MAPS += $(wildcard vendor/google_shared/build/release/go_devices/release_config_map.textproto)
 
 # Add the system properties.
 TARGET_SYSTEM_PROP += \
diff --git a/target/product/handheld_system.mk b/target/product/handheld_system.mk
index fb55f56..3f3bd01 100644
--- a/target/product/handheld_system.mk
+++ b/target/product/handheld_system.mk
@@ -33,6 +33,7 @@
 $(call inherit-product-if-exists, frameworks/webview/chromium/chromium.mk)
 
 PRODUCT_PACKAGES += \
+    android.software.window_magnification.prebuilt.xml \
     BasicDreams \
     BlockedNumberProvider \
     BluetoothMidiService \
@@ -88,9 +89,6 @@
 
 PRODUCT_PACKAGES += framework-audio_effects.xml
 
-PRODUCT_COPY_FILES += \
-    frameworks/native/data/etc/android.software.window_magnification.xml:$(TARGET_COPY_OUT_SYSTEM)/etc/permissions/android.software.window_magnification.xml \
-
 PRODUCT_VENDOR_PROPERTIES += \
     ro.carrier?=unknown \
     ro.config.notification_sound?=OnTheHunt.ogg \
diff --git a/target/product/media_system.mk b/target/product/media_system.mk
index 503c9b3..af3857e 100644
--- a/target/product/media_system.mk
+++ b/target/product/media_system.mk
@@ -21,6 +21,7 @@
 $(call inherit-product, $(SRC_TARGET_DIR)/product/base_system.mk)
 
 PRODUCT_PACKAGES += \
+    android.software.webview.prebuilt.xml \
     com.android.future.usb.accessory \
     com.android.mediadrm.signer \
     com.android.media.remotedisplay \
@@ -39,12 +40,9 @@
 PRODUCT_HOST_PACKAGES += \
     fsck.f2fs \
 
-PRODUCT_COPY_FILES += \
-    frameworks/native/data/etc/android.software.webview.xml:system/etc/permissions/android.software.webview.xml
-
 ifneq (REL,$(PLATFORM_VERSION_CODENAME))
-PRODUCT_COPY_FILES += \
-    frameworks/native/data/etc/android.software.preview_sdk.xml:system/etc/permissions/android.software.preview_sdk.xml
+PRODUCT_PACKAGES += \
+    android.software.preview_sdk.prebuilt.xml
 endif
 
 # The order here is the same order they end up on the classpath, so it matters.
diff --git a/target/product/security/Android.bp b/target/product/security/Android.bp
index 1e26d59..0d7b35e 100644
--- a/target/product/security/Android.bp
+++ b/target/product/security/Android.bp
@@ -25,3 +25,15 @@
     sub_dir: "security/fsverity",
     filename_from_src: true,
 }
+
+// otacerts: A keystore with the authorized keys in it, which is used to verify
+// the authenticity of downloaded OTA packages.
+// This module zips files defined in PRODUCT_DEFAULT_DEV_CERTIFICATE and
+// PRODUCT_EXTRA_OTA_KEYS for system or PRODUCT_EXTRA_RECOVERY_KEYS for recovery
+// image
+otacerts_zip {
+    name: "otacerts",
+    recovery_available: true,
+    relative_install_path: "security",
+    filename: "otacerts.zip",
+}
diff --git a/target/product/security/Android.mk b/target/product/security/Android.mk
index 4bd8efc..91b272c 100644
--- a/target/product/security/Android.mk
+++ b/target/product/security/Android.mk
@@ -15,54 +15,3 @@
     include $(BUILD_PREBUILT)
   endif
 endif
-
-
-#######################################
-# otacerts: A keystore with the authorized keys in it, which is used to verify the authenticity of
-# downloaded OTA packages.
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := otacerts
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := build/soong/licenses/LICENSE
-LOCAL_MODULE_CLASS := ETC
-LOCAL_MODULE_STEM := otacerts.zip
-LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/security
-include $(BUILD_SYSTEM)/base_rules.mk
-
-extra_ota_keys := $(addsuffix .x509.pem,$(PRODUCT_EXTRA_OTA_KEYS))
-
-$(LOCAL_BUILT_MODULE): PRIVATE_CERT := $(DEFAULT_SYSTEM_DEV_CERTIFICATE).x509.pem
-$(LOCAL_BUILT_MODULE): PRIVATE_EXTRA_OTA_KEYS := $(extra_ota_keys)
-$(LOCAL_BUILT_MODULE): \
-	    $(SOONG_ZIP) \
-	    $(DEFAULT_SYSTEM_DEV_CERTIFICATE).x509.pem \
-	    $(extra_ota_keys)
-	$(SOONG_ZIP) -o $@ -j -symlinks=false \
-	    $(addprefix -f ,$(PRIVATE_CERT) $(PRIVATE_EXTRA_OTA_KEYS))
-
-
-#######################################
-# otacerts for recovery image.
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := otacerts.recovery
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := build/soong/licenses/LICENSE
-LOCAL_MODULE_CLASS := ETC
-LOCAL_MODULE_STEM := otacerts.zip
-LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/system/etc/security
-include $(BUILD_SYSTEM)/base_rules.mk
-
-extra_recovery_keys := $(addsuffix .x509.pem,$(PRODUCT_EXTRA_RECOVERY_KEYS))
-
-$(LOCAL_BUILT_MODULE): PRIVATE_CERT := $(DEFAULT_SYSTEM_DEV_CERTIFICATE).x509.pem
-$(LOCAL_BUILT_MODULE): PRIVATE_EXTRA_RECOVERY_KEYS := $(extra_recovery_keys)
-$(LOCAL_BUILT_MODULE): \
-	    $(SOONG_ZIP) \
-	    $(DEFAULT_SYSTEM_DEV_CERTIFICATE).x509.pem \
-	    $(extra_recovery_keys)
-	$(SOONG_ZIP) -o $@ -j -symlinks=false \
-	    $(addprefix -f ,$(PRIVATE_CERT) $(PRIVATE_EXTRA_RECOVERY_KEYS))
diff --git a/target/product/virtual_ab_ota/OWNERS b/target/product/virtual_ab_ota/OWNERS
new file mode 100644
index 0000000..8eb0686
--- /dev/null
+++ b/target/product/virtual_ab_ota/OWNERS
@@ -0,0 +1,4 @@
+zhangkelvin@google.com
+dvander@google.com
+akailash@google.com
+
diff --git a/target/product/virtual_ab_ota/compression.mk b/target/product/virtual_ab_ota/compression.mk
index c964860..dc1ee3e 100644
--- a/target/product/virtual_ab_ota/compression.mk
+++ b/target/product/virtual_ab_ota/compression.mk
@@ -28,4 +28,5 @@
 PRODUCT_VIRTUAL_AB_COMPRESSION := true
 PRODUCT_PACKAGES += \
     snapuserd.vendor_ramdisk \
-    snapuserd
+    snapuserd \
+    snapuserd.recovery
diff --git a/target/product/virtual_ab_ota/compression_retrofit.mk b/target/product/virtual_ab_ota/compression_retrofit.mk
index 118d3f2..6c29cba 100644
--- a/target/product/virtual_ab_ota/compression_retrofit.mk
+++ b/target/product/virtual_ab_ota/compression_retrofit.mk
@@ -24,4 +24,5 @@
 # as well.
 PRODUCT_PACKAGES += \
     snapuserd.ramdisk \
-    snapuserd
+    snapuserd \
+    snapuserd.recovery
diff --git a/teams/Android.bp b/teams/Android.bp
index fbf1344..a9699d2 100644
--- a/teams/Android.bp
+++ b/teams/Android.bp
@@ -4412,3 +4412,10 @@
     // go/trendy/manage/engineers/5350750192762880
     trendy_team_id: "5350750192762880",
 }
+
+team {
+    name: "trendy_team_android_telemetry_client_infra",
+
+    // go/trendy/manage/engineers/5403245077430272
+    trendy_team_id: "5403245077430272",
+}
diff --git a/tools/aconfig/TEST_MAPPING b/tools/aconfig/TEST_MAPPING
index 448d8cf..15e4187 100644
--- a/tools/aconfig/TEST_MAPPING
+++ b/tools/aconfig/TEST_MAPPING
@@ -98,6 +98,10 @@
     {
       // aconfig_storage file cpp integration tests
       "name": "aconfig_storage_file.test.cpp"
+    },
+    {
+      // aconfig_storage file java integration tests
+      "name": "aconfig_storage_file.test.java"
     }
   ],
   "postsubmit": [
diff --git a/tools/aconfig/aconfig/src/codegen/java.rs b/tools/aconfig/aconfig/src/codegen/java.rs
index 3360ddd..d3f074a 100644
--- a/tools/aconfig/aconfig/src/codegen/java.rs
+++ b/tools/aconfig/aconfig/src/codegen/java.rs
@@ -505,11 +505,11 @@
                 try {
                     Properties properties = DeviceConfig.getProperties("aconfig_test");
                     disabledRw =
-                        properties.getBoolean("com.android.aconfig.test.disabled_rw", false);
+                        properties.getBoolean(Flags.FLAG_DISABLED_RW, false);
                     disabledRwExported =
-                        properties.getBoolean("com.android.aconfig.test.disabled_rw_exported", false);
+                        properties.getBoolean(Flags.FLAG_DISABLED_RW_EXPORTED, false);
                     enabledRw =
-                        properties.getBoolean("com.android.aconfig.test.enabled_rw", true);
+                        properties.getBoolean(Flags.FLAG_ENABLED_RW, true);
                 } catch (NullPointerException e) {
                     throw new RuntimeException(
                         "Cannot read value from namespace aconfig_test "
@@ -527,7 +527,7 @@
                 try {
                     Properties properties = DeviceConfig.getProperties("other_namespace");
                     disabledRwInOtherNamespace =
-                        properties.getBoolean("com.android.aconfig.test.disabled_rw_in_other_namespace", false);
+                        properties.getBoolean(Flags.FLAG_DISABLED_RW_IN_OTHER_NAMESPACE, false);
                 } catch (NullPointerException e) {
                     throw new RuntimeException(
                         "Cannot read value from namespace other_namespace "
@@ -700,11 +700,11 @@
                 try {
                     Properties properties = DeviceConfig.getProperties("aconfig_test");
                     disabledRwExported =
-                        properties.getBoolean("com.android.aconfig.test.disabled_rw_exported", false);
+                        properties.getBoolean(Flags.FLAG_DISABLED_RW_EXPORTED, false);
                     enabledFixedRoExported =
-                        properties.getBoolean("com.android.aconfig.test.enabled_fixed_ro_exported", false);
+                        properties.getBoolean(Flags.FLAG_ENABLED_FIXED_RO_EXPORTED, false);
                     enabledRoExported =
-                        properties.getBoolean("com.android.aconfig.test.enabled_ro_exported", false);
+                        properties.getBoolean(Flags.FLAG_ENABLED_RO_EXPORTED, false);
                 } catch (NullPointerException e) {
                     throw new RuntimeException(
                         "Cannot read value from namespace aconfig_test "
diff --git a/tools/aconfig/aconfig/templates/FeatureFlagsImpl.java.template b/tools/aconfig/aconfig/templates/FeatureFlagsImpl.java.template
index 6235e69..63c4f2d 100644
--- a/tools/aconfig/aconfig/templates/FeatureFlagsImpl.java.template
+++ b/tools/aconfig/aconfig/templates/FeatureFlagsImpl.java.template
@@ -27,7 +27,7 @@
 {{ -for flag in namespace_with_flags.flags }}
 {{ -if flag.is_read_write }}
             {flag.method_name} =
-                properties.getBoolean("{flag.device_config_flag}", {flag.default_value});
+                properties.getBoolean(Flags.FLAG_{flag.flag_name_constant_suffix}, {flag.default_value});
 {{ -endif }}
 {{ -endfor }}
         } catch (NullPointerException e) \{
diff --git a/tools/aconfig/aconfig_device_paths/Android.bp b/tools/aconfig/aconfig_device_paths/Android.bp
index 2c771e0..2d943de 100644
--- a/tools/aconfig/aconfig_device_paths/Android.bp
+++ b/tools/aconfig/aconfig_device_paths/Android.bp
@@ -42,10 +42,11 @@
     srcs: ["src/DevicePathsTemplate.java"],
     out: ["DevicePaths.java"],
     tool_files: ["partition_aconfig_flags_paths.txt"],
-    cmd: "sed -e '/TEMPLATE/{r$(location partition_aconfig_flags_paths.txt)' -e 'd}' $(in) > $(out)"
+    cmd: "sed -e '/TEMPLATE/{r$(location partition_aconfig_flags_paths.txt)' -e 'd}' $(in) > $(out)",
 }
 
 java_library {
     name: "aconfig_device_paths_java",
     srcs: [":libaconfig_java_device_paths_src"],
+    sdk_version: "core_current",
 }
diff --git a/tools/aconfig/aconfig_device_paths/src/DevicePathsTemplate.java b/tools/aconfig/aconfig_device_paths/src/DevicePathsTemplate.java
index f27b9bd..16355a3 100644
--- a/tools/aconfig/aconfig_device_paths/src/DevicePathsTemplate.java
+++ b/tools/aconfig/aconfig_device_paths/src/DevicePathsTemplate.java
@@ -36,7 +36,7 @@
      * Returns the list of all on-device aconfig protos paths.
      * @hide
      */
-    public List<String> parsedFlagsProtoPaths() {
+    public static List<String> parsedFlagsProtoPaths() {
         ArrayList<String> paths = new ArrayList(Arrays.asList(PATHS));
 
         File apexDirectory = new File(APEX_DIR);
diff --git a/tools/aconfig/aconfig_storage_file/Android.bp b/tools/aconfig/aconfig_storage_file/Android.bp
index e066e31..3859194 100644
--- a/tools/aconfig/aconfig_storage_file/Android.bp
+++ b/tools/aconfig/aconfig_storage_file/Android.bp
@@ -137,3 +137,12 @@
     min_sdk_version: "29",
     double_loadable: true,
 }
+
+// storage file parse api java cc_library
+java_library {
+    name: "aconfig_storage_file_java",
+    srcs: [
+        "srcs/**/*.java",
+    ],
+    sdk_version: "core_current",
+}
\ No newline at end of file
diff --git a/tools/ide_query/ide_query_proto/Android.bp b/tools/aconfig/aconfig_storage_file/srcs/android/aconfig/storage/AconfigStorageException.java
similarity index 62%
copy from tools/ide_query/ide_query_proto/Android.bp
copy to tools/aconfig/aconfig_storage_file/srcs/android/aconfig/storage/AconfigStorageException.java
index 70f15cd..86a75f2 100644
--- a/tools/ide_query/ide_query_proto/Android.bp
+++ b/tools/aconfig/aconfig_storage_file/srcs/android/aconfig/storage/AconfigStorageException.java
@@ -14,20 +14,14 @@
  * limitations under the License.
  */
 
-package {
-    default_applicable_licenses: ["Android-Apache-2.0"],
-}
+package android.aconfig.storage;
 
-cc_library_host_static {
-    name: "ide_query_proto",
-    srcs: [
-        "ide_query.proto",
-    ],
-    proto: {
-        export_proto_headers: true,
-        type: "full",
-        canonical_path_from_root: false,
-    },
-    compile_multilib: "64",
-    shared_libs: ["libprotobuf-cpp-full"],
+public class AconfigStorageException extends RuntimeException {
+    public AconfigStorageException(String msg) {
+        super(msg);
+    }
+
+    public AconfigStorageException(String msg, Throwable cause) {
+        super(msg, cause);
+    }
 }
diff --git a/tools/aconfig/aconfig_storage_file/srcs/android/aconfig/storage/ByteBufferReader.java b/tools/aconfig/aconfig_storage_file/srcs/android/aconfig/storage/ByteBufferReader.java
new file mode 100644
index 0000000..1c72364
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_file/srcs/android/aconfig/storage/ByteBufferReader.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.aconfig.storage;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.charset.StandardCharsets;
+
+public class ByteBufferReader {
+
+    private ByteBuffer mByteBuffer;
+
+    public ByteBufferReader(ByteBuffer byteBuffer) {
+        this.mByteBuffer = byteBuffer;
+        this.mByteBuffer.order(ByteOrder.LITTLE_ENDIAN);
+    }
+
+    public int readByte() {
+        return Byte.toUnsignedInt(mByteBuffer.get());
+    }
+
+    public int readShort() {
+        return Short.toUnsignedInt(mByteBuffer.getShort());
+    }
+
+    public int readInt() {
+        return this.mByteBuffer.getInt();
+    }
+
+    public String readString() {
+        int length = readInt();
+        byte[] bytes = new byte[length];
+        mByteBuffer.get(bytes, 0, length);
+        return new String(bytes, StandardCharsets.UTF_8);
+    }
+
+    public void position(int newPosition) {
+        mByteBuffer.position(newPosition);
+    }
+}
diff --git a/tools/aconfig/aconfig_storage_file/srcs/android/aconfig/storage/FileType.java b/tools/aconfig/aconfig_storage_file/srcs/android/aconfig/storage/FileType.java
new file mode 100644
index 0000000..b0b1b9b
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_file/srcs/android/aconfig/storage/FileType.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.aconfig.storage;
+
+public enum FileType {
+    PACKAGE_MAP(0),
+    FLAG_MAP(1),
+    FLAG_VAL(2),
+    FLAG_INFO(3);
+
+    public final int type;
+
+    FileType(int type) {
+        this.type = type;
+    }
+
+    public static FileType fromInt(int index) {
+        switch (index) {
+            case 0:
+                return PACKAGE_MAP;
+            case 1:
+                return FLAG_MAP;
+            case 2:
+                return FLAG_VAL;
+            case 3:
+                return FLAG_INFO;
+            default:
+                return null;
+        }
+    }
+}
diff --git a/tools/aconfig/aconfig_storage_file/srcs/android/aconfig/storage/FlagTable.java b/tools/aconfig/aconfig_storage_file/srcs/android/aconfig/storage/FlagTable.java
new file mode 100644
index 0000000..e85fdee
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_file/srcs/android/aconfig/storage/FlagTable.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.aconfig.storage;
+
+import java.nio.ByteBuffer;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+
+public class FlagTable {
+
+    private Header mHeader;
+    private Map<String, Node> mNodeMap;
+
+    public static FlagTable fromBytes(ByteBuffer bytes) {
+        FlagTable flagTable = new FlagTable();
+        ByteBufferReader reader = new ByteBufferReader(bytes);
+        Header header = Header.fromBytes(reader);
+        flagTable.mHeader = header;
+        flagTable.mNodeMap = new HashMap(TableUtils.getTableSize(header.mNumFlags));
+        reader.position(header.mNodeOffset);
+        for (int i = 0; i < header.mNumFlags; i++) {
+            Node node = Node.fromBytes(reader);
+            flagTable.mNodeMap.put(makeKey(node.mPackageId, node.mFlagName), node);
+        }
+        return flagTable;
+    }
+
+    public Node get(int packageId, String flagName) {
+        return mNodeMap.get(makeKey(packageId, flagName));
+    }
+
+    public Header getHeader() {
+        return mHeader;
+    }
+
+    private static String makeKey(int packageId, String flagName) {
+        StringBuilder ret = new StringBuilder();
+        return ret.append(packageId).append('/').append(flagName).toString();
+    }
+
+    public static class Header {
+
+        private int mVersion;
+        private String mContainer;
+        private FileType mFileType;
+        private int mFileSize;
+        private int mNumFlags;
+        private int mBucketOffset;
+        private int mNodeOffset;
+
+        public static Header fromBytes(ByteBufferReader reader) {
+            Header header = new Header();
+            header.mVersion = reader.readInt();
+            header.mContainer = reader.readString();
+            header.mFileType = FileType.fromInt(reader.readByte());
+            header.mFileSize = reader.readInt();
+            header.mNumFlags = reader.readInt();
+            header.mBucketOffset = reader.readInt();
+            header.mNodeOffset = reader.readInt();
+
+            if (header.mFileType != FileType.FLAG_MAP) {
+                throw new AconfigStorageException("binary file is not a flag map");
+            }
+
+            return header;
+        }
+
+        public int getVersion() {
+            return mVersion;
+        }
+
+        public String getContainer() {
+            return mContainer;
+        }
+
+        public FileType getFileType() {
+            return mFileType;
+        }
+
+        public int getFileSize() {
+            return mFileSize;
+        }
+
+        public int getNumFlags() {
+            return mNumFlags;
+        }
+
+        public int getBucketOffset() {
+            return mBucketOffset;
+        }
+
+        public int getNodeOffset() {
+            return mNodeOffset;
+        }
+    }
+
+    public static class Node {
+
+        private String mFlagName;
+        private FlagType mFlagType;
+        private int mPackageId;
+        private int mFlagIndex;
+        private int mNextOffset;
+
+        public static Node fromBytes(ByteBufferReader reader) {
+            Node node = new Node();
+            node.mPackageId = reader.readInt();
+            node.mFlagName = reader.readString();
+            node.mFlagType = FlagType.fromInt(reader.readShort());
+            node.mFlagIndex = reader.readShort();
+            node.mNextOffset = reader.readInt();
+            node.mNextOffset = node.mNextOffset == 0 ? -1 : node.mNextOffset;
+            return node;
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(mFlagName, mFlagType, mPackageId, mFlagIndex, mNextOffset);
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (this == obj) {
+                return true;
+            }
+
+            if (obj == null || !(obj instanceof Node)) {
+                return false;
+            }
+
+            Node other = (Node) obj;
+            return Objects.equals(mFlagName, other.mFlagName)
+                    && Objects.equals(mFlagType, other.mFlagType)
+                    && mPackageId == other.mPackageId
+                    && mFlagIndex == other.mFlagIndex
+                    && mNextOffset == other.mNextOffset;
+        }
+
+        public String getFlagName() {
+            return mFlagName;
+        }
+
+        public FlagType getFlagType() {
+            return mFlagType;
+        }
+
+        public int getPackageId() {
+            return mPackageId;
+        }
+
+        public int getFlagIndex() {
+            return mFlagIndex;
+        }
+
+        public int getNextOffset() {
+            return mNextOffset;
+        }
+    }
+}
diff --git a/tools/aconfig/aconfig_storage_file/srcs/android/aconfig/storage/FlagType.java b/tools/aconfig/aconfig_storage_file/srcs/android/aconfig/storage/FlagType.java
new file mode 100644
index 0000000..385e2d9
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_file/srcs/android/aconfig/storage/FlagType.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.aconfig.storage;
+
+public enum FlagType {
+    ReadWriteBoolean (0),
+    ReadOnlyBoolean(1),
+    FixedReadOnlyBoolean(2);
+
+    public final int type;
+
+    FlagType(int type) {
+        this.type = type;
+    }
+
+    public static FlagType fromInt(int index) {
+        switch (index) {
+            case 0:
+                return ReadWriteBoolean;
+            case 1:
+                return ReadOnlyBoolean;
+            case 2:
+                return FixedReadOnlyBoolean;
+            default:
+                return null;
+        }
+    }
+}
diff --git a/tools/aconfig/aconfig_storage_file/srcs/android/aconfig/storage/FlagValueList.java b/tools/aconfig/aconfig_storage_file/srcs/android/aconfig/storage/FlagValueList.java
new file mode 100644
index 0000000..0ddc147
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_file/srcs/android/aconfig/storage/FlagValueList.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.aconfig.storage;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
+
+public class FlagValueList {
+
+    private Header mHeader;
+    private List<Boolean> mList;
+
+    private int mSize;
+
+    public static FlagValueList fromBytes(ByteBuffer bytes) {
+        FlagValueList flagValueList = new FlagValueList();
+        ByteBufferReader reader = new ByteBufferReader(bytes);
+        Header header = Header.fromBytes(reader);
+        flagValueList.mHeader = header;
+        flagValueList.mList = new ArrayList(header.mNumFlags);
+        reader.position(header.mBooleanValueOffset);
+        for (int i = 0; i < header.mNumFlags; i++) {
+            boolean val = reader.readByte() == 1;
+            flagValueList.mList.add(val);
+        }
+        flagValueList.mSize = flagValueList.mList.size();
+        return flagValueList;
+    }
+
+    public boolean get(int index) {
+        return mList.get(index);
+    }
+
+    public Header getHeader() {
+        return mHeader;
+    }
+
+    public int size() {
+        return mSize;
+    }
+
+    public static class Header {
+
+        private int mVersion;
+        private String mContainer;
+        private FileType mFileType;
+        private int mFileSize;
+        private int mNumFlags;
+        private int mBooleanValueOffset;
+
+        public static Header fromBytes(ByteBufferReader reader) {
+            Header header = new Header();
+            header.mVersion = reader.readInt();
+            header.mContainer = reader.readString();
+            header.mFileType = FileType.fromInt(reader.readByte());
+            header.mFileSize = reader.readInt();
+            header.mNumFlags = reader.readInt();
+            header.mBooleanValueOffset = reader.readInt();
+
+            if (header.mFileType != FileType.FLAG_VAL) {
+                throw new AconfigStorageException("binary file is not a flag value file");
+            }
+
+            return header;
+        }
+
+        public int getVersion() {
+            return mVersion;
+        }
+
+        public String getContainer() {
+            return mContainer;
+        }
+
+        public FileType getFileType() {
+            return mFileType;
+        }
+
+        public int getFileSize() {
+            return mFileSize;
+        }
+
+        public int getNumFlags() {
+            return mNumFlags;
+        }
+
+        public int getBooleanValueOffset() {
+            return mBooleanValueOffset;
+        }
+    }
+}
diff --git a/tools/aconfig/aconfig_storage_file/srcs/android/aconfig/storage/PackageTable.java b/tools/aconfig/aconfig_storage_file/srcs/android/aconfig/storage/PackageTable.java
new file mode 100644
index 0000000..d04e1ac
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_file/srcs/android/aconfig/storage/PackageTable.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.aconfig.storage;
+
+import java.nio.ByteBuffer;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+
+public class PackageTable {
+
+    private Header mHeader;
+    private Map<String, Node> mNodeMap;
+
+    public static PackageTable fromBytes(ByteBuffer bytes) {
+        PackageTable packageTable = new PackageTable();
+        ByteBufferReader reader = new ByteBufferReader(bytes);
+        Header header = Header.fromBytes(reader);
+        packageTable.mHeader = header;
+        packageTable.mNodeMap = new HashMap(TableUtils.getTableSize(header.mNumPackages));
+        reader.position(header.mNodeOffset);
+        for (int i = 0; i < header.mNumPackages; i++) {
+            Node node = Node.fromBytes(reader);
+            packageTable.mNodeMap.put(node.mPackageName, node);
+        }
+        return packageTable;
+    }
+
+    public Node get(String packageName) {
+        return mNodeMap.get(packageName);
+    }
+
+    public Header getHeader() {
+        return mHeader;
+    }
+
+    public static class Header {
+
+        private int mVersion;
+        private String mContainer;
+        private FileType mFileType;
+        private int mFileSize;
+        private int mNumPackages;
+        private int mBucketOffset;
+        private int mNodeOffset;
+
+        public static Header fromBytes(ByteBufferReader reader) {
+            Header header = new Header();
+            header.mVersion = reader.readInt();
+            header.mContainer = reader.readString();
+            header.mFileType = FileType.fromInt(reader.readByte());
+            header.mFileSize = reader.readInt();
+            header.mNumPackages = reader.readInt();
+            header.mBucketOffset = reader.readInt();
+            header.mNodeOffset = reader.readInt();
+
+            if (header.mFileType != FileType.PACKAGE_MAP) {
+                throw new AconfigStorageException("binary file is not a package map");
+            }
+
+            return header;
+        }
+
+        public int getVersion() {
+            return mVersion;
+        }
+
+        public String getContainer() {
+            return mContainer;
+        }
+
+        public FileType getFileType() {
+            return mFileType;
+        }
+
+        public int getFileSize() {
+            return mFileSize;
+        }
+
+        public int getNumPackages() {
+            return mNumPackages;
+        }
+
+        public int getBucketOffset() {
+            return mBucketOffset;
+        }
+
+        public int getNodeOffset() {
+            return mNodeOffset;
+        }
+    }
+
+    public static class Node {
+
+        private String mPackageName;
+        private int mPackageId;
+        private int mBooleanStartIndex;
+        private int mNextOffset;
+
+        public static Node fromBytes(ByteBufferReader reader) {
+            Node node = new Node();
+            node.mPackageName = reader.readString();
+            node.mPackageId = reader.readInt();
+            node.mBooleanStartIndex = reader.readInt();
+            node.mNextOffset = reader.readInt();
+            node.mNextOffset = node.mNextOffset == 0 ? -1 : node.mNextOffset;
+            return node;
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(mPackageName, mPackageId, mBooleanStartIndex, mNextOffset);
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (this == obj) {
+                return true;
+            }
+
+            if (obj == null || !(obj instanceof Node)) {
+                return false;
+            }
+
+            Node other = (Node) obj;
+            return Objects.equals(mPackageName, other.mPackageName)
+                    && mPackageId == other.mPackageId
+                    && mBooleanStartIndex == other.mBooleanStartIndex
+                    && mNextOffset == other.mNextOffset;
+        }
+
+        public String getPackageName() {
+            return mPackageName;
+        }
+
+        public int getPackageId() {
+            return mPackageId;
+        }
+
+        public int getBooleanStartIndex() {
+            return mBooleanStartIndex;
+        }
+
+        public int getNextOffset() {
+            return mNextOffset;
+        }
+    }
+}
diff --git a/tools/aconfig/aconfig_storage_file/srcs/android/aconfig/storage/TableUtils.java b/tools/aconfig/aconfig_storage_file/srcs/android/aconfig/storage/TableUtils.java
new file mode 100644
index 0000000..714b53b
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_file/srcs/android/aconfig/storage/TableUtils.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.aconfig.storage;
+
+public class TableUtils {
+
+    private static final int[] HASH_PRIMES =
+            new int[] {
+                7,
+                17,
+                29,
+                53,
+                97,
+                193,
+                389,
+                769,
+                1543,
+                3079,
+                6151,
+                12289,
+                24593,
+                49157,
+                98317,
+                196613,
+                393241,
+                786433,
+                1572869,
+                3145739,
+                6291469,
+                12582917,
+                25165843,
+                50331653,
+                100663319,
+                201326611,
+                402653189,
+                805306457,
+                1610612741
+            };
+
+    public static int getTableSize(int numEntries) {
+        for (int i : HASH_PRIMES) {
+            if (i < 2 * numEntries) continue;
+            return i;
+        }
+        throw new AconfigStorageException("Number of items in a hash table exceeds limit");
+    }
+}
diff --git a/tools/aconfig/aconfig_storage_file/tests/Android.bp b/tools/aconfig/aconfig_storage_file/tests/Android.bp
index 26b7800..c33127f 100644
--- a/tools/aconfig/aconfig_storage_file/tests/Android.bp
+++ b/tools/aconfig/aconfig_storage_file/tests/Android.bp
@@ -1,4 +1,3 @@
-
 cc_test {
     name: "aconfig_storage_file.test.cpp",
     team: "trendy_team_android_core_experiments",
@@ -21,3 +20,27 @@
         "general-tests",
     ],
 }
+
+android_test {
+    name: "aconfig_storage_file.test.java",
+    team: "trendy_team_android_core_experiments",
+    srcs: [
+        "srcs/**/*.java",
+    ],
+    static_libs: [
+        "aconfig_storage_file_java",
+        "androidx.test.runner",
+        "junit",
+    ],
+    sdk_version: "test_current",
+    test_config: "AndroidStorageJaveTest.xml",
+    data: [
+        "package.map",
+        "flag.map",
+        "flag.val",
+        "flag.info",
+    ],
+    test_suites: [
+        "general-tests",
+    ],
+}
diff --git a/tools/aconfig/aconfig_storage_file/tests/AndroidManifest.xml b/tools/aconfig/aconfig_storage_file/tests/AndroidManifest.xml
new file mode 100644
index 0000000..5e01879
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_file/tests/AndroidManifest.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="android.aconfig.storage.test">
+    <application>
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+                     android:targetPackage="android.aconfig.storage.test" />
+
+</manifest>
diff --git a/tools/aconfig/aconfig_storage_file/tests/AndroidStorageJaveTest.xml b/tools/aconfig/aconfig_storage_file/tests/AndroidStorageJaveTest.xml
new file mode 100644
index 0000000..2d52d44
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_file/tests/AndroidStorageJaveTest.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<configuration description="Test aconfig storage java tests">
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+        <option name="cleanup-apks" value="true" />
+        <option name="test-file-name" value="aconfig_storage_file.test.java.apk" />
+    </target_preparer>
+    <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
+        <option name="cleanup" value="true" />
+        <option name="push" value="package.map->/data/local/tmp/aconfig_storage_file_test_java/testdata/package.map" />
+        <option name="push" value="flag.map->/data/local/tmp/aconfig_storage_file_test_java/testdata/flag.map" />
+        <option name="push" value="flag.val->/data/local/tmp/aconfig_storage_file_test_java/testdata/flag.val" />
+        <option name="push" value="flag.info->/data/local/tmp/aconfig_storage_file_test_java/testdata/flag.info" />
+    </target_preparer>
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+        <option name="package" value="android.aconfig.storage.test" />
+        <option name="runtime-hint" value="1m" />
+    </test>
+</configuration>
\ No newline at end of file
diff --git a/tools/aconfig/aconfig_storage_file/tests/srcs/ByteBufferReaderTest.java b/tools/aconfig/aconfig_storage_file/tests/srcs/ByteBufferReaderTest.java
new file mode 100644
index 0000000..66a8166
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_file/tests/srcs/ByteBufferReaderTest.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.aconfig.storage.test;
+
+import static org.junit.Assert.assertEquals;
+
+import android.aconfig.storage.ByteBufferReader;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.charset.StandardCharsets;
+
+@RunWith(JUnit4.class)
+public class ByteBufferReaderTest {
+
+    @Test
+    public void testReadByte() {
+        ByteBuffer buffer = ByteBuffer.allocate(1);
+        byte expect = 10;
+        buffer.put(expect).rewind();
+
+        ByteBufferReader reader = new ByteBufferReader(buffer);
+        assertEquals(expect, reader.readByte());
+    }
+
+    @Test
+    public void testReadShort() {
+        ByteBuffer buffer = ByteBuffer.allocate(4);
+        buffer.order(ByteOrder.LITTLE_ENDIAN);
+        short expect = Short.MAX_VALUE;
+        buffer.putShort(expect).rewind();
+
+        ByteBufferReader reader = new ByteBufferReader(buffer);
+        assertEquals(expect, reader.readShort());
+    }
+
+    @Test
+    public void testReadInt() {
+        ByteBuffer buffer = ByteBuffer.allocate(4);
+        buffer.order(ByteOrder.LITTLE_ENDIAN);
+        int expect = 10000;
+        buffer.putInt(expect).rewind();
+
+        ByteBufferReader reader = new ByteBufferReader(buffer);
+        assertEquals(expect, reader.readInt());
+    }
+
+    @Test
+    public void testReadString() {
+        String expect = "test read string";
+        byte[] bytes = expect.getBytes(StandardCharsets.UTF_8);
+
+        ByteBuffer buffer = ByteBuffer.allocate(expect.length() * 2 + 4);
+        buffer.order(ByteOrder.LITTLE_ENDIAN);
+        buffer.putInt(expect.length()).put(bytes).rewind();
+
+        ByteBufferReader reader = new ByteBufferReader(buffer);
+
+        assertEquals(expect, reader.readString());
+    }
+}
diff --git a/tools/aconfig/aconfig_storage_file/tests/srcs/FlagTableTest.java b/tools/aconfig/aconfig_storage_file/tests/srcs/FlagTableTest.java
new file mode 100644
index 0000000..fd40d4c
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_file/tests/srcs/FlagTableTest.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.aconfig.storage.test;
+
+import static org.junit.Assert.assertEquals;
+
+import android.aconfig.storage.FileType;
+import android.aconfig.storage.FlagTable;
+import android.aconfig.storage.FlagType;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class FlagTableTest {
+
+    @Test
+    public void testFlagTable_rightHeader() throws Exception {
+        FlagTable flagTable = FlagTable.fromBytes(TestDataUtils.getTestFlagMapByteBuffer());
+        FlagTable.Header header = flagTable.getHeader();
+        assertEquals(1, header.getVersion());
+        assertEquals("mockup", header.getContainer());
+        assertEquals(FileType.FLAG_MAP, header.getFileType());
+        assertEquals(321, header.getFileSize());
+        assertEquals(8, header.getNumFlags());
+        assertEquals(31, header.getBucketOffset());
+        assertEquals(99, header.getNodeOffset());
+    }
+
+    @Test
+    public void testFlagTable_rightNode() throws Exception {
+        FlagTable flagTable = FlagTable.fromBytes(TestDataUtils.getTestFlagMapByteBuffer());
+
+        FlagTable.Node node1 = flagTable.get(0, "enabled_ro");
+        FlagTable.Node node2 = flagTable.get(0, "enabled_rw");
+        FlagTable.Node node3 = flagTable.get(2, "enabled_rw");
+        FlagTable.Node node4 = flagTable.get(1, "disabled_rw");
+        FlagTable.Node node5 = flagTable.get(1, "enabled_fixed_ro");
+        FlagTable.Node node6 = flagTable.get(1, "enabled_ro");
+        FlagTable.Node node7 = flagTable.get(2, "enabled_fixed_ro");
+        FlagTable.Node node8 = flagTable.get(0, "disabled_rw");
+
+        assertEquals("enabled_ro", node1.getFlagName());
+        assertEquals("enabled_rw", node2.getFlagName());
+        assertEquals("enabled_rw", node3.getFlagName());
+        assertEquals("disabled_rw", node4.getFlagName());
+        assertEquals("enabled_fixed_ro", node5.getFlagName());
+        assertEquals("enabled_ro", node6.getFlagName());
+        assertEquals("enabled_fixed_ro", node7.getFlagName());
+        assertEquals("disabled_rw", node8.getFlagName());
+
+        assertEquals(0, node1.getPackageId());
+        assertEquals(0, node2.getPackageId());
+        assertEquals(2, node3.getPackageId());
+        assertEquals(1, node4.getPackageId());
+        assertEquals(1, node5.getPackageId());
+        assertEquals(1, node6.getPackageId());
+        assertEquals(2, node7.getPackageId());
+        assertEquals(0, node8.getPackageId());
+
+        assertEquals(FlagType.ReadOnlyBoolean, node1.getFlagType());
+        assertEquals(FlagType.ReadWriteBoolean, node2.getFlagType());
+        assertEquals(FlagType.ReadWriteBoolean, node3.getFlagType());
+        assertEquals(FlagType.ReadWriteBoolean, node4.getFlagType());
+        assertEquals(FlagType.FixedReadOnlyBoolean, node5.getFlagType());
+        assertEquals(FlagType.ReadOnlyBoolean, node6.getFlagType());
+        assertEquals(FlagType.FixedReadOnlyBoolean, node7.getFlagType());
+        assertEquals(FlagType.ReadWriteBoolean, node8.getFlagType());
+
+        assertEquals(1, node1.getFlagIndex());
+        assertEquals(2, node2.getFlagIndex());
+        assertEquals(1, node3.getFlagIndex());
+        assertEquals(0, node4.getFlagIndex());
+        assertEquals(1, node5.getFlagIndex());
+        assertEquals(2, node6.getFlagIndex());
+        assertEquals(0, node7.getFlagIndex());
+        assertEquals(0, node8.getFlagIndex());
+
+        assertEquals(-1, node1.getNextOffset());
+        assertEquals(151, node2.getNextOffset());
+        assertEquals(-1, node3.getNextOffset());
+        assertEquals(-1, node4.getNextOffset());
+        assertEquals(236, node5.getNextOffset());
+        assertEquals(-1, node6.getNextOffset());
+        assertEquals(-1, node7.getNextOffset());
+        assertEquals(-1, node8.getNextOffset());
+    }
+}
diff --git a/tools/aconfig/aconfig_storage_file/tests/srcs/FlagValueListTest.java b/tools/aconfig/aconfig_storage_file/tests/srcs/FlagValueListTest.java
new file mode 100644
index 0000000..c18590a
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_file/tests/srcs/FlagValueListTest.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.aconfig.storage.test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.aconfig.storage.FileType;
+import android.aconfig.storage.FlagTable;
+import android.aconfig.storage.FlagValueList;
+import android.aconfig.storage.PackageTable;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class FlagValueListTest {
+
+    @Test
+    public void testFlagValueList_rightHeader() throws Exception {
+        FlagValueList flagValueList =
+                FlagValueList.fromBytes(TestDataUtils.getTestFlagValByteBuffer());
+        FlagValueList.Header header = flagValueList.getHeader();
+        assertEquals(1, header.getVersion());
+        assertEquals("mockup", header.getContainer());
+        assertEquals(FileType.FLAG_VAL, header.getFileType());
+        assertEquals(35, header.getFileSize());
+        assertEquals(8, header.getNumFlags());
+        assertEquals(27, header.getBooleanValueOffset());
+    }
+
+    @Test
+    public void testFlagValueList_rightNode() throws Exception {
+        FlagValueList flagValueList =
+                FlagValueList.fromBytes(TestDataUtils.getTestFlagValByteBuffer());
+
+        boolean[] expected = new boolean[] {false, true, true, false, true, true, true, true};
+        assertEquals(expected.length, flagValueList.size());
+
+        for (int i = 0; i < flagValueList.size(); i++) {
+            assertEquals(expected[i], flagValueList.get(i));
+        }
+    }
+
+    @Test
+    public void testFlagValueList_getValue() throws Exception {
+        PackageTable packageTable =
+                PackageTable.fromBytes(TestDataUtils.getTestPackageMapByteBuffer());
+        FlagTable flagTable = FlagTable.fromBytes(TestDataUtils.getTestFlagMapByteBuffer());
+
+        FlagValueList flagValueList =
+                FlagValueList.fromBytes(TestDataUtils.getTestFlagValByteBuffer());
+
+        PackageTable.Node pNode = packageTable.get("com.android.aconfig.storage.test_1");
+        FlagTable.Node fNode = flagTable.get(pNode.getPackageId(), "enabled_rw");
+        assertTrue(flagValueList.get(pNode.getBooleanStartIndex() + fNode.getFlagIndex()));
+
+        pNode = packageTable.get("com.android.aconfig.storage.test_4");
+        fNode = flagTable.get(pNode.getPackageId(), "enabled_fixed_ro");
+        assertTrue(flagValueList.get(pNode.getBooleanStartIndex() + fNode.getFlagIndex()));
+    }
+}
diff --git a/tools/aconfig/aconfig_storage_file/tests/srcs/PackageTableTest.java b/tools/aconfig/aconfig_storage_file/tests/srcs/PackageTableTest.java
new file mode 100644
index 0000000..e7e19d8
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_file/tests/srcs/PackageTableTest.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.aconfig.storage.test;
+
+import static org.junit.Assert.assertEquals;
+
+import android.aconfig.storage.FileType;
+import android.aconfig.storage.PackageTable;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class PackageTableTest {
+
+    @Test
+    public void testPackageTable_rightHeader() throws Exception {
+        PackageTable packageTable =
+                PackageTable.fromBytes(TestDataUtils.getTestPackageMapByteBuffer());
+        PackageTable.Header header = packageTable.getHeader();
+        assertEquals(1, header.getVersion());
+        assertEquals("mockup", header.getContainer());
+        assertEquals(FileType.PACKAGE_MAP, header.getFileType());
+        assertEquals(209, header.getFileSize());
+        assertEquals(3, header.getNumPackages());
+        assertEquals(31, header.getBucketOffset());
+        assertEquals(59, header.getNodeOffset());
+    }
+
+    @Test
+    public void testPackageTable_rightNode() throws Exception {
+        PackageTable packageTable =
+                PackageTable.fromBytes(TestDataUtils.getTestPackageMapByteBuffer());
+
+        PackageTable.Node node1 = packageTable.get("com.android.aconfig.storage.test_1");
+        PackageTable.Node node2 = packageTable.get("com.android.aconfig.storage.test_2");
+        PackageTable.Node node4 = packageTable.get("com.android.aconfig.storage.test_4");
+
+        assertEquals("com.android.aconfig.storage.test_1", node1.getPackageName());
+        assertEquals("com.android.aconfig.storage.test_2", node2.getPackageName());
+        assertEquals("com.android.aconfig.storage.test_4", node4.getPackageName());
+
+        assertEquals(0, node1.getPackageId());
+        assertEquals(1, node2.getPackageId());
+        assertEquals(2, node4.getPackageId());
+
+        assertEquals(0, node1.getBooleanStartIndex());
+        assertEquals(3, node2.getBooleanStartIndex());
+        assertEquals(6, node4.getBooleanStartIndex());
+
+        assertEquals(159, node1.getNextOffset());
+        assertEquals(-1, node2.getNextOffset());
+        assertEquals(-1, node4.getNextOffset());
+    }
+}
diff --git a/tools/aconfig/aconfig_storage_file/tests/srcs/TestDataUtils.java b/tools/aconfig/aconfig_storage_file/tests/srcs/TestDataUtils.java
new file mode 100644
index 0000000..f35952d
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_file/tests/srcs/TestDataUtils.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.aconfig.storage.test;
+
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+
+public final class TestDataUtils {
+    private static final String TEST_PACKAGE_MAP_PATH = "package.map";
+    private static final String TEST_FLAG_MAP_PATH = "flag.map";
+    private static final String TEST_FLAG_VAL_PATH = "flag.val";
+    private static final String TEST_FLAG_INFO_PATH = "flag.info";
+
+    private static final String TESTDATA_PATH =
+            "/data/local/tmp/aconfig_storage_file_test_java/testdata/";
+
+    public static ByteBuffer getTestPackageMapByteBuffer() throws Exception {
+        return readFile(TESTDATA_PATH + TEST_PACKAGE_MAP_PATH);
+    }
+
+    public static ByteBuffer getTestFlagMapByteBuffer() throws Exception {
+        return readFile(TESTDATA_PATH + TEST_FLAG_MAP_PATH);
+    }
+
+    public static ByteBuffer getTestFlagValByteBuffer() throws Exception {
+        return readFile(TESTDATA_PATH + TEST_FLAG_VAL_PATH);
+    }
+
+    public static ByteBuffer getTestFlagInfoByteBuffer() throws Exception {
+        return readFile(TESTDATA_PATH + TEST_FLAG_INFO_PATH);
+    }
+
+    private static ByteBuffer readFile(String fileName) throws Exception {
+        InputStream input = new FileInputStream(fileName);
+        return ByteBuffer.wrap(input.readAllBytes());
+    }
+}
diff --git a/tools/aconfig/aconfig_storage_read_api/Android.bp b/tools/aconfig/aconfig_storage_read_api/Android.bp
index 5e9eb54..c2f4c18 100644
--- a/tools/aconfig/aconfig_storage_read_api/Android.bp
+++ b/tools/aconfig/aconfig_storage_read_api/Android.bp
@@ -154,7 +154,9 @@
 java_library {
     name: "libaconfig_storage_read_api_java",
     srcs: [
-        "srcs/**/*.java",
+        "srcs/android/aconfig/storage/AconfigStorageReadAPI.java",
+        "srcs/android/aconfig/storage/FlagReadContext.java",
+        "srcs/android/aconfig/storage/PackageReadContext.java",
     ],
     required: ["libaconfig_storage_read_api_rust_jni"],
     min_sdk_version: "UpsideDownCake",
@@ -163,3 +165,14 @@
         "//apex_available:platform",
     ],
 }
+
+java_library {
+    name: "aconfig_storage_reader_java",
+    srcs: [
+        "srcs/android/aconfig/storage/StorageInternalReader.java",
+    ],
+    static_libs: [
+        "aconfig_storage_file_java",
+    ],
+    sdk_version: "core_current",
+}
diff --git a/tools/aconfig/aconfig_storage_read_api/srcs/android/aconfig/storage/StorageInternalReader.java b/tools/aconfig/aconfig_storage_read_api/srcs/android/aconfig/storage/StorageInternalReader.java
new file mode 100644
index 0000000..5f31017
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_read_api/srcs/android/aconfig/storage/StorageInternalReader.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.aconfig.storage;
+
+import java.io.FileInputStream;
+import java.nio.MappedByteBuffer;
+import java.nio.channels.FileChannel;
+
+public class StorageInternalReader {
+
+    private static final String MAP_PATH = "/metadata/aconfig/maps/";
+    private static final String BOOT_PATH = "/metadata/aconfig/boot/";
+
+    private PackageTable mPackageTable;
+    private FlagValueList mFlagValueList;
+
+    private int mPackageBooleanStartOffset;
+
+    public StorageInternalReader(String container, String packageName) {
+        this(packageName, MAP_PATH + container + ".package.map", BOOT_PATH + container + ".val");
+    }
+
+    public StorageInternalReader(String packageName, String packageMapFile, String flagValueFile) {
+        mPackageTable = PackageTable.fromBytes(mapStorageFile(packageMapFile));
+        mFlagValueList = FlagValueList.fromBytes(mapStorageFile(flagValueFile));
+        mPackageBooleanStartOffset = getPackageBooleanStartOffset(packageName);
+    }
+
+    public boolean getBooleanFlagValue(int index) {
+        index += mPackageBooleanStartOffset;
+        if (index >= mFlagValueList.size()) {
+            throw new AconfigStorageException("Fail to get boolean flag value");
+        }
+        return mFlagValueList.get(index);
+    }
+
+    private int getPackageBooleanStartOffset(String packageName) {
+        PackageTable.Node pNode = mPackageTable.get(packageName);
+        if (pNode == null) {
+            PackageTable.Header header = mPackageTable.getHeader();
+            throw new AconfigStorageException(
+                    String.format(
+                            "Fail to get package %s from container %s",
+                            packageName, header.getContainer()));
+        }
+        return pNode.getBooleanStartIndex();
+    }
+
+    // Map a storage file given file path
+    private static MappedByteBuffer mapStorageFile(String file) {
+        try {
+            FileInputStream stream = new FileInputStream(file);
+            FileChannel channel = stream.getChannel();
+            return channel.map(FileChannel.MapMode.READ_ONLY, 0, channel.size());
+        } catch (Exception e) {
+            throw new AconfigStorageException(
+                    String.format("Fail to mmap storage file %s", file), e);
+        }
+    }
+}
diff --git a/tools/aconfig/aconfig_storage_read_api/tests/java/Android.bp b/tools/aconfig/aconfig_storage_read_api/tests/java/Android.bp
index d94b2b4..11b3824 100644
--- a/tools/aconfig/aconfig_storage_read_api/tests/java/Android.bp
+++ b/tools/aconfig/aconfig_storage_read_api/tests/java/Android.bp
@@ -1,7 +1,8 @@
 android_test {
     name: "aconfig_storage_read_api.test.java",
-    srcs: ["AconfigStorageReadAPITest.java"],
+    srcs: ["./**/*.java"],
     static_libs: [
+        "aconfig_storage_reader_java",
         "androidx.test.rules",
         "libaconfig_storage_read_api_java",
         "junit",
diff --git a/tools/aconfig/aconfig_storage_read_api/tests/java/StorageInternalReaderTest.java b/tools/aconfig/aconfig_storage_read_api/tests/java/StorageInternalReaderTest.java
new file mode 100644
index 0000000..3a1bba0
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_read_api/tests/java/StorageInternalReaderTest.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.aconfig.storage.test;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.aconfig.storage.StorageInternalReader;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class StorageInternalReaderTest {
+
+    private String mStorageDir = "/data/local/tmp/aconfig_java_api_test";
+
+    @Test
+    public void testStorageInternalReader_getFlag() {
+
+        String packageMapFile = mStorageDir + "/maps/mockup.package.map";
+        String flagValueFile = mStorageDir + "/boot/mockup.val";
+
+        StorageInternalReader reader =
+                new StorageInternalReader(
+                        "com.android.aconfig.storage.test_1", packageMapFile, flagValueFile);
+        assertFalse(reader.getBooleanFlagValue(0));
+        assertTrue(reader.getBooleanFlagValue(1));
+    }
+}
diff --git a/tools/aconfig/aconfig_storage_write_api/aconfig_storage_write_api.cpp b/tools/aconfig/aconfig_storage_write_api/aconfig_storage_write_api.cpp
index cabc65e..7b43574 100644
--- a/tools/aconfig/aconfig_storage_write_api/aconfig_storage_write_api.cpp
+++ b/tools/aconfig/aconfig_storage_write_api/aconfig_storage_write_api.cpp
@@ -1,6 +1,7 @@
 
 #include <android-base/file.h>
 #include <android-base/logging.h>
+#include <android-base/unique_fd.h>
 
 #include <sys/mman.h>
 #include <sys/stat.h>
@@ -13,8 +14,8 @@
 namespace aconfig_storage {
 
 /// Map a storage file
-android::base::Result<MutableMappedStorageFile*> map_mutable_storage_file(
-    std::string const& file) {
+android::base::Result<MutableMappedStorageFile *> map_mutable_storage_file(
+    std::string const &file) {
   struct stat file_stat;
   if (stat(file.c_str(), &file_stat) < 0) {
     return android::base::ErrnoError() << "stat failed";
@@ -26,13 +27,13 @@
 
   size_t file_size = file_stat.st_size;
 
-  const int fd = open(file.c_str(), O_RDWR | O_NOFOLLOW | O_CLOEXEC);
-  if (fd == -1) {
+  android::base::unique_fd ufd(open(file.c_str(), O_RDWR | O_NOFOLLOW | O_CLOEXEC));
+  if (ufd.get() == -1) {
     return android::base::ErrnoError() << "failed to open " << file;
   };
 
-  void* const map_result =
-      mmap(nullptr, file_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+  void *const map_result =
+      mmap(nullptr, file_size, PROT_READ | PROT_WRITE, MAP_SHARED, ufd.get(), 0);
   if (map_result == MAP_FAILED) {
     return android::base::ErrnoError() << "mmap failed";
   }
@@ -46,47 +47,56 @@
 
 /// Set boolean flag value
 android::base::Result<void> set_boolean_flag_value(
-    const MutableMappedStorageFile& file,
+    const MutableMappedStorageFile &file,
     uint32_t offset,
     bool value) {
   auto content = rust::Slice<uint8_t>(
-      static_cast<uint8_t*>(file.file_ptr), file.file_size);
+      static_cast<uint8_t *>(file.file_ptr), file.file_size);
   auto update_cxx = update_boolean_flag_value_cxx(content, offset, value);
   if (!update_cxx.update_success) {
     return android::base::Error() << update_cxx.error_message.c_str();
   }
+  if (!msync(static_cast<uint8_t *>(file.file_ptr) + update_cxx.offset, 1, MS_SYNC)) {
+    return android::base::ErrnoError() << "msync failed";
+  }
   return {};
 }
 
 /// Set if flag has server override
 android::base::Result<void> set_flag_has_server_override(
-    const MutableMappedStorageFile& file,
+    const MutableMappedStorageFile &file,
     FlagValueType value_type,
     uint32_t offset,
     bool value) {
   auto content = rust::Slice<uint8_t>(
-      static_cast<uint8_t*>(file.file_ptr), file.file_size);
+      static_cast<uint8_t *>(file.file_ptr), file.file_size);
   auto update_cxx = update_flag_has_server_override_cxx(
       content, static_cast<uint16_t>(value_type), offset, value);
   if (!update_cxx.update_success) {
     return android::base::Error() << update_cxx.error_message.c_str();
   }
+  if (!msync(static_cast<uint8_t *>(file.file_ptr) + update_cxx.offset, 1, MS_SYNC)) {
+    return android::base::ErrnoError() << "msync failed";
+  }
   return {};
 }
 
 /// Set if flag has local override
 android::base::Result<void> set_flag_has_local_override(
-    const MutableMappedStorageFile& file,
+    const MutableMappedStorageFile &file,
     FlagValueType value_type,
     uint32_t offset,
     bool value) {
   auto content = rust::Slice<uint8_t>(
-      static_cast<uint8_t*>(file.file_ptr), file.file_size);
+      static_cast<uint8_t *>(file.file_ptr), file.file_size);
   auto update_cxx = update_flag_has_local_override_cxx(
       content, static_cast<uint16_t>(value_type), offset, value);
   if (!update_cxx.update_success) {
     return android::base::Error() << update_cxx.error_message.c_str();
   }
+  if (!msync(static_cast<uint8_t *>(file.file_ptr) + update_cxx.offset, 1, MS_SYNC)) {
+    return android::base::ErrnoError() << "msync failed";
+  }
   return {};
 }
 
diff --git a/tools/aconfig/aconfig_storage_write_api/src/flag_info_update.rs b/tools/aconfig/aconfig_storage_write_api/src/flag_info_update.rs
index 6f03f12..7e60713 100644
--- a/tools/aconfig/aconfig_storage_write_api/src/flag_info_update.rs
+++ b/tools/aconfig/aconfig_storage_write_api/src/flag_info_update.rs
@@ -67,13 +67,13 @@
     flag_type: FlagValueType,
     flag_index: u32,
     value: bool,
-) -> Result<(), AconfigStorageError> {
+) -> Result<usize, AconfigStorageError> {
     let (attribute, head) = get_flag_attribute_and_offset(buf, flag_type, flag_index)?;
     let has_override = (attribute & (FlagInfoBit::HasServerOverride as u8)) != 0;
     if has_override != value {
         buf[head] = (attribute ^ FlagInfoBit::HasServerOverride as u8).to_le_bytes()[0];
     }
-    Ok(())
+    Ok(head)
 }
 
 /// Set if flag has local override
@@ -82,13 +82,13 @@
     flag_type: FlagValueType,
     flag_index: u32,
     value: bool,
-) -> Result<(), AconfigStorageError> {
+) -> Result<usize, AconfigStorageError> {
     let (attribute, head) = get_flag_attribute_and_offset(buf, flag_type, flag_index)?;
     let has_override = (attribute & (FlagInfoBit::HasLocalOverride as u8)) != 0;
     if has_override != value {
         buf[head] = (attribute ^ FlagInfoBit::HasLocalOverride as u8).to_le_bytes()[0];
     }
-    Ok(())
+    Ok(head)
 }
 
 #[cfg(test)]
diff --git a/tools/aconfig/aconfig_storage_write_api/src/flag_value_update.rs b/tools/aconfig/aconfig_storage_write_api/src/flag_value_update.rs
index 0938715..dd15c99 100644
--- a/tools/aconfig/aconfig_storage_write_api/src/flag_value_update.rs
+++ b/tools/aconfig/aconfig_storage_write_api/src/flag_value_update.rs
@@ -24,7 +24,7 @@
     buf: &mut [u8],
     flag_index: u32,
     flag_value: bool,
-) -> Result<(), AconfigStorageError> {
+) -> Result<usize, AconfigStorageError> {
     let interpreted_header = FlagValueHeader::from_bytes(buf)?;
     if interpreted_header.version > FILE_VERSION {
         return Err(AconfigStorageError::HigherStorageFileVersion(anyhow!(
@@ -43,7 +43,7 @@
     }
 
     buf[head] = u8::from(flag_value).to_le_bytes()[0];
-    Ok(())
+    Ok(head)
 }
 
 #[cfg(test)]
diff --git a/tools/aconfig/aconfig_storage_write_api/src/lib.rs b/tools/aconfig/aconfig_storage_write_api/src/lib.rs
index aec28de..0396a63 100644
--- a/tools/aconfig/aconfig_storage_write_api/src/lib.rs
+++ b/tools/aconfig/aconfig_storage_write_api/src/lib.rs
@@ -194,18 +194,21 @@
     // Flag value update return for cc interlop
     pub struct BooleanFlagValueUpdateCXX {
         pub update_success: bool,
+        pub offset: usize,
         pub error_message: String,
     }
 
     // Flag has server override update return for cc interlop
     pub struct FlagHasServerOverrideUpdateCXX {
         pub update_success: bool,
+        pub offset: usize,
         pub error_message: String,
     }
 
     // Flag has local override update return for cc interlop
     pub struct FlagHasLocalOverrideUpdateCXX {
         pub update_success: bool,
+        pub offset: usize,
         pub error_message: String,
     }
 
@@ -251,11 +254,14 @@
     value: bool,
 ) -> ffi::BooleanFlagValueUpdateCXX {
     match crate::flag_value_update::update_boolean_flag_value(file, offset, value) {
-        Ok(()) => {
-            ffi::BooleanFlagValueUpdateCXX { update_success: true, error_message: String::from("") }
-        }
+        Ok(head) => ffi::BooleanFlagValueUpdateCXX {
+            update_success: true,
+            offset: head,
+            error_message: String::from(""),
+        },
         Err(errmsg) => ffi::BooleanFlagValueUpdateCXX {
             update_success: false,
+            offset: usize::MAX,
             error_message: format!("{:?}", errmsg),
         },
     }
@@ -272,18 +278,21 @@
             match crate::flag_info_update::update_flag_has_server_override(
                 file, value_type, offset, value,
             ) {
-                Ok(()) => ffi::FlagHasServerOverrideUpdateCXX {
+                Ok(head) => ffi::FlagHasServerOverrideUpdateCXX {
                     update_success: true,
+                    offset: head,
                     error_message: String::from(""),
                 },
                 Err(errmsg) => ffi::FlagHasServerOverrideUpdateCXX {
                     update_success: false,
+                    offset: usize::MAX,
                     error_message: format!("{:?}", errmsg),
                 },
             }
         }
         Err(errmsg) => ffi::FlagHasServerOverrideUpdateCXX {
             update_success: false,
+            offset: usize::MAX,
             error_message: format!("{:?}", errmsg),
         },
     }
@@ -300,18 +309,21 @@
             match crate::flag_info_update::update_flag_has_local_override(
                 file, value_type, offset, value,
             ) {
-                Ok(()) => ffi::FlagHasLocalOverrideUpdateCXX {
+                Ok(head) => ffi::FlagHasLocalOverrideUpdateCXX {
                     update_success: true,
+                    offset: head,
                     error_message: String::from(""),
                 },
                 Err(errmsg) => ffi::FlagHasLocalOverrideUpdateCXX {
                     update_success: false,
+                    offset: usize::MAX,
                     error_message: format!("{:?}", errmsg),
                 },
             }
         }
         Err(errmsg) => ffi::FlagHasLocalOverrideUpdateCXX {
             update_success: false,
+            offset: usize::MAX,
             error_message: format!("{:?}", errmsg),
         },
     }
diff --git a/tools/aconfig/aconfig_storage_write_api/tests/Android.bp b/tools/aconfig/aconfig_storage_write_api/tests/Android.bp
index f6409b7..5508dac 100644
--- a/tools/aconfig/aconfig_storage_write_api/tests/Android.bp
+++ b/tools/aconfig/aconfig_storage_write_api/tests/Android.bp
@@ -38,5 +38,10 @@
         "device-tests",
         "general-tests",
     ],
-    ldflags: ["-Wl,--allow-multiple-definition"],
+    generated_headers: [
+        "cxx-bridge-header",
+        "libcxx_aconfig_storage_read_api_bridge_header",
+    ],
+    generated_sources: ["libcxx_aconfig_storage_read_api_bridge_code"],
+    whole_static_libs: ["libaconfig_storage_read_api_cxx_bridge"],
 }
diff --git a/tools/aconfig/aconfig_storage_write_api/tests/storage_write_api_test.cpp b/tools/aconfig/aconfig_storage_write_api/tests/storage_write_api_test.cpp
index 31183fa..133f5a0 100644
--- a/tools/aconfig/aconfig_storage_write_api/tests/storage_write_api_test.cpp
+++ b/tools/aconfig/aconfig_storage_write_api/tests/storage_write_api_test.cpp
@@ -25,6 +25,9 @@
 #include <android-base/file.h>
 #include <android-base/result.h>
 
+#include "rust/cxx.h"
+#include "aconfig_storage/lib.rs.h"
+
 using namespace android::base;
 
 namespace api = aconfig_storage;
@@ -85,6 +88,23 @@
     ASSERT_TRUE(value.ok());
     ASSERT_TRUE(*value);
   }
+
+  // load the file on disk and check has been updated
+  std::ifstream file(flag_val, std::ios::binary | std::ios::ate);
+  std::streamsize size = file.tellg();
+  file.seekg(0, std::ios::beg);
+
+  std::vector<uint8_t> buffer(size);
+  file.read(reinterpret_cast<char *>(buffer.data()), size);
+
+  auto content = rust::Slice<const uint8_t>(
+      buffer.data(), mapped_file->file_size);
+
+  for (int offset = 0; offset < 8; ++offset) {
+    auto value_cxx = get_boolean_flag_value_cxx(content, offset);
+    ASSERT_TRUE(value_cxx.query_success);
+    ASSERT_TRUE(value_cxx.flag_value);
+  }
 }
 
 /// Negative test to lock down the error when querying flag value out of range
@@ -112,15 +132,43 @@
         *mapped_file, api::FlagValueType::Boolean, offset);
     ASSERT_TRUE(attribute.ok());
     ASSERT_TRUE(*attribute & api::FlagInfoBit::HasServerOverride);
+  }
 
-    update_result = api::set_flag_has_server_override(
+  // load the file on disk and check has been updated
+  std::ifstream file(flag_info, std::ios::binary | std::ios::ate);
+  std::streamsize size = file.tellg();
+  file.seekg(0, std::ios::beg);
+
+  std::vector<uint8_t> buffer(size);
+  file.read(reinterpret_cast<char *>(buffer.data()), size);
+
+  auto content = rust::Slice<const uint8_t>(
+      buffer.data(), mapped_file->file_size);
+
+  for (int offset = 0; offset < 8; ++offset) {
+    auto attribute = get_flag_attribute_cxx(content, api::FlagValueType::Boolean, offset);
+    ASSERT_TRUE(attribute.query_success);
+    ASSERT_TRUE(attribute.flag_attribute & api::FlagInfoBit::HasServerOverride);
+  }
+
+  for (int offset = 0; offset < 8; ++offset) {
+    auto update_result = api::set_flag_has_server_override(
         *mapped_file, api::FlagValueType::Boolean, offset, false);
     ASSERT_TRUE(update_result.ok());
-    attribute = api::get_flag_attribute(
+    auto attribute = api::get_flag_attribute(
         *mapped_file, api::FlagValueType::Boolean, offset);
     ASSERT_TRUE(attribute.ok());
     ASSERT_FALSE(*attribute & api::FlagInfoBit::HasServerOverride);
   }
+
+  std::ifstream file2(flag_info, std::ios::binary);
+  buffer.clear();
+  file2.read(reinterpret_cast<char *>(buffer.data()), size);
+  for (int offset = 0; offset < 8; ++offset) {
+    auto attribute = get_flag_attribute_cxx(content, api::FlagValueType::Boolean, offset);
+    ASSERT_TRUE(attribute.query_success);
+    ASSERT_FALSE(attribute.flag_attribute & api::FlagInfoBit::HasServerOverride);
+  }
 }
 
 /// Test to lock down storage flag has local override update api
@@ -137,13 +185,41 @@
         *mapped_file, api::FlagValueType::Boolean, offset);
     ASSERT_TRUE(attribute.ok());
     ASSERT_TRUE(*attribute & api::FlagInfoBit::HasLocalOverride);
+  }
 
-    update_result = api::set_flag_has_local_override(
+  // load the file on disk and check has been updated
+  std::ifstream file(flag_info, std::ios::binary | std::ios::ate);
+  std::streamsize size = file.tellg();
+  file.seekg(0, std::ios::beg);
+
+  std::vector<uint8_t> buffer(size);
+  file.read(reinterpret_cast<char *>(buffer.data()), size);
+
+  auto content = rust::Slice<const uint8_t>(
+      buffer.data(), mapped_file->file_size);
+
+  for (int offset = 0; offset < 8; ++offset) {
+    auto attribute = get_flag_attribute_cxx(content, api::FlagValueType::Boolean, offset);
+    ASSERT_TRUE(attribute.query_success);
+    ASSERT_TRUE(attribute.flag_attribute & api::FlagInfoBit::HasLocalOverride);
+  }
+
+  for (int offset = 0; offset < 8; ++offset) {
+    auto update_result = api::set_flag_has_local_override(
         *mapped_file, api::FlagValueType::Boolean, offset, false);
     ASSERT_TRUE(update_result.ok());
-    attribute = api::get_flag_attribute(
+    auto attribute = api::get_flag_attribute(
         *mapped_file, api::FlagValueType::Boolean, offset);
     ASSERT_TRUE(attribute.ok());
     ASSERT_FALSE(*attribute & api::FlagInfoBit::HasLocalOverride);
   }
+
+  std::ifstream file2(flag_info, std::ios::binary);
+  buffer.clear();
+  file2.read(reinterpret_cast<char *>(buffer.data()), size);
+  for (int offset = 0; offset < 8; ++offset) {
+    auto attribute = get_flag_attribute_cxx(content, api::FlagValueType::Boolean, offset);
+    ASSERT_TRUE(attribute.query_success);
+    ASSERT_FALSE(attribute.flag_attribute & api::FlagInfoBit::HasLocalOverride);
+  }
 }
diff --git a/tools/filelistdiff/Android.bp b/tools/filelistdiff/Android.bp
new file mode 100644
index 0000000..632ada3
--- /dev/null
+++ b/tools/filelistdiff/Android.bp
@@ -0,0 +1,27 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+python_binary_host {
+    name: "file_list_diff",
+    srcs: ["file_list_diff.py"],
+}
+
+prebuilt_etc_host {
+    name: "system_image_diff_allowlist",
+    src: "allowlist",
+}
diff --git a/tools/filelistdiff/allowlist b/tools/filelistdiff/allowlist
new file mode 100644
index 0000000..0a51d0e
--- /dev/null
+++ b/tools/filelistdiff/allowlist
@@ -0,0 +1,83 @@
+# Known diffs only in the KATI system image
+etc/NOTICE.xml.gz
+etc/compatconfig/TeleService-platform-compat-config.xml
+etc/compatconfig/calendar-provider-compat-config.xml
+etc/compatconfig/contacts-provider-platform-compat-config.xml
+etc/compatconfig/documents-ui-compat-config.xml
+etc/compatconfig/framework-location-compat-config.xml
+etc/compatconfig/framework-platform-compat-config.xml
+etc/compatconfig/icu4j-platform-compat-config.xml
+etc/compatconfig/services-platform-compat-config.xml
+etc/permissions/android.software.credentials.xml
+etc/permissions/android.software.preview_sdk.xml
+etc/permissions/android.software.webview.xml
+etc/permissions/android.software.window_magnification.xml
+etc/permissions/com.android.adservices.sdksandbox.xml
+etc/security/otacerts.zip
+etc/vintf/compatibility_matrix.202404.xml
+etc/vintf/compatibility_matrix.202504.xml
+etc/vintf/compatibility_matrix.5.xml
+etc/vintf/compatibility_matrix.6.xml
+etc/vintf/compatibility_matrix.7.xml
+etc/vintf/compatibility_matrix.8.xml
+etc/vintf/compatibility_matrix.device.xml
+etc/vintf/manifest.xml
+framework/boot-apache-xml.vdex
+framework/boot-apache-xml.vdex.fsv_meta
+framework/boot-bouncycastle.vdex
+framework/boot-bouncycastle.vdex.fsv_meta
+framework/boot-core-icu4j.vdex
+framework/boot-core-icu4j.vdex.fsv_meta
+framework/boot-core-libart.vdex
+framework/boot-core-libart.vdex.fsv_meta
+framework/boot-ext.vdex
+framework/boot-ext.vdex.fsv_meta
+framework/boot-framework-adservices.vdex
+framework/boot-framework-adservices.vdex.fsv_meta
+framework/boot-framework-graphics.vdex
+framework/boot-framework-graphics.vdex.fsv_meta
+framework/boot-framework-location.vdex
+framework/boot-framework-location.vdex.fsv_meta
+framework/boot-framework.vdex
+framework/boot-framework.vdex.fsv_meta
+framework/boot-ims-common.vdex
+framework/boot-ims-common.vdex.fsv_meta
+framework/boot-okhttp.vdex
+framework/boot-okhttp.vdex.fsv_meta
+framework/boot-telephony-common.vdex
+framework/boot-telephony-common.vdex.fsv_meta
+framework/boot-voip-common.vdex
+framework/boot-voip-common.vdex.fsv_meta
+framework/boot.vdex
+framework/boot.vdex.fsv_meta
+framework/oat/x86_64/apex@com.android.compos@javalib@service-compos.jar@classes.odex
+framework/oat/x86_64/apex@com.android.compos@javalib@service-compos.jar@classes.odex.fsv_meta
+framework/oat/x86_64/apex@com.android.compos@javalib@service-compos.jar@classes.vdex
+framework/oat/x86_64/apex@com.android.compos@javalib@service-compos.jar@classes.vdex.fsv_meta
+lib/aaudio-aidl-cpp.so
+lib/android.hardware.biometrics.fingerprint@2.1.so
+lib/android.hardware.radio.config@1.0.so
+lib/android.hardware.radio.deprecated@1.0.so
+lib/android.hardware.radio@1.0.so
+lib/android.hardware.radio@1.1.so
+lib/android.hardware.radio@1.2.so
+lib/android.hardware.radio@1.3.so
+lib/android.hardware.radio@1.4.so
+lib/android.hardware.secure_element@1.0.so
+lib/com.android.media.aaudio-aconfig-cc.so
+lib/heapprofd_client.so
+lib/heapprofd_client_api.so
+lib/libaaudio.so
+lib/libaaudio_internal.so
+lib/libalarm_jni.so
+lib/libamidi.so
+lib/libcups.so
+lib/libjni_deviceAsWebcam.so
+lib/libprintspooler_jni.so
+lib/libvendorsupport.so
+lib/libwfds.so
+lib/libyuv.so
+
+# Known diffs only in the Soong system image
+lib/libhidcommand_jni.so
+lib/libuinputcommand_jni.so
\ No newline at end of file
diff --git a/tools/filelistdiff/file_list_diff.py b/tools/filelistdiff/file_list_diff.py
new file mode 100644
index 0000000..cdc5b2e
--- /dev/null
+++ b/tools/filelistdiff/file_list_diff.py
@@ -0,0 +1,66 @@
+# 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.
+
+import argparse
+import sys
+
+COLOR_WARNING = '\033[93m'
+COLOR_ERROR = '\033[91m'
+COLOR_NORMAL = '\033[0m'
+
+def find_unique_items(kati_installed_files, soong_installed_files, allowlist, system_module_name):
+    with open(kati_installed_files, 'r') as kati_list_file, \
+            open(soong_installed_files, 'r') as soong_list_file, \
+            open(allowlist, 'r') as allowlist_file:
+        kati_files = set(kati_list_file.read().split())
+        soong_files = set(soong_list_file.read().split())
+        allowed_files = set(filter(lambda x: len(x), map(lambda x: x.lstrip().split('#',1)[0].rstrip() , allowlist_file.read().split('\n'))))
+
+    def is_unknown_diff(filepath):
+        return not filepath in allowed_files
+
+    unique_in_kati = set(filter(is_unknown_diff, kati_files - soong_files))
+    unique_in_soong = set(filter(is_unknown_diff, soong_files - kati_files))
+
+    if unique_in_kati:
+        print(f'{COLOR_ERROR}Please add following modules into system image module {system_module_name}.{COLOR_NORMAL}')
+        print(f'{COLOR_WARNING}KATI only module(s):{COLOR_NORMAL}')
+        for item in sorted(unique_in_kati):
+            print(item)
+
+    if unique_in_soong:
+        if unique_in_kati:
+            print('')
+
+        print(f'{COLOR_ERROR}Please add following modules into build/make/target/product/base_system.mk.{COLOR_NORMAL}')
+        print(f'{COLOR_WARNING}Soong only module(s):{COLOR_NORMAL}')
+        for item in sorted(unique_in_soong):
+            print(item)
+
+    if unique_in_kati or unique_in_soong:
+        print('')
+        print(f'{COLOR_ERROR}FAILED: System image from KATI and SOONG differs from installed file list.{COLOR_NORMAL}')
+        sys.exit(1)
+
+
+if __name__ == '__main__':
+    parser = argparse.ArgumentParser()
+
+    parser.add_argument('kati_installed_file_list')
+    parser.add_argument('soong_installed_file_list')
+    parser.add_argument('allowlist')
+    parser.add_argument('system_module_name')
+    args = parser.parse_args()
+
+    find_unique_items(args.kati_installed_file_list, args.soong_installed_file_list, args.allowlist, args.system_module_name)
\ No newline at end of file
diff --git a/tools/ide_query/OWNERS b/tools/ide_query/OWNERS
new file mode 100644
index 0000000..914a9a2
--- /dev/null
+++ b/tools/ide_query/OWNERS
@@ -0,0 +1,4 @@
+ialiyev@google.com
+ivankirichenko@google.com
+kadircet@google.com
+michaelmerg@google.com
diff --git a/tools/ide_query/cc_analyzer/Android.bp b/tools/ide_query/cc_analyzer/Android.bp
index 3cbbb05..e85d445 100644
--- a/tools/ide_query/cc_analyzer/Android.bp
+++ b/tools/ide_query/cc_analyzer/Android.bp
@@ -58,7 +58,7 @@
     shared_libs: ["libclang-cpp_host"],
     static_libs: [
         "include_scanner",
-        "ide_query_proto",
+        "cc_analyzer_proto",
     ],
     defaults: ["ide_query_cc_analyzer_defaults"],
 }
@@ -72,7 +72,7 @@
         "libprotobuf-cpp-full",
     ],
     static_libs: [
-        "ide_query_proto",
+        "cc_analyzer_proto",
         "builtin_headers",
         "include_scanner",
         "analyzer",
diff --git a/tools/ide_query/cc_analyzer/analyzer.cc b/tools/ide_query/cc_analyzer/analyzer.cc
index bb7ca0b..4ccec54 100644
--- a/tools/ide_query/cc_analyzer/analyzer.cc
+++ b/tools/ide_query/cc_analyzer/analyzer.cc
@@ -20,9 +20,9 @@
 #include <utility>
 #include <vector>
 
+#include "cc_analyzer.pb.h"
 #include "clang/Tooling/CompilationDatabase.h"
 #include "clang/Tooling/JSONCompilationDatabase.h"
-#include "ide_query.pb.h"
 #include "include_scanner.h"
 #include "llvm/ADT/SmallString.h"
 #include "llvm/ADT/StringRef.h"
@@ -48,11 +48,11 @@
 }
 }  // namespace
 
-::ide_query::DepsResponse GetDeps(::ide_query::RepoState state) {
-  ::ide_query::DepsResponse results;
+::cc_analyzer::DepsResponse GetDeps(::cc_analyzer::RepoState state) {
+  ::cc_analyzer::DepsResponse results;
   auto db = LoadCompDB(state.comp_db_path());
   if (!db) {
-    results.mutable_status()->set_code(::ide_query::Status::FAILURE);
+    results.mutable_status()->set_code(::cc_analyzer::Status::FAILURE);
     results.mutable_status()->set_message(llvm::toString(db.takeError()));
     return results;
   }
@@ -63,7 +63,7 @@
     llvm::sys::path::append(abs_file, active_file);
     auto cmds = db->get()->getCompileCommands(active_file);
     if (cmds.empty()) {
-      result.mutable_status()->set_code(::ide_query::Status::FAILURE);
+      result.mutable_status()->set_code(::cc_analyzer::Status::FAILURE);
       result.mutable_status()->set_message(
           llvm::Twine("Can't find compile flags for file: ", abs_file).str());
       continue;
@@ -80,11 +80,11 @@
   return results;
 }
 
-::ide_query::IdeAnalysis GetBuildInputs(::ide_query::RepoState state) {
+::cc_analyzer::IdeAnalysis GetBuildInputs(::cc_analyzer::RepoState state) {
   auto db = LoadCompDB(state.comp_db_path());
-  ::ide_query::IdeAnalysis results;
+  ::cc_analyzer::IdeAnalysis results;
   if (!db) {
-    results.mutable_status()->set_code(::ide_query::Status::FAILURE);
+    results.mutable_status()->set_code(::cc_analyzer::Status::FAILURE);
     results.mutable_status()->set_message(llvm::toString(db.takeError()));
     return results;
   }
@@ -97,7 +97,6 @@
     genfile_root_abs.push_back('/');
   }
 
-  results.set_build_artifact_root(state.out_dir());
   for (llvm::StringRef active_file : state.active_file_path()) {
     auto& result = *results.add_sources();
     result.set_path(active_file.str());
@@ -106,7 +105,7 @@
     llvm::sys::path::append(abs_file, active_file);
     auto cmds = db->get()->getCompileCommands(abs_file);
     if (cmds.empty()) {
-      result.mutable_status()->set_code(::ide_query::Status::FAILURE);
+      result.mutable_status()->set_code(::cc_analyzer::Status::FAILURE);
       result.mutable_status()->set_message(
           llvm::Twine("Can't find compile flags for file: ", abs_file).str());
       continue;
@@ -114,7 +113,7 @@
     const auto& cmd = cmds.front();
     llvm::StringRef working_dir = cmd.Directory;
     if (!working_dir.consume_front(repo_dir)) {
-      result.mutable_status()->set_code(::ide_query::Status::FAILURE);
+      result.mutable_status()->set_code(::cc_analyzer::Status::FAILURE);
       result.mutable_status()->set_message("Command working dir " +
                                            working_dir.str() +
                                            " outside repository " + repo_dir);
@@ -127,7 +126,7 @@
     auto includes =
         ScanIncludes(cmds.front(), llvm::vfs::createPhysicalFileSystem());
     if (!includes) {
-      result.mutable_status()->set_code(::ide_query::Status::FAILURE);
+      result.mutable_status()->set_code(::cc_analyzer::Status::FAILURE);
       result.mutable_status()->set_message(
           llvm::toString(includes.takeError()));
       continue;
diff --git a/tools/ide_query/cc_analyzer/analyzer.h b/tools/ide_query/cc_analyzer/analyzer.h
index 3133795..fd19082 100644
--- a/tools/ide_query/cc_analyzer/analyzer.h
+++ b/tools/ide_query/cc_analyzer/analyzer.h
@@ -17,17 +17,17 @@
 #ifndef _TOOLS_IDE_QUERY_CC_ANALYZER_ANALYZER_H_
 #define _TOOLS_IDE_QUERY_CC_ANALYZER_ANALYZER_H_
 
-#include "ide_query.pb.h"
+#include "cc_analyzer.pb.h"
 
 namespace tools::ide_query::cc_analyzer {
 
 // Scans the build graph and returns target names from the build graph to
 // generate all the dependencies for the active files.
-::ide_query::DepsResponse GetDeps(::ide_query::RepoState state);
+::cc_analyzer::DepsResponse GetDeps(::cc_analyzer::RepoState state);
 
 // Scans the sources and returns all the source files required for analyzing the
 // active files.
-::ide_query::IdeAnalysis GetBuildInputs(::ide_query::RepoState state);
+::cc_analyzer::IdeAnalysis GetBuildInputs(::cc_analyzer::RepoState state);
 
 }  // namespace tools::ide_query::cc_analyzer
 
diff --git a/tools/ide_query/cc_analyzer/main.cc b/tools/ide_query/cc_analyzer/main.cc
index 8e00c63..d86fc8c 100644
--- a/tools/ide_query/cc_analyzer/main.cc
+++ b/tools/ide_query/cc_analyzer/main.cc
@@ -28,7 +28,7 @@
 
 #include "analyzer.h"
 #include "google/protobuf/message.h"
-#include "ide_query.pb.h"
+#include "cc_analyzer.pb.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/Support/CommandLine.h"
 #include "llvm/Support/TargetSelect.h"
@@ -48,9 +48,9 @@
     llvm::cl::desc("Print the list of headers to insert and remove"),
 };
 
-ide_query::IdeAnalysis ReturnError(llvm::StringRef message) {
-  ide_query::IdeAnalysis result;
-  result.mutable_status()->set_code(ide_query::Status::FAILURE);
+cc_analyzer::IdeAnalysis ReturnError(llvm::StringRef message) {
+  cc_analyzer::IdeAnalysis result;
+  result.mutable_status()->set_code(cc_analyzer::Status::FAILURE);
   result.mutable_status()->set_message(message.str());
   return result;
 }
@@ -61,7 +61,7 @@
   llvm::InitializeAllTargetInfos();
   llvm::cl::ParseCommandLineOptions(argc, argv);
 
-  ide_query::RepoState state;
+  cc_analyzer::RepoState state;
   if (!state.ParseFromFileDescriptor(STDIN_FILENO)) {
     llvm::errs() << "Failed to parse input!\n";
     return 1;
@@ -70,12 +70,12 @@
   std::unique_ptr<google::protobuf::Message> result;
   switch (mode) {
     case OpMode::DEPS: {
-      result = std::make_unique<ide_query::DepsResponse>(
+      result = std::make_unique<cc_analyzer::DepsResponse>(
           tools::ide_query::cc_analyzer::GetDeps(std::move(state)));
       break;
     }
     case OpMode::INPUTS: {
-      result = std::make_unique<ide_query::IdeAnalysis>(
+      result = std::make_unique<cc_analyzer::IdeAnalysis>(
           tools::ide_query::cc_analyzer::GetBuildInputs(std::move(state)));
       break;
     }
diff --git a/tools/ide_query/ide_query_proto/Android.bp b/tools/ide_query/cc_analyzer_proto/Android.bp
similarity index 93%
rename from tools/ide_query/ide_query_proto/Android.bp
rename to tools/ide_query/cc_analyzer_proto/Android.bp
index 70f15cd..0ed07b4 100644
--- a/tools/ide_query/ide_query_proto/Android.bp
+++ b/tools/ide_query/cc_analyzer_proto/Android.bp
@@ -19,9 +19,9 @@
 }
 
 cc_library_host_static {
-    name: "ide_query_proto",
+    name: "cc_analyzer_proto",
     srcs: [
-        "ide_query.proto",
+        "cc_analyzer.proto",
     ],
     proto: {
         export_proto_headers: true,
diff --git a/tools/ide_query/cc_analyzer_proto/cc_analyzer.pb.go b/tools/ide_query/cc_analyzer_proto/cc_analyzer.pb.go
new file mode 100644
index 0000000..debe5c0
--- /dev/null
+++ b/tools/ide_query/cc_analyzer_proto/cc_analyzer.pb.go
@@ -0,0 +1,789 @@
+//
+// 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.
+
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// 	protoc-gen-go v1.30.0
+// 	protoc        v3.21.12
+// source: cc_analyzer.proto
+
+package cc_analyzer_proto
+
+import (
+	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+	reflect "reflect"
+	sync "sync"
+)
+
+const (
+	// Verify that this generated code is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+	// Verify that runtime/protoimpl is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
+
+type Status_Code int32
+
+const (
+	Status_OK      Status_Code = 0
+	Status_FAILURE Status_Code = 1
+)
+
+// Enum value maps for Status_Code.
+var (
+	Status_Code_name = map[int32]string{
+		0: "OK",
+		1: "FAILURE",
+	}
+	Status_Code_value = map[string]int32{
+		"OK":      0,
+		"FAILURE": 1,
+	}
+)
+
+func (x Status_Code) Enum() *Status_Code {
+	p := new(Status_Code)
+	*p = x
+	return p
+}
+
+func (x Status_Code) String() string {
+	return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
+}
+
+func (Status_Code) Descriptor() protoreflect.EnumDescriptor {
+	return file_cc_analyzer_proto_enumTypes[0].Descriptor()
+}
+
+func (Status_Code) Type() protoreflect.EnumType {
+	return &file_cc_analyzer_proto_enumTypes[0]
+}
+
+func (x Status_Code) Number() protoreflect.EnumNumber {
+	return protoreflect.EnumNumber(x)
+}
+
+// Deprecated: Use Status_Code.Descriptor instead.
+func (Status_Code) EnumDescriptor() ([]byte, []int) {
+	return file_cc_analyzer_proto_rawDescGZIP(), []int{0, 0}
+}
+
+// Indicates the success/failure for analysis.
+type Status struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Code Status_Code `protobuf:"varint,1,opt,name=code,proto3,enum=cc_analyzer.Status_Code" json:"code,omitempty"`
+	// Details about the status, might be displayed to user.
+	Message *string `protobuf:"bytes,2,opt,name=message,proto3,oneof" json:"message,omitempty"`
+}
+
+func (x *Status) Reset() {
+	*x = Status{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_cc_analyzer_proto_msgTypes[0]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *Status) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Status) ProtoMessage() {}
+
+func (x *Status) ProtoReflect() protoreflect.Message {
+	mi := &file_cc_analyzer_proto_msgTypes[0]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use Status.ProtoReflect.Descriptor instead.
+func (*Status) Descriptor() ([]byte, []int) {
+	return file_cc_analyzer_proto_rawDescGZIP(), []int{0}
+}
+
+func (x *Status) GetCode() Status_Code {
+	if x != nil {
+		return x.Code
+	}
+	return Status_OK
+}
+
+func (x *Status) GetMessage() string {
+	if x != nil && x.Message != nil {
+		return *x.Message
+	}
+	return ""
+}
+
+// Represents an Android checkout on user's workstation.
+type RepoState struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// Absolute path for the checkout in the workstation.
+	// e.g. /home/user/work/android/
+	RepoDir string `protobuf:"bytes,1,opt,name=repo_dir,json=repoDir,proto3" json:"repo_dir,omitempty"`
+	// Relative to repo_dir.
+	ActiveFilePath []string `protobuf:"bytes,2,rep,name=active_file_path,json=activeFilePath,proto3" json:"active_file_path,omitempty"`
+	// Repository relative path to output directory in workstation.
+	OutDir string `protobuf:"bytes,3,opt,name=out_dir,json=outDir,proto3" json:"out_dir,omitempty"`
+	// Repository relative path to compile_commands.json in workstation.
+	CompDbPath string `protobuf:"bytes,4,opt,name=comp_db_path,json=compDbPath,proto3" json:"comp_db_path,omitempty"`
+}
+
+func (x *RepoState) Reset() {
+	*x = RepoState{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_cc_analyzer_proto_msgTypes[1]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *RepoState) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*RepoState) ProtoMessage() {}
+
+func (x *RepoState) ProtoReflect() protoreflect.Message {
+	mi := &file_cc_analyzer_proto_msgTypes[1]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use RepoState.ProtoReflect.Descriptor instead.
+func (*RepoState) Descriptor() ([]byte, []int) {
+	return file_cc_analyzer_proto_rawDescGZIP(), []int{1}
+}
+
+func (x *RepoState) GetRepoDir() string {
+	if x != nil {
+		return x.RepoDir
+	}
+	return ""
+}
+
+func (x *RepoState) GetActiveFilePath() []string {
+	if x != nil {
+		return x.ActiveFilePath
+	}
+	return nil
+}
+
+func (x *RepoState) GetOutDir() string {
+	if x != nil {
+		return x.OutDir
+	}
+	return ""
+}
+
+func (x *RepoState) GetCompDbPath() string {
+	if x != nil {
+		return x.CompDbPath
+	}
+	return ""
+}
+
+// Provides all the targets that are pre-requisities for running language
+// services on active_file_paths.
+type DepsResponse struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Deps   []*DepsResponse_Deps `protobuf:"bytes,1,rep,name=deps,proto3" json:"deps,omitempty"`
+	Status *Status              `protobuf:"bytes,2,opt,name=status,proto3,oneof" json:"status,omitempty"`
+}
+
+func (x *DepsResponse) Reset() {
+	*x = DepsResponse{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_cc_analyzer_proto_msgTypes[2]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *DepsResponse) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*DepsResponse) ProtoMessage() {}
+
+func (x *DepsResponse) ProtoReflect() protoreflect.Message {
+	mi := &file_cc_analyzer_proto_msgTypes[2]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use DepsResponse.ProtoReflect.Descriptor instead.
+func (*DepsResponse) Descriptor() ([]byte, []int) {
+	return file_cc_analyzer_proto_rawDescGZIP(), []int{2}
+}
+
+func (x *DepsResponse) GetDeps() []*DepsResponse_Deps {
+	if x != nil {
+		return x.Deps
+	}
+	return nil
+}
+
+func (x *DepsResponse) GetStatus() *Status {
+	if x != nil {
+		return x.Status
+	}
+	return nil
+}
+
+// Returns all the information necessary for providing language services for the
+// active files.
+type GeneratedFile struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// Path to the file relative to repository root.
+	Path string `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"`
+	// The text of the generated file, if not provided contents will be read
+	// from the path above in user's workstation.
+	Contents []byte `protobuf:"bytes,2,opt,name=contents,proto3,oneof" json:"contents,omitempty"`
+}
+
+func (x *GeneratedFile) Reset() {
+	*x = GeneratedFile{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_cc_analyzer_proto_msgTypes[3]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *GeneratedFile) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*GeneratedFile) ProtoMessage() {}
+
+func (x *GeneratedFile) ProtoReflect() protoreflect.Message {
+	mi := &file_cc_analyzer_proto_msgTypes[3]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use GeneratedFile.ProtoReflect.Descriptor instead.
+func (*GeneratedFile) Descriptor() ([]byte, []int) {
+	return file_cc_analyzer_proto_rawDescGZIP(), []int{3}
+}
+
+func (x *GeneratedFile) GetPath() string {
+	if x != nil {
+		return x.Path
+	}
+	return ""
+}
+
+func (x *GeneratedFile) GetContents() []byte {
+	if x != nil {
+		return x.Contents
+	}
+	return nil
+}
+
+type SourceFile struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// Path to the source file relative to repository root.
+	Path string `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"`
+	// Working directory used by the build system. All the relative
+	// paths in compiler_arguments should be relative to this path.
+	// Relative to repository root.
+	WorkingDir string `protobuf:"bytes,2,opt,name=working_dir,json=workingDir,proto3" json:"working_dir,omitempty"`
+	// Compiler arguments to compile the source file. If multiple variants
+	// of the module being compiled are possible, the query script will choose
+	// one.
+	CompilerArguments []string `protobuf:"bytes,3,rep,name=compiler_arguments,json=compilerArguments,proto3" json:"compiler_arguments,omitempty"`
+	// Any generated files that are used in compiling the file.
+	Generated []*GeneratedFile `protobuf:"bytes,4,rep,name=generated,proto3" json:"generated,omitempty"`
+	// Paths to all of the sources, like build files, code generators,
+	// proto files etc. that were used during analysis. Used to figure
+	// out when a set of build artifacts are stale and the query tool
+	// must be re-run.
+	// Relative to repository root.
+	Deps []string `protobuf:"bytes,5,rep,name=deps,proto3" json:"deps,omitempty"`
+	// Represents analysis status for this particular file. e.g. not part
+	// of the build graph.
+	Status *Status `protobuf:"bytes,6,opt,name=status,proto3,oneof" json:"status,omitempty"`
+}
+
+func (x *SourceFile) Reset() {
+	*x = SourceFile{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_cc_analyzer_proto_msgTypes[4]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *SourceFile) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*SourceFile) ProtoMessage() {}
+
+func (x *SourceFile) ProtoReflect() protoreflect.Message {
+	mi := &file_cc_analyzer_proto_msgTypes[4]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use SourceFile.ProtoReflect.Descriptor instead.
+func (*SourceFile) Descriptor() ([]byte, []int) {
+	return file_cc_analyzer_proto_rawDescGZIP(), []int{4}
+}
+
+func (x *SourceFile) GetPath() string {
+	if x != nil {
+		return x.Path
+	}
+	return ""
+}
+
+func (x *SourceFile) GetWorkingDir() string {
+	if x != nil {
+		return x.WorkingDir
+	}
+	return ""
+}
+
+func (x *SourceFile) GetCompilerArguments() []string {
+	if x != nil {
+		return x.CompilerArguments
+	}
+	return nil
+}
+
+func (x *SourceFile) GetGenerated() []*GeneratedFile {
+	if x != nil {
+		return x.Generated
+	}
+	return nil
+}
+
+func (x *SourceFile) GetDeps() []string {
+	if x != nil {
+		return x.Deps
+	}
+	return nil
+}
+
+func (x *SourceFile) GetStatus() *Status {
+	if x != nil {
+		return x.Status
+	}
+	return nil
+}
+
+type IdeAnalysis struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Sources []*SourceFile `protobuf:"bytes,2,rep,name=sources,proto3" json:"sources,omitempty"`
+	// Status representing overall analysis.
+	// Should fail only when no analysis can be performed.
+	Status *Status `protobuf:"bytes,3,opt,name=status,proto3,oneof" json:"status,omitempty"`
+}
+
+func (x *IdeAnalysis) Reset() {
+	*x = IdeAnalysis{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_cc_analyzer_proto_msgTypes[5]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *IdeAnalysis) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*IdeAnalysis) ProtoMessage() {}
+
+func (x *IdeAnalysis) ProtoReflect() protoreflect.Message {
+	mi := &file_cc_analyzer_proto_msgTypes[5]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use IdeAnalysis.ProtoReflect.Descriptor instead.
+func (*IdeAnalysis) Descriptor() ([]byte, []int) {
+	return file_cc_analyzer_proto_rawDescGZIP(), []int{5}
+}
+
+func (x *IdeAnalysis) GetSources() []*SourceFile {
+	if x != nil {
+		return x.Sources
+	}
+	return nil
+}
+
+func (x *IdeAnalysis) GetStatus() *Status {
+	if x != nil {
+		return x.Status
+	}
+	return nil
+}
+
+// Build dependencies of a source file for providing language services.
+type DepsResponse_Deps struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// Relative to repo_dir.
+	SourceFile string `protobuf:"bytes,1,opt,name=source_file,json=sourceFile,proto3" json:"source_file,omitempty"`
+	// Build target to execute for generating dep.
+	BuildTarget []string `protobuf:"bytes,2,rep,name=build_target,json=buildTarget,proto3" json:"build_target,omitempty"`
+	Status      *Status  `protobuf:"bytes,3,opt,name=status,proto3,oneof" json:"status,omitempty"`
+}
+
+func (x *DepsResponse_Deps) Reset() {
+	*x = DepsResponse_Deps{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_cc_analyzer_proto_msgTypes[6]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *DepsResponse_Deps) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*DepsResponse_Deps) ProtoMessage() {}
+
+func (x *DepsResponse_Deps) ProtoReflect() protoreflect.Message {
+	mi := &file_cc_analyzer_proto_msgTypes[6]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use DepsResponse_Deps.ProtoReflect.Descriptor instead.
+func (*DepsResponse_Deps) Descriptor() ([]byte, []int) {
+	return file_cc_analyzer_proto_rawDescGZIP(), []int{2, 0}
+}
+
+func (x *DepsResponse_Deps) GetSourceFile() string {
+	if x != nil {
+		return x.SourceFile
+	}
+	return ""
+}
+
+func (x *DepsResponse_Deps) GetBuildTarget() []string {
+	if x != nil {
+		return x.BuildTarget
+	}
+	return nil
+}
+
+func (x *DepsResponse_Deps) GetStatus() *Status {
+	if x != nil {
+		return x.Status
+	}
+	return nil
+}
+
+var File_cc_analyzer_proto protoreflect.FileDescriptor
+
+var file_cc_analyzer_proto_rawDesc = []byte{
+	0x0a, 0x11, 0x63, 0x63, 0x5f, 0x61, 0x6e, 0x61, 0x6c, 0x79, 0x7a, 0x65, 0x72, 0x2e, 0x70, 0x72,
+	0x6f, 0x74, 0x6f, 0x12, 0x0b, 0x63, 0x63, 0x5f, 0x61, 0x6e, 0x61, 0x6c, 0x79, 0x7a, 0x65, 0x72,
+	0x22, 0x7e, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x2c, 0x0a, 0x04, 0x63, 0x6f,
+	0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x18, 0x2e, 0x63, 0x63, 0x5f, 0x61, 0x6e,
+	0x61, 0x6c, 0x79, 0x7a, 0x65, 0x72, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x2e, 0x43, 0x6f,
+	0x64, 0x65, 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x1d, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73,
+	0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x07, 0x6d, 0x65, 0x73,
+	0x73, 0x61, 0x67, 0x65, 0x88, 0x01, 0x01, 0x22, 0x1b, 0x0a, 0x04, 0x43, 0x6f, 0x64, 0x65, 0x12,
+	0x06, 0x0a, 0x02, 0x4f, 0x4b, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x46, 0x41, 0x49, 0x4c, 0x55,
+	0x52, 0x45, 0x10, 0x01, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65,
+	0x22, 0x8b, 0x01, 0x0a, 0x09, 0x52, 0x65, 0x70, 0x6f, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x19,
+	0x0a, 0x08, 0x72, 0x65, 0x70, 0x6f, 0x5f, 0x64, 0x69, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
+	0x52, 0x07, 0x72, 0x65, 0x70, 0x6f, 0x44, 0x69, 0x72, 0x12, 0x28, 0x0a, 0x10, 0x61, 0x63, 0x74,
+	0x69, 0x76, 0x65, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20,
+	0x03, 0x28, 0x09, 0x52, 0x0e, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x50,
+	0x61, 0x74, 0x68, 0x12, 0x17, 0x0a, 0x07, 0x6f, 0x75, 0x74, 0x5f, 0x64, 0x69, 0x72, 0x18, 0x03,
+	0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6f, 0x75, 0x74, 0x44, 0x69, 0x72, 0x12, 0x20, 0x0a, 0x0c,
+	0x63, 0x6f, 0x6d, 0x70, 0x5f, 0x64, 0x62, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x04, 0x20, 0x01,
+	0x28, 0x09, 0x52, 0x0a, 0x63, 0x6f, 0x6d, 0x70, 0x44, 0x62, 0x50, 0x61, 0x74, 0x68, 0x22, 0x89,
+	0x02, 0x0a, 0x0c, 0x44, 0x65, 0x70, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12,
+	0x32, 0x0a, 0x04, 0x64, 0x65, 0x70, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e,
+	0x63, 0x63, 0x5f, 0x61, 0x6e, 0x61, 0x6c, 0x79, 0x7a, 0x65, 0x72, 0x2e, 0x44, 0x65, 0x70, 0x73,
+	0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x44, 0x65, 0x70, 0x73, 0x52, 0x04, 0x64,
+	0x65, 0x70, 0x73, 0x12, 0x30, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20,
+	0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x63, 0x63, 0x5f, 0x61, 0x6e, 0x61, 0x6c, 0x79, 0x7a, 0x65,
+	0x72, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x48, 0x00, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74,
+	0x75, 0x73, 0x88, 0x01, 0x01, 0x1a, 0x87, 0x01, 0x0a, 0x04, 0x44, 0x65, 0x70, 0x73, 0x12, 0x1f,
+	0x0a, 0x0b, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x01, 0x20,
+	0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x12,
+	0x21, 0x0a, 0x0c, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x18,
+	0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0b, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x54, 0x61, 0x72, 0x67,
+	0x65, 0x74, 0x12, 0x30, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x03, 0x20, 0x01,
+	0x28, 0x0b, 0x32, 0x13, 0x2e, 0x63, 0x63, 0x5f, 0x61, 0x6e, 0x61, 0x6c, 0x79, 0x7a, 0x65, 0x72,
+	0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x48, 0x00, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75,
+	0x73, 0x88, 0x01, 0x01, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x42,
+	0x09, 0x0a, 0x07, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x51, 0x0a, 0x0d, 0x47, 0x65,
+	0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x70,
+	0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12,
+	0x1f, 0x0a, 0x08, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28,
+	0x0c, 0x48, 0x00, 0x52, 0x08, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x88, 0x01, 0x01,
+	0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x22, 0xfb, 0x01,
+	0x0a, 0x0a, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x12, 0x0a, 0x04,
+	0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68,
+	0x12, 0x1f, 0x0a, 0x0b, 0x77, 0x6f, 0x72, 0x6b, 0x69, 0x6e, 0x67, 0x5f, 0x64, 0x69, 0x72, 0x18,
+	0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x77, 0x6f, 0x72, 0x6b, 0x69, 0x6e, 0x67, 0x44, 0x69,
+	0x72, 0x12, 0x2d, 0x0a, 0x12, 0x63, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x61, 0x72,
+	0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x11, 0x63,
+	0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x72, 0x41, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73,
+	0x12, 0x38, 0x0a, 0x09, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x18, 0x04, 0x20,
+	0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x63, 0x63, 0x5f, 0x61, 0x6e, 0x61, 0x6c, 0x79, 0x7a, 0x65,
+	0x72, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x46, 0x69, 0x6c, 0x65, 0x52,
+	0x09, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x65,
+	0x70, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x64, 0x65, 0x70, 0x73, 0x12, 0x30,
+	0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13,
+	0x2e, 0x63, 0x63, 0x5f, 0x61, 0x6e, 0x61, 0x6c, 0x79, 0x7a, 0x65, 0x72, 0x2e, 0x53, 0x74, 0x61,
+	0x74, 0x75, 0x73, 0x48, 0x00, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x88, 0x01, 0x01,
+	0x42, 0x09, 0x0a, 0x07, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x83, 0x01, 0x0a, 0x0b,
+	0x49, 0x64, 0x65, 0x41, 0x6e, 0x61, 0x6c, 0x79, 0x73, 0x69, 0x73, 0x12, 0x31, 0x0a, 0x07, 0x73,
+	0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x63,
+	0x63, 0x5f, 0x61, 0x6e, 0x61, 0x6c, 0x79, 0x7a, 0x65, 0x72, 0x2e, 0x53, 0x6f, 0x75, 0x72, 0x63,
+	0x65, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x07, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x30,
+	0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13,
+	0x2e, 0x63, 0x63, 0x5f, 0x61, 0x6e, 0x61, 0x6c, 0x79, 0x7a, 0x65, 0x72, 0x2e, 0x53, 0x74, 0x61,
+	0x74, 0x75, 0x73, 0x48, 0x00, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x88, 0x01, 0x01,
+	0x42, 0x09, 0x0a, 0x07, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x4a, 0x04, 0x08, 0x01, 0x10,
+	0x02, 0x42, 0x1d, 0x5a, 0x1b, 0x69, 0x64, 0x65, 0x5f, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2f, 0x63,
+	0x63, 0x5f, 0x61, 0x6e, 0x61, 0x6c, 0x79, 0x7a, 0x65, 0x72, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+	0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+}
+
+var (
+	file_cc_analyzer_proto_rawDescOnce sync.Once
+	file_cc_analyzer_proto_rawDescData = file_cc_analyzer_proto_rawDesc
+)
+
+func file_cc_analyzer_proto_rawDescGZIP() []byte {
+	file_cc_analyzer_proto_rawDescOnce.Do(func() {
+		file_cc_analyzer_proto_rawDescData = protoimpl.X.CompressGZIP(file_cc_analyzer_proto_rawDescData)
+	})
+	return file_cc_analyzer_proto_rawDescData
+}
+
+var file_cc_analyzer_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
+var file_cc_analyzer_proto_msgTypes = make([]protoimpl.MessageInfo, 7)
+var file_cc_analyzer_proto_goTypes = []interface{}{
+	(Status_Code)(0),          // 0: cc_analyzer.Status.Code
+	(*Status)(nil),            // 1: cc_analyzer.Status
+	(*RepoState)(nil),         // 2: cc_analyzer.RepoState
+	(*DepsResponse)(nil),      // 3: cc_analyzer.DepsResponse
+	(*GeneratedFile)(nil),     // 4: cc_analyzer.GeneratedFile
+	(*SourceFile)(nil),        // 5: cc_analyzer.SourceFile
+	(*IdeAnalysis)(nil),       // 6: cc_analyzer.IdeAnalysis
+	(*DepsResponse_Deps)(nil), // 7: cc_analyzer.DepsResponse.Deps
+}
+var file_cc_analyzer_proto_depIdxs = []int32{
+	0, // 0: cc_analyzer.Status.code:type_name -> cc_analyzer.Status.Code
+	7, // 1: cc_analyzer.DepsResponse.deps:type_name -> cc_analyzer.DepsResponse.Deps
+	1, // 2: cc_analyzer.DepsResponse.status:type_name -> cc_analyzer.Status
+	4, // 3: cc_analyzer.SourceFile.generated:type_name -> cc_analyzer.GeneratedFile
+	1, // 4: cc_analyzer.SourceFile.status:type_name -> cc_analyzer.Status
+	5, // 5: cc_analyzer.IdeAnalysis.sources:type_name -> cc_analyzer.SourceFile
+	1, // 6: cc_analyzer.IdeAnalysis.status:type_name -> cc_analyzer.Status
+	1, // 7: cc_analyzer.DepsResponse.Deps.status:type_name -> cc_analyzer.Status
+	8, // [8:8] is the sub-list for method output_type
+	8, // [8:8] is the sub-list for method input_type
+	8, // [8:8] is the sub-list for extension type_name
+	8, // [8:8] is the sub-list for extension extendee
+	0, // [0:8] is the sub-list for field type_name
+}
+
+func init() { file_cc_analyzer_proto_init() }
+func file_cc_analyzer_proto_init() {
+	if File_cc_analyzer_proto != nil {
+		return
+	}
+	if !protoimpl.UnsafeEnabled {
+		file_cc_analyzer_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*Status); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_cc_analyzer_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*RepoState); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_cc_analyzer_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*DepsResponse); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_cc_analyzer_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*GeneratedFile); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_cc_analyzer_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*SourceFile); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_cc_analyzer_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*IdeAnalysis); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_cc_analyzer_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*DepsResponse_Deps); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+	}
+	file_cc_analyzer_proto_msgTypes[0].OneofWrappers = []interface{}{}
+	file_cc_analyzer_proto_msgTypes[2].OneofWrappers = []interface{}{}
+	file_cc_analyzer_proto_msgTypes[3].OneofWrappers = []interface{}{}
+	file_cc_analyzer_proto_msgTypes[4].OneofWrappers = []interface{}{}
+	file_cc_analyzer_proto_msgTypes[5].OneofWrappers = []interface{}{}
+	file_cc_analyzer_proto_msgTypes[6].OneofWrappers = []interface{}{}
+	type x struct{}
+	out := protoimpl.TypeBuilder{
+		File: protoimpl.DescBuilder{
+			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+			RawDescriptor: file_cc_analyzer_proto_rawDesc,
+			NumEnums:      1,
+			NumMessages:   7,
+			NumExtensions: 0,
+			NumServices:   0,
+		},
+		GoTypes:           file_cc_analyzer_proto_goTypes,
+		DependencyIndexes: file_cc_analyzer_proto_depIdxs,
+		EnumInfos:         file_cc_analyzer_proto_enumTypes,
+		MessageInfos:      file_cc_analyzer_proto_msgTypes,
+	}.Build()
+	File_cc_analyzer_proto = out.File
+	file_cc_analyzer_proto_rawDesc = nil
+	file_cc_analyzer_proto_goTypes = nil
+	file_cc_analyzer_proto_depIdxs = nil
+}
diff --git a/tools/ide_query/cc_analyzer_proto/cc_analyzer.proto b/tools/ide_query/cc_analyzer_proto/cc_analyzer.proto
new file mode 100644
index 0000000..094eb49
--- /dev/null
+++ b/tools/ide_query/cc_analyzer_proto/cc_analyzer.proto
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+syntax = "proto3";
+
+package cc_analyzer;
+
+option go_package = "ide_query/cc_analyzer_proto";
+
+// Indicates the success/failure for analysis.
+message Status {
+  enum Code {
+    OK = 0;
+    FAILURE = 1;
+  }
+  Code code = 1;
+  // Details about the status, might be displayed to user.
+  optional string message = 2;
+}
+
+// Represents an Android checkout on user's workstation.
+message RepoState {
+  // Absolute path for the checkout in the workstation.
+  // e.g. /home/user/work/android/
+  string repo_dir = 1;
+  // Relative to repo_dir.
+  repeated string active_file_path = 2;
+  // Repository relative path to output directory in workstation.
+  string out_dir = 3;
+  // Repository relative path to compile_commands.json in workstation.
+  string comp_db_path = 4;
+}
+
+// Provides all the targets that are pre-requisities for running language
+// services on active_file_paths.
+message DepsResponse {
+  // Build dependencies of a source file for providing language services.
+  message Deps {
+    // Relative to repo_dir.
+    string source_file = 1;
+    // Build target to execute for generating dep.
+    repeated string build_target = 2;
+    optional Status status = 3;
+  }
+  repeated Deps deps = 1;
+  optional Status status = 2;
+}
+
+// Returns all the information necessary for providing language services for the
+// active files.
+message GeneratedFile {
+  // Path to the file relative to repository root.
+  string path = 1;
+
+  // The text of the generated file, if not provided contents will be read
+  // from the path above in user's workstation.
+  optional bytes contents = 2;
+}
+
+message SourceFile {
+  // Path to the source file relative to repository root.
+  string path = 1;
+
+  // Working directory used by the build system. All the relative
+  // paths in compiler_arguments should be relative to this path.
+  // Relative to repository root.
+  string working_dir = 2;
+
+  // Compiler arguments to compile the source file. If multiple variants
+  // of the module being compiled are possible, the query script will choose
+  // one.
+  repeated string compiler_arguments = 3;
+
+  // Any generated files that are used in compiling the file.
+  repeated GeneratedFile generated = 4;
+
+  // Paths to all of the sources, like build files, code generators,
+  // proto files etc. that were used during analysis. Used to figure
+  // out when a set of build artifacts are stale and the query tool
+  // must be re-run.
+  // Relative to repository root.
+  repeated string deps = 5;
+
+  // Represents analysis status for this particular file. e.g. not part
+  // of the build graph.
+  optional Status status = 6;
+}
+
+message IdeAnalysis {
+  repeated SourceFile sources = 2;
+
+  // Status representing overall analysis.
+  // Should fail only when no analysis can be performed.
+  optional Status status = 3;
+
+  reserved 1;
+}
diff --git a/tools/ide_query/cc_analyzer_proto/regen.sh b/tools/ide_query/cc_analyzer_proto/regen.sh
new file mode 100755
index 0000000..ef44f88
--- /dev/null
+++ b/tools/ide_query/cc_analyzer_proto/regen.sh
@@ -0,0 +1,3 @@
+#!/bin/bash
+
+aprotoc --go_out=paths=source_relative:. cc_analyzer.proto
diff --git a/tools/ide_query/ide_query.go b/tools/ide_query/ide_query.go
index de84fbe..23c7abd 100644
--- a/tools/ide_query/ide_query.go
+++ b/tools/ide_query/ide_query.go
@@ -33,6 +33,7 @@
 	"strings"
 
 	"google.golang.org/protobuf/proto"
+	apb "ide_query/cc_analyzer_proto"
 	pb "ide_query/ide_query_proto"
 )
 
@@ -42,9 +43,6 @@
 	RepoDir        string
 	OutDir         string
 	ClangToolsRoot string
-
-	CcFiles   []string
-	JavaFiles []string
 }
 
 // LunchTarget is a parsed Android lunch target.
@@ -83,7 +81,7 @@
 
 func main() {
 	var env Env
-	env.OutDir = os.Getenv("OUT_DIR")
+	env.OutDir = strings.TrimSuffix(os.Getenv("OUT_DIR"), "/")
 	env.RepoDir = os.Getenv("ANDROID_BUILD_TOP")
 	env.ClangToolsRoot = os.Getenv("PREBUILTS_CLANG_TOOLS_ROOT")
 	flag.Var(&env.LunchTarget, "lunch_target", "The lunch target to query")
@@ -95,12 +93,13 @@
 		return
 	}
 
+	var ccFiles, javaFiles []string
 	for _, f := range files {
 		switch {
 		case strings.HasSuffix(f, ".java") || strings.HasSuffix(f, ".kt"):
-			env.JavaFiles = append(env.JavaFiles, f)
+			javaFiles = append(javaFiles, f)
 		case strings.HasSuffix(f, ".cc") || strings.HasSuffix(f, ".cpp") || strings.HasSuffix(f, ".h"):
-			env.CcFiles = append(env.CcFiles, f)
+			ccFiles = append(ccFiles, f)
 		default:
 			log.Printf("File %q is supported - will be skipped.", f)
 		}
@@ -110,28 +109,54 @@
 	// TODO(michaelmerg): Figure out if module_bp_java_deps.json and compile_commands.json is outdated.
 	runMake(ctx, env, "nothing")
 
-	javaModules, javaFileToModuleMap, err := loadJavaModules(&env)
+	javaModules, err := loadJavaModules(env)
 	if err != nil {
 		log.Printf("Failed to load java modules: %v", err)
 	}
-	toMake := getJavaTargets(javaFileToModuleMap)
 
-	ccTargets, status := getCCTargets(ctx, &env)
-	if status != nil && status.Code != pb.Status_OK {
-		log.Fatalf("Failed to query cc targets: %v", *status.Message)
-	}
-	toMake = append(toMake, ccTargets...)
-	fmt.Fprintf(os.Stderr, "Running make for modules: %v\n", strings.Join(toMake, ", "))
-	if err := runMake(ctx, env, toMake...); err != nil {
-		log.Printf("Building deps failed: %v", err)
+	var targets []string
+	javaTargetsByFile := findJavaModules(javaFiles, javaModules)
+	for _, t := range javaTargetsByFile {
+		targets = append(targets, t)
 	}
 
-	res := getJavaInputs(&env, javaModules, javaFileToModuleMap)
-	ccAnalysis := getCCInputs(ctx, &env)
-	proto.Merge(res, ccAnalysis)
+	ccTargets, err := getCCTargets(ctx, env, ccFiles)
+	if err != nil {
+		log.Fatalf("Failed to query cc targets: %v", err)
+	}
+	targets = append(targets, ccTargets...)
+	if len(targets) == 0 {
+		fmt.Println("No targets found.")
+		os.Exit(1)
+		return
+	}
 
-	res.BuildArtifactRoot = env.OutDir
-	data, err := proto.Marshal(res)
+	fmt.Fprintf(os.Stderr, "Running make for modules: %v\n", strings.Join(targets, ", "))
+	if err := runMake(ctx, env, targets...); err != nil {
+		log.Printf("Building modules failed: %v", err)
+	}
+
+	var analysis pb.IdeAnalysis
+	results, units := getJavaInputs(env, javaTargetsByFile, javaModules)
+	analysis.Results = results
+	analysis.Units = units
+	if err != nil && analysis.Error == nil {
+		analysis.Error = &pb.AnalysisError{
+			ErrorMessage: err.Error(),
+		}
+	}
+
+	results, units, err = getCCInputs(ctx, env, ccFiles)
+	analysis.Results = append(analysis.Results, results...)
+	analysis.Units = append(analysis.Units, units...)
+	if err != nil && analysis.Error == nil {
+		analysis.Error = &pb.AnalysisError{
+			ErrorMessage: err.Error(),
+		}
+	}
+
+	analysis.BuildOutDir = env.OutDir
+	data, err := proto.Marshal(&analysis)
 	if err != nil {
 		log.Fatalf("Failed to marshal result proto: %v", err)
 	}
@@ -141,22 +166,22 @@
 		log.Fatalf("Failed to write result proto: %v", err)
 	}
 
-	for _, s := range res.Sources {
-		fmt.Fprintf(os.Stderr, "%s: %v (Deps: %d, Generated: %d)\n", s.GetPath(), s.GetStatus(), len(s.GetDeps()), len(s.GetGenerated()))
+	for _, r := range analysis.Results {
+		fmt.Fprintf(os.Stderr, "%s: %+v\n", r.GetSourceFilePath(), r.GetStatus())
 	}
 }
 
-func repoState(env *Env) *pb.RepoState {
+func repoState(env Env, filePaths []string) *apb.RepoState {
 	const compDbPath = "soong/development/ide/compdb/compile_commands.json"
-	return &pb.RepoState{
+	return &apb.RepoState{
 		RepoDir:        env.RepoDir,
-		ActiveFilePath: env.CcFiles,
+		ActiveFilePath: filePaths,
 		OutDir:         env.OutDir,
 		CompDbPath:     path.Join(env.OutDir, compDbPath),
 	}
 }
 
-func runCCanalyzer(ctx context.Context, env *Env, mode string, in []byte) ([]byte, error) {
+func runCCanalyzer(ctx context.Context, env Env, mode string, in []byte) ([]byte, error) {
 	ccAnalyzerPath := path.Join(env.ClangToolsRoot, "bin/ide_query_cc_analyzer")
 	outBuffer := new(bytes.Buffer)
 
@@ -176,127 +201,205 @@
 }
 
 // Execute cc_analyzer and get all the targets that needs to be build for analyzing files.
-func getCCTargets(ctx context.Context, env *Env) ([]string, *pb.Status) {
-	state := repoState(env)
-	bytes, err := proto.Marshal(state)
+func getCCTargets(ctx context.Context, env Env, filePaths []string) ([]string, error) {
+	state, err := proto.Marshal(repoState(env, filePaths))
 	if err != nil {
 		log.Fatalln("Failed to serialize state:", err)
 	}
 
-	resp := new(pb.DepsResponse)
-	result, err := runCCanalyzer(ctx, env, "deps", bytes)
-	if marshal_err := proto.Unmarshal(result, resp); marshal_err != nil {
-		return nil, &pb.Status{
-			Code:    pb.Status_FAILURE,
-			Message: proto.String("Malformed response from cc_analyzer: " + marshal_err.Error()),
-		}
+	resp := new(apb.DepsResponse)
+	result, err := runCCanalyzer(ctx, env, "deps", state)
+	if err != nil {
+		return nil, err
+	}
+
+	if err := proto.Unmarshal(result, resp); err != nil {
+		return nil, fmt.Errorf("malformed response from cc_analyzer: %v", err)
 	}
 
 	var targets []string
-	if resp.Status != nil && resp.Status.Code != pb.Status_OK {
-		return targets, resp.Status
+	if resp.Status != nil && resp.Status.Code != apb.Status_OK {
+		return targets, fmt.Errorf("cc_analyzer failed: %v", resp.Status.Message)
 	}
+
 	for _, deps := range resp.Deps {
 		targets = append(targets, deps.BuildTarget...)
 	}
-
-	status := &pb.Status{Code: pb.Status_OK}
-	if err != nil {
-		status = &pb.Status{
-			Code:    pb.Status_FAILURE,
-			Message: proto.String(err.Error()),
-		}
-	}
-	return targets, status
+	return targets, nil
 }
 
-func getCCInputs(ctx context.Context, env *Env) *pb.IdeAnalysis {
-	state := repoState(env)
-	bytes, err := proto.Marshal(state)
+func getCCInputs(ctx context.Context, env Env, filePaths []string) ([]*pb.AnalysisResult, []*pb.BuildableUnit, error) {
+	state, err := proto.Marshal(repoState(env, filePaths))
 	if err != nil {
 		log.Fatalln("Failed to serialize state:", err)
 	}
 
-	resp := new(pb.IdeAnalysis)
-	result, err := runCCanalyzer(ctx, env, "inputs", bytes)
-	if marshal_err := proto.Unmarshal(result, resp); marshal_err != nil {
-		resp.Status = &pb.Status{
-			Code:    pb.Status_FAILURE,
-			Message: proto.String("Malformed response from cc_analyzer: " + marshal_err.Error()),
-		}
-		return resp
+	resp := new(apb.IdeAnalysis)
+	result, err := runCCanalyzer(ctx, env, "inputs", state)
+	if err != nil {
+		return nil, nil, fmt.Errorf("cc_analyzer failed:", err)
+	}
+	if err := proto.Unmarshal(result, resp); err != nil {
+		return nil, nil, fmt.Errorf("malformed response from cc_analyzer: %v", err)
+	}
+	if resp.Status != nil && resp.Status.Code != apb.Status_OK {
+		return nil, nil, fmt.Errorf("cc_analyzer failed: %v", resp.Status.Message)
 	}
 
-	if err != nil && (resp.Status == nil || resp.Status.Code == pb.Status_OK) {
-		resp.Status = &pb.Status{
-			Code:    pb.Status_FAILURE,
-			Message: proto.String(err.Error()),
+	var results []*pb.AnalysisResult
+	var units []*pb.BuildableUnit
+	for _, s := range resp.Sources {
+		status := &pb.AnalysisResult_Status{
+			Code: pb.AnalysisResult_Status_CODE_OK,
 		}
-	}
-	return resp
-}
-
-func getJavaTargets(javaFileToModuleMap map[string]*javaModule) []string {
-	var targets []string
-	for _, m := range javaFileToModuleMap {
-		targets = append(targets, m.Name)
-	}
-	return targets
-}
-
-func getJavaInputs(env *Env, javaModules map[string]*javaModule, javaFileToModuleMap map[string]*javaModule) *pb.IdeAnalysis {
-	var sources []*pb.SourceFile
-	type depsAndGenerated struct {
-		Deps      []string
-		Generated []*pb.GeneratedFile
-	}
-	moduleToDeps := make(map[string]*depsAndGenerated)
-	for _, f := range env.JavaFiles {
-		file := &pb.SourceFile{
-			Path: f,
-		}
-		sources = append(sources, file)
-
-		m := javaFileToModuleMap[f]
-		if m == nil {
-			file.Status = &pb.Status{
-				Code:    pb.Status_FAILURE,
-				Message: proto.String("File not found in any module."),
-			}
-			continue
+		if s.GetStatus().GetCode() != apb.Status_OK {
+			status.Code = pb.AnalysisResult_Status_CODE_BUILD_FAILED
+			status.StatusMessage = proto.String(s.GetStatus().GetMessage())
 		}
 
-		file.Status = &pb.Status{Code: pb.Status_OK}
-		if moduleToDeps[m.Name] != nil {
-			file.Generated = moduleToDeps[m.Name].Generated
-			file.Deps = moduleToDeps[m.Name].Deps
-			continue
+		result := &pb.AnalysisResult{
+			SourceFilePath: s.GetPath(),
+			UnitId:         s.GetPath(),
+			Status:         status,
 		}
+		results = append(results, result)
 
-		deps := transitiveDeps(m, javaModules)
 		var generated []*pb.GeneratedFile
-		outPrefix := env.OutDir + "/"
-		for _, d := range deps {
-			if relPath, ok := strings.CutPrefix(d, outPrefix); ok {
-				contents, err := os.ReadFile(d)
-				if err != nil {
-					fmt.Printf("Generated file %q not found - will be skipped.\n", d)
-					continue
-				}
+		for _, f := range s.Generated {
+			generated = append(generated, &pb.GeneratedFile{
+				Path:     f.GetPath(),
+				Contents: f.GetContents(),
+			})
+		}
+		genUnit := &pb.BuildableUnit{
+			Id:              "genfiles_for_" + s.GetPath(),
+			SourceFilePaths: s.GetDeps(),
+			GeneratedFiles:  generated,
+		}
 
-				generated = append(generated, &pb.GeneratedFile{
-					Path:     relPath,
-					Contents: contents,
-				})
+		unit := &pb.BuildableUnit{
+			Id:                s.GetPath(),
+			Language:          pb.Language_LANGUAGE_CPP,
+			SourceFilePaths:   []string{s.GetPath()},
+			CompilerArguments: s.GetCompilerArguments(),
+			DependencyIds:     []string{genUnit.GetId()},
+		}
+		units = append(units, unit, genUnit)
+	}
+	return results, units, nil
+}
+
+// findJavaModules tries to find the modules that cover the given file paths.
+// If a file is covered by multiple modules, the first module is returned.
+func findJavaModules(paths []string, modules map[string]*javaModule) map[string]string {
+	ret := make(map[string]string)
+	for name, module := range modules {
+		if strings.HasSuffix(name, ".impl") {
+			continue
+		}
+
+		for i, p := range paths {
+			if slices.Contains(module.Srcs, p) {
+				ret[p] = name
+				paths = append(paths[:i], paths[i+1:]...)
+				break
 			}
 		}
-		moduleToDeps[m.Name] = &depsAndGenerated{deps, generated}
-		file.Generated = generated
-		file.Deps = deps
+		if len(paths) == 0 {
+			break
+		}
 	}
-	return &pb.IdeAnalysis{
-		Sources: sources,
+	return ret
+}
+
+func getJavaInputs(env Env, modulesByPath map[string]string, modules map[string]*javaModule) ([]*pb.AnalysisResult, []*pb.BuildableUnit) {
+	var results []*pb.AnalysisResult
+	unitsById := make(map[string]*pb.BuildableUnit)
+	for p, moduleName := range modulesByPath {
+		r := &pb.AnalysisResult{
+			SourceFilePath: p,
+		}
+		results = append(results, r)
+
+		m := modules[moduleName]
+		if m == nil {
+			r.Status = &pb.AnalysisResult_Status{
+				Code:          pb.AnalysisResult_Status_CODE_NOT_FOUND,
+				StatusMessage: proto.String("File not found in any module."),
+			}
+			continue
+		}
+
+		r.UnitId = moduleName
+		r.Status = &pb.AnalysisResult_Status{Code: pb.AnalysisResult_Status_CODE_OK}
+		if unitsById[r.UnitId] != nil {
+			// File is covered by an already created unit.
+			continue
+		}
+
+		u := &pb.BuildableUnit{
+			Id:              moduleName,
+			Language:        pb.Language_LANGUAGE_JAVA,
+			SourceFilePaths: m.Srcs,
+		}
+		unitsById[u.Id] = u
+
+		q := list.New()
+		for _, d := range m.Deps {
+			q.PushBack(d)
+		}
+		for q.Len() > 0 {
+			name := q.Remove(q.Front()).(string)
+			mod := modules[name]
+			if mod == nil || unitsById[name] != nil {
+				continue
+			}
+
+			var paths []string
+			paths = append(paths, mod.Srcs...)
+			paths = append(paths, mod.SrcJars...)
+			paths = append(paths, mod.Jars...)
+			unitsById[name] = &pb.BuildableUnit{
+				Id:              name,
+				SourceFilePaths: mod.Srcs,
+				GeneratedFiles:  genFiles(env, paths),
+			}
+
+			for _, d := range mod.Deps {
+				q.PushBack(d)
+			}
+		}
 	}
+
+	units := make([]*pb.BuildableUnit, 0, len(unitsById))
+	for _, u := range unitsById {
+		units = append(units, u)
+	}
+	return results, units
+}
+
+// genFiles returns the generated files (paths that start with outDir/) for the
+// given paths. Generated files that do not exist are ignored.
+func genFiles(env Env, paths []string) []*pb.GeneratedFile {
+	prefix := env.OutDir + "/"
+	var ret []*pb.GeneratedFile
+	for _, p := range paths {
+		relPath, ok := strings.CutPrefix(p, prefix)
+		if !ok {
+			continue
+		}
+
+		contents, err := os.ReadFile(path.Join(env.RepoDir, p))
+		if err != nil {
+			continue
+		}
+
+		ret = append(ret, &pb.GeneratedFile{
+			Path:     relPath,
+			Contents: contents,
+		})
+	}
+	return ret
 }
 
 // runMake runs Soong build for the given modules.
@@ -308,6 +411,7 @@
 		"TARGET_PRODUCT=" + env.LunchTarget.Product,
 		"TARGET_RELEASE=" + env.LunchTarget.Release,
 		"TARGET_BUILD_VARIANT=" + env.LunchTarget.Variant,
+		"TARGET_BUILD_TYPE=release",
 		"-k",
 	}
 	args = append(args, modules...)
@@ -319,7 +423,6 @@
 }
 
 type javaModule struct {
-	Name    string
 	Path    []string `json:"path,omitempty"`
 	Deps    []string `json:"dependencies,omitempty"`
 	Srcs    []string `json:"srcs,omitempty"`
@@ -327,66 +430,23 @@
 	SrcJars []string `json:"srcjars,omitempty"`
 }
 
-func loadJavaModules(env *Env) (map[string]*javaModule, map[string]*javaModule, error) {
+func loadJavaModules(env Env) (map[string]*javaModule, error) {
 	javaDepsPath := path.Join(env.RepoDir, env.OutDir, "soong/module_bp_java_deps.json")
 	data, err := os.ReadFile(javaDepsPath)
 	if err != nil {
-		return nil, nil, err
+		return nil, err
 	}
 
-	var moduleMapping map[string]*javaModule // module name -> module
-	if err = json.Unmarshal(data, &moduleMapping); err != nil {
-		return nil, nil, err
+	var ret map[string]*javaModule // module name -> module
+	if err = json.Unmarshal(data, &ret); err != nil {
+		return nil, err
 	}
 
-	javaModules := make(map[string]*javaModule)
-	javaFileToModuleMap := make(map[string]*javaModule)
-	for name, module := range moduleMapping {
-		if strings.HasSuffix(name, "-jarjar") || strings.HasSuffix(name, ".impl") {
-			continue
-		}
-		module.Name = name
-		javaModules[name] = module
-		for _, src := range module.Srcs {
-			if !slices.Contains(env.JavaFiles, src) {
-				// We are only interested in active files.
-				continue
-			}
-			if javaFileToModuleMap[src] != nil {
-				// TODO(michaelmerg): Handle the case where a file is covered by multiple modules.
-				log.Printf("File %q found in module %q but is already covered by module %q", src, module.Name, javaFileToModuleMap[src].Name)
-				continue
-			}
-			javaFileToModuleMap[src] = module
+	// Add top level java_sdk_library for .impl modules.
+	for name, module := range ret {
+		if striped := strings.TrimSuffix(name, ".impl"); striped != name {
+			ret[striped] = module
 		}
 	}
-	return javaModules, javaFileToModuleMap, nil
-}
-
-func transitiveDeps(m *javaModule, modules map[string]*javaModule) []string {
-	var ret []string
-	q := list.New()
-	q.PushBack(m.Name)
-	seen := make(map[string]bool) // module names -> true
-	for q.Len() > 0 {
-		name := q.Remove(q.Front()).(string)
-		mod := modules[name]
-		if mod == nil {
-			continue
-		}
-
-		ret = append(ret, mod.Srcs...)
-		ret = append(ret, mod.SrcJars...)
-		ret = append(ret, mod.Jars...)
-		for _, d := range mod.Deps {
-			if seen[d] {
-				continue
-			}
-			seen[d] = true
-			q.PushBack(d)
-		}
-	}
-	slices.Sort(ret)
-	ret = slices.Compact(ret)
-	return ret
+	return ret, nil
 }
diff --git a/tools/ide_query/ide_query.sh b/tools/ide_query/ide_query.sh
index 2df48d0..6f9b0c4 100755
--- a/tools/ide_query/ide_query.sh
+++ b/tools/ide_query/ide_query.sh
@@ -32,6 +32,7 @@
       ;;
 esac
 
+export BUILD_ENV_SEQUENCE_NUMBER=13
 export ANDROID_BUILD_TOP=$TOP
 export OUT_DIR=${OUT_DIR}
 exec "${PREBUILTS_GO_ROOT}/bin/go" "run" "ide_query" "$@"
diff --git a/tools/ide_query/ide_query_proto/ide_query.pb.go b/tools/ide_query/ide_query_proto/ide_query.pb.go
index f3a016d..a190223 100644
--- a/tools/ide_query/ide_query_proto/ide_query.pb.go
+++ b/tools/ide_query/ide_query_proto/ide_query.pb.go
@@ -1,6 +1,21 @@
+//
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 // Code generated by protoc-gen-go. DO NOT EDIT.
 // versions:
-// 	protoc-gen-go v1.25.0-devel
+// 	protoc-gen-go v1.30.0
 // 	protoc        v3.21.12
 // source: ide_query.proto
 
@@ -20,251 +35,113 @@
 	_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
 )
 
-type Status_Code int32
+type Language int32
 
 const (
-	Status_OK      Status_Code = 0
-	Status_FAILURE Status_Code = 1
+	Language_LANGUAGE_UNSPECIFIED Language = 0
+	Language_LANGUAGE_JAVA        Language = 1 // also includes Kotlin
+	Language_LANGUAGE_CPP         Language = 2
 )
 
-// Enum value maps for Status_Code.
+// Enum value maps for Language.
 var (
-	Status_Code_name = map[int32]string{
-		0: "OK",
-		1: "FAILURE",
+	Language_name = map[int32]string{
+		0: "LANGUAGE_UNSPECIFIED",
+		1: "LANGUAGE_JAVA",
+		2: "LANGUAGE_CPP",
 	}
-	Status_Code_value = map[string]int32{
-		"OK":      0,
-		"FAILURE": 1,
+	Language_value = map[string]int32{
+		"LANGUAGE_UNSPECIFIED": 0,
+		"LANGUAGE_JAVA":        1,
+		"LANGUAGE_CPP":         2,
 	}
 )
 
-func (x Status_Code) Enum() *Status_Code {
-	p := new(Status_Code)
+func (x Language) Enum() *Language {
+	p := new(Language)
 	*p = x
 	return p
 }
 
-func (x Status_Code) String() string {
+func (x Language) String() string {
 	return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
 }
 
-func (Status_Code) Descriptor() protoreflect.EnumDescriptor {
+func (Language) Descriptor() protoreflect.EnumDescriptor {
 	return file_ide_query_proto_enumTypes[0].Descriptor()
 }
 
-func (Status_Code) Type() protoreflect.EnumType {
+func (Language) Type() protoreflect.EnumType {
 	return &file_ide_query_proto_enumTypes[0]
 }
 
-func (x Status_Code) Number() protoreflect.EnumNumber {
+func (x Language) Number() protoreflect.EnumNumber {
 	return protoreflect.EnumNumber(x)
 }
 
-// Deprecated: Use Status_Code.Descriptor instead.
-func (Status_Code) EnumDescriptor() ([]byte, []int) {
-	return file_ide_query_proto_rawDescGZIP(), []int{0, 0}
-}
-
-// Indicates the success/failure for analysis.
-type Status struct {
-	state         protoimpl.MessageState
-	sizeCache     protoimpl.SizeCache
-	unknownFields protoimpl.UnknownFields
-
-	Code Status_Code `protobuf:"varint,1,opt,name=code,proto3,enum=ide_query.Status_Code" json:"code,omitempty"`
-	// Details about the status, might be displayed to user.
-	Message *string `protobuf:"bytes,2,opt,name=message,proto3,oneof" json:"message,omitempty"`
-}
-
-func (x *Status) Reset() {
-	*x = Status{}
-	if protoimpl.UnsafeEnabled {
-		mi := &file_ide_query_proto_msgTypes[0]
-		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
-		ms.StoreMessageInfo(mi)
-	}
-}
-
-func (x *Status) String() string {
-	return protoimpl.X.MessageStringOf(x)
-}
-
-func (*Status) ProtoMessage() {}
-
-func (x *Status) ProtoReflect() protoreflect.Message {
-	mi := &file_ide_query_proto_msgTypes[0]
-	if protoimpl.UnsafeEnabled && x != nil {
-		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
-		if ms.LoadMessageInfo() == nil {
-			ms.StoreMessageInfo(mi)
-		}
-		return ms
-	}
-	return mi.MessageOf(x)
-}
-
-// Deprecated: Use Status.ProtoReflect.Descriptor instead.
-func (*Status) Descriptor() ([]byte, []int) {
+// Deprecated: Use Language.Descriptor instead.
+func (Language) EnumDescriptor() ([]byte, []int) {
 	return file_ide_query_proto_rawDescGZIP(), []int{0}
 }
 
-func (x *Status) GetCode() Status_Code {
-	if x != nil {
-		return x.Code
+type AnalysisResult_Status_Code int32
+
+const (
+	AnalysisResult_Status_CODE_UNSPECIFIED  AnalysisResult_Status_Code = 0
+	AnalysisResult_Status_CODE_OK           AnalysisResult_Status_Code = 1
+	AnalysisResult_Status_CODE_NOT_FOUND    AnalysisResult_Status_Code = 2 // no target or module found for the source file.
+	AnalysisResult_Status_CODE_BUILD_FAILED AnalysisResult_Status_Code = 3
+)
+
+// Enum value maps for AnalysisResult_Status_Code.
+var (
+	AnalysisResult_Status_Code_name = map[int32]string{
+		0: "CODE_UNSPECIFIED",
+		1: "CODE_OK",
+		2: "CODE_NOT_FOUND",
+		3: "CODE_BUILD_FAILED",
 	}
-	return Status_OK
-}
-
-func (x *Status) GetMessage() string {
-	if x != nil && x.Message != nil {
-		return *x.Message
+	AnalysisResult_Status_Code_value = map[string]int32{
+		"CODE_UNSPECIFIED":  0,
+		"CODE_OK":           1,
+		"CODE_NOT_FOUND":    2,
+		"CODE_BUILD_FAILED": 3,
 	}
-	return ""
+)
+
+func (x AnalysisResult_Status_Code) Enum() *AnalysisResult_Status_Code {
+	p := new(AnalysisResult_Status_Code)
+	*p = x
+	return p
 }
 
-// Represents an Android checkout on user's workstation.
-type RepoState struct {
-	state         protoimpl.MessageState
-	sizeCache     protoimpl.SizeCache
-	unknownFields protoimpl.UnknownFields
-
-	// Absolute path for the checkout in the workstation.
-	// e.g. /home/user/work/android/
-	RepoDir string `protobuf:"bytes,1,opt,name=repo_dir,json=repoDir,proto3" json:"repo_dir,omitempty"`
-	// Relative to repo_dir.
-	ActiveFilePath []string `protobuf:"bytes,2,rep,name=active_file_path,json=activeFilePath,proto3" json:"active_file_path,omitempty"`
-	// Repository relative path to output directory in workstation.
-	OutDir string `protobuf:"bytes,3,opt,name=out_dir,json=outDir,proto3" json:"out_dir,omitempty"`
-	// Repository relative path to compile_commands.json in workstation.
-	CompDbPath string `protobuf:"bytes,4,opt,name=comp_db_path,json=compDbPath,proto3" json:"comp_db_path,omitempty"`
+func (x AnalysisResult_Status_Code) String() string {
+	return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
 }
 
-func (x *RepoState) Reset() {
-	*x = RepoState{}
-	if protoimpl.UnsafeEnabled {
-		mi := &file_ide_query_proto_msgTypes[1]
-		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
-		ms.StoreMessageInfo(mi)
-	}
+func (AnalysisResult_Status_Code) Descriptor() protoreflect.EnumDescriptor {
+	return file_ide_query_proto_enumTypes[1].Descriptor()
 }
 
-func (x *RepoState) String() string {
-	return protoimpl.X.MessageStringOf(x)
+func (AnalysisResult_Status_Code) Type() protoreflect.EnumType {
+	return &file_ide_query_proto_enumTypes[1]
 }
 
-func (*RepoState) ProtoMessage() {}
-
-func (x *RepoState) ProtoReflect() protoreflect.Message {
-	mi := &file_ide_query_proto_msgTypes[1]
-	if protoimpl.UnsafeEnabled && x != nil {
-		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
-		if ms.LoadMessageInfo() == nil {
-			ms.StoreMessageInfo(mi)
-		}
-		return ms
-	}
-	return mi.MessageOf(x)
+func (x AnalysisResult_Status_Code) Number() protoreflect.EnumNumber {
+	return protoreflect.EnumNumber(x)
 }
 
-// Deprecated: Use RepoState.ProtoReflect.Descriptor instead.
-func (*RepoState) Descriptor() ([]byte, []int) {
-	return file_ide_query_proto_rawDescGZIP(), []int{1}
+// Deprecated: Use AnalysisResult_Status_Code.Descriptor instead.
+func (AnalysisResult_Status_Code) EnumDescriptor() ([]byte, []int) {
+	return file_ide_query_proto_rawDescGZIP(), []int{3, 0, 0}
 }
 
-func (x *RepoState) GetRepoDir() string {
-	if x != nil {
-		return x.RepoDir
-	}
-	return ""
-}
-
-func (x *RepoState) GetActiveFilePath() []string {
-	if x != nil {
-		return x.ActiveFilePath
-	}
-	return nil
-}
-
-func (x *RepoState) GetOutDir() string {
-	if x != nil {
-		return x.OutDir
-	}
-	return ""
-}
-
-func (x *RepoState) GetCompDbPath() string {
-	if x != nil {
-		return x.CompDbPath
-	}
-	return ""
-}
-
-// Provides all the targets that are pre-requisities for running language
-// services on active_file_paths.
-type DepsResponse struct {
-	state         protoimpl.MessageState
-	sizeCache     protoimpl.SizeCache
-	unknownFields protoimpl.UnknownFields
-
-	Deps   []*DepsResponse_Deps `protobuf:"bytes,1,rep,name=deps,proto3" json:"deps,omitempty"`
-	Status *Status              `protobuf:"bytes,2,opt,name=status,proto3,oneof" json:"status,omitempty"`
-}
-
-func (x *DepsResponse) Reset() {
-	*x = DepsResponse{}
-	if protoimpl.UnsafeEnabled {
-		mi := &file_ide_query_proto_msgTypes[2]
-		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
-		ms.StoreMessageInfo(mi)
-	}
-}
-
-func (x *DepsResponse) String() string {
-	return protoimpl.X.MessageStringOf(x)
-}
-
-func (*DepsResponse) ProtoMessage() {}
-
-func (x *DepsResponse) ProtoReflect() protoreflect.Message {
-	mi := &file_ide_query_proto_msgTypes[2]
-	if protoimpl.UnsafeEnabled && x != nil {
-		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
-		if ms.LoadMessageInfo() == nil {
-			ms.StoreMessageInfo(mi)
-		}
-		return ms
-	}
-	return mi.MessageOf(x)
-}
-
-// Deprecated: Use DepsResponse.ProtoReflect.Descriptor instead.
-func (*DepsResponse) Descriptor() ([]byte, []int) {
-	return file_ide_query_proto_rawDescGZIP(), []int{2}
-}
-
-func (x *DepsResponse) GetDeps() []*DepsResponse_Deps {
-	if x != nil {
-		return x.Deps
-	}
-	return nil
-}
-
-func (x *DepsResponse) GetStatus() *Status {
-	if x != nil {
-		return x.Status
-	}
-	return nil
-}
-
-// Returns all the information necessary for providing language services for the
-// active files.
 type GeneratedFile struct {
 	state         protoimpl.MessageState
 	sizeCache     protoimpl.SizeCache
 	unknownFields protoimpl.UnknownFields
 
-	// Path to the file relative to IdeAnalysis.build_artifact_root.
+	// Path to the file relative to build_out_dir.
 	Path string `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"`
 	// The text of the generated file, if not provided contents will be read
 	// from the path above in user's workstation.
@@ -274,7 +151,7 @@
 func (x *GeneratedFile) Reset() {
 	*x = GeneratedFile{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_ide_query_proto_msgTypes[3]
+		mi := &file_ide_query_proto_msgTypes[0]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -287,7 +164,7 @@
 func (*GeneratedFile) ProtoMessage() {}
 
 func (x *GeneratedFile) ProtoReflect() protoreflect.Message {
-	mi := &file_ide_query_proto_msgTypes[3]
+	mi := &file_ide_query_proto_msgTypes[0]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -300,7 +177,7 @@
 
 // Deprecated: Use GeneratedFile.ProtoReflect.Descriptor instead.
 func (*GeneratedFile) Descriptor() ([]byte, []int) {
-	return file_ide_query_proto_rawDescGZIP(), []int{3}
+	return file_ide_query_proto_rawDescGZIP(), []int{0}
 }
 
 func (x *GeneratedFile) GetPath() string {
@@ -317,127 +194,29 @@
 	return nil
 }
 
-type SourceFile struct {
-	state         protoimpl.MessageState
-	sizeCache     protoimpl.SizeCache
-	unknownFields protoimpl.UnknownFields
-
-	// Path to the source file relative to repository root.
-	Path string `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"`
-	// Working directory used by the build system. All the relative
-	// paths in compiler_arguments should be relative to this path.
-	// Relative to repository root.
-	WorkingDir string `protobuf:"bytes,2,opt,name=working_dir,json=workingDir,proto3" json:"working_dir,omitempty"`
-	// Compiler arguments to compile the source file. If multiple variants
-	// of the module being compiled are possible, the query script will choose
-	// one.
-	CompilerArguments []string `protobuf:"bytes,3,rep,name=compiler_arguments,json=compilerArguments,proto3" json:"compiler_arguments,omitempty"`
-	// Any generated files that are used in compiling the file.
-	Generated []*GeneratedFile `protobuf:"bytes,4,rep,name=generated,proto3" json:"generated,omitempty"`
-	// Paths to all of the sources, like build files, code generators,
-	// proto files etc. that were used during analysis. Used to figure
-	// out when a set of build artifacts are stale and the query tool
-	// must be re-run.
-	// Relative to repository root.
-	Deps []string `protobuf:"bytes,5,rep,name=deps,proto3" json:"deps,omitempty"`
-	// Represents analysis status for this particular file. e.g. not part
-	// of the build graph.
-	Status *Status `protobuf:"bytes,6,opt,name=status,proto3,oneof" json:"status,omitempty"`
-}
-
-func (x *SourceFile) Reset() {
-	*x = SourceFile{}
-	if protoimpl.UnsafeEnabled {
-		mi := &file_ide_query_proto_msgTypes[4]
-		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
-		ms.StoreMessageInfo(mi)
-	}
-}
-
-func (x *SourceFile) String() string {
-	return protoimpl.X.MessageStringOf(x)
-}
-
-func (*SourceFile) ProtoMessage() {}
-
-func (x *SourceFile) ProtoReflect() protoreflect.Message {
-	mi := &file_ide_query_proto_msgTypes[4]
-	if protoimpl.UnsafeEnabled && x != nil {
-		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
-		if ms.LoadMessageInfo() == nil {
-			ms.StoreMessageInfo(mi)
-		}
-		return ms
-	}
-	return mi.MessageOf(x)
-}
-
-// Deprecated: Use SourceFile.ProtoReflect.Descriptor instead.
-func (*SourceFile) Descriptor() ([]byte, []int) {
-	return file_ide_query_proto_rawDescGZIP(), []int{4}
-}
-
-func (x *SourceFile) GetPath() string {
-	if x != nil {
-		return x.Path
-	}
-	return ""
-}
-
-func (x *SourceFile) GetWorkingDir() string {
-	if x != nil {
-		return x.WorkingDir
-	}
-	return ""
-}
-
-func (x *SourceFile) GetCompilerArguments() []string {
-	if x != nil {
-		return x.CompilerArguments
-	}
-	return nil
-}
-
-func (x *SourceFile) GetGenerated() []*GeneratedFile {
-	if x != nil {
-		return x.Generated
-	}
-	return nil
-}
-
-func (x *SourceFile) GetDeps() []string {
-	if x != nil {
-		return x.Deps
-	}
-	return nil
-}
-
-func (x *SourceFile) GetStatus() *Status {
-	if x != nil {
-		return x.Status
-	}
-	return nil
-}
-
 type IdeAnalysis struct {
 	state         protoimpl.MessageState
 	sizeCache     protoimpl.SizeCache
 	unknownFields protoimpl.UnknownFields
 
-	// Path relative to repository root, containing all the artifacts
-	// generated by the build system. GeneratedFile.path are always
-	// relative to this directory.
-	BuildArtifactRoot string        `protobuf:"bytes,1,opt,name=build_artifact_root,json=buildArtifactRoot,proto3" json:"build_artifact_root,omitempty"`
-	Sources           []*SourceFile `protobuf:"bytes,2,rep,name=sources,proto3" json:"sources,omitempty"`
-	// Status representing overall analysis.
-	// Should fail only when no analysis can be performed.
-	Status *Status `protobuf:"bytes,3,opt,name=status,proto3,oneof" json:"status,omitempty"`
+	// Directory that contains build outputs generated by the build system.
+	// Relative to repository root.
+	BuildOutDir string `protobuf:"bytes,1,opt,name=build_out_dir,json=buildOutDir,proto3" json:"build_out_dir,omitempty"`
+	// Working directory used by the build system.
+	// Relative to repository root.
+	WorkingDir string `protobuf:"bytes,4,opt,name=working_dir,json=workingDir,proto3" json:"working_dir,omitempty"`
+	// Only set if the whole query failed.
+	Error *AnalysisError `protobuf:"bytes,5,opt,name=error,proto3,oneof" json:"error,omitempty"`
+	// List of results, one per queried file.
+	Results []*AnalysisResult `protobuf:"bytes,6,rep,name=results,proto3" json:"results,omitempty"`
+	// List of buildable units directly or indirectly references by the results.
+	Units []*BuildableUnit `protobuf:"bytes,7,rep,name=units,proto3" json:"units,omitempty"`
 }
 
 func (x *IdeAnalysis) Reset() {
 	*x = IdeAnalysis{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_ide_query_proto_msgTypes[5]
+		mi := &file_ide_query_proto_msgTypes[1]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -450,7 +229,7 @@
 func (*IdeAnalysis) ProtoMessage() {}
 
 func (x *IdeAnalysis) ProtoReflect() protoreflect.Message {
-	mi := &file_ide_query_proto_msgTypes[5]
+	mi := &file_ide_query_proto_msgTypes[1]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -463,45 +242,345 @@
 
 // Deprecated: Use IdeAnalysis.ProtoReflect.Descriptor instead.
 func (*IdeAnalysis) Descriptor() ([]byte, []int) {
-	return file_ide_query_proto_rawDescGZIP(), []int{5}
+	return file_ide_query_proto_rawDescGZIP(), []int{1}
 }
 
-func (x *IdeAnalysis) GetBuildArtifactRoot() string {
+func (x *IdeAnalysis) GetBuildOutDir() string {
 	if x != nil {
-		return x.BuildArtifactRoot
+		return x.BuildOutDir
 	}
 	return ""
 }
 
-func (x *IdeAnalysis) GetSources() []*SourceFile {
+func (x *IdeAnalysis) GetWorkingDir() string {
 	if x != nil {
-		return x.Sources
+		return x.WorkingDir
+	}
+	return ""
+}
+
+func (x *IdeAnalysis) GetError() *AnalysisError {
+	if x != nil {
+		return x.Error
 	}
 	return nil
 }
 
-func (x *IdeAnalysis) GetStatus() *Status {
+func (x *IdeAnalysis) GetResults() []*AnalysisResult {
+	if x != nil {
+		return x.Results
+	}
+	return nil
+}
+
+func (x *IdeAnalysis) GetUnits() []*BuildableUnit {
+	if x != nil {
+		return x.Units
+	}
+	return nil
+}
+
+type AnalysisError struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// Human readable error message.
+	ErrorMessage string `protobuf:"bytes,1,opt,name=error_message,json=errorMessage,proto3" json:"error_message,omitempty"`
+}
+
+func (x *AnalysisError) Reset() {
+	*x = AnalysisError{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_ide_query_proto_msgTypes[2]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *AnalysisError) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*AnalysisError) ProtoMessage() {}
+
+func (x *AnalysisError) ProtoReflect() protoreflect.Message {
+	mi := &file_ide_query_proto_msgTypes[2]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use AnalysisError.ProtoReflect.Descriptor instead.
+func (*AnalysisError) Descriptor() ([]byte, []int) {
+	return file_ide_query_proto_rawDescGZIP(), []int{2}
+}
+
+func (x *AnalysisError) GetErrorMessage() string {
+	if x != nil {
+		return x.ErrorMessage
+	}
+	return ""
+}
+
+type AnalysisResult struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// Path to the source file that was queried, relative to repository root.
+	SourceFilePath string `protobuf:"bytes,1,opt,name=source_file_path,json=sourceFilePath,proto3" json:"source_file_path,omitempty"`
+	// Represents status for this result. e.g. not part of the build graph.
+	Status *AnalysisResult_Status `protobuf:"bytes,2,opt,name=status,proto3" json:"status,omitempty"`
+	// ID of buildable unit that contains the source file.
+	// The ide_query script can choose the most relevant unit from multiple
+	// options.
+	UnitId string `protobuf:"bytes,3,opt,name=unit_id,json=unitId,proto3" json:"unit_id,omitempty"`
+	// Invalidation rule to check if the result is still valid.
+	Invalidation *Invalidation `protobuf:"bytes,4,opt,name=invalidation,proto3" json:"invalidation,omitempty"`
+}
+
+func (x *AnalysisResult) Reset() {
+	*x = AnalysisResult{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_ide_query_proto_msgTypes[3]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *AnalysisResult) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*AnalysisResult) ProtoMessage() {}
+
+func (x *AnalysisResult) ProtoReflect() protoreflect.Message {
+	mi := &file_ide_query_proto_msgTypes[3]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use AnalysisResult.ProtoReflect.Descriptor instead.
+func (*AnalysisResult) Descriptor() ([]byte, []int) {
+	return file_ide_query_proto_rawDescGZIP(), []int{3}
+}
+
+func (x *AnalysisResult) GetSourceFilePath() string {
+	if x != nil {
+		return x.SourceFilePath
+	}
+	return ""
+}
+
+func (x *AnalysisResult) GetStatus() *AnalysisResult_Status {
 	if x != nil {
 		return x.Status
 	}
 	return nil
 }
 
-// Build dependencies of a source file for providing language services.
-type DepsResponse_Deps struct {
+func (x *AnalysisResult) GetUnitId() string {
+	if x != nil {
+		return x.UnitId
+	}
+	return ""
+}
+
+func (x *AnalysisResult) GetInvalidation() *Invalidation {
+	if x != nil {
+		return x.Invalidation
+	}
+	return nil
+}
+
+type BuildableUnit struct {
 	state         protoimpl.MessageState
 	sizeCache     protoimpl.SizeCache
 	unknownFields protoimpl.UnknownFields
 
-	// Relative to repo_dir.
-	SourceFile string `protobuf:"bytes,1,opt,name=source_file,json=sourceFile,proto3" json:"source_file,omitempty"`
-	// Build target to execute for generating dep.
-	BuildTarget []string `protobuf:"bytes,2,rep,name=build_target,json=buildTarget,proto3" json:"build_target,omitempty"`
-	Status      *Status  `protobuf:"bytes,3,opt,name=status,proto3,oneof" json:"status,omitempty"`
+	// Unique identifier of the buildable unit.
+	//
+	// Examples:
+	//   - Java: module or target name, e.g. "framework-bluetooth" or
+	//     "//third_party/hamcrest:hamcrest_java"
+	//   - C++: source file, e.g. "path/to/file.cc"
+	Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
+	// Language of the unit.
+	// Required for buildable units directly referenced by the AnalysisResult,
+	// e.g. the unit associated with the compilation stage for the source file.
+	Language Language `protobuf:"varint,2,opt,name=language,proto3,enum=ide_query.Language" json:"language,omitempty"`
+	// Source files that are part of this unit.
+	// Path to the file relative to working_dir.
+	SourceFilePaths []string `protobuf:"bytes,3,rep,name=source_file_paths,json=sourceFilePaths,proto3" json:"source_file_paths,omitempty"`
+	// Compiler arguments to compile the source files.
+	CompilerArguments []string `protobuf:"bytes,4,rep,name=compiler_arguments,json=compilerArguments,proto3" json:"compiler_arguments,omitempty"`
+	// List of generated files produced by this unit.
+	GeneratedFiles []*GeneratedFile `protobuf:"bytes,5,rep,name=generated_files,json=generatedFiles,proto3" json:"generated_files,omitempty"`
+	// List of other BuildableUnits this unit depend on.
+	DependencyIds []string `protobuf:"bytes,6,rep,name=dependency_ids,json=dependencyIds,proto3" json:"dependency_ids,omitempty"`
 }
 
-func (x *DepsResponse_Deps) Reset() {
-	*x = DepsResponse_Deps{}
+func (x *BuildableUnit) Reset() {
+	*x = BuildableUnit{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_ide_query_proto_msgTypes[4]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *BuildableUnit) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*BuildableUnit) ProtoMessage() {}
+
+func (x *BuildableUnit) ProtoReflect() protoreflect.Message {
+	mi := &file_ide_query_proto_msgTypes[4]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use BuildableUnit.ProtoReflect.Descriptor instead.
+func (*BuildableUnit) Descriptor() ([]byte, []int) {
+	return file_ide_query_proto_rawDescGZIP(), []int{4}
+}
+
+func (x *BuildableUnit) GetId() string {
+	if x != nil {
+		return x.Id
+	}
+	return ""
+}
+
+func (x *BuildableUnit) GetLanguage() Language {
+	if x != nil {
+		return x.Language
+	}
+	return Language_LANGUAGE_UNSPECIFIED
+}
+
+func (x *BuildableUnit) GetSourceFilePaths() []string {
+	if x != nil {
+		return x.SourceFilePaths
+	}
+	return nil
+}
+
+func (x *BuildableUnit) GetCompilerArguments() []string {
+	if x != nil {
+		return x.CompilerArguments
+	}
+	return nil
+}
+
+func (x *BuildableUnit) GetGeneratedFiles() []*GeneratedFile {
+	if x != nil {
+		return x.GeneratedFiles
+	}
+	return nil
+}
+
+func (x *BuildableUnit) GetDependencyIds() []string {
+	if x != nil {
+		return x.DependencyIds
+	}
+	return nil
+}
+
+// Invalidation rule to check if the result is still valid.
+// This should contain files/dirs that are not directly part of the build graph
+// but still affect the result. For example BUILD files, directory to the
+// toolchain or config files etc.
+type Invalidation struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// If any of these files change the result may become invalid.
+	// Path to the file relative to repository root.
+	FilePaths []string `protobuf:"bytes,1,rep,name=file_paths,json=filePaths,proto3" json:"file_paths,omitempty"`
+	// If any of these rules match a changed file the result may become invalid.
+	Wildcards []*Invalidation_Wildcard `protobuf:"bytes,4,rep,name=wildcards,proto3" json:"wildcards,omitempty"`
+}
+
+func (x *Invalidation) Reset() {
+	*x = Invalidation{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_ide_query_proto_msgTypes[5]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *Invalidation) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Invalidation) ProtoMessage() {}
+
+func (x *Invalidation) ProtoReflect() protoreflect.Message {
+	mi := &file_ide_query_proto_msgTypes[5]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use Invalidation.ProtoReflect.Descriptor instead.
+func (*Invalidation) Descriptor() ([]byte, []int) {
+	return file_ide_query_proto_rawDescGZIP(), []int{5}
+}
+
+func (x *Invalidation) GetFilePaths() []string {
+	if x != nil {
+		return x.FilePaths
+	}
+	return nil
+}
+
+func (x *Invalidation) GetWildcards() []*Invalidation_Wildcard {
+	if x != nil {
+		return x.Wildcards
+	}
+	return nil
+}
+
+// Indicates the success/failure for the query.
+type AnalysisResult_Status struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Code AnalysisResult_Status_Code `protobuf:"varint,1,opt,name=code,proto3,enum=ide_query.AnalysisResult_Status_Code" json:"code,omitempty"`
+	// Details about the status, might be displayed to user.
+	StatusMessage *string `protobuf:"bytes,2,opt,name=status_message,json=statusMessage,proto3,oneof" json:"status_message,omitempty"`
+}
+
+func (x *AnalysisResult_Status) Reset() {
+	*x = AnalysisResult_Status{}
 	if protoimpl.UnsafeEnabled {
 		mi := &file_ide_query_proto_msgTypes[6]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
@@ -509,13 +588,13 @@
 	}
 }
 
-func (x *DepsResponse_Deps) String() string {
+func (x *AnalysisResult_Status) String() string {
 	return protoimpl.X.MessageStringOf(x)
 }
 
-func (*DepsResponse_Deps) ProtoMessage() {}
+func (*AnalysisResult_Status) ProtoMessage() {}
 
-func (x *DepsResponse_Deps) ProtoReflect() protoreflect.Message {
+func (x *AnalysisResult_Status) ProtoReflect() protoreflect.Message {
 	mi := &file_ide_query_proto_msgTypes[6]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
@@ -527,104 +606,190 @@
 	return mi.MessageOf(x)
 }
 
-// Deprecated: Use DepsResponse_Deps.ProtoReflect.Descriptor instead.
-func (*DepsResponse_Deps) Descriptor() ([]byte, []int) {
-	return file_ide_query_proto_rawDescGZIP(), []int{2, 0}
+// Deprecated: Use AnalysisResult_Status.ProtoReflect.Descriptor instead.
+func (*AnalysisResult_Status) Descriptor() ([]byte, []int) {
+	return file_ide_query_proto_rawDescGZIP(), []int{3, 0}
 }
 
-func (x *DepsResponse_Deps) GetSourceFile() string {
+func (x *AnalysisResult_Status) GetCode() AnalysisResult_Status_Code {
 	if x != nil {
-		return x.SourceFile
+		return x.Code
+	}
+	return AnalysisResult_Status_CODE_UNSPECIFIED
+}
+
+func (x *AnalysisResult_Status) GetStatusMessage() string {
+	if x != nil && x.StatusMessage != nil {
+		return *x.StatusMessage
 	}
 	return ""
 }
 
-func (x *DepsResponse_Deps) GetBuildTarget() []string {
-	if x != nil {
-		return x.BuildTarget
-	}
-	return nil
+type Invalidation_Wildcard struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// Prefix of the file path (e.g. "path/to/")
+	Prefix *string `protobuf:"bytes,1,opt,name=prefix,proto3,oneof" json:"prefix,omitempty"`
+	// Suffix of the file path (e.g. "Android.bp")
+	Suffix *string `protobuf:"bytes,2,opt,name=suffix,proto3,oneof" json:"suffix,omitempty"`
+	// If false, the part of the path between the given `prefix` and `suffix`
+	// should not contain directory separators ('/').
+	CanCrossFolder *bool `protobuf:"varint,3,opt,name=can_cross_folder,json=canCrossFolder,proto3,oneof" json:"can_cross_folder,omitempty"`
 }
 
-func (x *DepsResponse_Deps) GetStatus() *Status {
-	if x != nil {
-		return x.Status
+func (x *Invalidation_Wildcard) Reset() {
+	*x = Invalidation_Wildcard{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_ide_query_proto_msgTypes[7]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
 	}
-	return nil
+}
+
+func (x *Invalidation_Wildcard) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Invalidation_Wildcard) ProtoMessage() {}
+
+func (x *Invalidation_Wildcard) ProtoReflect() protoreflect.Message {
+	mi := &file_ide_query_proto_msgTypes[7]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use Invalidation_Wildcard.ProtoReflect.Descriptor instead.
+func (*Invalidation_Wildcard) Descriptor() ([]byte, []int) {
+	return file_ide_query_proto_rawDescGZIP(), []int{5, 0}
+}
+
+func (x *Invalidation_Wildcard) GetPrefix() string {
+	if x != nil && x.Prefix != nil {
+		return *x.Prefix
+	}
+	return ""
+}
+
+func (x *Invalidation_Wildcard) GetSuffix() string {
+	if x != nil && x.Suffix != nil {
+		return *x.Suffix
+	}
+	return ""
+}
+
+func (x *Invalidation_Wildcard) GetCanCrossFolder() bool {
+	if x != nil && x.CanCrossFolder != nil {
+		return *x.CanCrossFolder
+	}
+	return false
 }
 
 var File_ide_query_proto protoreflect.FileDescriptor
 
 var file_ide_query_proto_rawDesc = []byte{
 	0x0a, 0x0f, 0x69, 0x64, 0x65, 0x5f, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74,
-	0x6f, 0x12, 0x09, 0x69, 0x64, 0x65, 0x5f, 0x71, 0x75, 0x65, 0x72, 0x79, 0x22, 0x7c, 0x0a, 0x06,
-	0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x2a, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x01,
-	0x20, 0x01, 0x28, 0x0e, 0x32, 0x16, 0x2e, 0x69, 0x64, 0x65, 0x5f, 0x71, 0x75, 0x65, 0x72, 0x79,
-	0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x2e, 0x43, 0x6f, 0x64, 0x65, 0x52, 0x04, 0x63, 0x6f,
-	0x64, 0x65, 0x12, 0x1d, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20,
-	0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x88, 0x01,
-	0x01, 0x22, 0x1b, 0x0a, 0x04, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x06, 0x0a, 0x02, 0x4f, 0x4b, 0x10,
-	0x00, 0x12, 0x0b, 0x0a, 0x07, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x10, 0x01, 0x42, 0x0a,
-	0x0a, 0x08, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x8b, 0x01, 0x0a, 0x09, 0x52,
-	0x65, 0x70, 0x6f, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x72, 0x65, 0x70, 0x6f,
-	0x5f, 0x64, 0x69, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x72, 0x65, 0x70, 0x6f,
-	0x44, 0x69, 0x72, 0x12, 0x28, 0x0a, 0x10, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x66, 0x69,
-	0x6c, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0e, 0x61,
-	0x63, 0x74, 0x69, 0x76, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x50, 0x61, 0x74, 0x68, 0x12, 0x17, 0x0a,
-	0x07, 0x6f, 0x75, 0x74, 0x5f, 0x64, 0x69, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06,
-	0x6f, 0x75, 0x74, 0x44, 0x69, 0x72, 0x12, 0x20, 0x0a, 0x0c, 0x63, 0x6f, 0x6d, 0x70, 0x5f, 0x64,
-	0x62, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6f,
-	0x6d, 0x70, 0x44, 0x62, 0x50, 0x61, 0x74, 0x68, 0x22, 0x83, 0x02, 0x0a, 0x0c, 0x44, 0x65, 0x70,
-	0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x30, 0x0a, 0x04, 0x64, 0x65, 0x70,
-	0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x69, 0x64, 0x65, 0x5f, 0x71, 0x75,
-	0x65, 0x72, 0x79, 0x2e, 0x44, 0x65, 0x70, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
-	0x2e, 0x44, 0x65, 0x70, 0x73, 0x52, 0x04, 0x64, 0x65, 0x70, 0x73, 0x12, 0x2e, 0x0a, 0x06, 0x73,
-	0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x69, 0x64,
-	0x65, 0x5f, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x48, 0x00,
-	0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x88, 0x01, 0x01, 0x1a, 0x85, 0x01, 0x0a, 0x04,
-	0x44, 0x65, 0x70, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x66,
-	0x69, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x6f, 0x75, 0x72, 0x63,
-	0x65, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x74,
-	0x61, 0x72, 0x67, 0x65, 0x74, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0b, 0x62, 0x75, 0x69,
-	0x6c, 0x64, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x12, 0x2e, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74,
-	0x75, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x69, 0x64, 0x65, 0x5f, 0x71,
-	0x75, 0x65, 0x72, 0x79, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x48, 0x00, 0x52, 0x06, 0x73,
-	0x74, 0x61, 0x74, 0x75, 0x73, 0x88, 0x01, 0x01, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x73, 0x74, 0x61,
-	0x74, 0x75, 0x73, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x51,
-	0x0a, 0x0d, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x46, 0x69, 0x6c, 0x65, 0x12,
-	0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70,
-	0x61, 0x74, 0x68, 0x12, 0x1f, 0x0a, 0x08, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x18,
-	0x02, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x08, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74,
-	0x73, 0x88, 0x01, 0x01, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74,
-	0x73, 0x22, 0xf7, 0x01, 0x0a, 0x0a, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x46, 0x69, 0x6c, 0x65,
-	0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04,
-	0x70, 0x61, 0x74, 0x68, 0x12, 0x1f, 0x0a, 0x0b, 0x77, 0x6f, 0x72, 0x6b, 0x69, 0x6e, 0x67, 0x5f,
-	0x64, 0x69, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x77, 0x6f, 0x72, 0x6b, 0x69,
-	0x6e, 0x67, 0x44, 0x69, 0x72, 0x12, 0x2d, 0x0a, 0x12, 0x63, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65,
-	0x72, 0x5f, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28,
-	0x09, 0x52, 0x11, 0x63, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x72, 0x41, 0x72, 0x67, 0x75, 0x6d,
-	0x65, 0x6e, 0x74, 0x73, 0x12, 0x36, 0x0a, 0x09, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65,
-	0x64, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x69, 0x64, 0x65, 0x5f, 0x71, 0x75,
-	0x65, 0x72, 0x79, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x46, 0x69, 0x6c,
-	0x65, 0x52, 0x09, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x12, 0x12, 0x0a, 0x04,
-	0x64, 0x65, 0x70, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x64, 0x65, 0x70, 0x73,
-	0x12, 0x2e, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b,
-	0x32, 0x11, 0x2e, 0x69, 0x64, 0x65, 0x5f, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2e, 0x53, 0x74, 0x61,
-	0x74, 0x75, 0x73, 0x48, 0x00, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x88, 0x01, 0x01,
-	0x42, 0x09, 0x0a, 0x07, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0xa9, 0x01, 0x0a, 0x0b,
-	0x49, 0x64, 0x65, 0x41, 0x6e, 0x61, 0x6c, 0x79, 0x73, 0x69, 0x73, 0x12, 0x2e, 0x0a, 0x13, 0x62,
-	0x75, 0x69, 0x6c, 0x64, 0x5f, 0x61, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x5f, 0x72, 0x6f,
-	0x6f, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x41,
-	0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x2f, 0x0a, 0x07, 0x73,
-	0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x69,
-	0x64, 0x65, 0x5f, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2e, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x46,
-	0x69, 0x6c, 0x65, 0x52, 0x07, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x2e, 0x0a, 0x06,
-	0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x69,
-	0x64, 0x65, 0x5f, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x48,
-	0x00, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x88, 0x01, 0x01, 0x42, 0x09, 0x0a, 0x07,
-	0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x42, 0x1b, 0x5a, 0x19, 0x69, 0x64, 0x65, 0x5f, 0x71,
-	0x75, 0x65, 0x72, 0x79, 0x2f, 0x69, 0x64, 0x65, 0x5f, 0x71, 0x75, 0x65, 0x72, 0x79, 0x5f, 0x70,
-	0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+	0x6f, 0x12, 0x09, 0x69, 0x64, 0x65, 0x5f, 0x71, 0x75, 0x65, 0x72, 0x79, 0x22, 0x51, 0x0a, 0x0d,
+	0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x12, 0x0a,
+	0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74,
+	0x68, 0x12, 0x1f, 0x0a, 0x08, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x02, 0x20,
+	0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x08, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x88,
+	0x01, 0x01, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x22,
+	0x82, 0x02, 0x0a, 0x0b, 0x49, 0x64, 0x65, 0x41, 0x6e, 0x61, 0x6c, 0x79, 0x73, 0x69, 0x73, 0x12,
+	0x22, 0x0a, 0x0d, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6f, 0x75, 0x74, 0x5f, 0x64, 0x69, 0x72,
+	0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x4f, 0x75, 0x74,
+	0x44, 0x69, 0x72, 0x12, 0x1f, 0x0a, 0x0b, 0x77, 0x6f, 0x72, 0x6b, 0x69, 0x6e, 0x67, 0x5f, 0x64,
+	0x69, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x77, 0x6f, 0x72, 0x6b, 0x69, 0x6e,
+	0x67, 0x44, 0x69, 0x72, 0x12, 0x33, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x05, 0x20,
+	0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x69, 0x64, 0x65, 0x5f, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2e,
+	0x41, 0x6e, 0x61, 0x6c, 0x79, 0x73, 0x69, 0x73, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x48, 0x00, 0x52,
+	0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x88, 0x01, 0x01, 0x12, 0x33, 0x0a, 0x07, 0x72, 0x65, 0x73,
+	0x75, 0x6c, 0x74, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x69, 0x64, 0x65,
+	0x5f, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2e, 0x41, 0x6e, 0x61, 0x6c, 0x79, 0x73, 0x69, 0x73, 0x52,
+	0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x07, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x12, 0x2e,
+	0x0a, 0x05, 0x75, 0x6e, 0x69, 0x74, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e,
+	0x69, 0x64, 0x65, 0x5f, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x61,
+	0x62, 0x6c, 0x65, 0x55, 0x6e, 0x69, 0x74, 0x52, 0x05, 0x75, 0x6e, 0x69, 0x74, 0x73, 0x42, 0x08,
+	0x0a, 0x06, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x4a, 0x04, 0x08, 0x02, 0x10, 0x03, 0x4a, 0x04,
+	0x08, 0x03, 0x10, 0x04, 0x22, 0x34, 0x0a, 0x0d, 0x41, 0x6e, 0x61, 0x6c, 0x79, 0x73, 0x69, 0x73,
+	0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x6d,
+	0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x65, 0x72,
+	0x72, 0x6f, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0xa5, 0x03, 0x0a, 0x0e, 0x41,
+	0x6e, 0x61, 0x6c, 0x79, 0x73, 0x69, 0x73, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x28, 0x0a,
+	0x10, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x70, 0x61, 0x74,
+	0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x46,
+	0x69, 0x6c, 0x65, 0x50, 0x61, 0x74, 0x68, 0x12, 0x38, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75,
+	0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x69, 0x64, 0x65, 0x5f, 0x71, 0x75,
+	0x65, 0x72, 0x79, 0x2e, 0x41, 0x6e, 0x61, 0x6c, 0x79, 0x73, 0x69, 0x73, 0x52, 0x65, 0x73, 0x75,
+	0x6c, 0x74, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75,
+	0x73, 0x12, 0x17, 0x0a, 0x07, 0x75, 0x6e, 0x69, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01,
+	0x28, 0x09, 0x52, 0x06, 0x75, 0x6e, 0x69, 0x74, 0x49, 0x64, 0x12, 0x3b, 0x0a, 0x0c, 0x69, 0x6e,
+	0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b,
+	0x32, 0x17, 0x2e, 0x69, 0x64, 0x65, 0x5f, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2e, 0x49, 0x6e, 0x76,
+	0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0c, 0x69, 0x6e, 0x76, 0x61, 0x6c,
+	0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0xd8, 0x01, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74,
+	0x75, 0x73, 0x12, 0x39, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e,
+	0x32, 0x25, 0x2e, 0x69, 0x64, 0x65, 0x5f, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2e, 0x41, 0x6e, 0x61,
+	0x6c, 0x79, 0x73, 0x69, 0x73, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x2e, 0x53, 0x74, 0x61, 0x74,
+	0x75, 0x73, 0x2e, 0x43, 0x6f, 0x64, 0x65, 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x2a, 0x0a,
+	0x0e, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18,
+	0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x0d, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x4d,
+	0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x88, 0x01, 0x01, 0x22, 0x54, 0x0a, 0x04, 0x43, 0x6f, 0x64,
+	0x65, 0x12, 0x14, 0x0a, 0x10, 0x43, 0x4f, 0x44, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43,
+	0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x43, 0x4f, 0x44, 0x45, 0x5f,
+	0x4f, 0x4b, 0x10, 0x01, 0x12, 0x12, 0x0a, 0x0e, 0x43, 0x4f, 0x44, 0x45, 0x5f, 0x4e, 0x4f, 0x54,
+	0x5f, 0x46, 0x4f, 0x55, 0x4e, 0x44, 0x10, 0x02, 0x12, 0x15, 0x0a, 0x11, 0x43, 0x4f, 0x44, 0x45,
+	0x5f, 0x42, 0x55, 0x49, 0x4c, 0x44, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x10, 0x03, 0x42,
+	0x11, 0x0a, 0x0f, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61,
+	0x67, 0x65, 0x22, 0x95, 0x02, 0x0a, 0x0d, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x61, 0x62, 0x6c, 0x65,
+	0x55, 0x6e, 0x69, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
+	0x52, 0x02, 0x69, 0x64, 0x12, 0x2f, 0x0a, 0x08, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65,
+	0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x13, 0x2e, 0x69, 0x64, 0x65, 0x5f, 0x71, 0x75, 0x65,
+	0x72, 0x79, 0x2e, 0x4c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x52, 0x08, 0x6c, 0x61, 0x6e,
+	0x67, 0x75, 0x61, 0x67, 0x65, 0x12, 0x2a, 0x0a, 0x11, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f,
+	0x66, 0x69, 0x6c, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09,
+	0x52, 0x0f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x50, 0x61, 0x74, 0x68,
+	0x73, 0x12, 0x2d, 0x0a, 0x12, 0x63, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x61, 0x72,
+	0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x11, 0x63,
+	0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x72, 0x41, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73,
+	0x12, 0x41, 0x0a, 0x0f, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x66, 0x69,
+	0x6c, 0x65, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x69, 0x64, 0x65, 0x5f,
+	0x71, 0x75, 0x65, 0x72, 0x79, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x46,
+	0x69, 0x6c, 0x65, 0x52, 0x0e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x46, 0x69,
+	0x6c, 0x65, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x64, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63,
+	0x79, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0d, 0x64, 0x65, 0x70,
+	0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x79, 0x49, 0x64, 0x73, 0x22, 0x8e, 0x02, 0x0a, 0x0c, 0x49,
+	0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1d, 0x0a, 0x0a, 0x66,
+	0x69, 0x6c, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52,
+	0x09, 0x66, 0x69, 0x6c, 0x65, 0x50, 0x61, 0x74, 0x68, 0x73, 0x12, 0x3e, 0x0a, 0x09, 0x77, 0x69,
+	0x6c, 0x64, 0x63, 0x61, 0x72, 0x64, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, 0x2e,
+	0x69, 0x64, 0x65, 0x5f, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2e, 0x49, 0x6e, 0x76, 0x61, 0x6c, 0x69,
+	0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x57, 0x69, 0x6c, 0x64, 0x63, 0x61, 0x72, 0x64, 0x52,
+	0x09, 0x77, 0x69, 0x6c, 0x64, 0x63, 0x61, 0x72, 0x64, 0x73, 0x1a, 0x9e, 0x01, 0x0a, 0x08, 0x57,
+	0x69, 0x6c, 0x64, 0x63, 0x61, 0x72, 0x64, 0x12, 0x1b, 0x0a, 0x06, 0x70, 0x72, 0x65, 0x66, 0x69,
+	0x78, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x06, 0x70, 0x72, 0x65, 0x66, 0x69,
+	0x78, 0x88, 0x01, 0x01, 0x12, 0x1b, 0x0a, 0x06, 0x73, 0x75, 0x66, 0x66, 0x69, 0x78, 0x18, 0x02,
+	0x20, 0x01, 0x28, 0x09, 0x48, 0x01, 0x52, 0x06, 0x73, 0x75, 0x66, 0x66, 0x69, 0x78, 0x88, 0x01,
+	0x01, 0x12, 0x2d, 0x0a, 0x10, 0x63, 0x61, 0x6e, 0x5f, 0x63, 0x72, 0x6f, 0x73, 0x73, 0x5f, 0x66,
+	0x6f, 0x6c, 0x64, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x48, 0x02, 0x52, 0x0e, 0x63,
+	0x61, 0x6e, 0x43, 0x72, 0x6f, 0x73, 0x73, 0x46, 0x6f, 0x6c, 0x64, 0x65, 0x72, 0x88, 0x01, 0x01,
+	0x42, 0x09, 0x0a, 0x07, 0x5f, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x42, 0x09, 0x0a, 0x07, 0x5f,
+	0x73, 0x75, 0x66, 0x66, 0x69, 0x78, 0x42, 0x13, 0x0a, 0x11, 0x5f, 0x63, 0x61, 0x6e, 0x5f, 0x63,
+	0x72, 0x6f, 0x73, 0x73, 0x5f, 0x66, 0x6f, 0x6c, 0x64, 0x65, 0x72, 0x2a, 0x49, 0x0a, 0x08, 0x4c,
+	0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x12, 0x18, 0x0a, 0x14, 0x4c, 0x41, 0x4e, 0x47, 0x55,
+	0x41, 0x47, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10,
+	0x00, 0x12, 0x11, 0x0a, 0x0d, 0x4c, 0x41, 0x4e, 0x47, 0x55, 0x41, 0x47, 0x45, 0x5f, 0x4a, 0x41,
+	0x56, 0x41, 0x10, 0x01, 0x12, 0x10, 0x0a, 0x0c, 0x4c, 0x41, 0x4e, 0x47, 0x55, 0x41, 0x47, 0x45,
+	0x5f, 0x43, 0x50, 0x50, 0x10, 0x02, 0x42, 0x1b, 0x5a, 0x19, 0x69, 0x64, 0x65, 0x5f, 0x71, 0x75,
+	0x65, 0x72, 0x79, 0x2f, 0x69, 0x64, 0x65, 0x5f, 0x71, 0x75, 0x65, 0x72, 0x79, 0x5f, 0x70, 0x72,
+	0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
 }
 
 var (
@@ -639,32 +804,35 @@
 	return file_ide_query_proto_rawDescData
 }
 
-var file_ide_query_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
-var file_ide_query_proto_msgTypes = make([]protoimpl.MessageInfo, 7)
+var file_ide_query_proto_enumTypes = make([]protoimpl.EnumInfo, 2)
+var file_ide_query_proto_msgTypes = make([]protoimpl.MessageInfo, 8)
 var file_ide_query_proto_goTypes = []interface{}{
-	(Status_Code)(0),          // 0: ide_query.Status.Code
-	(*Status)(nil),            // 1: ide_query.Status
-	(*RepoState)(nil),         // 2: ide_query.RepoState
-	(*DepsResponse)(nil),      // 3: ide_query.DepsResponse
-	(*GeneratedFile)(nil),     // 4: ide_query.GeneratedFile
-	(*SourceFile)(nil),        // 5: ide_query.SourceFile
-	(*IdeAnalysis)(nil),       // 6: ide_query.IdeAnalysis
-	(*DepsResponse_Deps)(nil), // 7: ide_query.DepsResponse.Deps
+	(Language)(0),                   // 0: ide_query.Language
+	(AnalysisResult_Status_Code)(0), // 1: ide_query.AnalysisResult.Status.Code
+	(*GeneratedFile)(nil),           // 2: ide_query.GeneratedFile
+	(*IdeAnalysis)(nil),             // 3: ide_query.IdeAnalysis
+	(*AnalysisError)(nil),           // 4: ide_query.AnalysisError
+	(*AnalysisResult)(nil),          // 5: ide_query.AnalysisResult
+	(*BuildableUnit)(nil),           // 6: ide_query.BuildableUnit
+	(*Invalidation)(nil),            // 7: ide_query.Invalidation
+	(*AnalysisResult_Status)(nil),   // 8: ide_query.AnalysisResult.Status
+	(*Invalidation_Wildcard)(nil),   // 9: ide_query.Invalidation.Wildcard
 }
 var file_ide_query_proto_depIdxs = []int32{
-	0, // 0: ide_query.Status.code:type_name -> ide_query.Status.Code
-	7, // 1: ide_query.DepsResponse.deps:type_name -> ide_query.DepsResponse.Deps
-	1, // 2: ide_query.DepsResponse.status:type_name -> ide_query.Status
-	4, // 3: ide_query.SourceFile.generated:type_name -> ide_query.GeneratedFile
-	1, // 4: ide_query.SourceFile.status:type_name -> ide_query.Status
-	5, // 5: ide_query.IdeAnalysis.sources:type_name -> ide_query.SourceFile
-	1, // 6: ide_query.IdeAnalysis.status:type_name -> ide_query.Status
-	1, // 7: ide_query.DepsResponse.Deps.status:type_name -> ide_query.Status
-	8, // [8:8] is the sub-list for method output_type
-	8, // [8:8] is the sub-list for method input_type
-	8, // [8:8] is the sub-list for extension type_name
-	8, // [8:8] is the sub-list for extension extendee
-	0, // [0:8] is the sub-list for field type_name
+	4, // 0: ide_query.IdeAnalysis.error:type_name -> ide_query.AnalysisError
+	5, // 1: ide_query.IdeAnalysis.results:type_name -> ide_query.AnalysisResult
+	6, // 2: ide_query.IdeAnalysis.units:type_name -> ide_query.BuildableUnit
+	8, // 3: ide_query.AnalysisResult.status:type_name -> ide_query.AnalysisResult.Status
+	7, // 4: ide_query.AnalysisResult.invalidation:type_name -> ide_query.Invalidation
+	0, // 5: ide_query.BuildableUnit.language:type_name -> ide_query.Language
+	2, // 6: ide_query.BuildableUnit.generated_files:type_name -> ide_query.GeneratedFile
+	9, // 7: ide_query.Invalidation.wildcards:type_name -> ide_query.Invalidation.Wildcard
+	1, // 8: ide_query.AnalysisResult.Status.code:type_name -> ide_query.AnalysisResult.Status.Code
+	9, // [9:9] is the sub-list for method output_type
+	9, // [9:9] is the sub-list for method input_type
+	9, // [9:9] is the sub-list for extension type_name
+	9, // [9:9] is the sub-list for extension extendee
+	0, // [0:9] is the sub-list for field type_name
 }
 
 func init() { file_ide_query_proto_init() }
@@ -674,42 +842,6 @@
 	}
 	if !protoimpl.UnsafeEnabled {
 		file_ide_query_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*Status); i {
-			case 0:
-				return &v.state
-			case 1:
-				return &v.sizeCache
-			case 2:
-				return &v.unknownFields
-			default:
-				return nil
-			}
-		}
-		file_ide_query_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*RepoState); i {
-			case 0:
-				return &v.state
-			case 1:
-				return &v.sizeCache
-			case 2:
-				return &v.unknownFields
-			default:
-				return nil
-			}
-		}
-		file_ide_query_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*DepsResponse); i {
-			case 0:
-				return &v.state
-			case 1:
-				return &v.sizeCache
-			case 2:
-				return &v.unknownFields
-			default:
-				return nil
-			}
-		}
-		file_ide_query_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
 			switch v := v.(*GeneratedFile); i {
 			case 0:
 				return &v.state
@@ -721,19 +853,7 @@
 				return nil
 			}
 		}
-		file_ide_query_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*SourceFile); i {
-			case 0:
-				return &v.state
-			case 1:
-				return &v.sizeCache
-			case 2:
-				return &v.unknownFields
-			default:
-				return nil
-			}
-		}
-		file_ide_query_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
+		file_ide_query_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
 			switch v := v.(*IdeAnalysis); i {
 			case 0:
 				return &v.state
@@ -745,8 +865,68 @@
 				return nil
 			}
 		}
+		file_ide_query_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*AnalysisError); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_ide_query_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*AnalysisResult); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_ide_query_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*BuildableUnit); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_ide_query_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*Invalidation); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
 		file_ide_query_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*DepsResponse_Deps); i {
+			switch v := v.(*AnalysisResult_Status); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_ide_query_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*Invalidation_Wildcard); i {
 			case 0:
 				return &v.state
 			case 1:
@@ -759,18 +939,16 @@
 		}
 	}
 	file_ide_query_proto_msgTypes[0].OneofWrappers = []interface{}{}
-	file_ide_query_proto_msgTypes[2].OneofWrappers = []interface{}{}
-	file_ide_query_proto_msgTypes[3].OneofWrappers = []interface{}{}
-	file_ide_query_proto_msgTypes[4].OneofWrappers = []interface{}{}
-	file_ide_query_proto_msgTypes[5].OneofWrappers = []interface{}{}
+	file_ide_query_proto_msgTypes[1].OneofWrappers = []interface{}{}
 	file_ide_query_proto_msgTypes[6].OneofWrappers = []interface{}{}
+	file_ide_query_proto_msgTypes[7].OneofWrappers = []interface{}{}
 	type x struct{}
 	out := protoimpl.TypeBuilder{
 		File: protoimpl.DescBuilder{
 			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
 			RawDescriptor: file_ide_query_proto_rawDesc,
-			NumEnums:      1,
-			NumMessages:   7,
+			NumEnums:      2,
+			NumMessages:   8,
 			NumExtensions: 0,
 			NumServices:   0,
 		},
diff --git a/tools/ide_query/ide_query_proto/ide_query.proto b/tools/ide_query/ide_query_proto/ide_query.proto
index 3d7a8e7..13f349c 100644
--- a/tools/ide_query/ide_query_proto/ide_query.proto
+++ b/tools/ide_query/ide_query_proto/ide_query.proto
@@ -16,51 +16,11 @@
 syntax = "proto3";
 
 package ide_query;
+
 option go_package = "ide_query/ide_query_proto";
 
-// Indicates the success/failure for analysis.
-message Status {
-  enum Code {
-    OK = 0;
-    FAILURE = 1;
-  }
-  Code code = 1;
-  // Details about the status, might be displayed to user.
-  optional string message = 2;
-}
-
-// Represents an Android checkout on user's workstation.
-message RepoState {
-  // Absolute path for the checkout in the workstation.
-  // e.g. /home/user/work/android/
-  string repo_dir = 1;
-  // Relative to repo_dir.
-  repeated string active_file_path = 2;
-  // Repository relative path to output directory in workstation.
-  string out_dir = 3;
-  // Repository relative path to compile_commands.json in workstation.
-  string comp_db_path = 4;
-}
-
-// Provides all the targets that are pre-requisities for running language
-// services on active_file_paths.
-message DepsResponse {
-  // Build dependencies of a source file for providing language services.
-  message Deps {
-    // Relative to repo_dir.
-    string source_file = 1;
-    // Build target to execute for generating dep.
-    repeated string build_target = 2;
-    optional Status status = 3;
-  }
-  repeated Deps deps = 1;
-  optional Status status = 2;
-}
-
-// Returns all the information necessary for providing language services for the
-// active files.
 message GeneratedFile {
-  // Path to the file relative to IdeAnalysis.build_artifact_root.
+  // Path to the file relative to build_out_dir.
   string path = 1;
 
   // The text of the generated file, if not provided contents will be read
@@ -68,44 +28,100 @@
   optional bytes contents = 2;
 }
 
-message SourceFile {
-  // Path to the source file relative to repository root.
-  string path = 1;
-
-  // Working directory used by the build system. All the relative
-  // paths in compiler_arguments should be relative to this path.
-  // Relative to repository root.
-  string working_dir = 2;
-
-  // Compiler arguments to compile the source file. If multiple variants
-  // of the module being compiled are possible, the query script will choose
-  // one.
-  repeated string compiler_arguments = 3;
-
-  // Any generated files that are used in compiling the file.
-  repeated GeneratedFile generated = 4;
-
-  // Paths to all of the sources, like build files, code generators,
-  // proto files etc. that were used during analysis. Used to figure
-  // out when a set of build artifacts are stale and the query tool
-  // must be re-run.
-  // Relative to repository root.
-  repeated string deps = 5;
-
-  // Represents analysis status for this particular file. e.g. not part
-  // of the build graph.
-  optional Status status = 6;
-}
-
 message IdeAnalysis {
-  // Path relative to repository root, containing all the artifacts
-  // generated by the build system. GeneratedFile.path are always
-  // relative to this directory.
-  string build_artifact_root = 1;
+  // Directory that contains build outputs generated by the build system.
+  // Relative to repository root.
+  string build_out_dir = 1;
+  // Working directory used by the build system.
+  // Relative to repository root.
+  string working_dir = 4;
+  // Only set if the whole query failed.
+  optional AnalysisError error = 5;
+  // List of results, one per queried file.
+  repeated AnalysisResult results = 6;
+  // List of buildable units directly or indirectly references by the results.
+  repeated BuildableUnit units = 7;
 
-  repeated SourceFile sources = 2;
-
-  // Status representing overall analysis.
-  // Should fail only when no analysis can be performed.
-  optional Status status = 3;
+  reserved 2, 3;
 }
+
+message AnalysisError {
+  // Human readable error message.
+  string error_message = 1;
+}
+
+message AnalysisResult {
+  // Path to the source file that was queried, relative to repository root.
+  string source_file_path = 1;
+  // Indicates the success/failure for the query.
+  message Status {
+    enum Code {
+      CODE_UNSPECIFIED = 0;
+      CODE_OK = 1;
+      CODE_NOT_FOUND = 2;  // no target or module found for the source file.
+      CODE_BUILD_FAILED = 3;
+    }
+    Code code = 1;
+    // Details about the status, might be displayed to user.
+    optional string status_message = 2;
+  }
+  // Represents status for this result. e.g. not part of the build graph.
+  Status status = 2;
+  // ID of buildable unit that contains the source file.
+  // The ide_query script can choose the most relevant unit from multiple
+  // options.
+  string unit_id = 3;
+  // Invalidation rule to check if the result is still valid.
+  Invalidation invalidation = 4;
+}
+
+enum Language {
+  LANGUAGE_UNSPECIFIED = 0;
+  LANGUAGE_JAVA = 1;  // also includes Kotlin
+  LANGUAGE_CPP = 2;
+}
+
+message BuildableUnit {
+  // Unique identifier of the buildable unit.
+  //
+  // Examples:
+  //   - Java: module or target name, e.g. "framework-bluetooth" or
+  //   "//third_party/hamcrest:hamcrest_java"
+  //   - C++: source file, e.g. "path/to/file.cc"
+  string id = 1;
+  // Language of the unit.
+  // Required for buildable units directly referenced by the AnalysisResult,
+  // e.g. the unit associated with the compilation stage for the source file.
+  Language language = 2;
+  // Source files that are part of this unit.
+  // Path to the file relative to working_dir.
+  repeated string source_file_paths = 3;
+  // Compiler arguments to compile the source files.
+  repeated string compiler_arguments = 4;
+  // List of generated files produced by this unit.
+  repeated GeneratedFile generated_files = 5;
+  // List of other BuildableUnits this unit depend on.
+  repeated string dependency_ids = 6;
+}
+
+// Invalidation rule to check if the result is still valid.
+// This should contain files/dirs that are not directly part of the build graph
+// but still affect the result. For example BUILD files, directory to the
+// toolchain or config files etc.
+message Invalidation {
+  // If any of these files change the result may become invalid.
+  // Path to the file relative to repository root.
+  repeated string file_paths = 1;
+
+  message Wildcard {
+    // Prefix of the file path (e.g. "path/to/")
+    optional string prefix = 1;
+    // Suffix of the file path (e.g. "Android.bp")
+    optional string suffix = 2;
+    // If false, the part of the path between the given `prefix` and `suffix`
+    // should not contain directory separators ('/').
+    optional bool can_cross_folder = 3;
+  }
+  // If any of these rules match a changed file the result may become invalid.
+  repeated Wildcard wildcards = 4;
+}
\ No newline at end of file
diff --git a/tools/ide_query/ide_query_proto/Android.bp b/tools/ide_query/prober_scripts/cpp/Android.bp
similarity index 74%
copy from tools/ide_query/ide_query_proto/Android.bp
copy to tools/ide_query/prober_scripts/cpp/Android.bp
index 70f15cd..5190210 100644
--- a/tools/ide_query/ide_query_proto/Android.bp
+++ b/tools/ide_query/prober_scripts/cpp/Android.bp
@@ -18,16 +18,14 @@
     default_applicable_licenses: ["Android-Apache-2.0"],
 }
 
-cc_library_host_static {
-    name: "ide_query_proto",
+cc_binary {
+    name: "ide_query_proberscript_cc",
     srcs: [
-        "ide_query.proto",
+        "general.cc",
+        "foo.proto",
     ],
+    cflags: ["-Wno-unused-parameter"],
     proto: {
-        export_proto_headers: true,
-        type: "full",
-        canonical_path_from_root: false,
+        type: "lite",
     },
-    compile_multilib: "64",
-    shared_libs: ["libprotobuf-cpp-full"],
 }
diff --git a/tools/ide_query/ide_query_proto/Android.bp b/tools/ide_query/prober_scripts/cpp/foo.proto
similarity index 62%
copy from tools/ide_query/ide_query_proto/Android.bp
copy to tools/ide_query/prober_scripts/cpp/foo.proto
index 70f15cd..5a85147 100644
--- a/tools/ide_query/ide_query_proto/Android.bp
+++ b/tools/ide_query/prober_scripts/cpp/foo.proto
@@ -14,20 +14,12 @@
  * limitations under the License.
  */
 
-package {
-    default_applicable_licenses: ["Android-Apache-2.0"],
-}
+syntax = "proto3";
 
-cc_library_host_static {
-    name: "ide_query_proto",
-    srcs: [
-        "ide_query.proto",
-    ],
-    proto: {
-        export_proto_headers: true,
-        type: "full",
-        canonical_path_from_root: false,
-    },
-    compile_multilib: "64",
-    shared_libs: ["libprotobuf-cpp-full"],
+package ide_query.prober_scripts;
+
+message ProtoMsg {
+  // Test proto field.
+  int64 some_field = 1;
+  //                   ^ some_field
 }
diff --git a/tools/ide_query/prober_scripts/cpp/general.cc b/tools/ide_query/prober_scripts/cpp/general.cc
new file mode 100644
index 0000000..0f0639b
--- /dev/null
+++ b/tools/ide_query/prober_scripts/cpp/general.cc
@@ -0,0 +1,119 @@
+// 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 <vector>
+
+#include "foo.pb.h"
+
+using ide_query::prober_scripts::ProtoMsg;
+
+void Foo(int x, double y) {}
+float Foo(float x, float y) { return 0.0f; }
+
+void TestCompletion() {
+  // Test completion on protos and fuzzy matching of completion suggestions.
+
+  ProtoMsg foo;
+
+  // ^
+
+  // step
+  // workspace.waitForReady()
+  // type("f")
+  // completion.trigger()
+  // assert completion.items.filter(label="foo")
+  // delline()
+  // type("foo.sf")
+  // completion.trigger()
+  // assert completion.items.filter(
+  //  label="some_field.*",
+  //  insertText="some_field.*",
+  // )
+  // delline()
+
+  std::vector<int> v;
+
+  // ^
+
+  // step
+  // workspace.waitForReady()
+  // type("v.push")
+  // completion.trigger()
+  // assert completion.items.filter(label="push_back.*")
+  // delline()
+}
+
+void TestNavigation() {
+  std::vector<int> ints;
+  //               |   | ints
+  //      ^
+
+  // step
+  // ; Test navigation to definition on STL types.
+  // workspace.waitForReady()
+  // navigation.trigger()
+  // assert navigation.items.filter(path=".*/vector")
+
+  ints.push_back(0);
+  // ^
+
+  // step
+  // ; Test navigation to definition on local symbols.
+  // workspace.waitForReady()
+  // navigation.trigger()
+  // assert navigation.items.filter(path=".*/general.cc", range=ints)
+
+  ProtoMsg msg;
+  msg.set_some_field(0);
+  //          ^
+
+  // step
+  // ; Test navigation to definition on proto fields. We do not check for a
+  // ; specific target as it can be in generated code.
+  // workspace.waitForReady()
+  // navigation.trigger()
+  // assert navigation.items
+}
+
+void TestParameterInfo() {
+  std::vector<int> v;
+  v.push_back(0);
+  //          ^
+
+  // step
+  // ; Test the signature help for STL functions. We do not check for a specific
+  // ; text as it can be implementation-dependent.
+  // workspace.waitForReady()
+  // paraminfo.trigger()
+  // assert paraminfo.items
+
+  Foo(0, 0.0);
+  //      ^
+
+  // step
+  // ; Test the signature help for the function 'Foo' having two overloads.
+  // workspace.waitForReady()
+  // paraminfo.trigger()
+  // assert paraminfo.items.filter(
+  //  active=true,
+  //  label="Foo\\(int x, double y\\) -> void",
+  //  selection="double y",
+  // )
+  // assert paraminfo.items.filter(
+  //  active=false,
+  //  label="Foo\\(float x, float y\\) -> float",
+  // )
+}
+
+int main() { return 0; }
diff --git a/tools/ide_query/prober_scripts/cpp_suite.textpb b/tools/ide_query/prober_scripts/cpp_suite.textpb
new file mode 100644
index 0000000..8377269
--- /dev/null
+++ b/tools/ide_query/prober_scripts/cpp_suite.textpb
@@ -0,0 +1,5 @@
+tests: {
+  name: "general"
+  scripts: "build/make/tools/ide_query/prober_scripts/cpp/general.cc"
+  scripts: "build/make/tools/ide_query/prober_scripts/cpp/foo.proto"
+}
diff --git a/tools/ide_query/prober_scripts/ide_query.out b/tools/ide_query/prober_scripts/ide_query.out
new file mode 100644
index 0000000..cd7ce6d
--- /dev/null
+++ b/tools/ide_query/prober_scripts/ide_query.out
@@ -0,0 +1,239 @@
+
+out–a
+8build/make/tools/ide_query/prober_scripts/cpp/general.cc8prebuilts/clang/host/linux-x86/clang-r522817/bin/clang++-mthumb-Os-fomit-frame-pointer-mllvm-enable-shrink-wrap=false-O2-Wall-Wextra-Winit-self-Wpointer-arith-Wunguarded-availability-Werror=date-time-Werror=int-conversion-Werror=pragma-pack&-Werror=pragma-pack-suspicious-include-Werror=sizeof-array-div-Werror=string-plus-int'-Werror=unreachable-code-loop-increment"-Wno-error=deprecated-declarations-Wno-c99-designator-Wno-gnu-folding-constant"-Wno-inconsistent-missing-override-Wno-error=reorder-init-list-Wno-reorder-init-list-Wno-sign-compare-Wno-unused	-DANDROID-DNDEBUG-UDEBUG(-D__compiler_offsetof=__builtin_offsetof*-D__ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__	-faddrsig-fdebug-default-version=5-fcolor-diagnostics-ffp-contract=off-fno-exceptions-fno-strict-aliasing-fmessage-length=0#-fno-relaxed-template-template-args-gsimple-template-names-gz=zstd-no-canonical-prefixes-Wno-error=format"-fdebug-prefix-map=/proc/self/cwd=-ftrivial-auto-var-init=zero-g-ffunction-sections-fdata-sections-fno-short-enums-funwind-tables-fstack-protector-strong-Wa,--noexecstack-D_FORTIFY_SOURCE=2-Wstrict-aliasing=2-Werror=return-type-Werror=non-virtual-dtor-Werror=address-Werror=sequence-point-Werror=format-security-nostdlibinc-fdebug-info-for-profiling-msoft-float-march=armv7-a-mfloat-abi=softfp
+-mfpu=neon/-Ibuild/make/tools/ide_query/prober_scripts/cpp³-Iout/soong/.intermediates/build/make/tools/ide_query/prober_scripts/cpp/ide_query_proberscript_cc/android_arm_armv7-a-neon/gen/proto/build/make/tools/ide_query/prober_scripts/cpp…-Iout/soong/.intermediates/build/make/tools/ide_query/prober_scripts/cpp/ide_query_proberscript_cc/android_arm_armv7-a-neon/gen/proto-D__LIBC_API__=10000-D__LIBM_API__=10000-D__LIBDL_API__=10000-Iexternal/protobuf/srcY-Iprebuilts/clang/host/linux-x86/clang-r522817/android_libc++/platform/arm/include/c++/v1=-Iprebuilts/clang/host/linux-x86/clang-r522817/include/c++/v1 -Ibionic/libc/async_safe/include-Isystem/logging/liblog/include'-Ibionic/libc/system_properties/include<-Isystem/core/property_service/libpropertyinfoparser/include-isystembionic/libc/include-isystembionic/libc/kernel/uapi/asm-arm-isystembionic/libc/kernel/uapi-isystembionic/libc/kernel/android/scsi-isystembionic/libc/kernel/android/uapi-targetarmv7a-linux-androideabi10000-DANDROID_STRICT-fPIE-Werror-Wno-unused-parameter-DGOOGLE_PROTOBUF_NO_RTTI-Wimplicit-fallthrough*-D_LIBCPP_ENABLE_THREAD_SAFETY_ANNOTATIONS-Wno-gnu-include-next-fvisibility-inlines-hidden-mllvm-enable-shrink-wrap=false-std=gnu++20	-fno-rtti-Isystem/core/include-Isystem/logging/liblog/include-Isystem/media/audio/include-Ihardware/libhardware/include%-Ihardware/libhardware_legacy/include-Ihardware/ril/include-Iframeworks/native/include"-Iframeworks/native/opengl/include-Iframeworks/av/include-Werror=bool-operation -Werror=format-insufficient-args%-Werror=implicit-int-float-conversion-Werror=int-in-bool-context-Werror=int-to-pointer-cast-Werror=pointer-to-int-cast-Werror=xor-used-as-pow-Wno-void-pointer-to-enum-cast-Wno-void-pointer-to-int-cast-Wno-pointer-to-int-cast-Werror=fortify-source-Wno-unused-variable-Wno-missing-field-initializers-Wno-packed-non-pod-Werror=address-of-temporary+-Werror=incompatible-function-pointer-types-Werror=null-dereference-Werror=return-type"-Wno-tautological-constant-compare$-Wno-tautological-type-limit-compare"-Wno-implicit-int-float-conversion!-Wno-tautological-overlap-compare-Wno-deprecated-copy-Wno-range-loop-construct"-Wno-zero-as-null-pointer-constant)-Wno-deprecated-anon-enum-enum-conversion$-Wno-deprecated-enum-enum-conversion-Wno-pessimizing-move-Wno-non-c-typedef-for-linkage-Wno-align-mismatch"-Wno-error=unused-but-set-variable#-Wno-error=unused-but-set-parameter-Wno-error=deprecated-builtins-Wno-error=deprecated2-Wno-error=single-bit-bitfield-constant-conversion$-Wno-error=enum-constexpr-conversion-Wno-error=invalid-offsetof&-Wno-deprecated-dynamic-exception-spec8build/make/tools/ide_query/prober_scripts/cpp/general.cc"Õ?
+¶soong/.intermediates/build/make/tools/ide_query/prober_scripts/cpp/ide_query_proberscript_cc/android_arm_armv7-a-neon/gen/proto/build/make/tools/ide_query/prober_scripts/cpp/foo.pb.h™>// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: build/make/tools/ide_query/prober_scripts/cpp/foo.proto
+
+#ifndef GOOGLE_PROTOBUF_INCLUDED_build_2fmake_2ftools_2fide_5fquery_2fprober_5fscripts_2fcpp_2ffoo_2eproto
+#define GOOGLE_PROTOBUF_INCLUDED_build_2fmake_2ftools_2fide_5fquery_2fprober_5fscripts_2fcpp_2ffoo_2eproto
+
+#include <cstdint>
+#include <limits>
+#include <string>
+
+#include <google/protobuf/port_def.inc>
+#if PROTOBUF_VERSION < 3021000
+#error This file was generated by a newer version of protoc which is
+#error incompatible with your Protocol Buffer headers. Please update
+#error your headers.
+#endif
+#if 3021012 < PROTOBUF_MIN_PROTOC_VERSION
+#error This file was generated by an older version of protoc which is
+#error incompatible with your Protocol Buffer headers. Please
+#error regenerate this file with a newer version of protoc.
+#endif
+
+#include <google/protobuf/port_undef.inc>
+#include <google/protobuf/io/coded_stream.h>
+#include <google/protobuf/arena.h>
+#include <google/protobuf/arenastring.h>
+#include <google/protobuf/generated_message_util.h>
+#include <google/protobuf/metadata_lite.h>
+#include <google/protobuf/message_lite.h>
+#include <google/protobuf/repeated_field.h>  // IWYU pragma: export
+#include <google/protobuf/extension_set.h>  // IWYU pragma: export
+// @@protoc_insertion_point(includes)
+#include <google/protobuf/port_def.inc>
+#define PROTOBUF_INTERNAL_EXPORT_build_2fmake_2ftools_2fide_5fquery_2fprober_5fscripts_2fcpp_2ffoo_2eproto
+PROTOBUF_NAMESPACE_OPEN
+namespace internal {
+class AnyMetadata;
+}  // namespace internal
+PROTOBUF_NAMESPACE_CLOSE
+
+// Internal implementation detail -- do not use these members.
+struct TableStruct_build_2fmake_2ftools_2fide_5fquery_2fprober_5fscripts_2fcpp_2ffoo_2eproto {
+  static const ::uint32_t offsets[];
+};
+namespace ide_query {
+namespace prober_scripts {
+class ProtoMsg;
+struct ProtoMsgDefaultTypeInternal;
+extern ProtoMsgDefaultTypeInternal _ProtoMsg_default_instance_;
+}  // namespace prober_scripts
+}  // namespace ide_query
+PROTOBUF_NAMESPACE_OPEN
+template<> ::ide_query::prober_scripts::ProtoMsg* Arena::CreateMaybeMessage<::ide_query::prober_scripts::ProtoMsg>(Arena*);
+PROTOBUF_NAMESPACE_CLOSE
+namespace ide_query {
+namespace prober_scripts {
+
+// ===================================================================
+
+class ProtoMsg final :
+    public ::PROTOBUF_NAMESPACE_ID::MessageLite /* @@protoc_insertion_point(class_definition:ide_query.prober_scripts.ProtoMsg) */ {
+ public:
+  inline ProtoMsg() : ProtoMsg(nullptr) {}
+  ~ProtoMsg() override;
+  explicit PROTOBUF_CONSTEXPR ProtoMsg(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized);
+
+  ProtoMsg(const ProtoMsg& from);
+  ProtoMsg(ProtoMsg&& from) noexcept
+    : ProtoMsg() {
+    *this = ::std::move(from);
+  }
+
+  inline ProtoMsg& operator=(const ProtoMsg& from) {
+    if (this == &from) return *this;
+    CopyFrom(from);
+    return *this;
+  }
+  inline ProtoMsg& operator=(ProtoMsg&& from) noexcept {
+    if (this == &from) return *this;
+    if (GetOwningArena() == from.GetOwningArena()
+  #ifdef PROTOBUF_FORCE_COPY_IN_MOVE
+        && GetOwningArena() != nullptr
+  #endif  // !PROTOBUF_FORCE_COPY_IN_MOVE
+    ) {
+      InternalSwap(&from);
+    } else {
+      CopyFrom(from);
+    }
+    return *this;
+  }
+
+  static const ProtoMsg& default_instance() {
+    return *internal_default_instance();
+  }
+  static inline const ProtoMsg* internal_default_instance() {
+    return reinterpret_cast<const ProtoMsg*>(
+               &_ProtoMsg_default_instance_);
+  }
+  static constexpr int kIndexInFileMessages =
+    0;
+
+  friend void swap(ProtoMsg& a, ProtoMsg& b) {
+    a.Swap(&b);
+  }
+  inline void Swap(ProtoMsg* other) {
+    if (other == this) return;
+  #ifdef PROTOBUF_FORCE_COPY_IN_SWAP
+    if (GetOwningArena() != nullptr &&
+        GetOwningArena() == other->GetOwningArena()) {
+   #else  // PROTOBUF_FORCE_COPY_IN_SWAP
+    if (GetOwningArena() == other->GetOwningArena()) {
+  #endif  // !PROTOBUF_FORCE_COPY_IN_SWAP
+      InternalSwap(other);
+    } else {
+      ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other);
+    }
+  }
+  void UnsafeArenaSwap(ProtoMsg* other) {
+    if (other == this) return;
+    GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena());
+    InternalSwap(other);
+  }
+
+  // implements Message ----------------------------------------------
+
+  ProtoMsg* New(::PROTOBUF_NAMESPACE_ID::Arena* arena) const final {
+    return CreateMaybeMessage<ProtoMsg>(arena);
+  }
+  ProtoMsg* New() const {
+    return New(nullptr);
+  }
+  void CheckTypeAndMergeFrom(const ::PROTOBUF_NAMESPACE_ID::MessageLite& from)  final;
+  void CopyFrom(const ProtoMsg& from);
+  void MergeFrom(const ProtoMsg& from);
+  PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;
+  bool IsInitialized() const final;
+
+  size_t ByteSizeLong() const final;
+  const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final;
+  ::uint8_t* _InternalSerialize(
+      ::uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final;
+  int GetCachedSize() const final { return _impl_._cached_size_.Get(); }
+
+  private:
+  void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned);
+  void SharedDtor();
+  void SetCachedSize(int size) const;
+  void InternalSwap(ProtoMsg* other);
+
+  private:
+  friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata;
+  static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {
+    return "ide_query.prober_scripts.ProtoMsg";
+  }
+  protected:
+  explicit ProtoMsg(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+                       bool is_message_owned = false);
+  public:
+
+  std::string GetTypeName() const final;
+
+  // nested types ----------------------------------------------------
+
+  // accessors -------------------------------------------------------
+
+  enum : int {
+    kSomeFieldFieldNumber = 1,
+  };
+  // int64 some_field = 1;
+  void clear_some_field();
+  ::int64_t some_field() const;
+  void set_some_field(::int64_t value);
+  private:
+  ::int64_t _internal_some_field() const;
+  void _internal_set_some_field(::int64_t value);
+  public:
+
+  // @@protoc_insertion_point(class_scope:ide_query.prober_scripts.ProtoMsg)
+ private:
+  class _Internal;
+
+  template <typename T> friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper;
+  typedef void InternalArenaConstructable_;
+  typedef void DestructorSkippable_;
+  struct Impl_ {
+    ::int64_t some_field_;
+    mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;
+  };
+  union { Impl_ _impl_; };
+  friend struct ::TableStruct_build_2fmake_2ftools_2fide_5fquery_2fprober_5fscripts_2fcpp_2ffoo_2eproto;
+};
+// ===================================================================
+
+
+// ===================================================================
+
+#ifdef __GNUC__
+  #pragma GCC diagnostic push
+  #pragma GCC diagnostic ignored "-Wstrict-aliasing"
+#endif  // __GNUC__
+// ProtoMsg
+
+// int64 some_field = 1;
+inline void ProtoMsg::clear_some_field() {
+  _impl_.some_field_ = ::int64_t{0};
+}
+inline ::int64_t ProtoMsg::_internal_some_field() const {
+  return _impl_.some_field_;
+}
+inline ::int64_t ProtoMsg::some_field() const {
+  // @@protoc_insertion_point(field_get:ide_query.prober_scripts.ProtoMsg.some_field)
+  return _internal_some_field();
+}
+inline void ProtoMsg::_internal_set_some_field(::int64_t value) {
+  
+  _impl_.some_field_ = value;
+}
+inline void ProtoMsg::set_some_field(::int64_t value) {
+  _internal_set_some_field(value);
+  // @@protoc_insertion_point(field_set:ide_query.prober_scripts.ProtoMsg.some_field)
+}
+
+#ifdef __GNUC__
+  #pragma GCC diagnostic pop
+#endif  // __GNUC__
+
+// @@protoc_insertion_point(namespace_scope)
+
+}  // namespace prober_scripts
+}  // namespace ide_query
+
+// @@protoc_insertion_point(global_scope)
+
+#include <google/protobuf/port_undef.inc>
+#endif  // GOOGLE_PROTOBUF_INCLUDED_GOOGLE_PROTOBUF_INCLUDED_build_2fmake_2ftools_2fide_5fquery_2fprober_5fscripts_2fcpp_2ffoo_2eproto
diff --git a/tools/ide_query/prober_scripts/regen.sh b/tools/ide_query/prober_scripts/regen.sh
new file mode 100755
index 0000000..2edfe53
--- /dev/null
+++ b/tools/ide_query/prober_scripts/regen.sh
@@ -0,0 +1,33 @@
+#!/bin/bash -e
+
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# This script is used to generate the ide_query.out file.
+#
+# The ide_query.out file is a pre-computed result of running ide_query.sh
+# on a set of files. This allows the prober to run its tests without running
+# ide_query.sh. The prober doesn't check-out the full source code, so it
+# can't run ide_query.sh itself.
+
+cd $(dirname $BASH_SOURCE)
+source $(pwd)/../../../shell_utils.sh
+require_top
+
+files_to_build=(
+  build/make/tools/ide_query/prober_scripts/cpp/general.cc
+)
+
+cd ${TOP}
+build/make/tools/ide_query/ide_query.sh --lunch_target=aosp_arm-trunk_staging-eng ${files_to_build[@]} > build/make/tools/ide_query/prober_scripts/ide_query.out
diff --git a/tools/releasetools/ota_from_raw_img.py b/tools/releasetools/ota_from_raw_img.py
index c186940..03b44f1 100644
--- a/tools/releasetools/ota_from_raw_img.py
+++ b/tools/releasetools/ota_from_raw_img.py
@@ -54,7 +54,7 @@
       prog=argv[0], description="Given a series of .img files, produces a full OTA package that installs thoese images")
   parser.add_argument("images", nargs="+", type=str,
                       help="List of images to generate OTA")
-  parser.add_argument("--partition_names", nargs='+', type=str,
+  parser.add_argument("--partition_names", nargs='?', type=str,
                       help="Partition names to install the images, default to basename of the image(no file name extension)")
   parser.add_argument('--output', type=str,
                       help='Paths to output merged ota', required=True)
@@ -74,18 +74,20 @@
       old_imgs[i], args.images[i] = img.split(":", maxsplit=1)
 
   if not args.partition_names:
-    args.partition_names = [os.path.os.path.splitext(os.path.basename(path))[
+    args.partition_names = [os.path.splitext(os.path.basename(path))[
         0] for path in args.images]
+  else:
+    args.partition_names = args.partition_names.split(",")
   with tempfile.NamedTemporaryFile() as unsigned_payload, tempfile.NamedTemporaryFile() as dynamic_partition_info_file:
     dynamic_partition_info_file.writelines(
         [b"virtual_ab=true\n", b"super_partition_groups=\n"])
     dynamic_partition_info_file.flush()
     cmd = [ResolveBinaryPath("delta_generator", args.search_path)]
-    cmd.append("--partition_names=" + ",".join(args.partition_names))
+    cmd.append("--partition_names=" + ":".join(args.partition_names))
     cmd.append("--dynamic_partition_info_file=" +
                dynamic_partition_info_file.name)
-    cmd.append("--old_partitions=" + ",".join(old_imgs))
-    cmd.append("--new_partitions=" + ",".join(args.images))
+    cmd.append("--old_partitions=" + ":".join(old_imgs))
+    cmd.append("--new_partitions=" + ":".join(args.images))
     cmd.append("--out_file=" + unsigned_payload.name)
     cmd.append("--is_partial_update")
     if args.max_timestamp: