Merge "Include init-mmd-prop.rc if mmd is not built" into main
diff --git a/core/Makefile b/core/Makefile
index 5cfd736..651b1e0 100644
--- a/core/Makefile
+++ b/core/Makefile
@@ -775,7 +775,13 @@
intermediates := \
$(call intermediates-dir-for,PACKAGING,apkcerts)
APKCERTS_FILE := $(intermediates)/$(name).txt
-all_apkcerts_files := $(sort $(foreach p,$(PACKAGES),$(PACKAGES.$(p).APKCERTS_FILE)))
+ifeq ($(RELEASE_APKCERTS_INSTALL_ONLY), true)
+ all_apkcerts_packages := $(filter $(call product-installed-modules,$(INTERNAL_PRODUCT)),$(PACKAGES))
+else
+ all_apkcerts_packages := $(PACKAGES)
+endif
+all_apkcerts_files := $(sort $(foreach p,$(all_apkcerts_packages),$(PACKAGES.$(p).APKCERTS_FILE)))
+
$(APKCERTS_FILE): $(all_apkcerts_files)
# We don't need to really build all the modules.
# TODO: rebuild APKCERTS_FILE if any app change its cert.
@@ -783,7 +789,7 @@
@echo APK certs list: $@
@mkdir -p $(dir $@)
@rm -f $@
- $(foreach p,$(sort $(PACKAGES)),\
+ $(foreach p,$(sort $(all_apkcerts_packages)),\
$(if $(PACKAGES.$(p).APKCERTS_FILE),\
$(call _apkcerts_merge,$(PACKAGES.$(p).APKCERTS_FILE), $@),\
$(if $(PACKAGES.$(p).EXTERNAL_KEY),\
@@ -7314,6 +7320,72 @@
_proguard_dict_zip_modules := $(unbundled_build_modules)
endif
+# Filter out list to avoid uncessary proguard related file generation
+ifeq (,$(TARGET_BUILD_UNBUNDLED))
+filter_out_proguard_dict_zip_modules :=
+# product.img
+ifndef BUILDING_PRODUCT_IMAGE
+filter_out_proguard_dict_zip_modules += $(PRODUCT_OUT)/product/%
+endif
+# system.img
+ifndef BUILDING_SYSTEM_IMAGE
+filter_out_proguard_dict_zip_modules += $(PRODUCT_OUT)/system/%
+endif
+# system_dlkm.img
+ifndef BUILDING_SYSTEM_DLKM_IMAGE
+filter_out_proguard_dict_zip_modules += $(PRODUCT_OUT)/system_dlkm/%
+endif
+# system_ext.img
+ifndef BUILDING_SYSTEM_EXT_IMAGE
+filter_out_proguard_dict_zip_modules += $(PRODUCT_OUT)/system_ext/%
+endif
+# system_other.img
+ifndef BUILDING_SYSTEM_OTHER_IMAGE
+filter_out_proguard_dict_zip_modules += $(PRODUCT_OUT)/system_other/%
+endif
+# odm.img
+ifndef BUILDING_ODM_IMAGE
+filter_out_proguard_dict_zip_modules += $(PRODUCT_OUT)/odm/%
+endif
+# odm_dlkm.img
+ifndef BUILDING_ODM_DLKM_IMAGE
+filter_out_proguard_dict_zip_modules += $(PRODUCT_OUT)/odm_dlkm/%
+endif
+# vendor.img
+ifndef BUILDING_VENDOR_IMAGE
+filter_out_proguard_dict_zip_modules += $(PRODUCT_OUT)/vendor/%
+endif
+# vendor_dlkm.img
+ifndef BUILDING_VENDOR_DLKM_IMAGE
+filter_out_proguard_dict_zip_modules += $(PRODUCT_OUT)/vendor_dlkm/%
+endif
+# cache.img
+ifndef BUILDING_CACHE_IMAGE
+filter_out_proguard_dict_zip_modules += $(PRODUCT_OUT)/cache/%
+endif
+# ramdisk.img
+ifndef BUILDING_RAMDISK_IMAGE
+filter_out_proguard_dict_zip_modules += $(PRODUCT_OUT)/ramdisk/%
+endif
+# recovery.img
+ifndef INSTALLED_RECOVERYIMAGE_TARGET
+filter_out_proguard_dict_zip_modules += $(PRODUCT_OUT)/recovery/%
+endif
+# userdata.img
+ifndef BUILDING_USERDATA_IMAGE
+filter_out_proguard_dict_zip_modules += $(PRODUCT_OUT)/data/%
+endif
+
+# Check the installed files of each module and return the module name
+# or return empty if none of the files remain to be installed
+define filter-out-proguard-modules
+$(if $(filter-out $(filter_out_proguard_dict_zip_modules),$(call module-installed-files,$(1))),$(1))
+endef
+
+# Filter out proguard dict zip modules those are not installed at the built image
+_proguard_dict_zip_modules := $(foreach m,$(_proguard_dict_zip_modules),$(strip $(call filter-out-proguard-modules,$(m))))
+endif
+
# The path to the zip file containing proguard dictionaries.
PROGUARD_DICT_ZIP :=$= $(PRODUCT_OUT)/$(TARGET_PRODUCT)-proguard-dict.zip
$(PROGUARD_DICT_ZIP): PRIVATE_SOONG_ZIP_ARGUMENTS := $(foreach m,$(_proguard_dict_zip_modules),$(ALL_MODULES.$(m).PROGUARD_DICTIONARY_SOONG_ZIP_ARGUMENTS))
diff --git a/core/android_soong_config_vars.mk b/core/android_soong_config_vars.mk
index 6aea680..f57ec81 100644
--- a/core/android_soong_config_vars.mk
+++ b/core/android_soong_config_vars.mk
@@ -108,6 +108,9 @@
SYSTEMUI_OPTIMIZE_JAVA ?= true
$(call add_soong_config_var,ANDROID,SYSTEMUI_OPTIMIZE_JAVA)
+# Flag to use baseline profile for SystemUI.
+$(call soong_config_set,ANDROID,release_systemui_use_speed_profile,$(RELEASE_SYSTEMUI_USE_SPEED_PROFILE))
+
# Flag for enabling compose for Launcher.
$(call soong_config_set,ANDROID,release_enable_compose_in_launcher,$(RELEASE_ENABLE_COMPOSE_IN_LAUNCHER))
@@ -216,6 +219,19 @@
# Required as platform_bootclasspath is using this namespace
$(call soong_config_set,bootclasspath,release_crashrecovery_module,$(RELEASE_CRASHRECOVERY_MODULE))
+
+# Add ondeviceintelligence module build flag to soong
+ifeq (true,$(RELEASE_ONDEVICE_INTELLIGENCE_MODULE))
+ $(call soong_config_set,ANDROID,release_ondevice_intelligence_module,true)
+ # Required as platform_bootclasspath is using this namespace
+ $(call soong_config_set,bootclasspath,release_ondevice_intelligence_module,true)
+
+else
+ $(call soong_config_set,ANDROID,release_ondevice_intelligence_platform,true)
+ $(call soong_config_set,bootclasspath,release_ondevice_intelligence_platform,true)
+
+endif
+
# Add uprobestats build flag to soong
$(call soong_config_set,ANDROID,release_uprobestats_module,$(RELEASE_UPROBESTATS_MODULE))
# Add uprobestats file move flags to soong, for both platform and module
diff --git a/core/board_config.mk b/core/board_config.mk
index ad89c03..16cf863 100644
--- a/core/board_config.mk
+++ b/core/board_config.mk
@@ -225,6 +225,7 @@
$(SRC_TARGET_DIR)/board/$(TARGET_DEVICE)/BoardConfig.mk \
device/generic/goldfish/board/$(TARGET_DEVICE)/BoardConfig.mk \
device/google/cuttlefish/board/$(TARGET_DEVICE)/BoardConfig.mk \
+ vendor/google/products/cuttlefish/pixel_watch/board/$(TARGET_DEVICE)/BoardConfig.mk \
$(shell test -d device && find -L device -maxdepth 4 -path '*/$(TARGET_DEVICE)/BoardConfig.mk') \
$(shell test -d vendor && find -L vendor -maxdepth 4 -path '*/$(TARGET_DEVICE)/BoardConfig.mk') \
)))
@@ -289,6 +290,7 @@
$(error Valid values of $(var) are "true", "false", and "". Not "$($(var))")))
include $(BUILD_SYSTEM)/board_config_wifi.mk
+include $(BUILD_SYSTEM)/board_config_wpa_supplicant.mk
# Set up soong config for "soong_config_value_variable".
-include hardware/interfaces/configstore/1.1/default/surfaceflinger.mk
diff --git a/core/board_config_wpa_supplicant.mk b/core/board_config_wpa_supplicant.mk
new file mode 100644
index 0000000..9ef438e
--- /dev/null
+++ b/core/board_config_wpa_supplicant.mk
@@ -0,0 +1,88 @@
+#
+# 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 file adds wpa_supplicant_8 variables into soong config namespace (`wpa_supplicant_8`)
+# ###############################################################
+
+ifdef BOARD_HOSTAPD_DRIVER
+$(call soong_config_set_bool,wpa_supplicant_8,wpa_build_hostapd,true)
+ifneq ($(BOARD_HOSTAPD_DRIVER),NL80211)
+ $(error BOARD_HOSTAPD_DRIVER set to $(BOARD_HOSTAPD_DRIVER) but current soong expected it should be NL80211 only!)
+endif
+endif
+
+ifdef BOARD_WPA_SUPPLICANT_DRIVER
+ifneq ($(BOARD_WPA_SUPPLICANT_DRIVER),NL80211)
+ $(error BOARD_WPA_SUPPLICANT_DRIVER set to $(BOARD_WPA_SUPPLICANT_DRIVER) but current soong expected it should be NL80211 only!)
+endif
+endif
+
+# This is for CONFIG_DRIVER_NL80211_BRCM, CONFIG_DRIVER_NL80211_SYNA, CONFIG_DRIVER_NL80211_QCA
+# And it is only used for a cflags setting in driver.
+$(call soong_config_set,wpa_supplicant_8,board_wlan_device,$(BOARD_WLAN_DEVICE))
+
+# Belong to CONFIG_IEEE80211AX definition
+ifeq ($(WIFI_FEATURE_HOSTAPD_11AX),true)
+$(call soong_config_set_bool,wpa_supplicant_8,hostapd_11ax,true)
+endif
+
+# Belong to CONFIG_IEEE80211BE definition
+ifeq ($(WIFI_FEATURE_HOSTAPD_11BE),true)
+$(call soong_config_set_bool,wpa_supplicant_8,hostapd_11be,true)
+endif
+
+# PLATFORM_VERSION
+$(call soong_config_set,wpa_supplicant_8,platform_version,$(PLATFORM_VERSION))
+
+# BOARD_HOSTAPD_PRIVATE_LIB
+ifeq ($(BOARD_HOSTAPD_PRIVATE_LIB),)
+$(call soong_config_set_bool,wpa_supplicant_8,hostapd_use_stub_lib,true)
+else
+$(call soong_config_set,wpa_supplicant_8,board_hostapd_private_lib,$(BOARD_HOSTAPD_PRIVATE_LIB))
+endif
+
+ifeq ($(BOARD_HOSTAPD_CONFIG_80211W_MFP_OPTIONAL),true)
+$(call soong_config_set_bool,wpa_supplicant_8,board_hostapd_config_80211w_mfp_optional,true)
+endif
+
+ifneq ($(BOARD_HOSTAPD_PRIVATE_LIB_EVENT),)
+$(call soong_config_set_bool,wpa_supplicant_8,board_hostapd_private_lib_event,true)
+endif
+
+# BOARD_WPA_SUPPLICANT_PRIVATE_LIB
+ifeq ($(BOARD_WPA_SUPPLICANT_PRIVATE_LIB),)
+$(call soong_config_set_bool,wpa_supplicant_8,wpa_supplicant_use_stub_lib,true)
+else
+$(call soong_config_set,wpa_supplicant_8,board_wpa_supplicant_private_lib,$(BOARD_WPA_SUPPLICANT_PRIVATE_LIB))
+endif
+
+ifneq ($(BOARD_WPA_SUPPLICANT_PRIVATE_LIB_EVENT),)
+$(call soong_config_set_bool,wpa_supplicant_8,board_wpa_supplicant_private_lib_event,true)
+endif
+
+ifeq ($(WIFI_PRIV_CMD_UPDATE_MBO_CELL_STATUS), enabled)
+$(call soong_config_set_bool,wpa_supplicant_8,wifi_priv_cmd_update_mbo_cell_status,true)
+endif
+
+ifeq ($(WIFI_HIDL_UNIFIED_SUPPLICANT_SERVICE_RC_ENTRY), true)
+$(call soong_config_set_bool,wpa_supplicant_8,wifi_hidl_unified_supplicant_service_rc_entry,true)
+endif
+
+# New added in internal main
+ifeq ($(WIFI_BRCM_OPEN_SOURCE_MULTI_AKM), enabled)
+$(call soong_config_set_bool,wpa_supplicant_8,wifi_brcm_open_source_multi_akm,true)
+endif
diff --git a/core/layoutlib_data.mk b/core/layoutlib_data.mk
index dabcfb2..f228ef6 100644
--- a/core/layoutlib_data.mk
+++ b/core/layoutlib_data.mk
@@ -3,11 +3,10 @@
FONT_TEMP := $(call intermediates-dir-for,PACKAGING,fonts,HOST,COMMON)
# The font configuration files - system_fonts.xml, fallback_fonts.xml etc.
-font_config := $(sort $(wildcard frameworks/base/data/fonts/*.xml))
+font_config := $(filter $(TARGET_OUT)/etc/font%.xml, $(INTERNAL_SYSTEMIMAGE_FILES))
font_config := $(addprefix $(FONT_TEMP)/, $(notdir $(font_config)))
-$(font_config): $(FONT_TEMP)/%.xml: \
- frameworks/base/data/fonts/%.xml
+$(font_config): $(FONT_TEMP)/%: $(TARGET_OUT)/etc/%
$(hide) mkdir -p $(dir $@)
$(hide) cp -vf $< $@
diff --git a/core/main.mk b/core/main.mk
index 447b07a..b9edfc1 100644
--- a/core/main.mk
+++ b/core/main.mk
@@ -1601,12 +1601,7 @@
$(INSTALLED_FILES_JSON_SYSTEMOTHER) \
$(INSTALLED_FILES_FILE_RECOVERY) \
$(INSTALLED_FILES_JSON_RECOVERY) \
- $(if $(BUILDING_SYSTEM_IMAGE), $(INSTALLED_BUILD_PROP_TARGET):build.prop) \
$(if $(BUILDING_VENDOR_IMAGE), $(INSTALLED_VENDOR_BUILD_PROP_TARGET):build.prop-vendor) \
- $(if $(BUILDING_PRODUCT_IMAGE), $(INSTALLED_PRODUCT_BUILD_PROP_TARGET):build.prop-product) \
- $(if $(BUILDING_ODM_IMAGE), $(INSTALLED_ODM_BUILD_PROP_TARGET):build.prop-odm) \
- $(if $(BUILDING_SYSTEM_EXT_IMAGE), $(INSTALLED_SYSTEM_EXT_BUILD_PROP_TARGET):build.prop-system_ext) \
- $(if $(BUILDING_RAMDISK_IMAGE), $(INSTALLED_RAMDISK_BUILD_PROP_TARGET):build.prop-ramdisk) \
$(INSTALLED_ANDROID_INFO_TXT_TARGET) \
$(INSTALLED_MISC_INFO_TARGET) \
$(INSTALLED_RAMDISK_TARGET) \
@@ -1700,7 +1695,6 @@
ALL_SDK_TARGETS := $(INTERNAL_SDK_TARGET)
sdk: $(ALL_SDK_TARGETS)
$(call dist-for-goals-with-filenametag,sdk,$(ALL_SDK_TARGETS))
-$(call dist-for-goals,sdk,$(INSTALLED_BUILD_PROP_TARGET))
endif
# umbrella targets to assit engineers in verifying builds
diff --git a/core/sysprop.mk b/core/sysprop.mk
index 9a9f509..5485a3a 100644
--- a/core/sysprop.mk
+++ b/core/sysprop.mk
@@ -79,6 +79,7 @@
echo "ro.$(1).build.version.release=$(PLATFORM_VERSION_LAST_STABLE)" >> $(2);\
echo "ro.$(1).build.version.release_or_codename=$(PLATFORM_VERSION)" >> $(2);\
echo "ro.$(1).build.version.sdk=$(PLATFORM_SDK_VERSION)" >> $(2);\
+ echo "ro.$(1).build.version.sdk_minor=$(PLATFORM_SDK_MINOR_VERSION)" >> $(2);\
endef
diff --git a/core/tasks/cts.mk b/core/tasks/cts.mk
index 1321c69..f5b3aa9 100644
--- a/core/tasks/cts.mk
+++ b/core/tasks/cts.mk
@@ -285,11 +285,7 @@
.PHONY: cts-api-map-all
# Put the test coverage report in the dist dir if "cts-api-coverage" is among the build goals.
-$(call dist-for-goals, cts-api-coverage, $(cts-test-coverage-report):cts-test-coverage-report.html)
-$(call dist-for-goals, cts-api-coverage, $(cts-system-api-coverage-report):cts-system-api-coverage-report.html)
$(call dist-for-goals, cts-api-coverage, $(cts-system-api-xml-coverage-report):cts-system-api-coverage-report.xml)
-$(call dist-for-goals, cts-api-coverage, $(cts-verifier-coverage-report):cts-verifier-coverage-report.html)
-$(call dist-for-goals, cts-api-coverage, $(cts-combined-coverage-report):cts-combined-coverage-report.html)
$(call dist-for-goals, cts-api-coverage, $(cts-combined-xml-coverage-report):cts-combined-coverage-report.xml)
ALL_TARGETS.$(cts-test-coverage-report).META_LIC:=$(module_license_metadata)
@@ -301,7 +297,6 @@
# Put the test api map report in the dist dir if "cts-api-map-all" is among the build goals.
$(call dist-for-goals, cts-api-map-all, $(cts-combined-api-map-xml-report):cts-api-map-report.xml)
-$(call dist-for-goals, cts-api-map-all, $(cts-combined-api-map-html-report):cts-api-map-report.html)
$(call dist-for-goals, cts-api-map-all, $(cts-combined-api-inherit-xml-report):cts-api-inherit-report.xml)
ALL_TARGETS.$(cts-api-map-xml-report).META_LIC:=$(module_license_metadata)
diff --git a/core/tasks/tools/package-modules.mk b/core/tasks/tools/package-modules.mk
index 4ec5520..4d7b0ee 100644
--- a/core/tasks/tools/package-modules.mk
+++ b/core/tasks/tools/package-modules.mk
@@ -96,7 +96,7 @@
$(my_package_zip): PRIVATE_COPY_PAIRS := $(my_copy_pairs)
$(my_package_zip): PRIVATE_STAGING_DIR := $(my_staging_dir)
$(my_package_zip): PRIVATE_PICKUP_FILES := $(my_pickup_files)
-$(my_package_zip) : $(my_built_modules)
+$(my_package_zip) : $(my_built_modules) $(SOONG_ZIP)
@echo "Package $@"
@rm -rf $(PRIVATE_STAGING_DIR) && mkdir -p $(PRIVATE_STAGING_DIR)
$(foreach p, $(PRIVATE_COPY_PAIRS),\
@@ -105,7 +105,7 @@
cp -Rf $(word 1,$(pair)) $(word 2,$(pair)) && ) true
$(hide) $(foreach f, $(PRIVATE_PICKUP_FILES),\
cp -RfL $(f) $(PRIVATE_STAGING_DIR) && ) true
- $(hide) cd $(PRIVATE_STAGING_DIR) && zip -rqX ../$(notdir $@) *
+ $(hide) $(SOONG_ZIP) -o $@ -C $(PRIVATE_STAGING_DIR) -D $(PRIVATE_STAGING_DIR)
rm -rf $(PRIVATE_STAGING_DIR)
my_makefile :=
diff --git a/core/version_util.mk b/core/version_util.mk
index 2bf41ec..ddcbda2 100644
--- a/core/version_util.mk
+++ b/core/version_util.mk
@@ -62,6 +62,12 @@
PLATFORM_SDK_VERSION := $(RELEASE_PLATFORM_SDK_VERSION)
.KATI_READONLY := PLATFORM_SDK_VERSION
+ifdef PLATFORM_SDK_MINOR_VERSION
+ $(error Do not set PLATFORM_SDK_MINOR_VERSION directly. Use RELEASE_PLATFORM_SDK_MINOR_VERSION. value: $(PLATFORM_SDK_MINOR_VERSION))
+endif
+PLATFORM_SDK_MINOR_VERSION := $(RELEASE_PLATFORM_SDK_MINOR_VERSION)
+.KATI_READONLY := PLATFORM_SDK_MINOR_VERSION
+
ifdef PLATFORM_SDK_EXTENSION_VERSION
$(error Do not set PLATFORM_SDK_EXTENSION_VERSION directly. Use RELEASE_PLATFORM_SDK_EXTENSION_VERSION. value: $(PLATFORM_SDK_EXTENSION_VERSION))
endif
diff --git a/target/product/app_function_extensions.mk b/target/product/app_function_extensions.mk
index a61afdc..e601fd7 100644
--- a/target/product/app_function_extensions.mk
+++ b/target/product/app_function_extensions.mk
@@ -18,5 +18,5 @@
# /system_ext packages
PRODUCT_PACKAGES += \
- com.google.android.appfunctions.sidecar \
- appfunctions.sidecar.xml
+ com.android.extensions.appfunctions \
+ appfunctions.extension.xml
diff --git a/target/product/base_system.mk b/target/product/base_system.mk
index d09c9e9..3fe97ba 100644
--- a/target/product/base_system.mk
+++ b/target/product/base_system.mk
@@ -205,7 +205,6 @@
libstdc++ \
libsysutils \
libui \
- libuprobestats_client \
libusbhost \
libutils \
libvintf_jni \
@@ -254,7 +253,6 @@
preinstalled-packages-asl-files.xml \
preinstalled-packages-platform.xml \
preinstalled-packages-strict-signature.xml \
- printflags \
privapp-permissions-platform.xml \
prng_seeder \
recovery-persist \
@@ -311,8 +309,20 @@
PRODUCT_PACKAGES += \
com.android.crashrecovery \
+else
+ PRODUCT_PACKAGES += \
+ framework-platformcrashrecovery \
+
endif
+# When we release ondeviceintelligence in neuralnetworks module
+ifneq ($(RELEASE_ONDEVICE_INTELLIGENCE_MODULE),true)
+ PRODUCT_PACKAGES += \
+ framework-ondeviceintelligence-platform
+
+endif
+
+
# When we release uprobestats module
ifeq ($(RELEASE_UPROBESTATS_MODULE),true)
PRODUCT_PACKAGES += \
@@ -321,6 +331,7 @@
else
PRODUCT_PACKAGES += \
uprobestats \
+ libuprobestats_client \
endif
@@ -351,8 +362,7 @@
# Check if the build supports Profiling module
ifeq ($(RELEASE_PACKAGE_PROFILING_MODULE),true)
PRODUCT_PACKAGES += \
- com.android.profiling \
- trace_redactor
+ com.android.profiling
endif
ifeq ($(RELEASE_USE_WEBVIEW_BOOTSTRAP_MODULE),true)
diff --git a/target/product/base_system_ext.mk b/target/product/base_system_ext.mk
index febe537..6767b9a 100644
--- a/target/product/base_system_ext.mk
+++ b/target/product/base_system_ext.mk
@@ -30,3 +30,8 @@
PRODUCT_PACKAGES_SHIPPING_API_LEVEL_34 += \
hwservicemanager \
android.hidl.allocator@1.0-service \
+
+# AppFunction Extensions
+ifneq (,$(RELEASE_APPFUNCTION_SIDECAR))
+ $(call inherit-product, $(SRC_TARGET_DIR)/product/app_function_extensions.mk)
+endif
\ No newline at end of file
diff --git a/target/product/build_variables.mk b/target/product/build_variables.mk
index e99ab06..562e5b7 100644
--- a/target/product/build_variables.mk
+++ b/target/product/build_variables.mk
@@ -31,3 +31,9 @@
# Use the configured MessageQueue implementation
$(call soong_config_set, messagequeue, release_package_messagequeue_implementation, $(RELEASE_PACKAGE_MESSAGEQUEUE_IMPLEMENTATION))
+
+# Use the configured version of WebView
+$(call soong_config_set, webview, release_package_webview_version, $(RELEASE_PACKAGE_WEBVIEW_VERSION))
+
+# Use the configured version of Cronet
+$(call soong_config_set,cronet,enable_cronet_tot,$(RELEASE_ENABLE_TOT_CRONET))
diff --git a/target/product/default_art_config.mk b/target/product/default_art_config.mk
index e543ccf..f91cb07 100644
--- a/target/product/default_art_config.mk
+++ b/target/product/default_art_config.mk
@@ -90,11 +90,27 @@
com.android.virt:framework-virtualization \
com.android.wifi:framework-wifi \
-# When we release crashrecovery module
+# When crashrecovery module is ready use apex jar
+# else put the platform jar in system
ifeq ($(RELEASE_CRASHRECOVERY_MODULE),true)
- PRODUCT_APEX_BOOT_JARS += \
+ PRODUCT_APEX_BOOT_JARS += \
com.android.crashrecovery:framework-crashrecovery \
+else
+ PRODUCT_BOOT_JARS += \
+ framework-platformcrashrecovery \
+
+endif
+
+# When we release ondeviceintelligence in NeuralNetworks module
+ifeq ($(RELEASE_ONDEVICE_INTELLIGENCE_MODULE),true)
+ PRODUCT_APEX_BOOT_JARS += \
+ com.android.neuralnetworks:framework-ondeviceintelligence \
+
+else
+ PRODUCT_BOOT_JARS += \
+ framework-ondeviceintelligence-platform \
+
endif
# Check if the build supports NFC apex or not
@@ -153,6 +169,13 @@
endif
+# When we release ondeviceintelligence in NeuralNetworks module
+ifeq ($(RELEASE_ONDEVICE_INTELLIGENCE_MODULE),true)
+ PRODUCT_APEX_SYSTEM_SERVER_JARS += \
+ com.android.neuralnetworks:service-ondeviceintelligence
+
+endif
+
ifeq ($(RELEASE_AVF_ENABLE_LLPVM_CHANGES),true)
PRODUCT_APEX_SYSTEM_SERVER_JARS += com.android.virt:service-virtualization
endif
diff --git a/target/product/generic/Android.bp b/target/product/generic/Android.bp
index 25c3bed..c90c61c 100644
--- a/target/product/generic/Android.bp
+++ b/target/product/generic/Android.bp
@@ -393,8 +393,8 @@
"charger",
] + select(release_flag("RELEASE_APPFUNCTION_SIDECAR"), {
true: [
- "com.google.android.appfunctions.sidecar",
- "appfunctions.sidecar.xml",
+ "com.android.extensions.appfunctions",
+ "appfunctions.extension.xml",
],
default: [],
}),
@@ -640,7 +640,6 @@
"preinstalled-packages-platform.xml", // base_system
"preinstalled-packages-strict-signature.xml", // base_system
"preloaded-classes", // ok
- "printflags", // base_system
"privapp-permissions-platform.xml", // base_system
"prng_seeder", // base_system
"public.libraries.android.txt",
@@ -695,11 +694,6 @@
default: [
"android.software.preview_sdk.prebuilt.xml", // media_system
],
- }) + select(soong_config_variable("ANDROID", "release_package_profiling_module"), {
- "true": [
- "trace_redactor", // base_system (RELEASE_PACKAGE_PROFILING_MODULE)
- ],
- default: [],
}) + select(release_flag("RELEASE_MEMORY_MANAGEMENT_DAEMON"), {
true: [
"mm_daemon", // base_system (RELEASE_MEMORY_MANAGEMENT_DAEMON)
@@ -871,7 +865,16 @@
"true": [
"com.android.crashrecovery", // base_system (RELEASE_CRASHRECOVERY_MODULE)
],
- default: [],
+ default: [
+ "framework-platformcrashrecovery", // base_system
+ ],
+ }) + select(release_flag("RELEASE_ONDEVICE_INTELLIGENCE_MODULE"), {
+ true: [
+ "com.android.neuralnetworks", // base_system (RELEASE_ONDEVICE_INTELLIGENCE_MODULE)
+ ],
+ default: [
+ "framework-ondeviceintelligence-platform", // base_system
+ ],
}) + select(soong_config_variable("ANDROID", "release_package_profiling_module"), {
"true": [
"com.android.profiling", // base_system (RELEASE_PACKAGE_PROFILING_MODULE)
diff --git a/target/product/media_system_ext.mk b/target/product/media_system_ext.mk
index 1179966..e79a7eb 100644
--- a/target/product/media_system_ext.mk
+++ b/target/product/media_system_ext.mk
@@ -22,8 +22,3 @@
# Window Extensions
$(call inherit-product, $(SRC_TARGET_DIR)/product/window_extensions_base.mk)
-
-# AppFunction Extensions
-ifneq (,$(RELEASE_APPFUNCTION_SIDECAR))
- $(call inherit-product, $(SRC_TARGET_DIR)/product/app_function_extensions.mk)
-endif
diff --git a/tools/aconfig/TEST_MAPPING b/tools/aconfig/TEST_MAPPING
index 6e53018..b1cc602 100644
--- a/tools/aconfig/TEST_MAPPING
+++ b/tools/aconfig/TEST_MAPPING
@@ -43,10 +43,6 @@
"name": "aflags.test"
},
{
- // printflags unit tests
- "name": "printflags.test"
- },
- {
// aconfig_protos unit tests
"name": "aconfig_protos.test"
},
diff --git a/tools/aconfig/aconfig/src/codegen/java.rs b/tools/aconfig/aconfig/src/codegen/java.rs
index 550ab5f..61802f2 100644
--- a/tools/aconfig/aconfig/src/codegen/java.rs
+++ b/tools/aconfig/aconfig/src/codegen/java.rs
@@ -641,7 +641,6 @@
package com.android.aconfig.test;
// TODO(b/303773055): Remove the annotation after access issue is resolved.
import android.compat.annotation.UnsupportedAppUsage;
- import android.os.Build;
import android.os.flagging.PlatformAconfigPackageInternal;
import android.util.Log;
/** @hide */
@@ -1379,6 +1378,56 @@
assert!(file_set.is_empty());
}
+ // Test that the SDK check isn't added unless the library is exported (even
+ // if the flag is present in finalized_flags).
+ #[test]
+ fn test_generate_java_code_flags_with_sdk_check() {
+ let parsed_flags = crate::test::parse_test_flags();
+ let mode = CodegenMode::Production;
+ let modified_parsed_flags =
+ crate::commands::modify_parsed_flags_based_on_mode(parsed_flags, mode).unwrap();
+ let flag_ids =
+ assign_flag_ids(crate::test::TEST_PACKAGE, modified_parsed_flags.iter()).unwrap();
+ let mut finalized_flags = FinalizedFlagMap::new();
+ finalized_flags.insert_if_new(
+ ApiLevel(36),
+ FinalizedFlag {
+ flag_name: "disabled_rw".to_string(),
+ package_name: "com.android.aconfig.test".to_string(),
+ },
+ );
+ let config = JavaCodegenConfig {
+ codegen_mode: mode,
+ flag_ids,
+ allow_instrumentation: true,
+ package_fingerprint: 5801144784618221668,
+ new_exported: true,
+ single_exported_file: false,
+ finalized_flags,
+ };
+ let generated_files = generate_java_code(
+ crate::test::TEST_PACKAGE,
+ modified_parsed_flags.into_iter(),
+ config,
+ )
+ .unwrap();
+
+ let expect_flags_content = EXPECTED_FLAG_COMMON_CONTENT.to_string()
+ + r#"
+ private static FeatureFlags FEATURE_FLAGS = new FeatureFlagsImpl();
+ }"#;
+
+ let file = generated_files.iter().find(|f| f.path.ends_with("Flags.java")).unwrap();
+ assert_eq!(
+ None,
+ crate::test::first_significant_code_diff(
+ &expect_flags_content,
+ &String::from_utf8(file.contents.clone()).unwrap()
+ ),
+ "Flags content is not correct"
+ );
+ }
+
#[test]
fn test_generate_java_code_test() {
let parsed_flags = crate::test::parse_test_flags();
diff --git a/tools/aconfig/aconfig/templates/FeatureFlagsImpl.new_storage.java.template b/tools/aconfig/aconfig/templates/FeatureFlagsImpl.new_storage.java.template
index 9492a83..8dc7581 100644
--- a/tools/aconfig/aconfig/templates/FeatureFlagsImpl.new_storage.java.template
+++ b/tools/aconfig/aconfig/templates/FeatureFlagsImpl.new_storage.java.template
@@ -2,7 +2,6 @@
// TODO(b/303773055): Remove the annotation after access issue is resolved.
import android.compat.annotation.UnsupportedAppUsage;
{{ -if runtime_lookup_required }}
-import android.os.Build;
{{ if is_platform_container }}
import android.os.flagging.PlatformAconfigPackageInternal;
{{ -else }} {#- else is_platform_container #}
diff --git a/tools/aconfig/aconfig/templates/Flags.java.template b/tools/aconfig/aconfig/templates/Flags.java.template
index 6fa2701..0cdc269 100644
--- a/tools/aconfig/aconfig/templates/Flags.java.template
+++ b/tools/aconfig/aconfig/templates/Flags.java.template
@@ -4,7 +4,7 @@
import android.compat.annotation.UnsupportedAppUsage;
{{ else }}
import android.os.Build;
-{{ -endif }}
+{{ -endif }} {#- end not library_exported#}
{{ -if single_exported_file }}
{{ -if library_exported }}
/**
@@ -33,11 +33,13 @@
@UnsupportedAppUsage
{{ -endif }}
public static boolean {item.method_name}() \{
+ {{ if library_exported- }}
{{ -if item.finalized_sdk_present }}
if (Build.VERSION.SDK_INT >= {item.finalized_sdk_value}) \{
return true;
}
{{ -endif}} {#- end finalized_sdk_present#}
+ {{ -endif}} {#- end library_exported#}
return FEATURE_FLAGS.{item.method_name}();
}
{{ -endfor }}
diff --git a/tools/aconfig/aconfig_storage_read_api/srcs/android/os/flagging/PlatformAconfigPackage.java b/tools/aconfig/aconfig_storage_read_api/srcs/android/os/flagging/PlatformAconfigPackage.java
index ddad249..63baf9e 100644
--- a/tools/aconfig/aconfig_storage_read_api/srcs/android/os/flagging/PlatformAconfigPackage.java
+++ b/tools/aconfig/aconfig_storage_read_api/srcs/android/os/flagging/PlatformAconfigPackage.java
@@ -65,7 +65,11 @@
/** @hide */
@UnsupportedAppUsage
public static final Set<String> PLATFORM_PACKAGE_MAP_FILES =
- Set.of("system.package.map", "vendor.package.map", "product.package.map");
+ Set.of(
+ "system.package.map",
+ "system_ext.package.map",
+ "vendor.package.map",
+ "product.package.map");
static {
for (String pf : PLATFORM_PACKAGE_MAP_FILES) {
diff --git a/tools/aconfig/aconfig_storage_read_api/tests/functional/srcs/PlatformAconfigPackageInternalTest.java b/tools/aconfig/aconfig_storage_read_api/tests/functional/srcs/PlatformAconfigPackageInternalTest.java
index 9896baf..0c5bc1c 100644
--- a/tools/aconfig/aconfig_storage_read_api/tests/functional/srcs/PlatformAconfigPackageInternalTest.java
+++ b/tools/aconfig/aconfig_storage_read_api/tests/functional/srcs/PlatformAconfigPackageInternalTest.java
@@ -42,7 +42,8 @@
@RunWith(JUnit4.class)
public class PlatformAconfigPackageInternalTest {
- private static final Set<String> PLATFORM_CONTAINERS = Set.of("system", "vendor", "product");
+ private static final Set<String> PLATFORM_CONTAINERS =
+ Set.of("system", "system_ext", "vendor", "product");
@Test
public void testPlatformAconfigPackageInternal_load() throws IOException {
diff --git a/tools/aconfig/aconfig_storage_read_api/tests/functional/srcs/PlatformAconfigPackageTest.java b/tools/aconfig/aconfig_storage_read_api/tests/functional/srcs/PlatformAconfigPackageTest.java
index 1c6c238..2b4ead8 100644
--- a/tools/aconfig/aconfig_storage_read_api/tests/functional/srcs/PlatformAconfigPackageTest.java
+++ b/tools/aconfig/aconfig_storage_read_api/tests/functional/srcs/PlatformAconfigPackageTest.java
@@ -42,7 +42,8 @@
@RunWith(JUnit4.class)
public class PlatformAconfigPackageTest {
- private static final Set<String> PLATFORM_CONTAINERS = Set.of("system", "vendor", "product");
+ private static final Set<String> PLATFORM_CONTAINERS =
+ Set.of("system", "system_ext", "vendor", "product");
@Test
public void testPlatformAconfigPackage_StorageFilesCache() throws IOException {
diff --git a/tools/aconfig/fake_device_config/src/android/os/flagging/PlatformAconfigPackage.java b/tools/aconfig/fake_device_config/src/android/os/flagging/PlatformAconfigPackage.java
index ec79f7d..c06a532 100644
--- a/tools/aconfig/fake_device_config/src/android/os/flagging/PlatformAconfigPackage.java
+++ b/tools/aconfig/fake_device_config/src/android/os/flagging/PlatformAconfigPackage.java
@@ -24,7 +24,11 @@
public class PlatformAconfigPackage {
public static final Set<String> PLATFORM_PACKAGE_MAP_FILES =
- Set.of("system.package.map", "vendor.package.map", "product.package.map");
+ Set.of(
+ "system.package.map",
+ "system_ext.package.map",
+ "vendor.package.map",
+ "product.package.map");
public static PlatformAconfigPackage load(String packageName) {
throw new UnsupportedOperationException("Stub!");
diff --git a/tools/aconfig/printflags/Android.bp b/tools/aconfig/printflags/Android.bp
deleted file mode 100644
index 6f7bca3..0000000
--- a/tools/aconfig/printflags/Android.bp
+++ /dev/null
@@ -1,31 +0,0 @@
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-rust_defaults {
- name: "printflags.defaults",
- edition: "2021",
- clippy_lints: "android",
- lints: "android",
- srcs: ["src/main.rs"],
- rustlibs: [
- "libaconfig_protos",
- "libanyhow",
- "libprotobuf",
- "libregex",
- ],
-}
-
-rust_binary {
- name: "printflags",
- defaults: ["printflags.defaults"],
- apex_available: [
- "//apex_available:platform",
- ],
-}
-
-rust_test_host {
- name: "printflags.test",
- defaults: ["printflags.defaults"],
- test_suites: ["general-tests"],
-}
diff --git a/tools/aconfig/printflags/Cargo.toml b/tools/aconfig/printflags/Cargo.toml
deleted file mode 100644
index 7313f5d..0000000
--- a/tools/aconfig/printflags/Cargo.toml
+++ /dev/null
@@ -1,15 +0,0 @@
-[package]
-name = "printflags"
-version = "0.1.0"
-edition = "2021"
-
-[features]
-default = ["cargo"]
-cargo = []
-
-[dependencies]
-anyhow = "1.0.69"
-paste = "1.0.11"
-protobuf = "3.2.0"
-regex = "1.10.3"
-aconfig_protos = { path = "../aconfig_protos" }
diff --git a/tools/aconfig/printflags/src/main.rs b/tools/aconfig/printflags/src/main.rs
deleted file mode 100644
index 7838b51..0000000
--- a/tools/aconfig/printflags/src/main.rs
+++ /dev/null
@@ -1,152 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-//! `printflags` is a device binary to print feature flags.
-
-use aconfig_protos::ProtoFlagState as State;
-use aconfig_protos::ProtoParsedFlags;
-use anyhow::{bail, Context, Result};
-use regex::Regex;
-use std::collections::BTreeMap;
-use std::collections::HashMap;
-use std::process::Command;
-use std::{fs, str};
-
-fn parse_device_config(raw: &str) -> HashMap<String, String> {
- let mut flags = HashMap::new();
- let regex = Regex::new(r"(?m)^([[[:alnum:]]_]+/[[[:alnum:]]_\.]+)=(true|false)$").unwrap();
- for capture in regex.captures_iter(raw) {
- let key = capture.get(1).unwrap().as_str().to_string();
- let value = match capture.get(2).unwrap().as_str() {
- "true" => format!("{:?} (device_config)", State::ENABLED),
- "false" => format!("{:?} (device_config)", State::DISABLED),
- _ => panic!(),
- };
- flags.insert(key, value);
- }
- flags
-}
-
-fn xxd(bytes: &[u8]) -> String {
- let n = 8.min(bytes.len());
- let mut v = Vec::with_capacity(n);
- for byte in bytes.iter().take(n) {
- v.push(format!("{:02x}", byte));
- }
- let trailer = match bytes.len() {
- 0..=8 => "",
- _ => " ..",
- };
- format!("[{}{}]", v.join(" "), trailer)
-}
-
-fn main() -> Result<()> {
- // read device_config
- let output = Command::new("/system/bin/device_config").arg("list").output()?;
- if !output.status.success() {
- let reason = match output.status.code() {
- Some(code) => format!("exit code {}", code),
- None => "terminated by signal".to_string(),
- };
- bail!("failed to execute device_config: {}", reason);
- }
- let dc_stdout = str::from_utf8(&output.stdout)?;
- let device_config_flags = parse_device_config(dc_stdout);
-
- // read aconfig_flags.pb files
- let apex_pattern = Regex::new(r"^/apex/[^@]+\.[^@]+$").unwrap();
- let mut mount_points = vec![
- "system".to_string(),
- "system_ext".to_string(),
- "product".to_string(),
- "vendor".to_string(),
- ];
- for apex in fs::read_dir("/apex")? {
- let path_name = apex?.path().display().to_string();
- if let Some(canonical_path) = apex_pattern.captures(&path_name) {
- mount_points.push(canonical_path.get(0).unwrap().as_str().to_owned());
- }
- }
-
- let mut flags: BTreeMap<String, Vec<String>> = BTreeMap::new();
- for mount_point in mount_points {
- let path = format!("/{}/etc/aconfig_flags.pb", mount_point);
- let Ok(bytes) = fs::read(&path) else {
- eprintln!("warning: failed to read {}", path);
- continue;
- };
- let parsed_flags: ProtoParsedFlags = protobuf::Message::parse_from_bytes(&bytes)
- .with_context(|| {
- format!("failed to parse {} ({}, {} byte(s))", path, xxd(&bytes), bytes.len())
- })?;
- for flag in parsed_flags.parsed_flag {
- let key = format!("{}/{}.{}", flag.namespace(), flag.package(), flag.name());
- let value = format!("{:?} + {:?} ({})", flag.permission(), flag.state(), mount_point);
- flags.entry(key).or_default().push(value);
- }
- }
-
- // print flags
- for (key, mut value) in flags {
- if let Some(dc_value) = device_config_flags.get(&key) {
- value.push(dc_value.to_string());
- }
- println!("{}: {}", key, value.join(", "));
- }
-
- Ok(())
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
-
- #[test]
- fn test_parse_device_config() {
- let input = r#"
-namespace_one/com.foo.bar.flag_one=true
-namespace_one/com.foo.bar.flag_two=false
-random_noise;
-namespace_two/android.flag_one=true
-namespace_two/android.flag_two=nonsense
-"#;
- let expected = HashMap::from([
- (
- "namespace_one/com.foo.bar.flag_one".to_string(),
- "ENABLED (device_config)".to_string(),
- ),
- (
- "namespace_one/com.foo.bar.flag_two".to_string(),
- "DISABLED (device_config)".to_string(),
- ),
- ("namespace_two/android.flag_one".to_string(), "ENABLED (device_config)".to_string()),
- ]);
- let actual = parse_device_config(input);
- assert_eq!(expected, actual);
- }
-
- #[test]
- fn test_xxd() {
- let input = [0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9];
- assert_eq!("[]", &xxd(&input[0..0]));
- assert_eq!("[00]", &xxd(&input[0..1]));
- assert_eq!("[00 01]", &xxd(&input[0..2]));
- assert_eq!("[00 01 02 03 04 05 06]", &xxd(&input[0..7]));
- assert_eq!("[00 01 02 03 04 05 06 07]", &xxd(&input[0..8]));
- assert_eq!("[00 01 02 03 04 05 06 07 ..]", &xxd(&input[0..9]));
- assert_eq!("[00 01 02 03 04 05 06 07 ..]", &xxd(&input));
- }
-}
diff --git a/tools/dependency_mapper/Android.bp b/tools/dependency_mapper/Android.bp
new file mode 100644
index 0000000..6763c0e
--- /dev/null
+++ b/tools/dependency_mapper/Android.bp
@@ -0,0 +1,45 @@
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+ default_team: "trendy_team_android_crumpet",
+}
+
+java_binary_host {
+ name: "dependency-mapper",
+ main_class: "com.android.dependencymapper.Main",
+ static_libs: [
+ "dependency-mapper-host-lib",
+ ],
+ visibility: ["//visibility:public"],
+}
+
+java_library_host {
+ name: "dependency-mapper-host-lib",
+ srcs: [
+ "src/**/*.java",
+ "proto/**/*.proto",
+ ],
+ static_libs: [
+ "gson",
+ "ow2-asm",
+ ],
+}
+
+java_test_host {
+ name: "dependency-mapper-tests",
+ srcs: ["tests/src/**/*.java"],
+ static_libs: [
+ "junit",
+ "dependency-mapper-host-lib",
+ ],
+ data: [
+ "tests/res/**/*",
+ ],
+ test_options: {
+ unit_test: true,
+ },
+}
+
+java_library {
+ name: "dependency-mapper-test-data",
+ srcs: ["tests/res/**/*.java"],
+}
diff --git a/tools/dependency_mapper/OWNERS b/tools/dependency_mapper/OWNERS
new file mode 100644
index 0000000..4477269
--- /dev/null
+++ b/tools/dependency_mapper/OWNERS
@@ -0,0 +1 @@
+himanshuz@google.com
\ No newline at end of file
diff --git a/tools/dependency_mapper/README.md b/tools/dependency_mapper/README.md
new file mode 100644
index 0000000..475aef2
--- /dev/null
+++ b/tools/dependency_mapper/README.md
@@ -0,0 +1,26 @@
+# Dependency Mapper
+
+[dependency-mapper] command line tool. This tool finds the usage based dependencies between java
+files by utilizing byte-code and java file analysis.
+
+# Getting Started
+
+## Inputs
+* rsp file, containing list of java files separated by whitespace.
+* jar file, containing class files generated after compiling the contents of rsp file.
+
+## Output
+* proto file, representing the list of dependencies for each java file present in input rsp file,
+represented by [proto/usage.proto]
+
+## Usage
+```
+dependency-mapper --src-path [src-list.rsp] --jar-path [classes.jar] --usage-map-path [usage-map.proto]"
+```
+
+# Notes
+## Dependencies enlisted are only within the java files present in input.
+## Ensure that [SourceFile] is present in the classes present in the jar.
+## To ensure dependencies are listed correctly
+* Classes jar should only contain class files generated from the source rsp files.
+* Classes jar should not exclude any class file that was generated from source rsp files.
\ No newline at end of file
diff --git a/tools/dependency_mapper/proto/dependency.proto b/tools/dependency_mapper/proto/dependency.proto
new file mode 100644
index 0000000..60a88f8
--- /dev/null
+++ b/tools/dependency_mapper/proto/dependency.proto
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2019 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 = "proto2";
+
+package com.android.dependencymapper;
+option java_package = "com.android.dependencymapper";
+option java_outer_classname = "DependencyProto";
+
+/**
+ * A com.android.dependencymapper.DependencyProto.FileDependency object.
+ */
+
+message FileDependency {
+
+ // java file path on disk
+ optional string file_path = 1;
+ // if a change in this file warrants recompiling all files
+ optional bool is_dependency_to_all = 2;
+ // class files generated when this java file is compiled
+ repeated string generated_classes = 3;
+ // dependencies of this file.
+ repeated string file_dependencies = 4;
+}
+
+/**
+ * A com.android.dependencymapper.DependencyProto.FileDependencyList object.
+ */
+message FileDependencyList {
+
+ // List of java file usages
+ repeated FileDependency fileDependency = 1;
+}
\ No newline at end of file
diff --git a/tools/dependency_mapper/src/com/android/dependencymapper/ClassDependenciesVisitor.java b/tools/dependency_mapper/src/com/android/dependencymapper/ClassDependenciesVisitor.java
new file mode 100644
index 0000000..ba65145
--- /dev/null
+++ b/tools/dependency_mapper/src/com/android/dependencymapper/ClassDependenciesVisitor.java
@@ -0,0 +1,316 @@
+/*
+ * Copyright (C) 2025 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 com.android.dependencymapper;
+
+import org.objectweb.asm.signature.SignatureReader;
+import org.objectweb.asm.signature.SignatureVisitor;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+import org.objectweb.asm.TypePath;
+
+import java.lang.annotation.RetentionPolicy;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * An ASM based class visitor to analyze and club all dependencies of a java file.
+ * Most of the logic of this class is inspired from
+ * <a href="https://github.com/gradle/gradle/blob/master/platforms/jvm/language-java/src/main/java/org/gradle/api/internal/tasks/compile/incremental/asm/ClassDependenciesVisitor.java">gradle incremental compilation</a>
+ */
+public class ClassDependenciesVisitor extends ClassVisitor {
+
+ private final static int API = Opcodes.ASM9;
+
+ private final Set<String> mClassTypes;
+ private final Set<Object> mConstantsDefined;
+ private final Set<Object> mInlinedUsages;
+ private String mSource;
+ private boolean isAnnotationType;
+ private boolean mIsDependencyToAll;
+ private final RetentionPolicyVisitor retentionPolicyVisitor;
+
+ private final ClassRelevancyFilter mClassFilter;
+
+ private ClassDependenciesVisitor(ClassReader reader, ClassRelevancyFilter filter) {
+ super(API);
+ this.mClassTypes = new HashSet<>();
+ this.mConstantsDefined = new HashSet<>();
+ this.mInlinedUsages = new HashSet<>();
+ this.retentionPolicyVisitor = new RetentionPolicyVisitor();
+ this.mClassFilter = filter;
+ collectRemainingClassDependencies(reader);
+ }
+
+ public static ClassDependencyData analyze(
+ String className, ClassReader reader, ClassRelevancyFilter filter) {
+ ClassDependenciesVisitor visitor = new ClassDependenciesVisitor(reader, filter);
+ reader.accept(visitor, ClassReader.SKIP_FRAMES);
+ // Sometimes a class may contain references to the same class, we remove such cases to
+ // prevent circular dependency.
+ visitor.getClassTypes().remove(className);
+ return new ClassDependencyData(Utils.buildPackagePrependedClassSource(
+ className, visitor.getSource()), className, visitor.getClassTypes(),
+ visitor.isDependencyToAll(), visitor.getConstantsDefined(),
+ visitor.getInlinedUsages());
+ }
+
+ @Override
+ public void visitSource(String source, String debug) {
+ mSource = source;
+ }
+
+ @Override
+ public void visit(int version, int access, String name, String signature, String superName,
+ String[] interfaces) {
+ isAnnotationType = isAnnotationType(interfaces);
+ maybeAddClassTypesFromSignature(signature, mClassTypes);
+ if (superName != null) {
+ // superName can be null if what we are analyzing is `java.lang.Object`
+ // which can happen when a custom Java SDK is on classpath (typically, android.jar)
+ Type type = Type.getObjectType(superName);
+ maybeAddClassType(mClassTypes, type);
+ }
+ for (String s : interfaces) {
+ Type interfaceType = Type.getObjectType(s);
+ maybeAddClassType(mClassTypes, interfaceType);
+ }
+ }
+
+ // performs a fast analysis of classes referenced in bytecode (method bodies)
+ // avoiding us to implement a costly visitor and potentially missing edge cases
+ private void collectRemainingClassDependencies(ClassReader reader) {
+ char[] charBuffer = new char[reader.getMaxStringLength()];
+ for (int i = 1; i < reader.getItemCount(); i++) {
+ int itemOffset = reader.getItem(i);
+ // see https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.4
+ if (itemOffset > 0 && reader.readByte(itemOffset - 1) == 7) {
+ // A CONSTANT_Class entry, read the class descriptor
+ String classDescriptor = reader.readUTF8(itemOffset, charBuffer);
+ Type type = Type.getObjectType(classDescriptor);
+ maybeAddClassType(mClassTypes, type);
+ }
+ }
+ }
+
+ private void maybeAddClassTypesFromSignature(String signature, Set<String> types) {
+ if (signature != null) {
+ SignatureReader signatureReader = new SignatureReader(signature);
+ signatureReader.accept(new SignatureVisitor(API) {
+ @Override
+ public void visitClassType(String className) {
+ Type type = Type.getObjectType(className);
+ maybeAddClassType(types, type);
+ }
+ });
+ }
+ }
+
+ protected void maybeAddClassType(Set<String> types, Type type) {
+ while (type.getSort() == Type.ARRAY) {
+ type = type.getElementType();
+ }
+ if (type.getSort() != Type.OBJECT) {
+ return;
+ }
+ //String name = Utils.classPackageToFilePath(type.getClassName());
+ String name = type.getClassName();
+ if (mClassFilter.test(name)) {
+ types.add(name);
+ }
+ }
+
+ public String getSource() {
+ return mSource;
+ }
+
+ public Set<String> getClassTypes() {
+ return mClassTypes;
+ }
+
+ public Set<Object> getConstantsDefined() {
+ return mConstantsDefined;
+ }
+
+ public Set<Object> getInlinedUsages() {
+ return mInlinedUsages;
+ }
+
+ private boolean isAnnotationType(String[] interfaces) {
+ return interfaces.length == 1 && interfaces[0].equals("java/lang/annotation/Annotation");
+ }
+
+ @Override
+ public FieldVisitor visitField(
+ int access, String name, String desc, String signature, Object value) {
+ maybeAddClassTypesFromSignature(signature, mClassTypes);
+ maybeAddClassType(mClassTypes, Type.getType(desc));
+ if (isAccessibleConstant(access, value)) {
+ mConstantsDefined.add(value);
+ }
+ return new FieldVisitor(mClassTypes);
+ }
+
+ @Override
+ public MethodVisitor visitMethod(
+ int access, String name, String desc, String signature, String[] exceptions) {
+ maybeAddClassTypesFromSignature(signature, mClassTypes);
+ Type methodType = Type.getMethodType(desc);
+ maybeAddClassType(mClassTypes, methodType.getReturnType());
+ for (Type argType : methodType.getArgumentTypes()) {
+ maybeAddClassType(mClassTypes, argType);
+ }
+ return new MethodVisitor(mClassTypes);
+ }
+
+ @Override
+ public org.objectweb.asm.AnnotationVisitor visitAnnotation(String desc, boolean visible) {
+ if (isAnnotationType && "Ljava/lang/annotation/Retention;".equals(desc)) {
+ return retentionPolicyVisitor;
+ } else {
+ maybeAddClassType(mClassTypes, Type.getType(desc));
+ return new AnnotationVisitor(mClassTypes);
+ }
+ }
+
+ private static boolean isAccessible(int access) {
+ return (access & Opcodes.ACC_PRIVATE) == 0;
+ }
+
+ private static boolean isAccessibleConstant(int access, Object value) {
+ return isConstant(access) && isAccessible(access) && value != null;
+ }
+
+ private static boolean isConstant(int access) {
+ return (access & Opcodes.ACC_FINAL) != 0 && (access & Opcodes.ACC_STATIC) != 0;
+ }
+
+ public boolean isDependencyToAll() {
+ return mIsDependencyToAll;
+ }
+
+ private class FieldVisitor extends org.objectweb.asm.FieldVisitor {
+ private final Set<String> types;
+
+ public FieldVisitor(Set<String> types) {
+ super(API);
+ this.types = types;
+ }
+
+ @Override
+ public org.objectweb.asm.AnnotationVisitor visitAnnotation(
+ String descriptor, boolean visible) {
+ maybeAddClassType(types, Type.getType(descriptor));
+ return new AnnotationVisitor(types);
+ }
+
+ @Override
+ public org.objectweb.asm.AnnotationVisitor visitTypeAnnotation(int typeRef,
+ TypePath typePath, String descriptor, boolean visible) {
+ maybeAddClassType(types, Type.getType(descriptor));
+ return new AnnotationVisitor(types);
+ }
+ }
+
+ private class MethodVisitor extends org.objectweb.asm.MethodVisitor {
+ private final Set<String> types;
+
+ protected MethodVisitor(Set<String> types) {
+ super(API);
+ this.types = types;
+ }
+
+ @Override
+ public void visitLdcInsn(Object value) {
+ mInlinedUsages.add(value);
+ super.visitLdcInsn(value);
+ }
+
+ @Override
+ public void visitLocalVariable(
+ String name, String desc, String signature, Label start, Label end, int index) {
+ maybeAddClassTypesFromSignature(signature, mClassTypes);
+ maybeAddClassType(mClassTypes, Type.getType(desc));
+ super.visitLocalVariable(name, desc, signature, start, end, index);
+ }
+
+ @Override
+ public org.objectweb.asm.AnnotationVisitor visitAnnotation(
+ String descriptor, boolean visible) {
+ maybeAddClassType(types, Type.getType(descriptor));
+ return new AnnotationVisitor(types);
+ }
+
+ @Override
+ public org.objectweb.asm.AnnotationVisitor visitParameterAnnotation(
+ int parameter, String descriptor, boolean visible) {
+ maybeAddClassType(types, Type.getType(descriptor));
+ return new AnnotationVisitor(types);
+ }
+
+ @Override
+ public org.objectweb.asm.AnnotationVisitor visitTypeAnnotation(
+ int typeRef, TypePath typePath, String descriptor, boolean visible) {
+ maybeAddClassType(types, Type.getType(descriptor));
+ return new AnnotationVisitor(types);
+ }
+ }
+
+ private class RetentionPolicyVisitor extends org.objectweb.asm.AnnotationVisitor {
+ public RetentionPolicyVisitor() {
+ super(ClassDependenciesVisitor.API);
+ }
+
+ @Override
+ public void visitEnum(String name, String desc, String value) {
+ if ("Ljava/lang/annotation/RetentionPolicy;".equals(desc)) {
+ RetentionPolicy policy = RetentionPolicy.valueOf(value);
+ if (policy == RetentionPolicy.SOURCE) {
+ mIsDependencyToAll = true;
+ }
+ }
+ }
+ }
+
+ private class AnnotationVisitor extends org.objectweb.asm.AnnotationVisitor {
+ private final Set<String> types;
+
+ public AnnotationVisitor(Set<String> types) {
+ super(ClassDependenciesVisitor.API);
+ this.types = types;
+ }
+
+ @Override
+ public void visit(String name, Object value) {
+ if (value instanceof Type) {
+ maybeAddClassType(types, (Type) value);
+ }
+ }
+
+ @Override
+ public org.objectweb.asm.AnnotationVisitor visitArray(String name) {
+ return this;
+ }
+
+ @Override
+ public org.objectweb.asm.AnnotationVisitor visitAnnotation(String name, String descriptor) {
+ maybeAddClassType(types, Type.getType(descriptor));
+ return this;
+ }
+ }
+}
\ No newline at end of file
diff --git a/tools/dependency_mapper/src/com/android/dependencymapper/ClassDependencyAnalyzer.java b/tools/dependency_mapper/src/com/android/dependencymapper/ClassDependencyAnalyzer.java
new file mode 100644
index 0000000..4a37b41
--- /dev/null
+++ b/tools/dependency_mapper/src/com/android/dependencymapper/ClassDependencyAnalyzer.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2025 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 com.android.dependencymapper;
+
+import org.objectweb.asm.ClassReader;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+
+/**
+ * An utility class that reads each class file present in the classes jar, then analyzes the same,
+ * collecting the dependencies in {@link List<ClassDependencyData>}
+ */
+public class ClassDependencyAnalyzer {
+
+ public static List<ClassDependencyData> analyze(Path classJar, ClassRelevancyFilter classFilter) {
+ List<ClassDependencyData> classAnalysisList = new ArrayList<>();
+ try (JarFile jarFile = new JarFile(classJar.toFile())) {
+ Enumeration<JarEntry> entries = jarFile.entries();
+ while (entries.hasMoreElements()) {
+ JarEntry entry = entries.nextElement();
+ if (entry.getName().endsWith(".class")) {
+ try (InputStream inputStream = jarFile.getInputStream(entry)) {
+ String name = Utils.trimAndConvertToPackageBasedPath(entry.getName());
+ ClassDependencyData classAnalysis = ClassDependenciesVisitor.analyze(name,
+ new ClassReader(inputStream), classFilter);
+ classAnalysisList.add(classAnalysis);
+ }
+ }
+ }
+ } catch (IOException e) {
+ System.err.println("Error reading the jar file at: " + classJar);
+ throw new RuntimeException(e);
+ }
+ return classAnalysisList;
+ }
+}
diff --git a/tools/dependency_mapper/src/com/android/dependencymapper/ClassDependencyData.java b/tools/dependency_mapper/src/com/android/dependencymapper/ClassDependencyData.java
new file mode 100644
index 0000000..58e388f
--- /dev/null
+++ b/tools/dependency_mapper/src/com/android/dependencymapper/ClassDependencyData.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2025 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 com.android.dependencymapper;
+
+import java.util.Set;
+
+/**
+ * Represents the Class Dependency Data collected via ASM analysis.
+ */
+public class ClassDependencyData {
+ private final String mPackagePrependedClassSource;
+ private final String mQualifiedName;
+ private final Set<String> mClassDependencies;
+ private final boolean mIsDependencyToAll;
+ private final Set<Object> mConstantsDefined;
+ private final Set<Object> mInlinedUsages;
+
+ public ClassDependencyData(String packagePrependedClassSource, String className,
+ Set<String> classDependencies, boolean isDependencyToAll, Set<Object> constantsDefined,
+ Set<Object> inlinedUsages) {
+ this.mPackagePrependedClassSource = packagePrependedClassSource;
+ this.mQualifiedName = className;
+ this.mClassDependencies = classDependencies;
+ this.mIsDependencyToAll = isDependencyToAll;
+ this.mConstantsDefined = constantsDefined;
+ this.mInlinedUsages = inlinedUsages;
+ }
+
+ public String getPackagePrependedClassSource() {
+ return mPackagePrependedClassSource;
+ }
+
+ public String getQualifiedName() {
+ return mQualifiedName;
+ }
+
+ public Set<String> getClassDependencies() {
+ return mClassDependencies;
+ }
+
+ public Set<Object> getConstantsDefined() {
+ return mConstantsDefined;
+ }
+
+ public Set<Object> inlinedUsages() {
+ return mInlinedUsages;
+ }
+
+ public boolean isDependencyToAll() {
+ return mIsDependencyToAll;
+ }
+}
diff --git a/tools/dependency_mapper/src/com/android/dependencymapper/ClassRelevancyFilter.java b/tools/dependency_mapper/src/com/android/dependencymapper/ClassRelevancyFilter.java
new file mode 100644
index 0000000..c46b53f
--- /dev/null
+++ b/tools/dependency_mapper/src/com/android/dependencymapper/ClassRelevancyFilter.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2025 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 com.android.dependencymapper;
+
+import java.util.Set;
+import java.util.function.Predicate;
+
+/**
+ * A filter representing the list of class files which are relevant for dependency analysis.
+ */
+public class ClassRelevancyFilter implements Predicate<String> {
+
+ private final Set<String> mAllowlistedClassNames;
+
+ public ClassRelevancyFilter(Set<String> allowlistedClassNames) {
+ this.mAllowlistedClassNames = allowlistedClassNames;
+ }
+
+ @Override
+ public boolean test(String className) {
+ return mAllowlistedClassNames.contains(className);
+ }
+}
diff --git a/tools/dependency_mapper/src/com/android/dependencymapper/DependencyMapper.java b/tools/dependency_mapper/src/com/android/dependencymapper/DependencyMapper.java
new file mode 100644
index 0000000..ecf520c
--- /dev/null
+++ b/tools/dependency_mapper/src/com/android/dependencymapper/DependencyMapper.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2025 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 com.android.dependencymapper;
+
+import com.android.dependencymapper.DependencyProto;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * This class binds {@link List<ClassDependencyData>} and {@link List<JavaSourceData>} together as a
+ * flat map, which represents dependency related attributes of a java file.
+ */
+public class DependencyMapper {
+ private final List<ClassDependencyData> mClassAnalysisList;
+ private final List<JavaSourceData> mJavaSourceDataList;
+ private final Map<String, String> mClassToSourceMap = new HashMap<>();
+ private final Map<String, Set<String>> mFileDependencies = new HashMap<>();
+ private final Set<String> mDependencyToAll = new HashSet<>();
+ private final Map<String, Set<String>> mSourceToClasses = new HashMap<>();
+
+ public DependencyMapper(List<ClassDependencyData> classAnalysisList, List<JavaSourceData> javaSourceDataList) {
+ this.mClassAnalysisList = classAnalysisList;
+ this.mJavaSourceDataList = javaSourceDataList;
+ }
+
+ public DependencyProto.FileDependencyList buildDependencyMaps() {
+ buildClassDependencyMaps();
+ buildSourceToClassMap();
+ return createFileDependencies();
+ }
+
+ private void buildClassDependencyMaps() {
+ // Create a map between package appended file names and file paths.
+ Map<String, String> sourcePaths = generateSourcePaths();
+ // A map between qualified className and its dependencies
+ Map<String, Set<String>> classDependencies = new HashMap<>();
+ // A map between constant values and the their declarations.
+ Map<Object, Set<String>> constantRegistry = new HashMap<>();
+ // A map between constant values and the their inlined usages.
+ Map<Object, Set<String>> inlinedUsages = new HashMap<>();
+
+ for (ClassDependencyData analysis : mClassAnalysisList) {
+ String className = analysis.getQualifiedName();
+
+ // Compute qualified class name to source path map.
+ String sourceKey = analysis.getPackagePrependedClassSource();
+ String sourcePath = sourcePaths.get(sourceKey);
+ mClassToSourceMap.put(className, sourcePath);
+
+ // compute classDependencies
+ classDependencies.computeIfAbsent(className, k ->
+ new HashSet<>()).addAll(analysis.getClassDependencies());
+
+ // Compute constantRegistry
+ analysis.getConstantsDefined().forEach(c ->
+ constantRegistry.computeIfAbsent(c, k -> new HashSet<>()).add(className));
+ // Compute inlinedUsages map.
+ analysis.inlinedUsages().forEach(u ->
+ inlinedUsages.computeIfAbsent(u, k -> new HashSet<>()).add(className));
+
+ if (analysis.isDependencyToAll()) {
+ mDependencyToAll.add(sourcePath);
+ }
+ }
+ // Finally build file dependencies
+ buildFileDependencies(
+ combineDependencies(classDependencies, inlinedUsages, constantRegistry));
+ }
+
+ private Map<String, String> generateSourcePaths() {
+ Map<String, String> sourcePaths = new HashMap<>();
+ mJavaSourceDataList.forEach(data ->
+ sourcePaths.put(data.getPackagePrependedFileName(), data.getFilePath()));
+ return sourcePaths;
+ }
+
+ private Map<String, Set<String>> combineDependencies(Map<String, Set<String>> classDependencies,
+ Map<Object, Set<String>> inlinedUsages,
+ Map<Object, Set<String>> constantRegistry) {
+ Map<String, Set<String>> combined = new HashMap<>(
+ buildConstantDependencies(inlinedUsages, constantRegistry));
+ classDependencies.forEach((k, v) ->
+ combined.computeIfAbsent(k, key -> new HashSet<>()).addAll(v));
+ return combined;
+ }
+
+ private Map<String, Set<String>> buildConstantDependencies(
+ Map<Object, Set<String>> inlinedUsages, Map<Object, Set<String>> constantRegistry) {
+ Map<String, Set<String>> constantDependencies = new HashMap<>();
+ for (Map.Entry<Object, Set<String>> usageEntry : inlinedUsages.entrySet()) {
+ Object usage = usageEntry.getKey();
+ Set<String> usageClasses = usageEntry.getValue();
+ if (constantRegistry.containsKey(usage)) {
+ Set<String> declarationClasses = constantRegistry.get(usage);
+ for (String usageClass : usageClasses) {
+ // Sometimes Usage and Declarations are in the same file, we remove such cases
+ // to prevent circular dependency.
+ declarationClasses.remove(usageClass);
+ constantDependencies.computeIfAbsent(usageClass, k ->
+ new HashSet<>()).addAll(declarationClasses);
+ }
+ }
+ }
+
+ return constantDependencies;
+ }
+
+ private void buildFileDependencies(Map<String, Set<String>> combinedClassDependencies) {
+ combinedClassDependencies.forEach((className, dependencies) -> {
+ String sourceFile = mClassToSourceMap.get(className);
+ if (sourceFile == null) {
+ throw new IllegalArgumentException("Class '" + className
+ + "' does not have a corresponding source file.");
+ }
+ mFileDependencies.computeIfAbsent(sourceFile, k -> new HashSet<>());
+ dependencies.forEach(dependency -> {
+ String dependencySource = mClassToSourceMap.get(dependency);
+ if (dependencySource == null) {
+ throw new IllegalArgumentException("Dependency '" + dependency
+ + "' does not have a corresponding source file.");
+ }
+ mFileDependencies.get(sourceFile).add(dependencySource);
+ });
+ });
+ }
+
+ private void buildSourceToClassMap() {
+ mClassToSourceMap.forEach((className, sourceFile) ->
+ mSourceToClasses.computeIfAbsent(sourceFile, k ->
+ new HashSet<>()).add(className));
+ }
+
+ private DependencyProto.FileDependencyList createFileDependencies() {
+ List<DependencyProto.FileDependency> fileDependencies = new ArrayList<>();
+ mFileDependencies.forEach((file, dependencies) -> {
+ DependencyProto.FileDependency dependency = DependencyProto.FileDependency.newBuilder()
+ .setFilePath(file)
+ .setIsDependencyToAll(mDependencyToAll.contains(file))
+ .addAllGeneratedClasses(mSourceToClasses.get(file))
+ .addAllFileDependencies(dependencies)
+ .build();
+ fileDependencies.add(dependency);
+ });
+ return DependencyProto.FileDependencyList.newBuilder()
+ .addAllFileDependency(fileDependencies).build();
+ }
+}
diff --git a/tools/dependency_mapper/src/com/android/dependencymapper/JavaSourceAnalyzer.java b/tools/dependency_mapper/src/com/android/dependencymapper/JavaSourceAnalyzer.java
new file mode 100644
index 0000000..3a4efad
--- /dev/null
+++ b/tools/dependency_mapper/src/com/android/dependencymapper/JavaSourceAnalyzer.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2025 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 com.android.dependencymapper;
+
+import java.io.BufferedReader;
+import java.io.FileReader;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * An utility class that reads each java file present in the rsp content then analyzes the same,
+ * collecting the analysis in {@link List<JavaSourceData>}
+ */
+public class JavaSourceAnalyzer {
+
+ // Regex that matches against "package abc.xyz.lmn;" declarations in a java file.
+ private static final String PACKAGE_REGEX = "^package\\s+([a-zA-Z_][a-zA-Z0-9_.]*);";
+
+ public static List<JavaSourceData> analyze(Path srcRspFile) {
+ List<JavaSourceData> javaSourceDataList = new ArrayList<>();
+ try (BufferedReader reader = new BufferedReader(new FileReader(srcRspFile.toFile()))) {
+ String line;
+ while ((line = reader.readLine()) != null) {
+ // Split the line by spaces, tabs, multiple java files can be on a single line.
+ String[] files = line.trim().split("\\s+");
+ for (String file : files) {
+ Path p = Paths.get("", file);
+ System.out.println(p.toAbsolutePath().toString());
+ javaSourceDataList
+ .add(new JavaSourceData(file, constructPackagePrependedFileName(file)));
+ }
+ }
+ } catch (IOException e) {
+ System.err.println("Error reading rsp file at: " + srcRspFile);
+ throw new RuntimeException(e);
+ }
+ return javaSourceDataList;
+ }
+
+ private static String constructPackagePrependedFileName(String filePath) {
+ String packageAppendedFileName = null;
+ // if the file path is abc/def/ghi/JavaFile.java we extract JavaFile.java
+ String javaFileName = filePath.substring(filePath.lastIndexOf("/") + 1);
+ try (BufferedReader reader = new BufferedReader(new FileReader(filePath))) {
+ String line;
+ // Process each line and match against the package regex pattern.
+ while ((line = reader.readLine()) != null) {
+ Pattern pattern = Pattern.compile(PACKAGE_REGEX);
+ Matcher matcher = pattern.matcher(line);
+ if (matcher.find()) {
+ packageAppendedFileName = matcher.group(1) + "." + javaFileName;
+ break;
+ }
+ }
+ } catch (IOException e) {
+ System.err.println("Error reading java file at: " + filePath);
+ throw new RuntimeException(e);
+ }
+ // Should not be null
+ assert packageAppendedFileName != null;
+ return packageAppendedFileName;
+ }
+}
diff --git a/tools/dependency_mapper/src/com/android/dependencymapper/JavaSourceData.java b/tools/dependency_mapper/src/com/android/dependencymapper/JavaSourceData.java
new file mode 100644
index 0000000..89453d0
--- /dev/null
+++ b/tools/dependency_mapper/src/com/android/dependencymapper/JavaSourceData.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2025 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 com.android.dependencymapper;
+
+/**
+ * POJO representing the data collected from Java Source file analysis.
+ */
+public class JavaSourceData {
+
+ private final String mFilePath;
+ private final String mPackagePrependedFileName;
+
+ public JavaSourceData(String filePath, String packagePrependedFileName) {
+ mFilePath = filePath;
+ mPackagePrependedFileName = packagePrependedFileName;
+ }
+
+ public String getFilePath() {
+ return mFilePath;
+ }
+
+ public String getPackagePrependedFileName() {
+ return mPackagePrependedFileName;
+ }
+}
diff --git a/tools/dependency_mapper/src/com/android/dependencymapper/Main.java b/tools/dependency_mapper/src/com/android/dependencymapper/Main.java
new file mode 100644
index 0000000..131c931
--- /dev/null
+++ b/tools/dependency_mapper/src/com/android/dependencymapper/Main.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2025 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 com.android.dependencymapper;
+
+import static com.android.dependencymapper.Utils.listClassesInJar;
+
+import com.android.dependencymapper.DependencyProto;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.List;
+import java.util.Set;
+
+public class Main {
+
+ public static void main(String[] args) throws IOException, InterruptedException {
+ try {
+ InputData input = parseAndValidateInput(args);
+ generateDependencyMap(input);
+ } catch (IllegalArgumentException e) {
+ System.err.println("Error: " + e.getMessage());
+ showUsage();
+ }
+ }
+
+ private static class InputData {
+ public Path srcList;
+ public Path classesJar;
+ public Path dependencyMapProto;
+
+ public InputData(Path srcList, Path classesJar, Path dependencyMapProto) {
+ this.srcList = srcList;
+ this.classesJar = classesJar;
+ this.dependencyMapProto = dependencyMapProto;
+ }
+ }
+
+ private static InputData parseAndValidateInput(String[] args) {
+ for (String arg : args) {
+ if ("--help".equals(arg)) {
+ showUsage();
+ System.exit(0); // Indicate successful exit after showing help
+ }
+ }
+
+ if (args.length != 6) { // Explicitly check for the correct number of arguments
+ throw new IllegalArgumentException("Incorrect number of arguments");
+ }
+
+ Path srcList = null;
+ Path classesJar = null;
+ Path dependencyMapProto = null;
+
+ for (int i = 0; i < args.length; i += 2) {
+ String arg = args[i].trim();
+ String argValue = args[i + 1].trim();
+
+ switch (arg) {
+ case "--src-path" -> srcList = Path.of(argValue);
+ case "--jar-path" -> classesJar = Path.of(argValue);
+ case "--dependency-map-path" -> dependencyMapProto = Path.of(argValue);
+ default -> throw new IllegalArgumentException("Unknown argument: " + arg);
+ }
+ }
+
+ // Validate file existence and readability
+ validateFile(srcList, "--src-path");
+ validateFile(classesJar, "--jar-path");
+
+ return new InputData(srcList, classesJar, dependencyMapProto);
+ }
+
+ private static void validateFile(Path path, String argName) {
+ if (path == null) {
+ throw new IllegalArgumentException(argName + " is required");
+ }
+ if (!Files.exists(path)) {
+ throw new IllegalArgumentException(argName + " does not exist: " + path);
+ }
+ if (!Files.isReadable(path)) {
+ throw new IllegalArgumentException(argName + " is not readable: " + path);
+ }
+ }
+
+ private static void generateDependencyMap(InputData input) {
+ // First collect all classes in the jar.
+ Set<String> classesInJar = listClassesInJar(input.classesJar);
+ // Perform dependency analysis.
+ List<ClassDependencyData> classDependencyDataList = ClassDependencyAnalyzer
+ .analyze(input.classesJar, new ClassRelevancyFilter(classesInJar));
+ // Perform java source analysis.
+ List<JavaSourceData> javaSourceDataList = JavaSourceAnalyzer.analyze(input.srcList);
+ // Collect all dependencies and map them as DependencyProto.FileDependencyList
+ DependencyMapper dp = new DependencyMapper(classDependencyDataList, javaSourceDataList);
+ DependencyProto.FileDependencyList dependencyList = dp.buildDependencyMaps();
+
+ // Write the proto to output file
+ Utils.writeContentsToProto(dependencyList, input.dependencyMapProto);
+ }
+
+ private static void showUsage() {
+ System.err.println(
+ "Usage: dependency-mapper "
+ + "--src-path [src-list.rsp] "
+ + "--jar-path [classes.jar] "
+ + "--dependency-map-path [dependency-map.proto]");
+ }
+
+}
\ No newline at end of file
diff --git a/tools/dependency_mapper/src/com/android/dependencymapper/Utils.java b/tools/dependency_mapper/src/com/android/dependencymapper/Utils.java
new file mode 100644
index 0000000..5dd5f35
--- /dev/null
+++ b/tools/dependency_mapper/src/com/android/dependencymapper/Utils.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2025 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 com.android.dependencymapper;
+
+import com.android.dependencymapper.DependencyProto;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+
+public class Utils {
+
+ public static String trimAndConvertToPackageBasedPath(String fileBasedPath) {
+ // Remove ".class" from the fileBasedPath, then replace "/" with "."
+ return fileBasedPath.replaceAll("\\..*", "").replaceAll("/", ".");
+ }
+
+ public static String buildPackagePrependedClassSource(String qualifiedClassPath,
+ String classSource) {
+ // Find the location of the start of classname in the qualifiedClassPath
+ int classNameSt = qualifiedClassPath.lastIndexOf(".") + 1;
+ // Replace the classname in qualifiedClassPath with classSource
+ return qualifiedClassPath.substring(0, classNameSt) + classSource;
+ }
+
+ public static void writeContentsToJson(DependencyProto.FileDependencyList contents, Path jsonOut) {
+ Gson gson = new GsonBuilder().setPrettyPrinting().create();
+ Map<String, Set<String>> jsonMap = new HashMap<>();
+ for (DependencyProto.FileDependency fileDependency : contents.getFileDependencyList()) {
+ jsonMap.putIfAbsent(fileDependency.getFilePath(),
+ Set.copyOf(fileDependency.getFileDependenciesList()));
+ }
+ String json = gson.toJson(jsonMap);
+ try (FileWriter file = new FileWriter(jsonOut.toFile())) {
+ file.write(json);
+ } catch (IOException e) {
+ System.err.println("Error writing json output to: " + jsonOut);
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static void writeContentsToProto(DependencyProto.FileDependencyList usages, Path protoOut) {
+ try {
+ OutputStream outputStream = Files.newOutputStream(protoOut);
+ usages.writeDelimitedTo(outputStream);
+ } catch (IOException e) {
+ System.err.println("Error writing proto output to: " + protoOut);
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static Set<String> listClassesInJar(Path classesJarPath) {
+ Set<String> classes = new HashSet<>();
+ try (JarFile jarFile = new JarFile(classesJarPath.toFile())) {
+ Enumeration<JarEntry> entries = jarFile.entries();
+ while (entries.hasMoreElements()) {
+ JarEntry entry = entries.nextElement();
+ if (entry.getName().endsWith(".class")) {
+ String name = Utils.trimAndConvertToPackageBasedPath(entry.getName());
+ classes.add(name);
+ }
+ }
+ } catch (IOException e) {
+ System.err.println("Error reading the jar file at: " + classesJarPath);
+ throw new RuntimeException(e);
+ }
+ return classes;
+ }
+}
diff --git a/tools/dependency_mapper/tests/res/testdata/annotation/AnnotationUsage.java b/tools/dependency_mapper/tests/res/testdata/annotation/AnnotationUsage.java
new file mode 100644
index 0000000..bb40776
--- /dev/null
+++ b/tools/dependency_mapper/tests/res/testdata/annotation/AnnotationUsage.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2025 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 res.testdata.annotation;
+
+@res.testdata.annotation.RuntimeAnnotation
+public class AnnotationUsage {
+
+ private final int mSourceAnnField;
+
+ public AnnotationUsage(@res.testdata.annotation.SourceAnnotation int sourceAnnField) {
+ mSourceAnnField = sourceAnnField;
+ }
+
+ public @res.testdata.annotation.SourceAnnotation int getSourceAnnField() {
+ return mSourceAnnField;
+ }
+}
diff --git a/tools/dependency_mapper/tests/res/testdata/annotation/RuntimeAnnotation.java b/tools/dependency_mapper/tests/res/testdata/annotation/RuntimeAnnotation.java
new file mode 100644
index 0000000..99a6074
--- /dev/null
+++ b/tools/dependency_mapper/tests/res/testdata/annotation/RuntimeAnnotation.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2025 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 res.testdata.annotation;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+@Retention(RetentionPolicy.RUNTIME)
+public @interface RuntimeAnnotation {
+}
diff --git a/tools/dependency_mapper/tests/res/testdata/annotation/SourceAnnotation.java b/tools/dependency_mapper/tests/res/testdata/annotation/SourceAnnotation.java
new file mode 100644
index 0000000..dec3e83
--- /dev/null
+++ b/tools/dependency_mapper/tests/res/testdata/annotation/SourceAnnotation.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2025 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 res.testdata.annotation;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+@Retention(RetentionPolicy.SOURCE)
+public @interface SourceAnnotation {
+}
diff --git a/tools/dependency_mapper/tests/res/testdata/constants/ConstantDefinition.java b/tools/dependency_mapper/tests/res/testdata/constants/ConstantDefinition.java
new file mode 100644
index 0000000..3f0a789
--- /dev/null
+++ b/tools/dependency_mapper/tests/res/testdata/constants/ConstantDefinition.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2025 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 res.testdata.constants;
+
+public class ConstantDefinition {
+ public static final String TEST_CONSTANT = "test_constant";
+}
diff --git a/tools/dependency_mapper/tests/res/testdata/constants/ConstantUsage.java b/tools/dependency_mapper/tests/res/testdata/constants/ConstantUsage.java
new file mode 100644
index 0000000..852e4d5
--- /dev/null
+++ b/tools/dependency_mapper/tests/res/testdata/constants/ConstantUsage.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2025 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 res.testdata.constants;
+
+public class ConstantUsage {
+
+ public ConstantUsage(){}
+
+ public String useConstantInMethodBody() {
+ return res.testdata.constants.ConstantDefinition.TEST_CONSTANT;
+ }
+}
diff --git a/tools/dependency_mapper/tests/res/testdata/inheritance/BaseClass.java b/tools/dependency_mapper/tests/res/testdata/inheritance/BaseClass.java
new file mode 100644
index 0000000..3b11eb1
--- /dev/null
+++ b/tools/dependency_mapper/tests/res/testdata/inheritance/BaseClass.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2025 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 res.testdata.inheritance;
+
+public class BaseClass {
+}
diff --git a/tools/dependency_mapper/tests/res/testdata/inheritance/BaseImpl.java b/tools/dependency_mapper/tests/res/testdata/inheritance/BaseImpl.java
new file mode 100644
index 0000000..7c2698b
--- /dev/null
+++ b/tools/dependency_mapper/tests/res/testdata/inheritance/BaseImpl.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2025 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 res.testdata.inheritance;
+
+public interface BaseImpl {
+
+ void baseImpl();
+}
diff --git a/tools/dependency_mapper/tests/res/testdata/inheritance/InheritanceUsage.java b/tools/dependency_mapper/tests/res/testdata/inheritance/InheritanceUsage.java
new file mode 100644
index 0000000..f892479
--- /dev/null
+++ b/tools/dependency_mapper/tests/res/testdata/inheritance/InheritanceUsage.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2025 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 res.testdata.inheritance;
+
+public class InheritanceUsage extends res.testdata.inheritance.BaseClass implements
+ res.testdata.inheritance.BaseImpl {
+ @Override
+ public void baseImpl() {
+
+ }
+}
diff --git a/tools/dependency_mapper/tests/res/testdata/methods/FieldUsage.java b/tools/dependency_mapper/tests/res/testdata/methods/FieldUsage.java
new file mode 100644
index 0000000..0d97312
--- /dev/null
+++ b/tools/dependency_mapper/tests/res/testdata/methods/FieldUsage.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2025 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 res.testdata.methods;
+
+public class FieldUsage {
+
+ private res.testdata.methods.ReferenceClass1 mReferenceClass1;
+}
diff --git a/tools/dependency_mapper/tests/res/testdata/methods/MethodUsage.java b/tools/dependency_mapper/tests/res/testdata/methods/MethodUsage.java
new file mode 100644
index 0000000..9dd0223
--- /dev/null
+++ b/tools/dependency_mapper/tests/res/testdata/methods/MethodUsage.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2025 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 res.testdata.methods;
+
+public class MethodUsage {
+
+ public void methodReferences(res.testdata.methods.ReferenceClass1 mReferenceClass1) {
+ res.testdata.methods.ReferenceClass2 referenceClass2 =
+ new res.testdata.methods.ReferenceClass2();
+ }
+}
diff --git a/tools/dependency_mapper/tests/res/testdata/methods/ReferenceClass1.java b/tools/dependency_mapper/tests/res/testdata/methods/ReferenceClass1.java
new file mode 100644
index 0000000..f56c0a9
--- /dev/null
+++ b/tools/dependency_mapper/tests/res/testdata/methods/ReferenceClass1.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2025 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 res.testdata.methods;
+
+public class ReferenceClass1 {
+
+ public ReferenceClass1(){}
+}
diff --git a/tools/dependency_mapper/tests/res/testdata/methods/ReferenceClass2.java b/tools/dependency_mapper/tests/res/testdata/methods/ReferenceClass2.java
new file mode 100644
index 0000000..09e7422
--- /dev/null
+++ b/tools/dependency_mapper/tests/res/testdata/methods/ReferenceClass2.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2025 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 res.testdata.methods;
+
+public class ReferenceClass2 {
+ public ReferenceClass2(){}
+}
diff --git a/tools/dependency_mapper/tests/res/testfiles/dependency-mapper-test-data.jar b/tools/dependency_mapper/tests/res/testfiles/dependency-mapper-test-data.jar
new file mode 100644
index 0000000..98f5893
--- /dev/null
+++ b/tools/dependency_mapper/tests/res/testfiles/dependency-mapper-test-data.jar
Binary files differ
diff --git a/tools/dependency_mapper/tests/res/testfiles/sources.rsp b/tools/dependency_mapper/tests/res/testfiles/sources.rsp
new file mode 100644
index 0000000..d895033
--- /dev/null
+++ b/tools/dependency_mapper/tests/res/testfiles/sources.rsp
@@ -0,0 +1,12 @@
+tests/res/testdata/annotation/AnnotationUsage.java
+tests/res/testdata/annotation/SourceAnnotation.java
+tests/res/testdata/annotation/RuntimeAnnotation.java
+tests/res/testdata/constants/ConstantDefinition.java
+tests/res/testdata/constants/ConstantUsage.java
+tests/res/testdata/inheritance/InheritanceUsage.java
+tests/res/testdata/inheritance/BaseClass.java
+tests/res/testdata/inheritance/BaseImpl.java
+tests/res/testdata/methods/FieldUsage.java
+tests/res/testdata/methods/MethodUsage.java
+tests/res/testdata/methods/ReferenceClass1.java
+tests/res/testdata/methods/ReferenceClass2.java
\ No newline at end of file
diff --git a/tools/dependency_mapper/tests/src/com/android/dependencymapper/ClassDependencyAnalyzerTest.java b/tools/dependency_mapper/tests/src/com/android/dependencymapper/ClassDependencyAnalyzerTest.java
new file mode 100644
index 0000000..95492c8
--- /dev/null
+++ b/tools/dependency_mapper/tests/src/com/android/dependencymapper/ClassDependencyAnalyzerTest.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2025 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 com.android.dependencymapper;
+
+import static com.android.dependencymapper.Utils.listClassesInJar;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import java.net.URISyntaxException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+public class ClassDependencyAnalyzerTest {
+
+ private static List<ClassDependencyData> mClassDependencyDataList;
+
+ private static final String CLASSES_JAR_PATH =
+ "tests/res/testfiles/dependency-mapper-test-data.jar";
+
+ @BeforeClass
+ public static void beforeClass() throws URISyntaxException {
+ Path path = Paths.get(CLASSES_JAR_PATH);
+ Set<String> classesInJar = listClassesInJar(path);
+ // Perform dependency analysis.
+ mClassDependencyDataList = ClassDependencyAnalyzer.analyze(path,
+ new ClassRelevancyFilter(classesInJar));
+ }
+
+ @Test
+ public void testAnnotationDeps(){
+ String annoClass = "res.testdata.annotation.AnnotationUsage";
+ String sourceAnno = "res.testdata.annotation.SourceAnnotation";
+ String runTimeAnno = "res.testdata.annotation.RuntimeAnnotation";
+
+ dependencyVerifier(annoClass,
+ new HashSet<>(List.of(runTimeAnno)), new HashSet<>(List.of(sourceAnno)));
+
+ for (ClassDependencyData dep : mClassDependencyDataList) {
+ if (dep.getQualifiedName().equals(sourceAnno)) {
+ assertTrue(sourceAnno + " is not dependencyToAll ", dep.isDependencyToAll());
+ }
+ if (dep.getQualifiedName().equals(runTimeAnno)) {
+ assertFalse(runTimeAnno + " is dependencyToAll ", dep.isDependencyToAll());
+ }
+ }
+ }
+
+ @Test
+ public void testConstantsDeps(){
+ String constDefined = "test_constant";
+ String constDefClass = "res.testdata.constants.ConstantDefinition";
+ String constUsageClass = "res.testdata.constants.ConstantUsage";
+
+ boolean constUsageClassFound = false;
+ boolean constDefClassFound = false;
+ for (ClassDependencyData dep : mClassDependencyDataList) {
+ if (dep.getQualifiedName().equals(constUsageClass)) {
+ constUsageClassFound = true;
+ assertTrue("InlinedUsage of : " + constDefined + " not found",
+ dep.inlinedUsages().contains(constDefined));
+ }
+ if (dep.getQualifiedName().equals(constDefClass)) {
+ constDefClassFound = true;
+ assertTrue("Constant " + constDefined + " not defined",
+ dep.getConstantsDefined().contains(constDefined));
+ }
+ }
+ assertTrue("Class " + constUsageClass + " not found", constUsageClassFound);
+ assertTrue("Class " + constDefClass + " not found", constDefClassFound);
+ }
+
+ @Test
+ public void testInheritanceDeps(){
+ String sourceClass = "res.testdata.inheritance.InheritanceUsage";
+ String baseClass = "res.testdata.inheritance.BaseClass";
+ String baseImpl = "res.testdata.inheritance.BaseImpl";
+
+ dependencyVerifier(sourceClass,
+ new HashSet<>(List.of(baseClass, baseImpl)), new HashSet<>());
+ }
+
+
+ @Test
+ public void testMethodDeps(){
+ String fieldUsage = "res.testdata.methods.FieldUsage";
+ String methodUsage = "res.testdata.methods.MethodUsage";
+ String ref1 = "res.testdata.methods.ReferenceClass1";
+ String ref2 = "res.testdata.methods.ReferenceClass2";
+
+ dependencyVerifier(fieldUsage,
+ new HashSet<>(List.of(ref1)), new HashSet<>(List.of(ref2)));
+ dependencyVerifier(methodUsage,
+ new HashSet<>(List.of(ref1, ref2)), new HashSet<>());
+ }
+
+ private void dependencyVerifier(String qualifiedName, Set<String> deps, Set<String> nonDeps) {
+ boolean depFound = false;
+ for (ClassDependencyData classDependencyData : mClassDependencyDataList) {
+ if (classDependencyData.getQualifiedName().equals(qualifiedName)) {
+ depFound = true;
+ for (String dep : deps) {
+ assertTrue(qualifiedName + " does not depends on " + dep,
+ classDependencyData.getClassDependencies().contains(dep));
+ }
+ for (String nonDep : nonDeps) {
+ assertFalse(qualifiedName + " depends on " + nonDep,
+ classDependencyData.getClassDependencies().contains(nonDep));
+ }
+ }
+ }
+ assertTrue("Class " + qualifiedName + " not found", depFound);
+ }
+}
diff --git a/tools/dependency_mapper/tests/src/com/android/dependencymapper/ClassRelevancyFilterTest.java b/tools/dependency_mapper/tests/src/com/android/dependencymapper/ClassRelevancyFilterTest.java
new file mode 100644
index 0000000..9a80c4b
--- /dev/null
+++ b/tools/dependency_mapper/tests/src/com/android/dependencymapper/ClassRelevancyFilterTest.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2025 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 com.android.dependencymapper;
+
+import static com.android.dependencymapper.Utils.listClassesInJar;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+
+import com.android.dependencymapper.ClassDependencyAnalyzer;
+import com.android.dependencymapper.ClassDependencyData;
+import com.android.dependencymapper.ClassRelevancyFilter;
+
+import org.junit.Test;
+
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.List;
+import java.util.Set;
+
+public class ClassRelevancyFilterTest {
+
+ private static final String CLASSES_JAR_PATH =
+ "tests/res/testfiles/dependency-mapper-test-data.jar";
+
+ @Test
+ public void testClassRelevancyFilter() {
+ Path path = Paths.get(CLASSES_JAR_PATH);
+ Set<String> classesInJar = listClassesInJar(path);
+
+ // Add a relevancy filter that skips a class.
+ String skippedClass = "res.testdata.BaseClass";
+ classesInJar.remove(skippedClass);
+
+ // Perform dependency analysis.
+ List<ClassDependencyData> classDependencyDataList =
+ ClassDependencyAnalyzer.analyze(path, new ClassRelevancyFilter(classesInJar));
+
+ // check that the skipped class is not present in classDepsList
+ for (ClassDependencyData dep : classDependencyDataList) {
+ assertNotEquals("SkippedClass " + skippedClass + " is present",
+ skippedClass, dep.getQualifiedName());
+ assertFalse("SkippedClass " + skippedClass + " is present as dependency of " + dep,
+ dep.getClassDependencies().contains(skippedClass));
+ }
+ }
+}
diff --git a/tools/dependency_mapper/tests/src/com/android/dependencymapper/DependencyMapperTest.java b/tools/dependency_mapper/tests/src/com/android/dependencymapper/DependencyMapperTest.java
new file mode 100644
index 0000000..9c08e79
--- /dev/null
+++ b/tools/dependency_mapper/tests/src/com/android/dependencymapper/DependencyMapperTest.java
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2025 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 com.android.dependencymapper;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+
+public class DependencyMapperTest {
+
+ private static final List<JavaSourceData> mJavaSourceData = new ArrayList<>();
+ private static final List<ClassDependencyData> mClassDependencyData = new ArrayList<>();
+
+ private static Map<String, DependencyProto.FileDependency> mFileDependencyMap;
+
+ public static String AUDIO_CONS = "AUDIO_CONS";
+ public static String AUDIO_CONS_PATH = "frameworks/base/audio/AudioPermission.java";
+ public static String AUDIO_CONS_PACKAGE = "com.android.audio.AudioPermission";
+
+ public static String AUDIO_TONE_CONS_1 = "AUDIO_TONE_CONS_1";
+ public static String AUDIO_TONE_CONS_2 = "AUDIO_TONE_CONS_2";
+ public static String AUDIO_TONE_CONS_PATH = "frameworks/base/audio/Audio$Tones.java";
+ public static String AUDIO_TONE_CONS_PACKAGE = "com.android.audio.Audio$Tones";
+
+ public static String ST_MANAGER_PATH = "frameworks/base/core/storage/StorageManager.java";
+ public static String ST_MANAGER_PACKAGE = "com.android.storage.StorageManager";
+
+ public static String CONST_OUTSIDE_SCOPE = "CONST_OUTSIDE_SCOPE";
+ public static String PERM_MANAGER_PATH = "frameworks/base/core/permission/PermissionManager.java";
+ public static String PERM_MANAGER_PACKAGE = "com.android.permission.PermissionManager";
+
+ public static String SOURCE_ANNO_PATH = "frameworks/base/anno/SourceAnno.java";
+ public static String SOURCE_ANNO_PACKAGE = "com.android.anno.SourceAnno";
+
+ public static String PERM_SOURCE_PATH = "frameworks/base/core/permission/PermissionSources.java";
+ public static String PERM_SOURCE_PACKAGE = "com.android.permission.PermissionSources";
+
+ public static String PERM_DATA_PATH = "frameworks/base/core/permission/PermissionSources$Data.java";
+ public static String PERM_DATA_PACKAGE = "com.android.permission.PermissionSources$Data";
+
+ static {
+ JavaSourceData audioConstants = new JavaSourceData(AUDIO_CONS_PATH, AUDIO_CONS_PACKAGE + ".java");
+ JavaSourceData audioToneConstants =
+ new JavaSourceData(AUDIO_TONE_CONS_PATH, AUDIO_TONE_CONS_PACKAGE + ".java"); //f2
+ JavaSourceData stManager = new JavaSourceData( ST_MANAGER_PATH, ST_MANAGER_PACKAGE + ".java");
+ JavaSourceData permManager = new JavaSourceData(PERM_MANAGER_PATH, PERM_MANAGER_PACKAGE + ".java");
+ JavaSourceData permSource = new JavaSourceData(PERM_SOURCE_PATH, PERM_SOURCE_PACKAGE + ".java");
+ JavaSourceData permSourceData = new JavaSourceData(PERM_DATA_PATH, PERM_DATA_PACKAGE + ".java");
+
+ JavaSourceData sourceNotPresentInClass =
+ new JavaSourceData(SOURCE_ANNO_PATH, SOURCE_ANNO_PACKAGE);
+
+ mJavaSourceData.addAll(List.of(audioConstants, audioToneConstants, stManager,
+ permManager, permSource, permSourceData, sourceNotPresentInClass));
+
+ ClassDependencyData audioConstantsDeps =
+ new ClassDependencyData(AUDIO_CONS_PACKAGE + ".java",
+ AUDIO_CONS_PACKAGE, new HashSet<>(), false,
+ new HashSet<>(List.of(AUDIO_CONS)), new HashSet<>());
+
+ ClassDependencyData audioToneConstantsDeps =
+ new ClassDependencyData(AUDIO_TONE_CONS_PACKAGE + ".java",
+ AUDIO_TONE_CONS_PACKAGE, new HashSet<>(), false,
+ new HashSet<>(List.of(AUDIO_TONE_CONS_1, AUDIO_TONE_CONS_2)),
+ new HashSet<>());
+
+ ClassDependencyData stManagerDeps =
+ new ClassDependencyData(ST_MANAGER_PACKAGE + ".java",
+ ST_MANAGER_PACKAGE, new HashSet<>(List.of(PERM_SOURCE_PACKAGE)), false,
+ new HashSet<>(), new HashSet<>(List.of(AUDIO_CONS, AUDIO_TONE_CONS_1)));
+
+ ClassDependencyData permManagerDeps =
+ new ClassDependencyData(PERM_MANAGER_PACKAGE + ".java", PERM_MANAGER_PACKAGE,
+ new HashSet<>(List.of(PERM_SOURCE_PACKAGE, PERM_DATA_PACKAGE)), false,
+ new HashSet<>(), new HashSet<>(List.of(CONST_OUTSIDE_SCOPE)));
+
+ ClassDependencyData permSourceDeps =
+ new ClassDependencyData(PERM_SOURCE_PACKAGE + ".java",
+ PERM_SOURCE_PACKAGE, new HashSet<>(), false,
+ new HashSet<>(), new HashSet<>());
+
+ ClassDependencyData permSourceDataDeps =
+ new ClassDependencyData(PERM_DATA_PACKAGE + ".java",
+ PERM_DATA_PACKAGE, new HashSet<>(), false,
+ new HashSet<>(), new HashSet<>());
+
+ mClassDependencyData.addAll(List.of(audioConstantsDeps, audioToneConstantsDeps,
+ stManagerDeps, permManagerDeps, permSourceDeps, permSourceDataDeps));
+ }
+
+ @BeforeClass
+ public static void beforeAll(){
+ mFileDependencyMap = buildActualDepsMap(
+ new DependencyMapper(mClassDependencyData, mJavaSourceData).buildDependencyMaps());
+ }
+
+ @Test
+ public void testFileDependencies() {
+ // Test for AUDIO_CONS_PATH
+ DependencyProto.FileDependency audioDepsActual = mFileDependencyMap.get(AUDIO_CONS_PATH);
+ assertNotNull(AUDIO_CONS_PATH + " not found in dependencyList", audioDepsActual);
+ // This file should have 0 dependencies.
+ validateDependencies(audioDepsActual, AUDIO_CONS_PATH, 0, new ArrayList<>());
+
+ // Test for AUDIO_TONE_CONS_PATH
+ DependencyProto.FileDependency audioToneDepsActual =
+ mFileDependencyMap.get(AUDIO_TONE_CONS_PATH);
+ assertNotNull(AUDIO_TONE_CONS_PATH + " not found in dependencyList", audioDepsActual);
+ // This file should have 0 dependencies.
+ validateDependencies(audioToneDepsActual, AUDIO_TONE_CONS_PATH, 0, new ArrayList<>());
+
+ // Test for ST_MANAGER_PATH
+ DependencyProto.FileDependency stManagerDepsActual =
+ mFileDependencyMap.get(ST_MANAGER_PATH);
+ assertNotNull(ST_MANAGER_PATH + " not found in dependencyList", audioDepsActual);
+ // This file should have 3 dependencies.
+ validateDependencies(stManagerDepsActual, ST_MANAGER_PATH, 3,
+ new ArrayList<>(List.of(AUDIO_CONS_PATH, AUDIO_TONE_CONS_PATH, PERM_SOURCE_PATH)));
+
+ // Test for PERM_MANAGER_PATH
+ DependencyProto.FileDependency permManagerDepsActual =
+ mFileDependencyMap.get(PERM_MANAGER_PATH);
+ assertNotNull(PERM_MANAGER_PATH + " not found in dependencyList", audioDepsActual);
+ // This file should have 2 dependencies.
+ validateDependencies(permManagerDepsActual, PERM_MANAGER_PATH, 2,
+ new ArrayList<>(List.of(PERM_SOURCE_PATH, PERM_DATA_PATH)));
+
+ // Test for PERM_SOURCE_PATH
+ DependencyProto.FileDependency permSourceDepsActual =
+ mFileDependencyMap.get(PERM_SOURCE_PATH);
+ assertNotNull(PERM_SOURCE_PATH + " not found in dependencyList", audioDepsActual);
+ // This file should have 0 dependencies.
+ validateDependencies(permSourceDepsActual, PERM_SOURCE_PATH, 0, new ArrayList<>());
+
+ // Test for PERM_DATA_PATH
+ DependencyProto.FileDependency permDataDepsActual =
+ mFileDependencyMap.get(PERM_DATA_PATH);
+ assertNotNull(PERM_DATA_PATH + " not found in dependencyList", audioDepsActual);
+ // This file should have 0 dependencies.
+ validateDependencies(permDataDepsActual, PERM_DATA_PATH, 0, new ArrayList<>());
+ }
+
+ private void validateDependencies(DependencyProto.FileDependency dependency, String fileName, int fileDepsCount, List<String> fileDeps) {
+ assertEquals(fileName + " does not have expected dependencies", fileDepsCount, dependency.getFileDependenciesCount());
+ assertTrue(fileName + " does not have expected dependencies", dependency.getFileDependenciesList().containsAll(fileDeps));
+ }
+
+ private static Map<String, DependencyProto.FileDependency> buildActualDepsMap(
+ DependencyProto.FileDependencyList fileDependencyList) {
+ Map<String, DependencyProto.FileDependency> dependencyMap = new HashMap<>();
+ for (DependencyProto.FileDependency fileDependency : fileDependencyList.getFileDependencyList()) {
+ if (fileDependency.getFilePath().equals(AUDIO_CONS_PATH)) {
+ dependencyMap.put(AUDIO_CONS_PATH, fileDependency);
+ }
+ if (fileDependency.getFilePath().equals(AUDIO_TONE_CONS_PATH)) {
+ dependencyMap.put(AUDIO_TONE_CONS_PATH, fileDependency);
+ }
+ if (fileDependency.getFilePath().equals(ST_MANAGER_PATH)) {
+ dependencyMap.put(ST_MANAGER_PATH, fileDependency);
+ }
+ if (fileDependency.getFilePath().equals(PERM_MANAGER_PATH)) {
+ dependencyMap.put(PERM_MANAGER_PATH, fileDependency);
+ }
+ if (fileDependency.getFilePath().equals(PERM_SOURCE_PATH)) {
+ dependencyMap.put(PERM_SOURCE_PATH, fileDependency);
+ }
+ if (fileDependency.getFilePath().equals(PERM_DATA_PATH)) {
+ dependencyMap.put(PERM_DATA_PATH, fileDependency);
+ }
+ if (fileDependency.getFilePath().equals(SOURCE_ANNO_PATH)) {
+ dependencyMap.put(SOURCE_ANNO_PATH, fileDependency);
+ }
+ }
+ assertFalse(SOURCE_ANNO_PATH + " found in dependencyList",
+ dependencyMap.containsKey(SOURCE_ANNO_PATH));
+ return dependencyMap;
+ }
+}
diff --git a/tools/dependency_mapper/tests/src/com/android/dependencymapper/JavaSourceAnalyzerTest.java b/tools/dependency_mapper/tests/src/com/android/dependencymapper/JavaSourceAnalyzerTest.java
new file mode 100644
index 0000000..1ca2b2a
--- /dev/null
+++ b/tools/dependency_mapper/tests/src/com/android/dependencymapper/JavaSourceAnalyzerTest.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2025 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 com.android.dependencymapper;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import java.net.URISyntaxException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class JavaSourceAnalyzerTest {
+ private static List<JavaSourceData> mJavaSourceDataList;
+
+ private static final String SOURCES_RSP_PATH =
+ "tests/res/testfiles/sources.rsp";
+
+ @BeforeClass
+ public static void beforeClass() throws URISyntaxException {
+ Path path = Paths.get(SOURCES_RSP_PATH);
+ // Perform source analysis.
+ mJavaSourceDataList = JavaSourceAnalyzer.analyze(path);
+ }
+
+ @Test
+ public void validateSourceData() {
+ Map<String, String> expectedSourceData = expectedSourceData();
+ int expectedFileCount = expectedSourceData.size();
+ int actualFileCount = 0;
+ for (JavaSourceData javaSourceData : mJavaSourceDataList) {
+ String file = javaSourceData.getFilePath();
+ if (expectedSourceData.containsKey(file)) {
+ actualFileCount++;
+ assertEquals("Source Data not generated correctly for " + file,
+ expectedSourceData.get(file), javaSourceData.getPackagePrependedFileName());
+ }
+ }
+ assertEquals("Not all source files processed", expectedFileCount, actualFileCount);
+ }
+
+ private Map<String, String> expectedSourceData() {
+ Map<String, String> expectedSourceData = new HashMap<>();
+ expectedSourceData.put("tests/res/testdata/annotation/AnnotationUsage.java",
+ "res.testdata.annotation.AnnotationUsage.java");
+ expectedSourceData.put("tests/res/testdata/constants/ConstantUsage.java",
+ "res.testdata.constants.ConstantUsage.java");
+ expectedSourceData.put("tests/res/testdata/inheritance/BaseClass.java",
+ "res.testdata.inheritance.BaseClass.java");
+ expectedSourceData.put("tests/res/testdata/methods/FieldUsage.java",
+ "res.testdata.methods.FieldUsage.java");
+ return expectedSourceData;
+ }
+}
diff --git a/tools/dependency_mapper/tests/src/com/android/dependencymapper/UtilsTest.java b/tools/dependency_mapper/tests/src/com/android/dependencymapper/UtilsTest.java
new file mode 100644
index 0000000..39c5190
--- /dev/null
+++ b/tools/dependency_mapper/tests/src/com/android/dependencymapper/UtilsTest.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2025 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 com.android.dependencymapper;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.dependencymapper.Utils;
+
+public class UtilsTest {
+
+ @Test
+ public void testTrimAndConvertToPackageBasedPath() {
+ String testPath1 = "com/android/storage/StorageManager.class";
+ String testPath2 = "com/android/package/PackageManager$Package.class";
+
+ String expectedPackageBasedPath1 = "com.android.storage.StorageManager";
+ String expectedPackageBasedPath2 = "com.android.package.PackageManager$Package";
+
+ assertEquals("Package Based Path not constructed correctly",
+ expectedPackageBasedPath1, Utils.trimAndConvertToPackageBasedPath(testPath1));
+ assertEquals("Package Based Path not constructed correctly",
+ expectedPackageBasedPath2, Utils.trimAndConvertToPackageBasedPath(testPath2));
+ }
+
+ @Test
+ public void testBuildPackagePrependedClassSource() {
+ String qualifiedClassPath1 = "com.android.storage.StorageManager";
+ String sourcePath1 = "StorageManager.java";
+ String qualifiedClassPath2 = "com.android.package.PackageManager$Package";
+ String sourcePath2 = "PackageManager.java";
+ String qualifiedClassPath3 = "com.android.storage.StorageManager$Storage";
+ String sourcePath3 = "StorageManager$Storage.java";
+
+
+ String expectedPackagePrependedPath1 = "com.android.storage.StorageManager.java";
+ String expectedPackagePrependedPath2 = "com.android.package.PackageManager.java";
+ String expectedPackagePrependedPath3 = "com.android.storage.StorageManager$Storage.java";
+
+ assertEquals("Package Prepended Class Source not constructed correctly",
+ expectedPackagePrependedPath1,
+ Utils.buildPackagePrependedClassSource(qualifiedClassPath1, sourcePath1));
+ assertEquals("Package Prepended Class Source not constructed correctly",
+ expectedPackagePrependedPath2,
+ Utils.buildPackagePrependedClassSource(qualifiedClassPath2, sourcePath2));
+ assertEquals("Package Prepended Class Source not constructed correctly",
+ expectedPackagePrependedPath3,
+ Utils.buildPackagePrependedClassSource(qualifiedClassPath3, sourcePath3));
+ }
+}
diff --git a/tools/finalization/environment.sh b/tools/finalization/environment.sh
index 0d3a9e1..c76980d 100755
--- a/tools/finalization/environment.sh
+++ b/tools/finalization/environment.sh
@@ -34,3 +34,4 @@
export FINAL_CORRESPONDING_PLATFORM_VERSION='16'
export FINAL_NEXT_BOARD_API_LEVEL='202604'
export FINAL_NEXT_CORRESPONDING_VERSION_LETTER='C'
+export FINAL_NEXT_CORRESPONDING_SDK_VERSION='37'
diff --git a/tools/finalization/finalize-vintf-resources.sh b/tools/finalization/finalize-vintf-resources.sh
index 6f1a6f6..45efc10 100755
--- a/tools/finalization/finalize-vintf-resources.sh
+++ b/tools/finalization/finalize-vintf-resources.sh
@@ -16,6 +16,13 @@
export TARGET_RELEASE=fina_0
export TARGET_PRODUCT=aosp_arm64
+ # build/soong
+ local vendor_api_level_map="case ${FINAL_NEXT_BOARD_API_LEVEL}:"
+ if ! grep -q "$vendor_api_level_map" "$top/build/soong/android/vendor_api_levels.go" ; then
+ sed -i -e "/case ${FINAL_BOARD_API_LEVEL}:/{N;a \\\t$vendor_api_level_map\n\t\tsdkVersion = ${FINAL_NEXT_CORRESPONDING_SDK_VERSION}
+ }" "$top/build/soong/android/vendor_api_levels.go"
+ fi
+
# system/sepolicy
"$top/system/sepolicy/tools/finalize-vintf-resources.sh" "$top" "$FINAL_BOARD_API_LEVEL"
diff --git a/tools/perf/benchmarks b/tools/perf/benchmarks
index 228c1d0..8cb26c8 100755
--- a/tools/perf/benchmarks
+++ b/tools/perf/benchmarks
@@ -573,7 +573,7 @@
# --dist-one requires that only one benchmark be supplied
if self._args.dist_one and len(self.Benchmarks()) != 1:
- self._error("--dist-one requires that exactly one --benchmark.")
+ self._error("--dist-one requires exactly one --benchmark.")
if self._had_error:
raise FatalError()
diff --git a/tools/product_config/TEST_MAPPING b/tools/product_config/TEST_MAPPING
deleted file mode 100644
index d3568f1..0000000
--- a/tools/product_config/TEST_MAPPING
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "presubmit": [
- {
- "name": "product_config_test"
- }
- ]
-}