Merge "Revert^2 "Fix signing failure for split APKs"" into main
diff --git a/ci/build_test_suites.py b/ci/build_test_suites.py
index 6e1f88c..ac5023f 100644
--- a/ci/build_test_suites.py
+++ b/ci/build_test_suites.py
@@ -28,6 +28,7 @@
REQUIRED_ENV_VARS = frozenset(['TARGET_PRODUCT', 'TARGET_RELEASE', 'TOP'])
SOONG_UI_EXE_REL_PATH = 'build/soong/soong_ui.bash'
+LOG_PATH = 'logs/build_test_suites.log'
class Error(Exception):
@@ -181,4 +182,12 @@
def main(argv):
+ dist_dir = os.environ.get('DIST_DIR')
+ if dist_dir:
+ log_file = pathlib.Path(dist_dir) / LOG_PATH
+ logging.basicConfig(
+ level=logging.DEBUG,
+ format='%(asctime)s %(levelname)s %(message)s',
+ filename=log_file,
+ )
sys.exit(build_test_suites(argv))
diff --git a/core/Makefile b/core/Makefile
index b76a4f8..5ec5a94 100644
--- a/core/Makefile
+++ b/core/Makefile
@@ -3582,8 +3582,19 @@
ifeq ($(BOARD_AVB_ENABLE),true)
$(BUILT_SYSTEMIMAGE): $(BOARD_AVB_SYSTEM_KEY_PATH)
endif
+
+ifeq ($(USE_SOONG_DEFINED_SYSTEM_IMAGE),true)
+ifeq ($(PRODUCT_SOONG_DEFINED_SYSTEM_IMAGE),)
+$(error PRODUCT_SOONG_DEFINED_SYSTEM_IMAGE must be set if USE_SOONG_DEFINED_SYSTEM_IMAGE is true)
+endif
+soong_defined_system_image := $(call intermediates-dir-for,ETC,$(PRODUCT_SOONG_DEFINED_SYSTEM_IMAGE))/$(PRODUCT_SOONG_DEFINED_SYSTEM_IMAGE)
+$(BUILT_SYSTEMIMAGE): $(INSTALLED_FILES_FILE) $(systemimage_intermediates)/file_list.txt $(soong_defined_system_image)
+$(eval $(call copy-one-file, $(soong_defined_system_image), $(BUILT_SYSTEMIMAGE)))
+soong_defined_system_image :=
+else
$(BUILT_SYSTEMIMAGE): $(FULL_SYSTEMIMAGE_DEPS) $(INSTALLED_FILES_FILE) $(systemimage_intermediates)/file_list.txt
$(call build-systemimage-target,$@)
+endif
$(call declare-1p-container,$(BUILT_SYSTEMIMAGE),system/extras)
$(call declare-container-license-deps,$(BUILT_SYSTEMIMAGE),$(FULL_SYSTEMIMAGE_DEPS),$(PRODUCT_OUT)/:/)
@@ -7851,6 +7862,31 @@
# Extract platform fonts used in Layoutlib
include $(BUILD_SYSTEM)/layoutlib_data.mk
+# -----------------------------------------------------------------
+# Desktop pack image hook.
+ifneq (,$(strip $(PACK_DESKTOP_FILESYSTEM_IMAGES)))
+PACK_IMAGE_TARGET := $(PRODUCT_OUT)/android-desktop_image.bin
+PACK_IMAGE_SCRIPT := $(HOST_OUT_EXECUTABLES)/pack_image
+IMAGES := $(INSTALLED_BOOTIMAGE_TARGET) \
+ $(INSTALLED_SUPERIMAGE_TARGET) \
+ $(INSTALLED_INIT_BOOT_IMAGE_TARGET) \
+ $(INSTALLED_VENDOR_BOOTIMAGE_TARGET) \
+ $(INSTALLED_VBMETAIMAGE_TARGET) \
+ $(INSTALLED_USERDATAIMAGE_TARGET)
+
+$(PACK_IMAGE_TARGET): $(IMAGES) $(PACK_IMAGE_SCRIPT)
+ $(PACK_IMAGE_SCRIPT) --out_dir $(PRODUCT_OUT) --noarchive
+
+PACKED_IMAGE_ARCHIVE_TARGET := $(PACK_IMAGE_TARGET).gz
+
+$(PACKED_IMAGE_ARCHIVE_TARGET): $(PACK_IMAGE_TARGET) | $(GZIP)
+ $(GZIP) -fk $(PACK_IMAGE_TARGET)
+
+droidcore-unbundled: $(PACKED_IMAGE_ARCHIVE_TARGET)
+
+$(call dist-for-goals,dist_files,$(PACKED_IMAGE_ARCHIVE_TARGET))
+
+endif # PACK_DESKTOP_FILESYSTEM_IMAGES
# -----------------------------------------------------------------
# OS Licensing
diff --git a/core/android_soong_config_vars.mk b/core/android_soong_config_vars.mk
index f2ff286..461b284 100644
--- a/core/android_soong_config_vars.mk
+++ b/core/android_soong_config_vars.mk
@@ -28,6 +28,7 @@
$(call add_soong_config_var,ANDROID,BOARD_USES_ODMIMAGE)
$(call soong_config_set_bool,ANDROID,BOARD_USES_RECOVERY_AS_BOOT,$(BOARD_USES_RECOVERY_AS_BOOT))
+$(call soong_config_set_bool,ANDROID,BOARD_MOVE_GSI_AVB_KEYS_TO_VENDOR_BOOT,$(BOARD_MOVE_GSI_AVB_KEYS_TO_VENDOR_BOOT))
$(call add_soong_config_var,ANDROID,CHECK_DEV_TYPE_VIOLATIONS)
$(call add_soong_config_var,ANDROID,PLATFORM_SEPOLICY_VERSION)
$(call add_soong_config_var,ANDROID,PLATFORM_SEPOLICY_COMPAT_VERSIONS)
@@ -36,9 +37,15 @@
$(call add_soong_config_var,ANDROID,TARGET_ENABLE_MEDIADRM_64)
$(call add_soong_config_var,ANDROID,TARGET_DYNAMIC_64_32_MEDIASERVER)
+# For Sanitizers
+$(call soong_config_set_bool,ANDROID,ASAN_ENABLED,$(if $(filter address,$(SANITIZE_TARGET)),true,false))
+$(call soong_config_set_bool,ANDROID,SANITIZE_TARGET_SYSTEM_ENABLED,$(if $(filter true,$(SANITIZE_TARGET_SYSTEM)),true,false))
+
# PRODUCT_PRECOMPILED_SEPOLICY defaults to true. Explicitly check if it's "false" or not.
$(call soong_config_set_bool,ANDROID,PRODUCT_PRECOMPILED_SEPOLICY,$(if $(filter false,$(PRODUCT_PRECOMPILED_SEPOLICY)),false,true))
+# For art modules
+$(call soong_config_set_bool,art_module,host_prefer_32_bit,$(if $(filter true,$(HOST_PREFER_32_BIT)),true,false))
ifdef ART_DEBUG_OPT_FLAG
$(call soong_config_set,art_module,art_debug_opt_flag,$(ART_DEBUG_OPT_FLAG))
endif
@@ -90,6 +97,7 @@
$(call add_soong_config_var_value,ANDROID,release_avf_allow_preinstalled_apps,$(RELEASE_AVF_ALLOW_PREINSTALLED_APPS))
$(call add_soong_config_var_value,ANDROID,release_avf_enable_device_assignment,$(RELEASE_AVF_ENABLE_DEVICE_ASSIGNMENT))
$(call add_soong_config_var_value,ANDROID,release_avf_enable_dice_changes,$(RELEASE_AVF_ENABLE_DICE_CHANGES))
+$(call add_soong_config_var_value,ANDROID,release_avf_enable_early_vm,$(RELEASE_AVF_ENABLE_EARLY_VM))
$(call add_soong_config_var_value,ANDROID,release_avf_enable_llpvm_changes,$(RELEASE_AVF_ENABLE_LLPVM_CHANGES))
$(call add_soong_config_var_value,ANDROID,release_avf_enable_multi_tenant_microdroid_vm,$(RELEASE_AVF_ENABLE_MULTI_TENANT_MICRODROID_VM))
$(call add_soong_config_var_value,ANDROID,release_avf_enable_network,$(RELEASE_AVF_ENABLE_NETWORK))
@@ -162,3 +170,9 @@
# Enable Profiling module. Also used by platform_bootclasspath.
$(call soong_config_set,ANDROID,release_package_profiling_module,$(RELEASE_PACKAGE_PROFILING_MODULE))
$(call soong_config_set,bootclasspath,release_package_profiling_module,$(RELEASE_PACKAGE_PROFILING_MODULE))
+
+# Add perf-setup build flag to soong
+# Note: BOARD_PERFSETUP_SCRIPT location must be under platform_testing/scripts/perf-setup/.
+ifdef BOARD_PERFSETUP_SCRIPT
+ $(call soong_config_set,perf,board_perfsetup_script,$(notdir $(BOARD_PERFSETUP_SCRIPT)))
+endif
diff --git a/core/base_rules.mk b/core/base_rules.mk
index a9d6494..ca553f6 100644
--- a/core/base_rules.mk
+++ b/core/base_rules.mk
@@ -728,6 +728,14 @@
ifneq (true,$(LOCAL_UNINSTALLABLE_MODULE))
+ifeq ($(EXCLUDE_MCTS),true)
+ ifneq (,$(test_config))
+ ifneq (,$(filter mcts-%,$(LOCAL_COMPATIBILITY_SUITE)))
+ LOCAL_COMPATIBILITY_SUITE := $(filter-out cts,$(LOCAL_COMPATIBILITY_SUITE))
+ endif
+ endif
+endif
+
# If we are building a native test or benchmark and its stem variants are not defined,
# separate the multiple architectures into subdirectories of the testcase folder.
arch_dir :=
diff --git a/core/binary.mk b/core/binary.mk
index 0bc9469..1e98bc0 100644
--- a/core/binary.mk
+++ b/core/binary.mk
@@ -205,8 +205,6 @@
my_api_level := $(my_ndk_api)
endif
- my_ndk_source_root := \
- $(HISTORICAL_NDK_VERSIONS_ROOT)/$(LOCAL_NDK_VERSION)/sources
my_built_ndk := $(SOONG_OUT_DIR)/ndk
my_ndk_triple := $($(LOCAL_2ND_ARCH_VAR_PREFIX)TARGET_NDK_TRIPLE)
my_ndk_sysroot_include := \
@@ -239,16 +237,18 @@
endif
ifeq (system,$(LOCAL_NDK_STL_VARIANT))
+ my_ndk_source_root := \
+ $(HISTORICAL_NDK_VERSIONS_ROOT)/$(LOCAL_NDK_VERSION)/sources
my_ndk_stl_include_path := $(my_ndk_source_root)/cxx-stl/system/include
my_system_shared_libraries += libstdc++
else ifneq (,$(filter c++_%, $(LOCAL_NDK_STL_VARIANT)))
- my_ndk_stl_include_path := \
- $(my_ndk_source_root)/cxx-stl/llvm-libc++/include
- my_ndk_stl_include_path += \
- $(my_ndk_source_root)/cxx-stl/llvm-libc++abi/include
+ my_llvm_dir := $(LLVM_PREBUILTS_BASE)/$(BUILD_OS)-x86/$(LLVM_PREBUILTS_VERSION)
+ my_libcxx_arch_dir := $(my_llvm_dir)/android_libc++/ndk/$($(LOCAL_2ND_ARCH_VAR_PREFIX)PREBUILT_LIBCXX_ARCH_DIR)
- my_libcxx_libdir := \
- $(my_ndk_source_root)/cxx-stl/llvm-libc++/libs/$(my_cpu_variant)
+ # Include the target-specific __config_site file followed by the generic libc++ headers.
+ my_ndk_stl_include_path := $(my_libcxx_arch_dir)/include/c++/v1
+ my_ndk_stl_include_path += $(my_llvm_dir)/include/c++/v1
+ my_libcxx_libdir := $(my_libcxx_arch_dir)/lib
ifeq (c++_static,$(LOCAL_NDK_STL_VARIANT))
my_ndk_stl_static_lib := \
@@ -258,7 +258,7 @@
my_ndk_stl_shared_lib_fullpath := $(my_libcxx_libdir)/libc++_shared.so
endif
- my_ndk_stl_static_lib += $(my_libcxx_libdir)/libunwind.a
+ my_ndk_stl_static_lib += $($(LOCAL_2ND_ARCH_VAR_PREFIX)TARGET_LIBUNWIND)
my_ldlibs += -ldl
else # LOCAL_NDK_STL_VARIANT must be none
# Do nothing.
diff --git a/core/clang/TARGET_arm.mk b/core/clang/TARGET_arm.mk
index f18747a..126482f 100644
--- a/core/clang/TARGET_arm.mk
+++ b/core/clang/TARGET_arm.mk
@@ -4,7 +4,10 @@
$(clang_2nd_arch_prefix)TARGET_LIBPROFILE_RT := $(LLVM_RTLIB_PATH)/libclang_rt.profile-arm-android.a
$(clang_2nd_arch_prefix)TARGET_LIBCRT_BUILTINS := $(LLVM_RTLIB_PATH)/libclang_rt.builtins-arm-android.a
+$(clang_2nd_arch_prefix)TARGET_LIBUNWIND := $(LLVM_RTLIB_PATH)/arm/libunwind.a
# Address sanitizer clang config
$(clang_2nd_arch_prefix)ADDRESS_SANITIZER_LINKER := /system/bin/linker_asan
$(clang_2nd_arch_prefix)ADDRESS_SANITIZER_LINKER_FILE := /system/bin/bootstrap/linker_asan
+
+$(clang_2nd_arch_prefix)PREBUILT_LIBCXX_ARCH_DIR := arm
diff --git a/core/clang/TARGET_arm64.mk b/core/clang/TARGET_arm64.mk
index 42bed0a..e7ab6cb 100644
--- a/core/clang/TARGET_arm64.mk
+++ b/core/clang/TARGET_arm64.mk
@@ -4,7 +4,10 @@
TARGET_LIBPROFILE_RT := $(LLVM_RTLIB_PATH)/libclang_rt.profile-aarch64-android.a
TARGET_LIBCRT_BUILTINS := $(LLVM_RTLIB_PATH)/libclang_rt.builtins-aarch64-android.a
+TARGET_LIBUNWIND := $(LLVM_RTLIB_PATH)/aarch64/libunwind.a
# Address sanitizer clang config
ADDRESS_SANITIZER_LINKER := /system/bin/linker_asan64
ADDRESS_SANITIZER_LINKER_FILE := /system/bin/bootstrap/linker_asan64
+
+PREBUILT_LIBCXX_ARCH_DIR := aarch64
diff --git a/core/clang/TARGET_riscv64.mk b/core/clang/TARGET_riscv64.mk
index cfb5c7d..58c9c7b 100644
--- a/core/clang/TARGET_riscv64.mk
+++ b/core/clang/TARGET_riscv64.mk
@@ -4,7 +4,10 @@
TARGET_LIBPROFILE_RT := $(LLVM_RTLIB_PATH)/libclang_rt.profile-riscv64-android.a
TARGET_LIBCRT_BUILTINS := $(LLVM_RTLIB_PATH)/libclang_rt.builtins-riscv64-android.a
+TARGET_LIBUNWIND := $(LLVM_RTLIB_PATH)/riscv64/libunwind.a
# Address sanitizer clang config
ADDRESS_SANITIZER_LINKER := /system/bin/linker_asan64
ADDRESS_SANITIZER_LINKER_FILE := /system/bin/bootstrap/linker_asan64
+
+PREBUILT_LIBCXX_ARCH_DIR := riscv64
diff --git a/core/clang/TARGET_x86.mk b/core/clang/TARGET_x86.mk
index 5491a05..1a08c79 100644
--- a/core/clang/TARGET_x86.mk
+++ b/core/clang/TARGET_x86.mk
@@ -4,7 +4,10 @@
$(clang_2nd_arch_prefix)TARGET_LIBPROFILE_RT := $(LLVM_RTLIB_PATH)/libclang_rt.profile-i686-android.a
$(clang_2nd_arch_prefix)TARGET_LIBCRT_BUILTINS := $(LLVM_RTLIB_PATH)/libclang_rt.builtins-i686-android.a
+$(clang_2nd_arch_prefix)TARGET_LIBUNWIND := $(LLVM_RTLIB_PATH)/i386/libunwind.a
# Address sanitizer clang config
$(clang_2nd_arch_prefix)ADDRESS_SANITIZER_LINKER := /system/bin/linker_asan
$(clang_2nd_arch_prefix)ADDRESS_SANITIZER_LINKER_FILE := /system/bin/bootstrap/linker_asan
+
+$(clang_2nd_arch_prefix)PREBUILT_LIBCXX_ARCH_DIR := i386
diff --git a/core/clang/TARGET_x86_64.mk b/core/clang/TARGET_x86_64.mk
index 167db72..f39b41e 100644
--- a/core/clang/TARGET_x86_64.mk
+++ b/core/clang/TARGET_x86_64.mk
@@ -4,7 +4,10 @@
TARGET_LIBPROFILE_RT := $(LLVM_RTLIB_PATH)/libclang_rt.profile-x86_64-android.a
TARGET_LIBCRT_BUILTINS := $(LLVM_RTLIB_PATH)/libclang_rt.builtins-x86_64-android.a
+TARGET_LIBUNWIND := $(LLVM_RTLIB_PATH)/x86_64/libunwind.a
# Address sanitizer clang config
ADDRESS_SANITIZER_LINKER := /system/bin/linker_asan64
ADDRESS_SANITIZER_LINKER_FILE := /system/bin/bootstrap/linker_asan64
+
+PREBUILT_LIBCXX_ARCH_DIR := x86_64
diff --git a/core/config.mk b/core/config.mk
index ae65944..0c8a87f 100644
--- a/core/config.mk
+++ b/core/config.mk
@@ -1246,6 +1246,12 @@
# in the source tree.
dont_bother_goals := out product-graph
+ifeq ($(TARGET_SYSTEM_PROP),)
+TARGET_SYSTEM_PROP := $(wildcard $(TARGET_DEVICE_DIR)/system.prop)
+endif
+
+.KATI_READONLY += TARGET_SYSTEM_PROP
+
include $(BUILD_SYSTEM)/sysprop_config.mk
# Make ANDROID Soong config variables visible to Android.mk files, for
diff --git a/core/install_jni_libs_internal.mk b/core/install_jni_libs_internal.mk
index 5491247..4959edd 100644
--- a/core/install_jni_libs_internal.mk
+++ b/core/install_jni_libs_internal.mk
@@ -38,8 +38,9 @@
$(error LOCAL_SDK_VERSION must be defined with LOCAL_NDK_STL_VARIANT, \
LOCAL_PACKAGE_NAME=$(LOCAL_PACKAGE_NAME))
endif
+ my_libcxx_arch := $($(LOCAL_2ND_ARCH_VAR_PREFIX)PREBUILT_LIBCXX_ARCH_DIR)
my_jni_shared_libraries += \
- $(HISTORICAL_NDK_VERSIONS_ROOT)/$(LOCAL_NDK_VERSION)/sources/cxx-stl/llvm-libc++/libs/$(TARGET_$(my_2nd_arch_prefix)CPU_ABI)/libc++_shared.so
+ $(LLVM_PREBUILTS_BASE)/$(BUILD_OS)-x86/$(LLVM_PREBUILTS_VERSION)/android_libc++/ndk/$(my_libcxx_arch)/lib/libc++_shared.so
endif
# Set the abi directory used by the local JNI shared libraries.
diff --git a/core/main.mk b/core/main.mk
index f3980f1..8d73793 100644
--- a/core/main.mk
+++ b/core/main.mk
@@ -688,11 +688,16 @@
$(eval my_testcases := $(HOST_OUT_TESTCASES)),\
$(eval my_testcases := $$(COMPATIBILITY_TESTCASES_OUT_$(suite))))\
$(eval target := $(my_testcases)/$(lastword $(subst /, ,$(dir $(f))))/$(notdir $(f)))\
+ $(eval link_target := ../../../$(lastword $(subst /, ,$(dir $(f))))/$(notdir $(f)))\
+ $(eval symlink := $(my_testcases)/$(m)/shared_libs/$(lastword $(subst /, ,$(dir $(f))))/$(notdir $(f)))\
+ $(eval COMPATIBILITY.$(suite).SYMLINKS := \
+ $$(COMPATIBILITY.$(suite).SYMLINKS) $(f):$(link_target):$(symlink))\
$(if $(strip $(ALL_TARGETS.$(target).META_LIC)),,$(call declare-copy-target-license-metadata,$(target),$(f)))\
$(eval COMPATIBILITY.$(suite).HOST_SHARED_LIBRARY.FILES := \
$$(COMPATIBILITY.$(suite).HOST_SHARED_LIBRARY.FILES) $(f):$(target))\
$(eval COMPATIBILITY.$(suite).HOST_SHARED_LIBRARY.FILES := \
- $(sort $(COMPATIBILITY.$(suite).HOST_SHARED_LIBRARY.FILES)))))))
+ $(sort $(COMPATIBILITY.$(suite).HOST_SHARED_LIBRARY.FILES))))))\
+ $(eval COMPATIBILITY.$(suite).SYMLINKS := $(sort $(COMPATIBILITY.$(suite).SYMLINKS))))
endef
$(call resolve-shared-libs-depes,TARGET_)
diff --git a/core/ravenwood_test_config_template.xml b/core/ravenwood_test_config_template.xml
index 16a22c0..088a55a 100644
--- a/core/ravenwood_test_config_template.xml
+++ b/core/ravenwood_test_config_template.xml
@@ -18,7 +18,7 @@
<option name="test-suite-tag" value="ravenwood" />
<option name="test-suite-tag" value="ravenwood-tests" />
- <option name="java-folder" value="prebuilts/jdk/jdk17/linux-x86/" />
+ <option name="java-folder" value="prebuilts/jdk/jdk21/linux-x86/" />
<option name="use-ravenwood-resources" value="true" />
<option name="exclude-paths" value="java" />
<option name="socket-timeout" value="10000" />
diff --git a/core/release_config.mk b/core/release_config.mk
index 2898868..fe2170e 100644
--- a/core/release_config.mk
+++ b/core/release_config.mk
@@ -131,6 +131,9 @@
_args += --guard=false
endif
_args += --allow-missing=true
+ ifneq (,$(TARGET_PRODUCT))
+ _args += --product $(TARGET_PRODUCT)
+ endif
_flags_dir:=$(OUT_DIR)/soong/release-config
_flags_file:=$(_flags_dir)/release_config-$(TARGET_PRODUCT)-$(TARGET_RELEASE).vars
# release-config generates $(_flags_varmk)
diff --git a/core/robolectric_test_config_template.xml b/core/robolectric_test_config_template.xml
index 56d2312..b1d0c2f 100644
--- a/core/robolectric_test_config_template.xml
+++ b/core/robolectric_test_config_template.xml
@@ -18,7 +18,7 @@
<option name="test-suite-tag" value="robolectric" />
<option name="test-suite-tag" value="robolectric-tests" />
- <option name="java-folder" value="prebuilts/jdk/jdk17/linux-x86/" />
+ <option name="java-folder" value="prebuilts/jdk/jdk21/linux-x86/" />
<option name="exclude-paths" value="java" />
<option name="use-robolectric-resources" value="true" />
diff --git a/core/rust_device_test_config_template.xml b/core/rust_device_test_config_template.xml
index bfd2f47..aacabcb 100644
--- a/core/rust_device_test_config_template.xml
+++ b/core/rust_device_test_config_template.xml
@@ -20,11 +20,11 @@
<target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
<option name="cleanup" value="true" />
- <option name="push" value="{MODULE}->/data/local/tmp/{MODULE}" />
+ <option name="push" value="{MODULE}->{TEST_INSTALL_BASE}/{MODULE}" />
</target_preparer>
<test class="com.android.tradefed.testtype.rust.RustBinaryTest" >
- <option name="test-device-path" value="/data/local/tmp" />
+ <option name="test-device-path" value="{TEST_INSTALL_BASE}" />
<option name="module-name" value="{MODULE}" />
</test>
</configuration>
diff --git a/core/soong_config.mk b/core/soong_config.mk
index 12b4135..09ee938 100644
--- a/core/soong_config.mk
+++ b/core/soong_config.mk
@@ -152,7 +152,6 @@
$(call add_json_str, VendorApiLevel, $(BOARD_API_LEVEL))
$(call add_json_list, ExtraVndkVersions, $(PRODUCT_EXTRA_VNDK_VERSIONS))
$(call add_json_list, DeviceSystemSdkVersions, $(BOARD_SYSTEMSDK_VERSIONS))
-$(call add_json_str, RecoverySnapshotVersion, $(RECOVERY_SNAPSHOT_VERSION))
$(call add_json_list, Platform_systemsdk_versions, $(PLATFORM_SYSTEMSDK_VERSIONS))
$(call add_json_bool, Malloc_low_memory, $(findstring true,$(MALLOC_SVELTE) $(MALLOC_LOW_MEMORY)))
$(call add_json_bool, Malloc_zero_contents, $(call invert_bool,$(filter false,$(MALLOC_ZERO_CONTENTS))))
@@ -167,8 +166,6 @@
$(call add_json_list, BootJars, $(PRODUCT_BOOT_JARS))
$(call add_json_list, ApexBootJars, $(filter-out $(APEX_BOOT_JARS_EXCLUDED), $(PRODUCT_APEX_BOOT_JARS)))
-$(call add_json_bool, VndkSnapshotBuildArtifacts, $(VNDK_SNAPSHOT_BUILD_ARTIFACTS))
-
$(call add_json_map, BuildFlags)
$(foreach flag,$(_ALL_RELEASE_FLAGS),\
$(call add_json_str,$(flag),$(_ALL_RELEASE_FLAGS.$(flag).VALUE)))
@@ -178,24 +175,6 @@
$(call add_json_str,$(flag),$(_ALL_RELEASE_FLAGS.$(flag).TYPE)))
$(call end_json_map)
-$(call add_json_bool, DirectedVendorSnapshot, $(DIRECTED_VENDOR_SNAPSHOT))
-$(call add_json_map, VendorSnapshotModules)
-$(foreach module,$(VENDOR_SNAPSHOT_MODULES),\
- $(call add_json_bool,$(module),true))
-$(call end_json_map)
-
-$(call add_json_bool, DirectedRecoverySnapshot, $(DIRECTED_RECOVERY_SNAPSHOT))
-$(call add_json_map, RecoverySnapshotModules)
-$(foreach module,$(RECOVERY_SNAPSHOT_MODULES),\
- $(call add_json_bool,$(module),true))
-$(call end_json_map)
-
-$(call add_json_list, VendorSnapshotDirsIncluded, $(VENDOR_SNAPSHOT_DIRS_INCLUDED))
-$(call add_json_list, VendorSnapshotDirsExcluded, $(VENDOR_SNAPSHOT_DIRS_EXCLUDED))
-$(call add_json_list, RecoverySnapshotDirsIncluded, $(RECOVERY_SNAPSHOT_DIRS_INCLUDED))
-$(call add_json_list, RecoverySnapshotDirsExcluded, $(RECOVERY_SNAPSHOT_DIRS_EXCLUDED))
-$(call add_json_bool, HostFakeSnapshotEnabled, $(HOST_FAKE_SNAPSHOT_ENABLE))
-
$(call add_json_bool, MultitreeUpdateMeta, $(filter true,$(TARGET_MULTITREE_UPDATE_META)))
$(call add_json_bool, Treble_linker_namespaces, $(filter true,$(PRODUCT_TREBLE_LINKER_NAMESPACES)))
@@ -361,6 +340,19 @@
$(call add_json_list, OemProperties, $(PRODUCT_OEM_PROPERTIES))
+$(call add_json_list, SystemPropFiles, $(TARGET_SYSTEM_PROP))
+
+# Do not set ArtTargetIncludeDebugBuild into any value if PRODUCT_ART_TARGET_INCLUDE_DEBUG_BUILD is not set,
+# to have the same behavior from runtime_libart.mk.
+ifneq ($(PRODUCT_ART_TARGET_INCLUDE_DEBUG_BUILD),)
+$(call add_json_bool, ArtTargetIncludeDebugBuild, $(PRODUCT_ART_TARGET_INCLUDE_DEBUG_BUILD))
+endif
+
+_config_enable_uffd_gc := \
+ $(firstword $(OVERRIDE_ENABLE_UFFD_GC) $(PRODUCT_ENABLE_UFFD_GC) default)
+$(call add_json_str, EnableUffdGc, $(_config_enable_uffd_gc))
+_config_enable_uffd_gc :=
+
$(call json_end)
$(file >$(SOONG_VARIABLES).tmp,$(json_contents))
diff --git a/core/soong_extra_config.mk b/core/soong_extra_config.mk
index e4432d2..76da0d7 100644
--- a/core/soong_extra_config.mk
+++ b/core/soong_extra_config.mk
@@ -48,11 +48,6 @@
$(call add_json_bool, SdkBuild, $(filter sdk sdk_addon,$(MAKECMDGOALS)))
-_config_enable_uffd_gc := \
- $(firstword $(OVERRIDE_ENABLE_UFFD_GC) $(PRODUCT_ENABLE_UFFD_GC) default)
-$(call add_json_str, EnableUffdGc, $(_config_enable_uffd_gc))
-_config_enable_uffd_gc :=
-
$(call add_json_str, SystemServerCompilerFilter, $(PRODUCT_SYSTEM_SERVER_COMPILER_FILTER))
$(call add_json_bool, Product16KDeveloperOption, $(filter true,$(PRODUCT_16K_DEVELOPER_OPTION)))
@@ -93,6 +88,8 @@
$(call add_json_list, BuildVersionTags, $(BUILD_VERSION_TAGS))
+$(call add_json_bool, ProductNotDebuggableInUserdebug, $(PRODUCT_NOT_DEBUGGABLE_IN_USERDEBUG))
+
$(call json_end)
$(shell mkdir -p $(dir $(SOONG_EXTRA_VARIABLES)))
diff --git a/core/sysprop.mk b/core/sysprop.mk
index 47d8a41..7dd756a 100644
--- a/core/sysprop.mk
+++ b/core/sysprop.mk
@@ -33,34 +33,26 @@
echo "# from generate-common-build-props" >> $(2);\
echo "# These properties identify this partition image." >> $(2);\
echo "####################################" >> $(2);\
- $(if $(filter system,$(1)),\
- echo "ro.product.$(1).brand=$(PRODUCT_SYSTEM_BRAND)" >> $(2);\
- echo "ro.product.$(1).device=$(PRODUCT_SYSTEM_DEVICE)" >> $(2);\
- echo "ro.product.$(1).manufacturer=$(PRODUCT_SYSTEM_MANUFACTURER)" >> $(2);\
- echo "ro.product.$(1).model=$(PRODUCT_SYSTEM_MODEL)" >> $(2);\
- echo "ro.product.$(1).name=$(PRODUCT_SYSTEM_NAME)" >> $(2);\
- ,\
- echo "ro.product.$(1).brand=$(PRODUCT_BRAND)" >> $(2);\
- echo "ro.product.$(1).device=$(TARGET_DEVICE)" >> $(2);\
- echo "ro.product.$(1).manufacturer=$(PRODUCT_MANUFACTURER)" >> $(2);\
- echo "ro.product.$(1).model=$(PRODUCT_MODEL)" >> $(2);\
- echo "ro.product.$(1).name=$(TARGET_PRODUCT)" >> $(2);\
- if [ -n "$(strip $(PRODUCT_MODEL_FOR_ATTESTATION))" ]; then \
- echo "ro.product.model_for_attestation=$(PRODUCT_MODEL_FOR_ATTESTATION)" >> $(2);\
- fi; \
- if [ -n "$(strip $(PRODUCT_BRAND_FOR_ATTESTATION))" ]; then \
- echo "ro.product.brand_for_attestation=$(PRODUCT_BRAND_FOR_ATTESTATION)" >> $(2);\
- fi; \
- if [ -n "$(strip $(PRODUCT_NAME_FOR_ATTESTATION))" ]; then \
- echo "ro.product.name_for_attestation=$(PRODUCT_NAME_FOR_ATTESTATION)" >> $(2);\
- fi; \
- if [ -n "$(strip $(PRODUCT_DEVICE_FOR_ATTESTATION))" ]; then \
- echo "ro.product.device_for_attestation=$(PRODUCT_DEVICE_FOR_ATTESTATION)" >> $(2);\
- fi; \
- if [ -n "$(strip $(PRODUCT_MANUFACTURER_FOR_ATTESTATION))" ]; then \
- echo "ro.product.manufacturer_for_attestation=$(PRODUCT_MANUFACTURER_FOR_ATTESTATION)" >> $(2);\
- fi; \
- )\
+ echo "ro.product.$(1).brand=$(PRODUCT_BRAND)" >> $(2);\
+ echo "ro.product.$(1).device=$(TARGET_DEVICE)" >> $(2);\
+ echo "ro.product.$(1).manufacturer=$(PRODUCT_MANUFACTURER)" >> $(2);\
+ echo "ro.product.$(1).model=$(PRODUCT_MODEL)" >> $(2);\
+ echo "ro.product.$(1).name=$(TARGET_PRODUCT)" >> $(2);\
+ if [ -n "$(strip $(PRODUCT_MODEL_FOR_ATTESTATION))" ]; then \
+ echo "ro.product.model_for_attestation=$(PRODUCT_MODEL_FOR_ATTESTATION)" >> $(2);\
+ fi; \
+ if [ -n "$(strip $(PRODUCT_BRAND_FOR_ATTESTATION))" ]; then \
+ echo "ro.product.brand_for_attestation=$(PRODUCT_BRAND_FOR_ATTESTATION)" >> $(2);\
+ fi; \
+ if [ -n "$(strip $(PRODUCT_NAME_FOR_ATTESTATION))" ]; then \
+ echo "ro.product.name_for_attestation=$(PRODUCT_NAME_FOR_ATTESTATION)" >> $(2);\
+ fi; \
+ if [ -n "$(strip $(PRODUCT_DEVICE_FOR_ATTESTATION))" ]; then \
+ echo "ro.product.device_for_attestation=$(PRODUCT_DEVICE_FOR_ATTESTATION)" >> $(2);\
+ fi; \
+ if [ -n "$(strip $(PRODUCT_MANUFACTURER_FOR_ATTESTATION))" ]; then \
+ echo "ro.product.manufacturer_for_attestation=$(PRODUCT_MANUFACTURER_FOR_ATTESTATION)" >> $(2);\
+ fi; \
$(if $(filter true,$(ZYGOTE_FORCE_64)),\
$(if $(filter vendor,$(1)),\
echo "ro.$(1).product.cpu.abilist=$(TARGET_CPU_ABI_LIST_64_BIT)" >> $(2);\
@@ -226,50 +218,11 @@
# -----------------------------------------------------------------
# system/build.prop
#
-# Note: parts of this file that can't be generated by the build-properties
-# macro are manually created as separate files and then fed into the macro
-
-buildinfo_prop := $(call intermediates-dir-for,ETC,buildinfo.prop)/buildinfo.prop
-
-ifdef TARGET_SYSTEM_PROP
-system_prop_file := $(TARGET_SYSTEM_PROP)
-else
-system_prop_file := $(wildcard $(TARGET_DEVICE_DIR)/system.prop)
-endif
-
-_prop_files_ := \
- $(buildinfo_prop) \
- $(system_prop_file)
-
-# Order matters here. When there are duplicates, the last one wins.
-# TODO(b/117892318): don't allow duplicates so that the ordering doesn't matter
-_prop_vars_ := \
- ADDITIONAL_SYSTEM_PROPERTIES \
- PRODUCT_SYSTEM_PROPERTIES
-
-# TODO(b/117892318): deprecate this
-_prop_vars_ += \
- PRODUCT_SYSTEM_DEFAULT_PROPERTIES
-
-ifndef property_overrides_split_enabled
-_prop_vars_ += \
- ADDITIONAL_VENDOR_PROPERTIES \
- PRODUCT_VENDOR_PROPERTIES
-endif
+# system/build.prop is built by Soong. See system-build.prop module in
+# build/soong/Android.bp.
INSTALLED_BUILD_PROP_TARGET := $(TARGET_OUT)/build.prop
-$(eval $(call build-properties,\
- system,\
- $(INSTALLED_BUILD_PROP_TARGET),\
- $(_prop_files_),\
- $(_prop_vars_),\
- $(PRODUCT_SYSTEM_PROPERTY_BLACKLIST),\
- $(empty),\
- $(empty)))
-
-$(eval $(call declare-1p-target,$(INSTALLED_BUILD_PROP_TARGET)))
-
# -----------------------------------------------------------------
# vendor/build.prop
#
diff --git a/core/sysprop_config.mk b/core/sysprop_config.mk
index f9b9d1c..543b86b 100644
--- a/core/sysprop_config.mk
+++ b/core/sysprop_config.mk
@@ -15,28 +15,7 @@
)
_additional_prop_var_names :=
-#
-# -----------------------------------------------------------------
-# Add the product-defined properties to the build properties.
-ifneq ($(BOARD_PROPERTY_OVERRIDES_SPLIT_ENABLED), true)
- ADDITIONAL_SYSTEM_PROPERTIES += $(PRODUCT_PROPERTY_OVERRIDES)
-else
- ifndef BOARD_VENDORIMAGE_FILE_SYSTEM_TYPE
- ADDITIONAL_SYSTEM_PROPERTIES += $(PRODUCT_PROPERTY_OVERRIDES)
- endif
-endif
-
-ADDITIONAL_SYSTEM_PROPERTIES += ro.treble.enabled=${PRODUCT_FULL_TREBLE}
-
-# Set ro.llndk.api_level to show the maximum vendor API level that the LLNDK in
-# the system partition supports.
-ifdef RELEASE_BOARD_API_LEVEL
-ADDITIONAL_SYSTEM_PROPERTIES += ro.llndk.api_level=$(RELEASE_BOARD_API_LEVEL)
-endif
-
-# Sets ro.actionable_compatible_property.enabled to know on runtime whether the
-# allowed list of actionable compatible properties is enabled or not.
-ADDITIONAL_SYSTEM_PROPERTIES += ro.actionable_compatible_property.enabled=true
+$(KATI_obsolete_var ADDITIONAL_SYSTEM_PROPERTIES,Use build/soong/scripts/gen_build_prop.py instead)
# Add the system server compiler filter if they are specified for the product.
ifneq (,$(PRODUCT_SYSTEM_SERVER_COMPILER_FILTER))
@@ -56,23 +35,6 @@
ADDITIONAL_PRODUCT_PROPERTIES += ro.product.page_size=4096
endif
-# Enable core platform API violation warnings on userdebug and eng builds.
-ifneq ($(TARGET_BUILD_VARIANT),user)
-ADDITIONAL_SYSTEM_PROPERTIES += persist.debug.dalvik.vm.core_platform_api_policy=just-warn
-endif
-
-# Define ro.sanitize.<name> properties for all global sanitizers.
-ADDITIONAL_SYSTEM_PROPERTIES += $(foreach s,$(SANITIZE_TARGET),ro.sanitize.$(s)=true)
-
-# Sets the default value of ro.postinstall.fstab.prefix to /system.
-# Device board config should override the value to /product when needed by:
-#
-# PRODUCT_PRODUCT_PROPERTIES += ro.postinstall.fstab.prefix=/product
-#
-# It then uses ${ro.postinstall.fstab.prefix}/etc/fstab.postinstall to
-# mount system_other partition.
-ADDITIONAL_SYSTEM_PROPERTIES += ro.postinstall.fstab.prefix=/system
-
# Add cpu properties for bionic and ART.
ADDITIONAL_VENDOR_PROPERTIES += ro.bionic.arch=$(TARGET_ARCH)
ADDITIONAL_VENDOR_PROPERTIES += ro.bionic.cpu_variant=$(TARGET_CPU_VARIANT_RUNTIME)
@@ -200,87 +162,6 @@
endif
user_variant := $(filter user userdebug,$(TARGET_BUILD_VARIANT))
-enable_target_debugging := true
-enable_dalvik_lock_contention_logging := true
-ifneq (,$(user_variant))
- # Target is secure in user builds.
- ADDITIONAL_SYSTEM_PROPERTIES += ro.secure=1
- ADDITIONAL_SYSTEM_PROPERTIES += security.perf_harden=1
-
- ifeq ($(user_variant),user)
- ADDITIONAL_SYSTEM_PROPERTIES += ro.adb.secure=1
- endif
-
- ifneq ($(user_variant),userdebug)
- # Disable debugging in plain user builds.
- enable_target_debugging :=
- enable_dalvik_lock_contention_logging :=
- else
- # Disable debugging in userdebug builds if PRODUCT_NOT_DEBUGGABLE_IN_USERDEBUG
- # is set.
- ifneq (,$(strip $(PRODUCT_NOT_DEBUGGABLE_IN_USERDEBUG)))
- enable_target_debugging :=
- endif
- endif
-
- # Disallow mock locations by default for user builds
- ADDITIONAL_SYSTEM_PROPERTIES += ro.allow.mock.location=0
-
-else # !user_variant
- # Turn on checkjni for non-user builds.
- ADDITIONAL_SYSTEM_PROPERTIES += ro.kernel.android.checkjni=1
- # Set device insecure for non-user builds.
- ADDITIONAL_SYSTEM_PROPERTIES += ro.secure=0
- # Allow mock locations by default for non user builds
- ADDITIONAL_SYSTEM_PROPERTIES += ro.allow.mock.location=1
-endif # !user_variant
-
-ifeq (true,$(strip $(enable_dalvik_lock_contention_logging)))
- # Enable Dalvik lock contention logging.
- ADDITIONAL_SYSTEM_PROPERTIES += dalvik.vm.lockprof.threshold=500
-endif # !enable_dalvik_lock_contention_logging
-
-ifeq (true,$(strip $(enable_target_debugging)))
- # Target is more debuggable and adbd is on by default
- ADDITIONAL_SYSTEM_PROPERTIES += ro.debuggable=1
-else # !enable_target_debugging
- # Target is less debuggable and adbd is off by default
- ADDITIONAL_SYSTEM_PROPERTIES += ro.debuggable=0
-endif # !enable_target_debugging
-
-enable_target_debugging:=
-enable_dalvik_lock_contention_logging:=
-
-ifneq ($(filter sdk sdk_addon,$(MAKECMDGOALS)),)
-_is_sdk_build := true
-endif
-
-ifeq ($(TARGET_BUILD_VARIANT),eng)
-ifneq ($(filter ro.setupwizard.mode=ENABLED, $(call collapse-pairs, $(ADDITIONAL_SYSTEM_PROPERTIES))),)
- # Don't require the setup wizard on eng builds
- ADDITIONAL_SYSTEM_PROPERTIES := $(filter-out ro.setupwizard.mode=%,\
- $(call collapse-pairs, $(ADDITIONAL_SYSTEM_PROPERTIES))) \
- ro.setupwizard.mode=OPTIONAL
-endif
-ifndef _is_sdk_build
- # To speedup startup of non-preopted builds, don't verify or compile the boot image.
- ADDITIONAL_SYSTEM_PROPERTIES += dalvik.vm.image-dex2oat-filter=extract
-endif
-# b/323566535
-ADDITIONAL_SYSTEM_PROPERTIES += init.svc_debug.no_fatal.zygote=true
-endif
-
-ifdef _is_sdk_build
-ADDITIONAL_SYSTEM_PROPERTIES += xmpp.auto-presence=true
-ADDITIONAL_SYSTEM_PROPERTIES += ro.config.nocheckin=yes
-endif
-
-_is_sdk_build :=
-
-ADDITIONAL_SYSTEM_PROPERTIES += net.bt.name=Android
-
-# This property is set by flashing debug boot image, so default to false.
-ADDITIONAL_SYSTEM_PROPERTIES += ro.force.debuggable=0
config_enable_uffd_gc := \
$(firstword $(OVERRIDE_ENABLE_UFFD_GC) $(PRODUCT_ENABLE_UFFD_GC) default)
@@ -291,11 +172,9 @@
# If the value is "default", it will be mangled by post_process_props.py.
ADDITIONAL_PRODUCT_PROPERTIES += ro.dalvik.vm.enable_uffd_gc=$(config_enable_uffd_gc)
-ADDITIONAL_SYSTEM_PROPERTIES := $(strip $(ADDITIONAL_SYSTEM_PROPERTIES))
ADDITIONAL_PRODUCT_PROPERTIES := $(strip $(ADDITIONAL_PRODUCT_PROPERTIES))
ADDITIONAL_VENDOR_PROPERTIES := $(strip $(ADDITIONAL_VENDOR_PROPERTIES))
.KATI_READONLY += \
- ADDITIONAL_SYSTEM_PROPERTIES \
ADDITIONAL_PRODUCT_PROPERTIES \
ADDITIONAL_VENDOR_PROPERTIES
diff --git a/core/tasks/host-unit-tests.mk b/core/tasks/host-unit-tests.mk
index 733a2e2..4cb23c0 100644
--- a/core/tasks/host-unit-tests.mk
+++ b/core/tasks/host-unit-tests.mk
@@ -29,15 +29,28 @@
$(eval _cmf_src := $(word 1,$(_cmf_tuple))) \
$(_cmf_src)))
+my_symlinks_for_host_unit_tests := $(foreach f,$(COMPATIBILITY.host-unit-tests.SYMLINKS),\
+ $(strip $(eval _cmf_tuple := $(subst :, ,$(f))) \
+ $(eval _cmf_dep := $(word 1,$(_cmf_tuple))) \
+ $(eval _cmf_src := $(word 2,$(_cmf_tuple))) \
+ $(eval _cmf_dest := $(word 3,$(_cmf_tuple))) \
+ $(call symlink-file,$(_cmf_dep),$(_cmf_src),$(_cmf_dest)) \
+ $(_cmf_dest)))
+
$(host_unit_tests_zip) : PRIVATE_HOST_SHARED_LIBS := $(my_host_shared_lib_for_host_unit_tests)
-$(host_unit_tests_zip) : $(COMPATIBILITY.host-unit-tests.FILES) $(my_host_shared_lib_for_host_unit_tests) $(SOONG_ZIP)
+$(host_unit_tests_zip) : PRIVATE_SYMLINKS := $(my_symlinks_for_host_unit_tests)
+
+$(host_unit_tests_zip) : $(COMPATIBILITY.host-unit-tests.FILES) $(my_host_shared_lib_for_host_unit_tests) $(my_symlinks_for_host_unit_tests) $(SOONG_ZIP)
echo $(sort $(COMPATIBILITY.host-unit-tests.FILES)) | tr " " "\n" > $@.list
grep $(HOST_OUT_TESTCASES) $@.list > $@-host.list || true
echo "" >> $@-host-libs.list
$(hide) for shared_lib in $(PRIVATE_HOST_SHARED_LIBS); do \
echo $$shared_lib >> $@-host-libs.list; \
done
+ $(hide) for symlink in $(PRIVATE_SYMLINKS); do \
+ echo $$symlink >> $@-host.list; \
+ done
grep $(TARGET_OUT_TESTCASES) $@.list > $@-target.list || true
$(hide) $(SOONG_ZIP) -d -o $@ -P host -C $(HOST_OUT) -l $@-host.list \
-P target -C $(PRODUCT_OUT) -l $@-target.list \
diff --git a/core/tasks/meta-lic.mk b/core/tasks/meta-lic.mk
index 85357eb..24adfc8 100644
--- a/core/tasks/meta-lic.mk
+++ b/core/tasks/meta-lic.mk
@@ -83,6 +83,40 @@
$(eval $(call declare-1p-copy-files,device/google/coral,audio_policy_configuration.xml))
$(eval $(call declare-1p-copy-files,device/google/coral,display_19260504575090817.xml))
+# Moved here from device/google/cuttlefish/Android.mk
+$(eval $(call declare-copy-files-license-metadata,device/google/cuttlefish,.idc,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
+$(eval $(call declare-copy-files-license-metadata,device/google/cuttlefish,default-permissions.xml,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
+$(eval $(call declare-copy-files-license-metadata,device/google/cuttlefish,libnfc-nci.conf,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
+$(eval $(call declare-copy-files-license-metadata,device/google/cuttlefish,fstab.postinstall,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
+$(eval $(call declare-copy-files-license-metadata,device/google/cuttlefish,ueventd.rc,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
+$(eval $(call declare-copy-files-license-metadata,device/google/cuttlefish,wpa_supplicant.conf,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
+$(eval $(call declare-copy-files-license-metadata,device/google/cuttlefish,hals.conf,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
+$(eval $(call declare-copy-files-license-metadata,device/google/cuttlefish,device_state_configuration.xml,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
+$(eval $(call declare-copy-files-license-metadata,device/google/cuttlefish,p2p_supplicant.conf,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
+$(eval $(call declare-copy-files-license-metadata,device/google/cuttlefish,wpa_supplicant.conf,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
+$(eval $(call declare-copy-files-license-metadata,device/google/cuttlefish,wpa_supplicant_overlay.conf,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
+$(eval $(call declare-copy-files-license-metadata,device/google/cuttlefish,wpa_supplicant.rc,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
+$(eval $(call declare-copy-files-license-metadata,device/google/cuttlefish,init.cutf_cvm.rc,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
+$(eval $(call declare-copy-files-license-metadata,device/google/cuttlefish,fstab.cf.f2fs.hctr2,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
+$(eval $(call declare-copy-files-license-metadata,device/google/cuttlefish,fstab.cf.f2fs.cts,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
+$(eval $(call declare-copy-files-license-metadata,device/google/cuttlefish,fstab.cf.ext4.hctr2,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
+$(eval $(call declare-copy-files-license-metadata,device/google/cuttlefish,fstab.cf.ext4.cts,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
+$(eval $(call declare-copy-files-license-metadata,device/google/cuttlefish,init.rc,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
+$(eval $(call declare-copy-files-license-metadata,device/google/cuttlefish,audio_policy.conf,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
+
+$(eval $(call declare-copy-files-license-metadata,device/google/cuttlefish/shared/config,pci.ids,SPDX-license-identifier-BSD-3-Clause,notice,device/google/cuttlefish/shared/config/LICENSE_BSD,))
+
+$(eval $(call declare-1p-copy-files,device/google/cuttlefish,privapp-permissions-cuttlefish.xml))
+$(eval $(call declare-1p-copy-files,device/google/cuttlefish,media_profiles_V1_0.xml))
+$(eval $(call declare-1p-copy-files,device/google/cuttlefish,media_codecs_performance.xml))
+$(eval $(call declare-1p-copy-files,device/google/cuttlefish,cuttlefish_excluded_hardware.xml))
+$(eval $(call declare-1p-copy-files,device/google/cuttlefish,media_codecs.xml))
+$(eval $(call declare-1p-copy-files,device/google/cuttlefish,media_codecs_google_video.xml))
+$(eval $(call declare-1p-copy-files,device/google/cuttlefish,car_audio_configuration.xml))
+$(eval $(call declare-1p-copy-files,device/google/cuttlefish,audio_policy_configuration.xml))
+$(eval $(call declare-1p-copy-files,device/google/cuttlefish,preinstalled-packages-product-car-cuttlefish.xml))
+$(eval $(call declare-1p-copy-files,hardware/google/camera/devices,.json))
+
# Moved here from device/google/gs101/Android.mk
$(eval $(call declare-copy-files-license-metadata,device/google/gs101,default-permissions.xml,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
$(eval $(call declare-copy-files-license-metadata,device/google/gs101,libnfc-nci.conf,SPDX-license-identifier-Apache-2.0,notice,build/soong/licenses/LICENSE,))
diff --git a/core/tasks/recovery_snapshot.mk b/core/tasks/recovery_snapshot.mk
deleted file mode 100644
index 525273b..0000000
--- a/core/tasks/recovery_snapshot.mk
+++ /dev/null
@@ -1,34 +0,0 @@
-# Copyright (C) 2020 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.
-
-current_makefile := $(lastword $(MAKEFILE_LIST))
-
-# RECOVERY_SNAPSHOT_VERSION must be set to 'current' in order to generate a recovery snapshot.
-ifeq ($(RECOVERY_SNAPSHOT_VERSION),current)
-
-.PHONY: recovery-snapshot
-recovery-snapshot: $(SOONG_RECOVERY_SNAPSHOT_ZIP)
-
-$(call dist-for-goals, recovery-snapshot, $(SOONG_RECOVERY_SNAPSHOT_ZIP))
-
-else # RECOVERY_SNAPSHOT_VERSION is NOT set to 'current'
-
-.PHONY: recovery-snapshot
-recovery-snapshot: PRIVATE_MAKEFILE := $(current_makefile)
-recovery-snapshot:
- $(call echo-error,$(PRIVATE_MAKEFILE),\
- "CANNOT generate Recovery snapshot. RECOVERY_SNAPSHOT_VERSION must be set to 'current'.")
- exit 1
-
-endif # RECOVERY_SNAPSHOT_VERSION
diff --git a/core/tasks/vendor_snapshot.mk b/core/tasks/vendor_snapshot.mk
deleted file mode 100644
index 83c1379..0000000
--- a/core/tasks/vendor_snapshot.mk
+++ /dev/null
@@ -1,46 +0,0 @@
-# Copyright (C) 2020 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.
-
-current_makefile := $(lastword $(MAKEFILE_LIST))
-
-# BOARD_VNDK_VERSION must be set to 'current' in order to generate a vendor snapshot.
-ifeq ($(BOARD_VNDK_VERSION),current)
-
-.PHONY: vendor-snapshot
-vendor-snapshot: $(SOONG_VENDOR_SNAPSHOT_ZIP)
-
-$(call dist-for-goals, vendor-snapshot, $(SOONG_VENDOR_SNAPSHOT_ZIP))
-
-.PHONY: vendor-fake-snapshot
-vendor-fake-snapshot: $(SOONG_VENDOR_FAKE_SNAPSHOT_ZIP)
-
-$(call dist-for-goals, vendor-fake-snapshot, $(SOONG_VENDOR_FAKE_SNAPSHOT_ZIP):fake/$(notdir $(SOONG_VENDOR_FAKE_SNAPSHOT_ZIP)))
-
-else # BOARD_VNDK_VERSION is NOT set to 'current'
-
-.PHONY: vendor-snapshot
-vendor-snapshot: PRIVATE_MAKEFILE := $(current_makefile)
-vendor-snapshot:
- $(call echo-error,$(PRIVATE_MAKEFILE),\
- "CANNOT generate Vendor snapshot. BOARD_VNDK_VERSION must be set to 'current'.")
- exit 1
-
-.PHONY: vendor-fake-snapshot
-vendor-fake-snapshot: PRIVATE_MAKEFILE := $(current_makefile)
-vendor-fake-snapshot:
- $(call echo-error,$(PRIVATE_MAKEFILE),\
- "CANNOT generate Vendor snapshot. BOARD_VNDK_VERSION must be set to 'current'.")
- exit 1
-
-endif # BOARD_VNDK_VERSION
diff --git a/target/board/Android.mk b/target/board/Android.mk
index decc345..8133af9 100644
--- a/target/board/Android.mk
+++ b/target/board/Android.mk
@@ -67,7 +67,6 @@
$(GEN): $(DEVICE_MANIFEST_FILE) $(HOST_OUT_EXECUTABLES)/assemble_vintf
BOARD_SEPOLICY_VERS=$(BOARD_SEPOLICY_VERS) \
PRODUCT_ENFORCE_VINTF_MANIFEST=$(PRODUCT_ENFORCE_VINTF_MANIFEST) \
- PRODUCT_SHIPPING_API_LEVEL=$(PRODUCT_SHIPPING_API_LEVEL) \
$(HOST_OUT_EXECUTABLES)/assemble_vintf -o $@ \
-i $(call normalize-path-list,$(PRIVATE_DEVICE_MANIFEST_FILE))
@@ -99,7 +98,6 @@
$$(GEN): $$(my_fragment_files) $$(HOST_OUT_EXECUTABLES)/assemble_vintf
BOARD_SEPOLICY_VERS=$$(BOARD_SEPOLICY_VERS) \
PRODUCT_ENFORCE_VINTF_MANIFEST=$$(PRODUCT_ENFORCE_VINTF_MANIFEST) \
- PRODUCT_SHIPPING_API_LEVEL=$$(PRODUCT_SHIPPING_API_LEVEL) \
$$(HOST_OUT_EXECUTABLES)/assemble_vintf -o $$@ \
-i $$(call normalize-path-list,$$(PRIVATE_SRC_FILES))
diff --git a/target/product/base_system.mk b/target/product/base_system.mk
index 795d3f8..5b54051 100644
--- a/target/product/base_system.mk
+++ b/target/product/base_system.mk
@@ -280,6 +280,7 @@
storaged \
surfaceflinger \
svc \
+ system-build.prop \
task_profiles.json \
tc \
telecom \
@@ -403,7 +404,6 @@
BugReport \
adb \
adevice \
- art-tools \
atest \
bcc \
bit \
@@ -434,6 +434,21 @@
tz_version_host \
tz_version_host_tzdata_apex \
+# For art-tools, if the dependencies have changed, please sync them to art/Android.bp as well.
+PRODUCT_HOST_PACKAGES += \
+ ahat \
+ dexdump \
+ hprof-conv
+# A subset of the tools are disabled when HOST_PREFER_32_BIT is defined as make reports that
+# they are not supported on host (b/129323791). This is likely due to art_apex disabling host
+# APEX builds when HOST_PREFER_32_BIT is set (b/120617876).
+ifneq ($(HOST_PREFER_32_BIT),true)
+PRODUCT_HOST_PACKAGES += \
+ dexlist \
+ oatdump
+endif
+
+
PRODUCT_PACKAGES += init.usb.rc init.usb.configfs.rc
PRODUCT_PACKAGES += etc_hosts
@@ -444,6 +459,11 @@
PRODUCT_SYSTEM_PROPERTIES += debug.atrace.tags.enableflags=0
PRODUCT_SYSTEM_PROPERTIES += persist.traced.enable=1
+# Include kernel configs.
+PRODUCT_PACKAGES += \
+ approved-ogki-builds.xml \
+ kernel-lifetimes.xml
+
# Packages included only for eng or userdebug builds, previously debug tagged
PRODUCT_PACKAGES_DEBUG := \
adb_keys \
diff --git a/target/product/base_vendor.mk b/target/product/base_vendor.mk
index 1854f97..52e2583 100644
--- a/target/product/base_vendor.mk
+++ b/target/product/base_vendor.mk
@@ -105,3 +105,9 @@
PRODUCT_PACKAGES += \
adb_debug.prop \
userdebug_plat_sepolicy.cil
+
+# On eng or userdebug builds, build in perf-setup-sh by default.
+ifneq (,$(filter userdebug eng,$(TARGET_BUILD_VARIANT)))
+PRODUCT_PACKAGES += \
+ perf-setup-sh
+endif
diff --git a/target/product/build_variables.mk b/target/product/build_variables.mk
index 5fe5333..9fc9ff9 100644
--- a/target/product/build_variables.mk
+++ b/target/product/build_variables.mk
@@ -17,5 +17,8 @@
# This file contains the trunk-stable flags that should be exported to all
# Android targets.
+# Control libbinder client caching
+$(call soong_config_set, libbinder, release_libbinder_client_cache, $(RELEASE_LIBBINDER_CLIENT_CACHE))
+
# Use the configured release of sqlite
$(call soong_config_set, libsqlite3, release_package_libsqlite3, $(RELEASE_PACKAGE_LIBSQLITE3))
diff --git a/target/product/go_defaults_common.mk b/target/product/go_defaults_common.mk
index 5218f29..fd4047a 100644
--- a/target/product/go_defaults_common.mk
+++ b/target/product/go_defaults_common.mk
@@ -37,9 +37,9 @@
# leave less information available via JDWP.
PRODUCT_MINIMIZE_JAVA_DEBUG_INFO := true
-# Disable Scudo outside of eng builds to save RAM.
+# Use the low memory allocator outside of eng builds to save RSS.
ifneq (,$(filter eng, $(TARGET_BUILD_VARIANT)))
- PRODUCT_DISABLE_SCUDO := true
+ MALLOC_LOW_MEMORY := true
endif
# Add the system properties.
diff --git a/target/product/runtime_libart.mk b/target/product/runtime_libart.mk
index dc78368..58234a8 100644
--- a/target/product/runtime_libart.mk
+++ b/target/product/runtime_libart.mk
@@ -178,3 +178,6 @@
PRODUCT_SYSTEM_PROPERTIES += \
dalvik.vm.useartservice=true \
dalvik.vm.enable_pr_dexopt=true
+
+# Copy preopted files from system_b on first boot.
+PRODUCT_SYSTEM_PROPERTIES += ro.cp_system_other_odex=1
diff --git a/target/product/sdk.mk b/target/product/sdk.mk
index 1a07363..3d56a80 100644
--- a/target/product/sdk.mk
+++ b/target/product/sdk.mk
@@ -40,3 +40,6 @@
ifeq ($(WITHOUT_CHECK_API),true)
$(error WITHOUT_CHECK_API cannot be set to true for SDK product builds)
endif
+
+# Include Wear flag values so that Wear-related APIs are build in sdks.
+PRODUCT_RELEASE_CONFIG_MAPS += $(wildcard vendor/google_shared/wear/release/release_config_map.textproto)
diff --git a/tools/aconfig/TEST_MAPPING b/tools/aconfig/TEST_MAPPING
index 448d8cf..15e4187 100644
--- a/tools/aconfig/TEST_MAPPING
+++ b/tools/aconfig/TEST_MAPPING
@@ -98,6 +98,10 @@
{
// aconfig_storage file cpp integration tests
"name": "aconfig_storage_file.test.cpp"
+ },
+ {
+ // aconfig_storage file java integration tests
+ "name": "aconfig_storage_file.test.java"
}
],
"postsubmit": [
diff --git a/tools/aconfig/aconfig/src/codegen/java.rs b/tools/aconfig/aconfig/src/codegen/java.rs
index d3f074a..727f810 100644
--- a/tools/aconfig/aconfig/src/codegen/java.rs
+++ b/tools/aconfig/aconfig/src/codegen/java.rs
@@ -1,18 +1,18 @@
/*
- * 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.
- */
+* 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.
+*/
use anyhow::Result;
use serde::Serialize;
@@ -20,22 +20,24 @@
use std::path::PathBuf;
use tinytemplate::TinyTemplate;
-use aconfig_protos::{ProtoFlagPermission, ProtoFlagState, ProtoParsedFlag};
-
use crate::codegen;
use crate::codegen::CodegenMode;
use crate::commands::OutputFile;
+use aconfig_protos::{ProtoFlagPermission, ProtoFlagState, ProtoParsedFlag};
+use std::collections::HashMap;
pub fn generate_java_code<I>(
package: &str,
parsed_flags_iter: I,
codegen_mode: CodegenMode,
+ flag_ids: HashMap<String, u16>,
+ allow_instrumentation: bool,
) -> Result<Vec<OutputFile>>
where
I: Iterator<Item = ProtoParsedFlag>,
{
let flag_elements: Vec<FlagElement> =
- parsed_flags_iter.map(|pf| create_flag_element(package, &pf)).collect();
+ parsed_flags_iter.map(|pf| create_flag_element(package, &pf, flag_ids.clone())).collect();
let namespace_flags = gen_flags_by_namespace(&flag_elements);
let properties_set: BTreeSet<String> =
flag_elements.iter().map(|fe| format_property_name(&fe.device_config_namespace)).collect();
@@ -43,7 +45,7 @@
let library_exported = codegen_mode == CodegenMode::Exported;
let runtime_lookup_required =
flag_elements.iter().any(|elem| elem.is_read_write) || library_exported;
-
+ let container = (flag_elements.first().expect("zero template flags").container).to_string();
let context = Context {
flag_elements,
namespace_flags,
@@ -52,6 +54,8 @@
properties_set,
package_name: package.to_string(),
library_exported,
+ allow_instrumentation,
+ container,
};
let mut template = TinyTemplate::new();
template.add_template("Flags.java", include_str!("../../templates/Flags.java.template"))?;
@@ -117,6 +121,8 @@
pub properties_set: BTreeSet<String>,
pub package_name: String,
pub library_exported: bool,
+ pub allow_instrumentation: bool,
+ pub container: String,
}
#[derive(Serialize, Debug)]
@@ -127,23 +133,31 @@
#[derive(Serialize, Clone, Debug)]
struct FlagElement {
+ pub container: String,
pub default_value: bool,
pub device_config_namespace: String,
pub device_config_flag: String,
pub flag_name_constant_suffix: String,
+ pub flag_offset: u16,
pub is_read_write: bool,
pub method_name: String,
pub properties: String,
}
-fn create_flag_element(package: &str, pf: &ProtoParsedFlag) -> FlagElement {
+fn create_flag_element(
+ package: &str,
+ pf: &ProtoParsedFlag,
+ flag_offsets: HashMap<String, u16>,
+) -> FlagElement {
let device_config_flag = codegen::create_device_config_ident(package, pf.name())
.expect("values checked at flag parse time");
FlagElement {
+ container: pf.container().to_string(),
default_value: pf.state() == ProtoFlagState::ENABLED,
device_config_namespace: pf.namespace().to_string(),
device_config_flag,
flag_name_constant_suffix: pf.name().to_ascii_uppercase(),
+ flag_offset: *flag_offsets.get(pf.name()).expect("didnt find package offset :("),
is_read_write: pf.permission() == ProtoFlagPermission::READ_WRITE,
method_name: format_java_method_name(pf.name()),
properties: format_property_name(pf.namespace()),
@@ -179,6 +193,7 @@
#[cfg(test)]
mod tests {
use super::*;
+ use crate::commands::assign_flag_ids;
use std::collections::HashMap;
const EXPECTED_FEATUREFLAGS_COMMON_CONTENT: &str = r#"
@@ -477,70 +492,40 @@
let mode = CodegenMode::Production;
let modified_parsed_flags =
crate::commands::modify_parsed_flags_based_on_mode(parsed_flags, mode).unwrap();
- let generated_files =
- generate_java_code(crate::test::TEST_PACKAGE, modified_parsed_flags.into_iter(), mode)
- .unwrap();
+ let flag_ids =
+ assign_flag_ids(crate::test::TEST_PACKAGE, modified_parsed_flags.iter()).unwrap();
+ let generated_files = generate_java_code(
+ crate::test::TEST_PACKAGE,
+ modified_parsed_flags.into_iter(),
+ mode,
+ flag_ids,
+ false,
+ )
+ .unwrap();
let expect_flags_content = EXPECTED_FLAG_COMMON_CONTENT.to_string()
+ r#"
private static FeatureFlags FEATURE_FLAGS = new FeatureFlagsImpl();
}"#;
- let expect_featureflagsimpl_content = r#"
+ let expected_featureflagsmpl_content_0 = r#"
package com.android.aconfig.test;
// TODO(b/303773055): Remove the annotation after access issue is resolved.
import android.compat.annotation.UnsupportedAppUsage;
import android.provider.DeviceConfig;
import android.provider.DeviceConfig.Properties;
+ "#;
+
+ let expected_featureflagsmpl_content_1 = r#"
/** @hide */
public final class FeatureFlagsImpl implements FeatureFlags {
- private static boolean aconfig_test_is_cached = false;
- private static boolean other_namespace_is_cached = false;
+ private static volatile boolean aconfig_test_is_cached = false;
+ private static volatile boolean other_namespace_is_cached = false;
private static boolean disabledRw = false;
private static boolean disabledRwExported = false;
private static boolean disabledRwInOtherNamespace = false;
private static boolean enabledRw = true;
-
-
- private void load_overrides_aconfig_test() {
- try {
- Properties properties = DeviceConfig.getProperties("aconfig_test");
- disabledRw =
- properties.getBoolean(Flags.FLAG_DISABLED_RW, false);
- disabledRwExported =
- properties.getBoolean(Flags.FLAG_DISABLED_RW_EXPORTED, false);
- enabledRw =
- properties.getBoolean(Flags.FLAG_ENABLED_RW, true);
- } catch (NullPointerException e) {
- throw new RuntimeException(
- "Cannot read value from namespace aconfig_test "
- + "from DeviceConfig. It could be that the code using flag "
- + "executed before SettingsProvider initialization. Please use "
- + "fixed read-only flag by adding is_fixed_read_only: true in "
- + "flag declaration.",
- e
- );
- }
- aconfig_test_is_cached = true;
- }
-
- private void load_overrides_other_namespace() {
- try {
- Properties properties = DeviceConfig.getProperties("other_namespace");
- disabledRwInOtherNamespace =
- properties.getBoolean(Flags.FLAG_DISABLED_RW_IN_OTHER_NAMESPACE, false);
- } catch (NullPointerException e) {
- throw new RuntimeException(
- "Cannot read value from namespace other_namespace "
- + "from DeviceConfig. It could be that the code using flag "
- + "executed before SettingsProvider initialization. Please use "
- + "fixed read-only flag by adding is_fixed_read_only: true in "
- + "flag declaration.",
- e
- );
- }
- other_namespace_is_cached = true;
- }
-
+ "#;
+ let expected_featureflagsmpl_content_2 = r#"
@Override
@com.android.aconfig.annotations.AconfigFlagAccessor
@UnsupportedAppUsage
@@ -609,9 +594,213 @@
}
}
"#;
+
+ let expect_featureflagsimpl_content_old = expected_featureflagsmpl_content_0.to_owned()
+ + expected_featureflagsmpl_content_1
+ + r#"
+ private void load_overrides_aconfig_test() {
+ try {
+ Properties properties = DeviceConfig.getProperties("aconfig_test");
+ disabledRw =
+ properties.getBoolean(Flags.FLAG_DISABLED_RW, false);
+ disabledRwExported =
+ properties.getBoolean(Flags.FLAG_DISABLED_RW_EXPORTED, false);
+ enabledRw =
+ properties.getBoolean(Flags.FLAG_ENABLED_RW, true);
+ } catch (NullPointerException e) {
+ throw new RuntimeException(
+ "Cannot read value from namespace aconfig_test "
+ + "from DeviceConfig. It could be that the code using flag "
+ + "executed before SettingsProvider initialization. Please use "
+ + "fixed read-only flag by adding is_fixed_read_only: true in "
+ + "flag declaration.",
+ e
+ );
+ }
+ aconfig_test_is_cached = true;
+ }
+
+ private void load_overrides_other_namespace() {
+ try {
+ Properties properties = DeviceConfig.getProperties("other_namespace");
+ disabledRwInOtherNamespace =
+ properties.getBoolean(Flags.FLAG_DISABLED_RW_IN_OTHER_NAMESPACE, false);
+ } catch (NullPointerException e) {
+ throw new RuntimeException(
+ "Cannot read value from namespace other_namespace "
+ + "from DeviceConfig. It could be that the code using flag "
+ + "executed before SettingsProvider initialization. Please use "
+ + "fixed read-only flag by adding is_fixed_read_only: true in "
+ + "flag declaration.",
+ e
+ );
+ }
+ other_namespace_is_cached = true;
+ }"#
+ + expected_featureflagsmpl_content_2;
+
let mut file_set = HashMap::from([
("com/android/aconfig/test/Flags.java", expect_flags_content.as_str()),
- ("com/android/aconfig/test/FeatureFlagsImpl.java", expect_featureflagsimpl_content),
+ (
+ "com/android/aconfig/test/FeatureFlagsImpl.java",
+ &expect_featureflagsimpl_content_old,
+ ),
+ ("com/android/aconfig/test/FeatureFlags.java", EXPECTED_FEATUREFLAGS_COMMON_CONTENT),
+ (
+ "com/android/aconfig/test/CustomFeatureFlags.java",
+ EXPECTED_CUSTOMFEATUREFLAGS_CONTENT,
+ ),
+ (
+ "com/android/aconfig/test/FakeFeatureFlagsImpl.java",
+ EXPECTED_FAKEFEATUREFLAGSIMPL_CONTENT,
+ ),
+ ]);
+
+ for file in generated_files {
+ let file_path = file.path.to_str().unwrap();
+ assert!(file_set.contains_key(file_path), "Cannot find {}", file_path);
+ assert_eq!(
+ None,
+ crate::test::first_significant_code_diff(
+ file_set.get(file_path).unwrap(),
+ &String::from_utf8(file.contents).unwrap()
+ ),
+ "File {} content is not correct",
+ file_path
+ );
+ file_set.remove(file_path);
+ }
+
+ assert!(file_set.is_empty());
+
+ 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 generated_files = generate_java_code(
+ crate::test::TEST_PACKAGE,
+ modified_parsed_flags.into_iter(),
+ mode,
+ flag_ids,
+ true,
+ )
+ .unwrap();
+
+ let expect_featureflagsimpl_content_new = expected_featureflagsmpl_content_0.to_owned()
+ + r#"
+ import android.aconfig.storage.StorageInternalReader;
+ import android.util.Log;
+ "#
+ + expected_featureflagsmpl_content_1
+ + r#"
+ StorageInternalReader reader;
+ boolean readFromNewStorage;
+
+ private final static String TAG = "AconfigJavaCodegen";
+ private final static String SUCCESS_LOG = "success: %s value matches";
+ private final static String MISMATCH_LOG = "error: %s value mismatch, new storage value is %s, old storage value is %s";
+ private final static String ERROR_LOG = "error: failed to read flag value";
+
+ private void init() {
+ if (reader != null) return;
+ if (DeviceConfig.getBoolean("core_experiments_team_internal", "com.android.providers.settings.storage_test_mission_1", false)) {
+ readFromNewStorage = true;
+ try {
+ reader = new StorageInternalReader("system", "com.android.aconfig.test");
+ } catch (Exception e) {
+ reader = null;
+ }
+ }
+ }
+
+ private void load_overrides_aconfig_test() {
+ try {
+ Properties properties = DeviceConfig.getProperties("aconfig_test");
+ disabledRw =
+ properties.getBoolean(Flags.FLAG_DISABLED_RW, false);
+ disabledRwExported =
+ properties.getBoolean(Flags.FLAG_DISABLED_RW_EXPORTED, false);
+ enabledRw =
+ properties.getBoolean(Flags.FLAG_ENABLED_RW, true);
+ } catch (NullPointerException e) {
+ throw new RuntimeException(
+ "Cannot read value from namespace aconfig_test "
+ + "from DeviceConfig. It could be that the code using flag "
+ + "executed before SettingsProvider initialization. Please use "
+ + "fixed read-only flag by adding is_fixed_read_only: true in "
+ + "flag declaration.",
+ e
+ );
+ }
+ aconfig_test_is_cached = true;
+ init();
+ if (readFromNewStorage && reader != null) {
+ boolean val;
+ try {
+ val = reader.getBooleanFlagValue(1);
+ if (val == disabledRw) {
+ Log.i(TAG, String.format(SUCCESS_LOG, "disabledRw"));
+ } else {
+ Log.i(TAG, String.format(MISMATCH_LOG, "disabledRw", val, disabledRw));
+ }
+ val = reader.getBooleanFlagValue(2);
+ if (val == disabledRwExported) {
+ Log.i(TAG, String.format(SUCCESS_LOG, "disabledRwExported"));
+ } else {
+ Log.i(TAG, String.format(MISMATCH_LOG, "disabledRwExported", val, disabledRwExported));
+ }
+ val = reader.getBooleanFlagValue(8);
+ if (val == enabledRw) {
+ Log.i(TAG, String.format(SUCCESS_LOG, "enabledRw"));
+ } else {
+ Log.i(TAG, String.format(MISMATCH_LOG, "enabledRw", val, enabledRw));
+ }
+ } catch (Exception e) {
+ Log.e(TAG, ERROR_LOG, e);
+ }
+ }
+ }
+
+ private void load_overrides_other_namespace() {
+ try {
+ Properties properties = DeviceConfig.getProperties("other_namespace");
+ disabledRwInOtherNamespace =
+ properties.getBoolean(Flags.FLAG_DISABLED_RW_IN_OTHER_NAMESPACE, false);
+ } catch (NullPointerException e) {
+ throw new RuntimeException(
+ "Cannot read value from namespace other_namespace "
+ + "from DeviceConfig. It could be that the code using flag "
+ + "executed before SettingsProvider initialization. Please use "
+ + "fixed read-only flag by adding is_fixed_read_only: true in "
+ + "flag declaration.",
+ e
+ );
+ }
+ other_namespace_is_cached = true;
+ init();
+ if (readFromNewStorage && reader != null) {
+ boolean val;
+ try {
+ val = reader.getBooleanFlagValue(3);
+ if (val == disabledRwInOtherNamespace) {
+ Log.i(TAG, String.format(SUCCESS_LOG, "disabledRwInOtherNamespace"));
+ } else {
+ Log.i(TAG, String.format(MISMATCH_LOG, "disabledRwInOtherNamespace", val, disabledRwInOtherNamespace));
+ }
+ } catch (Exception e) {
+ Log.e(TAG, ERROR_LOG, e);
+ }
+ }
+ }"# + expected_featureflagsmpl_content_2;
+
+ let mut file_set = HashMap::from([
+ ("com/android/aconfig/test/Flags.java", expect_flags_content.as_str()),
+ (
+ "com/android/aconfig/test/FeatureFlagsImpl.java",
+ &expect_featureflagsimpl_content_new,
+ ),
("com/android/aconfig/test/FeatureFlags.java", EXPECTED_FEATUREFLAGS_COMMON_CONTENT),
(
"com/android/aconfig/test/CustomFeatureFlags.java",
@@ -647,9 +836,16 @@
let mode = CodegenMode::Exported;
let modified_parsed_flags =
crate::commands::modify_parsed_flags_based_on_mode(parsed_flags, mode).unwrap();
- let generated_files =
- generate_java_code(crate::test::TEST_PACKAGE, modified_parsed_flags.into_iter(), mode)
- .unwrap();
+ let flag_ids =
+ assign_flag_ids(crate::test::TEST_PACKAGE, modified_parsed_flags.iter()).unwrap();
+ let generated_files = generate_java_code(
+ crate::test::TEST_PACKAGE,
+ modified_parsed_flags.into_iter(),
+ mode,
+ flag_ids,
+ true,
+ )
+ .unwrap();
let expect_flags_content = r#"
package com.android.aconfig.test;
@@ -690,7 +886,7 @@
import android.provider.DeviceConfig.Properties;
/** @hide */
public final class FeatureFlagsImpl implements FeatureFlags {
- private static boolean aconfig_test_is_cached = false;
+ private static volatile boolean aconfig_test_is_cached = false;
private static boolean disabledRwExported = false;
private static boolean enabledFixedRoExported = false;
private static boolean enabledRoExported = false;
@@ -833,9 +1029,16 @@
let mode = CodegenMode::Test;
let modified_parsed_flags =
crate::commands::modify_parsed_flags_based_on_mode(parsed_flags, mode).unwrap();
- let generated_files =
- generate_java_code(crate::test::TEST_PACKAGE, modified_parsed_flags.into_iter(), mode)
- .unwrap();
+ let flag_ids =
+ assign_flag_ids(crate::test::TEST_PACKAGE, modified_parsed_flags.iter()).unwrap();
+ let generated_files = generate_java_code(
+ crate::test::TEST_PACKAGE,
+ modified_parsed_flags.into_iter(),
+ mode,
+ flag_ids,
+ true,
+ )
+ .unwrap();
let expect_flags_content = EXPECTED_FLAG_COMMON_CONTENT.to_string()
+ r#"
@@ -850,69 +1053,58 @@
"#;
let expect_featureflagsimpl_content = r#"
package com.android.aconfig.test;
- // TODO(b/303773055): Remove the annotation after access issue is resolved.
- import android.compat.annotation.UnsupportedAppUsage;
/** @hide */
public final class FeatureFlagsImpl implements FeatureFlags {
@Override
@com.android.aconfig.annotations.AconfigFlagAccessor
- @UnsupportedAppUsage
public boolean disabledRo() {
throw new UnsupportedOperationException(
"Method is not implemented.");
}
@Override
@com.android.aconfig.annotations.AconfigFlagAccessor
- @UnsupportedAppUsage
public boolean disabledRw() {
throw new UnsupportedOperationException(
"Method is not implemented.");
}
@Override
@com.android.aconfig.annotations.AconfigFlagAccessor
- @UnsupportedAppUsage
public boolean disabledRwExported() {
throw new UnsupportedOperationException(
"Method is not implemented.");
}
@Override
@com.android.aconfig.annotations.AconfigFlagAccessor
- @UnsupportedAppUsage
public boolean disabledRwInOtherNamespace() {
throw new UnsupportedOperationException(
"Method is not implemented.");
}
@Override
@com.android.aconfig.annotations.AconfigFlagAccessor
- @UnsupportedAppUsage
public boolean enabledFixedRo() {
throw new UnsupportedOperationException(
"Method is not implemented.");
}
@Override
@com.android.aconfig.annotations.AconfigFlagAccessor
- @UnsupportedAppUsage
public boolean enabledFixedRoExported() {
throw new UnsupportedOperationException(
"Method is not implemented.");
}
@Override
@com.android.aconfig.annotations.AconfigFlagAccessor
- @UnsupportedAppUsage
public boolean enabledRo() {
throw new UnsupportedOperationException(
"Method is not implemented.");
}
@Override
@com.android.aconfig.annotations.AconfigFlagAccessor
- @UnsupportedAppUsage
public boolean enabledRoExported() {
throw new UnsupportedOperationException(
"Method is not implemented.");
}
@Override
@com.android.aconfig.annotations.AconfigFlagAccessor
- @UnsupportedAppUsage
public boolean enabledRw() {
throw new UnsupportedOperationException(
"Method is not implemented.");
@@ -958,9 +1150,16 @@
let mode = CodegenMode::ForceReadOnly;
let modified_parsed_flags =
crate::commands::modify_parsed_flags_based_on_mode(parsed_flags, mode).unwrap();
- let generated_files =
- generate_java_code(crate::test::TEST_PACKAGE, modified_parsed_flags.into_iter(), mode)
- .unwrap();
+ let flag_ids =
+ assign_flag_ids(crate::test::TEST_PACKAGE, modified_parsed_flags.iter()).unwrap();
+ let generated_files = generate_java_code(
+ crate::test::TEST_PACKAGE,
+ modified_parsed_flags.into_iter(),
+ mode,
+ flag_ids,
+ true,
+ )
+ .unwrap();
let expect_featureflags_content = r#"
package com.android.aconfig.test;
// TODO(b/303773055): Remove the annotation after access issue is resolved.
diff --git a/tools/aconfig/aconfig/src/commands.rs b/tools/aconfig/aconfig/src/commands.rs
index 6945fd4..6d1c2f5 100644
--- a/tools/aconfig/aconfig/src/commands.rs
+++ b/tools/aconfig/aconfig/src/commands.rs
@@ -191,15 +191,25 @@
Ok(output)
}
-pub fn create_java_lib(mut input: Input, codegen_mode: CodegenMode) -> Result<Vec<OutputFile>> {
+pub fn create_java_lib(
+ mut input: Input,
+ codegen_mode: CodegenMode,
+ allow_instrumentation: bool,
+) -> Result<Vec<OutputFile>> {
let parsed_flags = input.try_parse_flags()?;
let modified_parsed_flags = modify_parsed_flags_based_on_mode(parsed_flags, codegen_mode)?;
let Some(package) = find_unique_package(&modified_parsed_flags) else {
bail!("no parsed flags, or the parsed flags use different packages");
};
let package = package.to_string();
- let _flag_ids = assign_flag_ids(&package, modified_parsed_flags.iter())?;
- generate_java_code(&package, modified_parsed_flags.into_iter(), codegen_mode)
+ let flag_ids = assign_flag_ids(&package, modified_parsed_flags.iter())?;
+ generate_java_code(
+ &package,
+ modified_parsed_flags.into_iter(),
+ codegen_mode,
+ flag_ids,
+ allow_instrumentation,
+ )
}
pub fn create_cpp_lib(
diff --git a/tools/aconfig/aconfig/src/main.rs b/tools/aconfig/aconfig/src/main.rs
index 72be1c9..7ec272f 100644
--- a/tools/aconfig/aconfig/src/main.rs
+++ b/tools/aconfig/aconfig/src/main.rs
@@ -72,6 +72,12 @@
.long("mode")
.value_parser(EnumValueParser::<CodegenMode>::new())
.default_value("production"),
+ )
+ .arg(
+ Arg::new("allow-instrumentation")
+ .long("allow-instrumentation")
+ .value_parser(clap::value_parser!(bool))
+ .default_value("false"),
),
)
.subcommand(
@@ -237,8 +243,10 @@
Some(("create-java-lib", sub_matches)) => {
let cache = open_single_file(sub_matches, "cache")?;
let mode = get_required_arg::<CodegenMode>(sub_matches, "mode")?;
- let generated_files =
- commands::create_java_lib(cache, *mode).context("failed to create java lib")?;
+ let allow_instrumentation =
+ get_required_arg::<bool>(sub_matches, "allow-instrumentation")?;
+ let generated_files = commands::create_java_lib(cache, *mode, *allow_instrumentation)
+ .context("failed to create java lib")?;
let dir = PathBuf::from(get_required_arg::<String>(sub_matches, "out")?);
generated_files
.iter()
diff --git a/tools/aconfig/aconfig/templates/FeatureFlagsImpl.java.template b/tools/aconfig/aconfig/templates/FeatureFlagsImpl.java.template
index 63c4f2d..97d1254 100644
--- a/tools/aconfig/aconfig/templates/FeatureFlagsImpl.java.template
+++ b/tools/aconfig/aconfig/templates/FeatureFlagsImpl.java.template
@@ -1,18 +1,28 @@
package {package_name};
+{{ -if not is_test_mode }}
{{ if not library_exported- }}
// TODO(b/303773055): Remove the annotation after access issue is resolved.
import android.compat.annotation.UnsupportedAppUsage;
{{ -endif }}
-{{ -if not is_test_mode }}
+
{{ -if runtime_lookup_required }}
import android.provider.DeviceConfig;
import android.provider.DeviceConfig.Properties;
-{{ endif }}
+
+
+{{ -if not library_exported }}
+{{ -if allow_instrumentation }}
+import android.aconfig.storage.StorageInternalReader;
+import android.util.Log;
+{{ -endif }}
+{{ -endif }}
+
+{{ -endif }}
/** @hide */
public final class FeatureFlagsImpl implements FeatureFlags \{
{{ -if runtime_lookup_required }}
{{ -for namespace_with_flags in namespace_flags }}
- private static boolean {namespace_with_flags.namespace}_is_cached = false;
+ private static volatile boolean {namespace_with_flags.namespace}_is_cached = false;
{{ -endfor- }}
{{ for flag in flag_elements }}
@@ -20,6 +30,30 @@
private static boolean {flag.method_name} = {flag.default_value};
{{ -endif }}
{{ -endfor }}
+{{ -if not library_exported }}
+{{ -if allow_instrumentation }}
+ StorageInternalReader reader;
+ boolean readFromNewStorage;
+
+ private final static String TAG = "AconfigJavaCodegen";
+ private final static String SUCCESS_LOG = "success: %s value matches";
+ private final static String MISMATCH_LOG = "error: %s value mismatch, new storage value is %s, old storage value is %s";
+ private final static String ERROR_LOG = "error: failed to read flag value";
+
+ private void init() \{
+ if (reader != null) return;
+ if (DeviceConfig.getBoolean("core_experiments_team_internal", "com.android.providers.settings.storage_test_mission_1", false)) \{
+ readFromNewStorage = true;
+ try \{
+ reader = new StorageInternalReader("{container}", "{package_name}");
+ } catch (Exception e) \{
+ reader = null;
+ }
+ }
+ }
+
+{{ -endif }}
+{{ -endif }}
{{ for namespace_with_flags in namespace_flags }}
private void load_overrides_{namespace_with_flags.namespace}() \{
try \{
@@ -41,6 +75,29 @@
);
}
{namespace_with_flags.namespace}_is_cached = true;
+{{ -if not library_exported }}
+{{ -if allow_instrumentation }}
+ init();
+ if (readFromNewStorage && reader != null) \{
+ boolean val;
+ try \{
+{{ -for flag in namespace_with_flags.flags }}
+{{ -if flag.is_read_write }}
+
+ val = reader.getBooleanFlagValue({flag.flag_offset});
+ if (val == {flag.method_name}) \{
+ Log.i(TAG, String.format(SUCCESS_LOG, "{flag.method_name}"));
+ } else \{
+ Log.i(TAG, String.format(MISMATCH_LOG, "{flag.method_name}", val, {flag.method_name}));
+ }
+{{ -endif }}
+{{ -endfor }}
+ } catch (Exception e) \{
+ Log.e(TAG, ERROR_LOG, e);
+ }
+ }
+{{ -endif }}
+{{ -endif }}
}
{{ endfor- }}
{{ -endif }}{#- end of runtime_lookup_required #}
@@ -70,7 +127,6 @@
@Override
{{ -if not library_exported }}
@com.android.aconfig.annotations.AconfigFlagAccessor
- @UnsupportedAppUsage
{{ -endif }}
public boolean {flag.method_name}() \{
throw new UnsupportedOperationException(
diff --git a/tools/aconfig/aconfig_device_paths/Android.bp b/tools/aconfig/aconfig_device_paths/Android.bp
index 2c771e0..2d943de 100644
--- a/tools/aconfig/aconfig_device_paths/Android.bp
+++ b/tools/aconfig/aconfig_device_paths/Android.bp
@@ -42,10 +42,11 @@
srcs: ["src/DevicePathsTemplate.java"],
out: ["DevicePaths.java"],
tool_files: ["partition_aconfig_flags_paths.txt"],
- cmd: "sed -e '/TEMPLATE/{r$(location partition_aconfig_flags_paths.txt)' -e 'd}' $(in) > $(out)"
+ cmd: "sed -e '/TEMPLATE/{r$(location partition_aconfig_flags_paths.txt)' -e 'd}' $(in) > $(out)",
}
java_library {
name: "aconfig_device_paths_java",
srcs: [":libaconfig_java_device_paths_src"],
+ sdk_version: "core_current",
}
diff --git a/tools/aconfig/aconfig_device_paths/src/DevicePathsTemplate.java b/tools/aconfig/aconfig_device_paths/src/DevicePathsTemplate.java
index f27b9bd..16355a3 100644
--- a/tools/aconfig/aconfig_device_paths/src/DevicePathsTemplate.java
+++ b/tools/aconfig/aconfig_device_paths/src/DevicePathsTemplate.java
@@ -36,7 +36,7 @@
* Returns the list of all on-device aconfig protos paths.
* @hide
*/
- public List<String> parsedFlagsProtoPaths() {
+ public static List<String> parsedFlagsProtoPaths() {
ArrayList<String> paths = new ArrayList(Arrays.asList(PATHS));
File apexDirectory = new File(APEX_DIR);
diff --git a/tools/aconfig/aconfig_storage_file/Android.bp b/tools/aconfig/aconfig_storage_file/Android.bp
index e066e31..40b4464 100644
--- a/tools/aconfig/aconfig_storage_file/Android.bp
+++ b/tools/aconfig/aconfig_storage_file/Android.bp
@@ -137,3 +137,29 @@
min_sdk_version: "29",
double_loadable: true,
}
+
+// storage file parse api java library
+java_library {
+ name: "aconfig_storage_file_java",
+ srcs: [
+ "srcs/**/*.java",
+ ],
+ sdk_version: "core_current",
+ min_sdk_version: "29",
+ host_supported: true,
+ apex_available: [
+ "//apex_available:platform",
+ "//apex_available:anyapex",
+ ],
+}
+
+// storage file parse api java library for core library
+java_library {
+ name: "aconfig_storage_file_java_none",
+ srcs: [
+ "srcs/**/*.java",
+ ],
+ sdk_version: "none",
+ system_modules: "core-all-system-modules",
+ host_supported: true,
+}
diff --git a/tools/aconfig/aconfig_storage_file/srcs/android/aconfig/storage/AconfigStorageException.java b/tools/aconfig/aconfig_storage_file/srcs/android/aconfig/storage/AconfigStorageException.java
new file mode 100644
index 0000000..86a75f2
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_file/srcs/android/aconfig/storage/AconfigStorageException.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.aconfig.storage;
+
+public class AconfigStorageException extends RuntimeException {
+ public AconfigStorageException(String msg) {
+ super(msg);
+ }
+
+ public AconfigStorageException(String msg, Throwable cause) {
+ super(msg, cause);
+ }
+}
diff --git a/tools/aconfig/aconfig_storage_file/srcs/android/aconfig/storage/ByteBufferReader.java b/tools/aconfig/aconfig_storage_file/srcs/android/aconfig/storage/ByteBufferReader.java
new file mode 100644
index 0000000..1c72364
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_file/srcs/android/aconfig/storage/ByteBufferReader.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.aconfig.storage;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.charset.StandardCharsets;
+
+public class ByteBufferReader {
+
+ private ByteBuffer mByteBuffer;
+
+ public ByteBufferReader(ByteBuffer byteBuffer) {
+ this.mByteBuffer = byteBuffer;
+ this.mByteBuffer.order(ByteOrder.LITTLE_ENDIAN);
+ }
+
+ public int readByte() {
+ return Byte.toUnsignedInt(mByteBuffer.get());
+ }
+
+ public int readShort() {
+ return Short.toUnsignedInt(mByteBuffer.getShort());
+ }
+
+ public int readInt() {
+ return this.mByteBuffer.getInt();
+ }
+
+ public String readString() {
+ int length = readInt();
+ byte[] bytes = new byte[length];
+ mByteBuffer.get(bytes, 0, length);
+ return new String(bytes, StandardCharsets.UTF_8);
+ }
+
+ public void position(int newPosition) {
+ mByteBuffer.position(newPosition);
+ }
+}
diff --git a/tools/aconfig/aconfig_storage_file/srcs/android/aconfig/storage/FileType.java b/tools/aconfig/aconfig_storage_file/srcs/android/aconfig/storage/FileType.java
new file mode 100644
index 0000000..b0b1b9b
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_file/srcs/android/aconfig/storage/FileType.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.aconfig.storage;
+
+public enum FileType {
+ PACKAGE_MAP(0),
+ FLAG_MAP(1),
+ FLAG_VAL(2),
+ FLAG_INFO(3);
+
+ public final int type;
+
+ FileType(int type) {
+ this.type = type;
+ }
+
+ public static FileType fromInt(int index) {
+ switch (index) {
+ case 0:
+ return PACKAGE_MAP;
+ case 1:
+ return FLAG_MAP;
+ case 2:
+ return FLAG_VAL;
+ case 3:
+ return FLAG_INFO;
+ default:
+ return null;
+ }
+ }
+}
diff --git a/tools/aconfig/aconfig_storage_file/srcs/android/aconfig/storage/FlagTable.java b/tools/aconfig/aconfig_storage_file/srcs/android/aconfig/storage/FlagTable.java
new file mode 100644
index 0000000..e85fdee
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_file/srcs/android/aconfig/storage/FlagTable.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.aconfig.storage;
+
+import java.nio.ByteBuffer;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+
+public class FlagTable {
+
+ private Header mHeader;
+ private Map<String, Node> mNodeMap;
+
+ public static FlagTable fromBytes(ByteBuffer bytes) {
+ FlagTable flagTable = new FlagTable();
+ ByteBufferReader reader = new ByteBufferReader(bytes);
+ Header header = Header.fromBytes(reader);
+ flagTable.mHeader = header;
+ flagTable.mNodeMap = new HashMap(TableUtils.getTableSize(header.mNumFlags));
+ reader.position(header.mNodeOffset);
+ for (int i = 0; i < header.mNumFlags; i++) {
+ Node node = Node.fromBytes(reader);
+ flagTable.mNodeMap.put(makeKey(node.mPackageId, node.mFlagName), node);
+ }
+ return flagTable;
+ }
+
+ public Node get(int packageId, String flagName) {
+ return mNodeMap.get(makeKey(packageId, flagName));
+ }
+
+ public Header getHeader() {
+ return mHeader;
+ }
+
+ private static String makeKey(int packageId, String flagName) {
+ StringBuilder ret = new StringBuilder();
+ return ret.append(packageId).append('/').append(flagName).toString();
+ }
+
+ public static class Header {
+
+ private int mVersion;
+ private String mContainer;
+ private FileType mFileType;
+ private int mFileSize;
+ private int mNumFlags;
+ private int mBucketOffset;
+ private int mNodeOffset;
+
+ public static Header fromBytes(ByteBufferReader reader) {
+ Header header = new Header();
+ header.mVersion = reader.readInt();
+ header.mContainer = reader.readString();
+ header.mFileType = FileType.fromInt(reader.readByte());
+ header.mFileSize = reader.readInt();
+ header.mNumFlags = reader.readInt();
+ header.mBucketOffset = reader.readInt();
+ header.mNodeOffset = reader.readInt();
+
+ if (header.mFileType != FileType.FLAG_MAP) {
+ throw new AconfigStorageException("binary file is not a flag map");
+ }
+
+ return header;
+ }
+
+ public int getVersion() {
+ return mVersion;
+ }
+
+ public String getContainer() {
+ return mContainer;
+ }
+
+ public FileType getFileType() {
+ return mFileType;
+ }
+
+ public int getFileSize() {
+ return mFileSize;
+ }
+
+ public int getNumFlags() {
+ return mNumFlags;
+ }
+
+ public int getBucketOffset() {
+ return mBucketOffset;
+ }
+
+ public int getNodeOffset() {
+ return mNodeOffset;
+ }
+ }
+
+ public static class Node {
+
+ private String mFlagName;
+ private FlagType mFlagType;
+ private int mPackageId;
+ private int mFlagIndex;
+ private int mNextOffset;
+
+ public static Node fromBytes(ByteBufferReader reader) {
+ Node node = new Node();
+ node.mPackageId = reader.readInt();
+ node.mFlagName = reader.readString();
+ node.mFlagType = FlagType.fromInt(reader.readShort());
+ node.mFlagIndex = reader.readShort();
+ node.mNextOffset = reader.readInt();
+ node.mNextOffset = node.mNextOffset == 0 ? -1 : node.mNextOffset;
+ return node;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mFlagName, mFlagType, mPackageId, mFlagIndex, mNextOffset);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+
+ if (obj == null || !(obj instanceof Node)) {
+ return false;
+ }
+
+ Node other = (Node) obj;
+ return Objects.equals(mFlagName, other.mFlagName)
+ && Objects.equals(mFlagType, other.mFlagType)
+ && mPackageId == other.mPackageId
+ && mFlagIndex == other.mFlagIndex
+ && mNextOffset == other.mNextOffset;
+ }
+
+ public String getFlagName() {
+ return mFlagName;
+ }
+
+ public FlagType getFlagType() {
+ return mFlagType;
+ }
+
+ public int getPackageId() {
+ return mPackageId;
+ }
+
+ public int getFlagIndex() {
+ return mFlagIndex;
+ }
+
+ public int getNextOffset() {
+ return mNextOffset;
+ }
+ }
+}
diff --git a/tools/aconfig/aconfig_storage_file/srcs/android/aconfig/storage/FlagType.java b/tools/aconfig/aconfig_storage_file/srcs/android/aconfig/storage/FlagType.java
new file mode 100644
index 0000000..385e2d9
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_file/srcs/android/aconfig/storage/FlagType.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.aconfig.storage;
+
+public enum FlagType {
+ ReadWriteBoolean (0),
+ ReadOnlyBoolean(1),
+ FixedReadOnlyBoolean(2);
+
+ public final int type;
+
+ FlagType(int type) {
+ this.type = type;
+ }
+
+ public static FlagType fromInt(int index) {
+ switch (index) {
+ case 0:
+ return ReadWriteBoolean;
+ case 1:
+ return ReadOnlyBoolean;
+ case 2:
+ return FixedReadOnlyBoolean;
+ default:
+ return null;
+ }
+ }
+}
diff --git a/tools/aconfig/aconfig_storage_file/srcs/android/aconfig/storage/FlagValueList.java b/tools/aconfig/aconfig_storage_file/srcs/android/aconfig/storage/FlagValueList.java
new file mode 100644
index 0000000..0ddc147
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_file/srcs/android/aconfig/storage/FlagValueList.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.aconfig.storage;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
+
+public class FlagValueList {
+
+ private Header mHeader;
+ private List<Boolean> mList;
+
+ private int mSize;
+
+ public static FlagValueList fromBytes(ByteBuffer bytes) {
+ FlagValueList flagValueList = new FlagValueList();
+ ByteBufferReader reader = new ByteBufferReader(bytes);
+ Header header = Header.fromBytes(reader);
+ flagValueList.mHeader = header;
+ flagValueList.mList = new ArrayList(header.mNumFlags);
+ reader.position(header.mBooleanValueOffset);
+ for (int i = 0; i < header.mNumFlags; i++) {
+ boolean val = reader.readByte() == 1;
+ flagValueList.mList.add(val);
+ }
+ flagValueList.mSize = flagValueList.mList.size();
+ return flagValueList;
+ }
+
+ public boolean get(int index) {
+ return mList.get(index);
+ }
+
+ public Header getHeader() {
+ return mHeader;
+ }
+
+ public int size() {
+ return mSize;
+ }
+
+ public static class Header {
+
+ private int mVersion;
+ private String mContainer;
+ private FileType mFileType;
+ private int mFileSize;
+ private int mNumFlags;
+ private int mBooleanValueOffset;
+
+ public static Header fromBytes(ByteBufferReader reader) {
+ Header header = new Header();
+ header.mVersion = reader.readInt();
+ header.mContainer = reader.readString();
+ header.mFileType = FileType.fromInt(reader.readByte());
+ header.mFileSize = reader.readInt();
+ header.mNumFlags = reader.readInt();
+ header.mBooleanValueOffset = reader.readInt();
+
+ if (header.mFileType != FileType.FLAG_VAL) {
+ throw new AconfigStorageException("binary file is not a flag value file");
+ }
+
+ return header;
+ }
+
+ public int getVersion() {
+ return mVersion;
+ }
+
+ public String getContainer() {
+ return mContainer;
+ }
+
+ public FileType getFileType() {
+ return mFileType;
+ }
+
+ public int getFileSize() {
+ return mFileSize;
+ }
+
+ public int getNumFlags() {
+ return mNumFlags;
+ }
+
+ public int getBooleanValueOffset() {
+ return mBooleanValueOffset;
+ }
+ }
+}
diff --git a/tools/aconfig/aconfig_storage_file/srcs/android/aconfig/storage/PackageTable.java b/tools/aconfig/aconfig_storage_file/srcs/android/aconfig/storage/PackageTable.java
new file mode 100644
index 0000000..d04e1ac
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_file/srcs/android/aconfig/storage/PackageTable.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.aconfig.storage;
+
+import java.nio.ByteBuffer;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+
+public class PackageTable {
+
+ private Header mHeader;
+ private Map<String, Node> mNodeMap;
+
+ public static PackageTable fromBytes(ByteBuffer bytes) {
+ PackageTable packageTable = new PackageTable();
+ ByteBufferReader reader = new ByteBufferReader(bytes);
+ Header header = Header.fromBytes(reader);
+ packageTable.mHeader = header;
+ packageTable.mNodeMap = new HashMap(TableUtils.getTableSize(header.mNumPackages));
+ reader.position(header.mNodeOffset);
+ for (int i = 0; i < header.mNumPackages; i++) {
+ Node node = Node.fromBytes(reader);
+ packageTable.mNodeMap.put(node.mPackageName, node);
+ }
+ return packageTable;
+ }
+
+ public Node get(String packageName) {
+ return mNodeMap.get(packageName);
+ }
+
+ public Header getHeader() {
+ return mHeader;
+ }
+
+ public static class Header {
+
+ private int mVersion;
+ private String mContainer;
+ private FileType mFileType;
+ private int mFileSize;
+ private int mNumPackages;
+ private int mBucketOffset;
+ private int mNodeOffset;
+
+ public static Header fromBytes(ByteBufferReader reader) {
+ Header header = new Header();
+ header.mVersion = reader.readInt();
+ header.mContainer = reader.readString();
+ header.mFileType = FileType.fromInt(reader.readByte());
+ header.mFileSize = reader.readInt();
+ header.mNumPackages = reader.readInt();
+ header.mBucketOffset = reader.readInt();
+ header.mNodeOffset = reader.readInt();
+
+ if (header.mFileType != FileType.PACKAGE_MAP) {
+ throw new AconfigStorageException("binary file is not a package map");
+ }
+
+ return header;
+ }
+
+ public int getVersion() {
+ return mVersion;
+ }
+
+ public String getContainer() {
+ return mContainer;
+ }
+
+ public FileType getFileType() {
+ return mFileType;
+ }
+
+ public int getFileSize() {
+ return mFileSize;
+ }
+
+ public int getNumPackages() {
+ return mNumPackages;
+ }
+
+ public int getBucketOffset() {
+ return mBucketOffset;
+ }
+
+ public int getNodeOffset() {
+ return mNodeOffset;
+ }
+ }
+
+ public static class Node {
+
+ private String mPackageName;
+ private int mPackageId;
+ private int mBooleanStartIndex;
+ private int mNextOffset;
+
+ public static Node fromBytes(ByteBufferReader reader) {
+ Node node = new Node();
+ node.mPackageName = reader.readString();
+ node.mPackageId = reader.readInt();
+ node.mBooleanStartIndex = reader.readInt();
+ node.mNextOffset = reader.readInt();
+ node.mNextOffset = node.mNextOffset == 0 ? -1 : node.mNextOffset;
+ return node;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mPackageName, mPackageId, mBooleanStartIndex, mNextOffset);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+
+ if (obj == null || !(obj instanceof Node)) {
+ return false;
+ }
+
+ Node other = (Node) obj;
+ return Objects.equals(mPackageName, other.mPackageName)
+ && mPackageId == other.mPackageId
+ && mBooleanStartIndex == other.mBooleanStartIndex
+ && mNextOffset == other.mNextOffset;
+ }
+
+ public String getPackageName() {
+ return mPackageName;
+ }
+
+ public int getPackageId() {
+ return mPackageId;
+ }
+
+ public int getBooleanStartIndex() {
+ return mBooleanStartIndex;
+ }
+
+ public int getNextOffset() {
+ return mNextOffset;
+ }
+ }
+}
diff --git a/tools/aconfig/aconfig_storage_file/srcs/android/aconfig/storage/TableUtils.java b/tools/aconfig/aconfig_storage_file/srcs/android/aconfig/storage/TableUtils.java
new file mode 100644
index 0000000..714b53b
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_file/srcs/android/aconfig/storage/TableUtils.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.aconfig.storage;
+
+public class TableUtils {
+
+ private static final int[] HASH_PRIMES =
+ new int[] {
+ 7,
+ 17,
+ 29,
+ 53,
+ 97,
+ 193,
+ 389,
+ 769,
+ 1543,
+ 3079,
+ 6151,
+ 12289,
+ 24593,
+ 49157,
+ 98317,
+ 196613,
+ 393241,
+ 786433,
+ 1572869,
+ 3145739,
+ 6291469,
+ 12582917,
+ 25165843,
+ 50331653,
+ 100663319,
+ 201326611,
+ 402653189,
+ 805306457,
+ 1610612741
+ };
+
+ public static int getTableSize(int numEntries) {
+ for (int i : HASH_PRIMES) {
+ if (i < 2 * numEntries) continue;
+ return i;
+ }
+ throw new AconfigStorageException("Number of items in a hash table exceeds limit");
+ }
+}
diff --git a/tools/aconfig/aconfig_storage_file/tests/Android.bp b/tools/aconfig/aconfig_storage_file/tests/Android.bp
index 26b7800..e2e225d 100644
--- a/tools/aconfig/aconfig_storage_file/tests/Android.bp
+++ b/tools/aconfig/aconfig_storage_file/tests/Android.bp
@@ -1,4 +1,3 @@
-
cc_test {
name: "aconfig_storage_file.test.cpp",
team: "trendy_team_android_core_experiments",
@@ -21,3 +20,26 @@
"general-tests",
],
}
+
+android_test {
+ name: "aconfig_storage_file.test.java",
+ team: "trendy_team_android_core_experiments",
+ srcs: [
+ "srcs/**/*.java",
+ ],
+ static_libs: [
+ "androidx.test.runner",
+ "junit",
+ ],
+ test_config: "AndroidStorageJaveTest.xml",
+ certificate: "platform",
+ data: [
+ "package.map",
+ "flag.map",
+ "flag.val",
+ "flag.info",
+ ],
+ test_suites: [
+ "general-tests",
+ ],
+}
diff --git a/tools/aconfig/aconfig_storage_file/tests/AndroidManifest.xml b/tools/aconfig/aconfig_storage_file/tests/AndroidManifest.xml
new file mode 100644
index 0000000..5e01879
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_file/tests/AndroidManifest.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2024 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.aconfig.storage.test">
+ <application>
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.aconfig.storage.test" />
+
+</manifest>
diff --git a/tools/aconfig/aconfig_storage_file/tests/AndroidStorageJaveTest.xml b/tools/aconfig/aconfig_storage_file/tests/AndroidStorageJaveTest.xml
new file mode 100644
index 0000000..2d52d44
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_file/tests/AndroidStorageJaveTest.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2024 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<configuration description="Test aconfig storage java tests">
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="aconfig_storage_file.test.java.apk" />
+ </target_preparer>
+ <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
+ <option name="cleanup" value="true" />
+ <option name="push" value="package.map->/data/local/tmp/aconfig_storage_file_test_java/testdata/package.map" />
+ <option name="push" value="flag.map->/data/local/tmp/aconfig_storage_file_test_java/testdata/flag.map" />
+ <option name="push" value="flag.val->/data/local/tmp/aconfig_storage_file_test_java/testdata/flag.val" />
+ <option name="push" value="flag.info->/data/local/tmp/aconfig_storage_file_test_java/testdata/flag.info" />
+ </target_preparer>
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="android.aconfig.storage.test" />
+ <option name="runtime-hint" value="1m" />
+ </test>
+</configuration>
\ No newline at end of file
diff --git a/tools/aconfig/aconfig_storage_file/tests/srcs/ByteBufferReaderTest.java b/tools/aconfig/aconfig_storage_file/tests/srcs/ByteBufferReaderTest.java
new file mode 100644
index 0000000..66a8166
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_file/tests/srcs/ByteBufferReaderTest.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.aconfig.storage.test;
+
+import static org.junit.Assert.assertEquals;
+
+import android.aconfig.storage.ByteBufferReader;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.charset.StandardCharsets;
+
+@RunWith(JUnit4.class)
+public class ByteBufferReaderTest {
+
+ @Test
+ public void testReadByte() {
+ ByteBuffer buffer = ByteBuffer.allocate(1);
+ byte expect = 10;
+ buffer.put(expect).rewind();
+
+ ByteBufferReader reader = new ByteBufferReader(buffer);
+ assertEquals(expect, reader.readByte());
+ }
+
+ @Test
+ public void testReadShort() {
+ ByteBuffer buffer = ByteBuffer.allocate(4);
+ buffer.order(ByteOrder.LITTLE_ENDIAN);
+ short expect = Short.MAX_VALUE;
+ buffer.putShort(expect).rewind();
+
+ ByteBufferReader reader = new ByteBufferReader(buffer);
+ assertEquals(expect, reader.readShort());
+ }
+
+ @Test
+ public void testReadInt() {
+ ByteBuffer buffer = ByteBuffer.allocate(4);
+ buffer.order(ByteOrder.LITTLE_ENDIAN);
+ int expect = 10000;
+ buffer.putInt(expect).rewind();
+
+ ByteBufferReader reader = new ByteBufferReader(buffer);
+ assertEquals(expect, reader.readInt());
+ }
+
+ @Test
+ public void testReadString() {
+ String expect = "test read string";
+ byte[] bytes = expect.getBytes(StandardCharsets.UTF_8);
+
+ ByteBuffer buffer = ByteBuffer.allocate(expect.length() * 2 + 4);
+ buffer.order(ByteOrder.LITTLE_ENDIAN);
+ buffer.putInt(expect.length()).put(bytes).rewind();
+
+ ByteBufferReader reader = new ByteBufferReader(buffer);
+
+ assertEquals(expect, reader.readString());
+ }
+}
diff --git a/tools/aconfig/aconfig_storage_file/tests/srcs/FlagTableTest.java b/tools/aconfig/aconfig_storage_file/tests/srcs/FlagTableTest.java
new file mode 100644
index 0000000..fd40d4c
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_file/tests/srcs/FlagTableTest.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.aconfig.storage.test;
+
+import static org.junit.Assert.assertEquals;
+
+import android.aconfig.storage.FileType;
+import android.aconfig.storage.FlagTable;
+import android.aconfig.storage.FlagType;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class FlagTableTest {
+
+ @Test
+ public void testFlagTable_rightHeader() throws Exception {
+ FlagTable flagTable = FlagTable.fromBytes(TestDataUtils.getTestFlagMapByteBuffer());
+ FlagTable.Header header = flagTable.getHeader();
+ assertEquals(1, header.getVersion());
+ assertEquals("mockup", header.getContainer());
+ assertEquals(FileType.FLAG_MAP, header.getFileType());
+ assertEquals(321, header.getFileSize());
+ assertEquals(8, header.getNumFlags());
+ assertEquals(31, header.getBucketOffset());
+ assertEquals(99, header.getNodeOffset());
+ }
+
+ @Test
+ public void testFlagTable_rightNode() throws Exception {
+ FlagTable flagTable = FlagTable.fromBytes(TestDataUtils.getTestFlagMapByteBuffer());
+
+ FlagTable.Node node1 = flagTable.get(0, "enabled_ro");
+ FlagTable.Node node2 = flagTable.get(0, "enabled_rw");
+ FlagTable.Node node3 = flagTable.get(2, "enabled_rw");
+ FlagTable.Node node4 = flagTable.get(1, "disabled_rw");
+ FlagTable.Node node5 = flagTable.get(1, "enabled_fixed_ro");
+ FlagTable.Node node6 = flagTable.get(1, "enabled_ro");
+ FlagTable.Node node7 = flagTable.get(2, "enabled_fixed_ro");
+ FlagTable.Node node8 = flagTable.get(0, "disabled_rw");
+
+ assertEquals("enabled_ro", node1.getFlagName());
+ assertEquals("enabled_rw", node2.getFlagName());
+ assertEquals("enabled_rw", node3.getFlagName());
+ assertEquals("disabled_rw", node4.getFlagName());
+ assertEquals("enabled_fixed_ro", node5.getFlagName());
+ assertEquals("enabled_ro", node6.getFlagName());
+ assertEquals("enabled_fixed_ro", node7.getFlagName());
+ assertEquals("disabled_rw", node8.getFlagName());
+
+ assertEquals(0, node1.getPackageId());
+ assertEquals(0, node2.getPackageId());
+ assertEquals(2, node3.getPackageId());
+ assertEquals(1, node4.getPackageId());
+ assertEquals(1, node5.getPackageId());
+ assertEquals(1, node6.getPackageId());
+ assertEquals(2, node7.getPackageId());
+ assertEquals(0, node8.getPackageId());
+
+ assertEquals(FlagType.ReadOnlyBoolean, node1.getFlagType());
+ assertEquals(FlagType.ReadWriteBoolean, node2.getFlagType());
+ assertEquals(FlagType.ReadWriteBoolean, node3.getFlagType());
+ assertEquals(FlagType.ReadWriteBoolean, node4.getFlagType());
+ assertEquals(FlagType.FixedReadOnlyBoolean, node5.getFlagType());
+ assertEquals(FlagType.ReadOnlyBoolean, node6.getFlagType());
+ assertEquals(FlagType.FixedReadOnlyBoolean, node7.getFlagType());
+ assertEquals(FlagType.ReadWriteBoolean, node8.getFlagType());
+
+ assertEquals(1, node1.getFlagIndex());
+ assertEquals(2, node2.getFlagIndex());
+ assertEquals(1, node3.getFlagIndex());
+ assertEquals(0, node4.getFlagIndex());
+ assertEquals(1, node5.getFlagIndex());
+ assertEquals(2, node6.getFlagIndex());
+ assertEquals(0, node7.getFlagIndex());
+ assertEquals(0, node8.getFlagIndex());
+
+ assertEquals(-1, node1.getNextOffset());
+ assertEquals(151, node2.getNextOffset());
+ assertEquals(-1, node3.getNextOffset());
+ assertEquals(-1, node4.getNextOffset());
+ assertEquals(236, node5.getNextOffset());
+ assertEquals(-1, node6.getNextOffset());
+ assertEquals(-1, node7.getNextOffset());
+ assertEquals(-1, node8.getNextOffset());
+ }
+}
diff --git a/tools/aconfig/aconfig_storage_file/tests/srcs/FlagValueListTest.java b/tools/aconfig/aconfig_storage_file/tests/srcs/FlagValueListTest.java
new file mode 100644
index 0000000..c18590a
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_file/tests/srcs/FlagValueListTest.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.aconfig.storage.test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.aconfig.storage.FileType;
+import android.aconfig.storage.FlagTable;
+import android.aconfig.storage.FlagValueList;
+import android.aconfig.storage.PackageTable;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class FlagValueListTest {
+
+ @Test
+ public void testFlagValueList_rightHeader() throws Exception {
+ FlagValueList flagValueList =
+ FlagValueList.fromBytes(TestDataUtils.getTestFlagValByteBuffer());
+ FlagValueList.Header header = flagValueList.getHeader();
+ assertEquals(1, header.getVersion());
+ assertEquals("mockup", header.getContainer());
+ assertEquals(FileType.FLAG_VAL, header.getFileType());
+ assertEquals(35, header.getFileSize());
+ assertEquals(8, header.getNumFlags());
+ assertEquals(27, header.getBooleanValueOffset());
+ }
+
+ @Test
+ public void testFlagValueList_rightNode() throws Exception {
+ FlagValueList flagValueList =
+ FlagValueList.fromBytes(TestDataUtils.getTestFlagValByteBuffer());
+
+ boolean[] expected = new boolean[] {false, true, true, false, true, true, true, true};
+ assertEquals(expected.length, flagValueList.size());
+
+ for (int i = 0; i < flagValueList.size(); i++) {
+ assertEquals(expected[i], flagValueList.get(i));
+ }
+ }
+
+ @Test
+ public void testFlagValueList_getValue() throws Exception {
+ PackageTable packageTable =
+ PackageTable.fromBytes(TestDataUtils.getTestPackageMapByteBuffer());
+ FlagTable flagTable = FlagTable.fromBytes(TestDataUtils.getTestFlagMapByteBuffer());
+
+ FlagValueList flagValueList =
+ FlagValueList.fromBytes(TestDataUtils.getTestFlagValByteBuffer());
+
+ PackageTable.Node pNode = packageTable.get("com.android.aconfig.storage.test_1");
+ FlagTable.Node fNode = flagTable.get(pNode.getPackageId(), "enabled_rw");
+ assertTrue(flagValueList.get(pNode.getBooleanStartIndex() + fNode.getFlagIndex()));
+
+ pNode = packageTable.get("com.android.aconfig.storage.test_4");
+ fNode = flagTable.get(pNode.getPackageId(), "enabled_fixed_ro");
+ assertTrue(flagValueList.get(pNode.getBooleanStartIndex() + fNode.getFlagIndex()));
+ }
+}
diff --git a/tools/aconfig/aconfig_storage_file/tests/srcs/PackageTableTest.java b/tools/aconfig/aconfig_storage_file/tests/srcs/PackageTableTest.java
new file mode 100644
index 0000000..e7e19d8
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_file/tests/srcs/PackageTableTest.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.aconfig.storage.test;
+
+import static org.junit.Assert.assertEquals;
+
+import android.aconfig.storage.FileType;
+import android.aconfig.storage.PackageTable;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class PackageTableTest {
+
+ @Test
+ public void testPackageTable_rightHeader() throws Exception {
+ PackageTable packageTable =
+ PackageTable.fromBytes(TestDataUtils.getTestPackageMapByteBuffer());
+ PackageTable.Header header = packageTable.getHeader();
+ assertEquals(1, header.getVersion());
+ assertEquals("mockup", header.getContainer());
+ assertEquals(FileType.PACKAGE_MAP, header.getFileType());
+ assertEquals(209, header.getFileSize());
+ assertEquals(3, header.getNumPackages());
+ assertEquals(31, header.getBucketOffset());
+ assertEquals(59, header.getNodeOffset());
+ }
+
+ @Test
+ public void testPackageTable_rightNode() throws Exception {
+ PackageTable packageTable =
+ PackageTable.fromBytes(TestDataUtils.getTestPackageMapByteBuffer());
+
+ PackageTable.Node node1 = packageTable.get("com.android.aconfig.storage.test_1");
+ PackageTable.Node node2 = packageTable.get("com.android.aconfig.storage.test_2");
+ PackageTable.Node node4 = packageTable.get("com.android.aconfig.storage.test_4");
+
+ assertEquals("com.android.aconfig.storage.test_1", node1.getPackageName());
+ assertEquals("com.android.aconfig.storage.test_2", node2.getPackageName());
+ assertEquals("com.android.aconfig.storage.test_4", node4.getPackageName());
+
+ assertEquals(0, node1.getPackageId());
+ assertEquals(1, node2.getPackageId());
+ assertEquals(2, node4.getPackageId());
+
+ assertEquals(0, node1.getBooleanStartIndex());
+ assertEquals(3, node2.getBooleanStartIndex());
+ assertEquals(6, node4.getBooleanStartIndex());
+
+ assertEquals(159, node1.getNextOffset());
+ assertEquals(-1, node2.getNextOffset());
+ assertEquals(-1, node4.getNextOffset());
+ }
+}
diff --git a/tools/aconfig/aconfig_storage_file/tests/srcs/TestDataUtils.java b/tools/aconfig/aconfig_storage_file/tests/srcs/TestDataUtils.java
new file mode 100644
index 0000000..f35952d
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_file/tests/srcs/TestDataUtils.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.aconfig.storage.test;
+
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+
+public final class TestDataUtils {
+ private static final String TEST_PACKAGE_MAP_PATH = "package.map";
+ private static final String TEST_FLAG_MAP_PATH = "flag.map";
+ private static final String TEST_FLAG_VAL_PATH = "flag.val";
+ private static final String TEST_FLAG_INFO_PATH = "flag.info";
+
+ private static final String TESTDATA_PATH =
+ "/data/local/tmp/aconfig_storage_file_test_java/testdata/";
+
+ public static ByteBuffer getTestPackageMapByteBuffer() throws Exception {
+ return readFile(TESTDATA_PATH + TEST_PACKAGE_MAP_PATH);
+ }
+
+ public static ByteBuffer getTestFlagMapByteBuffer() throws Exception {
+ return readFile(TESTDATA_PATH + TEST_FLAG_MAP_PATH);
+ }
+
+ public static ByteBuffer getTestFlagValByteBuffer() throws Exception {
+ return readFile(TESTDATA_PATH + TEST_FLAG_VAL_PATH);
+ }
+
+ public static ByteBuffer getTestFlagInfoByteBuffer() throws Exception {
+ return readFile(TESTDATA_PATH + TEST_FLAG_INFO_PATH);
+ }
+
+ private static ByteBuffer readFile(String fileName) throws Exception {
+ InputStream input = new FileInputStream(fileName);
+ return ByteBuffer.wrap(input.readAllBytes());
+ }
+}
diff --git a/tools/aconfig/aconfig_storage_read_api/Android.bp b/tools/aconfig/aconfig_storage_read_api/Android.bp
index 5e9eb54..619b488 100644
--- a/tools/aconfig/aconfig_storage_read_api/Android.bp
+++ b/tools/aconfig/aconfig_storage_read_api/Android.bp
@@ -87,6 +87,9 @@
generated_sources: ["libcxx_aconfig_storage_read_api_bridge_code"],
whole_static_libs: ["libaconfig_storage_read_api_cxx_bridge"],
export_include_dirs: ["include"],
+ static_libs: [
+ "libbase",
+ ],
host_supported: true,
vendor_available: true,
product_available: true,
@@ -154,7 +157,9 @@
java_library {
name: "libaconfig_storage_read_api_java",
srcs: [
- "srcs/**/*.java",
+ "srcs/android/aconfig/storage/AconfigStorageReadAPI.java",
+ "srcs/android/aconfig/storage/FlagReadContext.java",
+ "srcs/android/aconfig/storage/PackageReadContext.java",
],
required: ["libaconfig_storage_read_api_rust_jni"],
min_sdk_version: "UpsideDownCake",
@@ -163,3 +168,39 @@
"//apex_available:platform",
],
}
+
+java_library {
+ name: "aconfig_storage_reader_java",
+ srcs: [
+ "srcs/android/aconfig/storage/StorageInternalReader.java",
+ ],
+ libs: [
+ "unsupportedappusage",
+ ],
+ static_libs: [
+ "aconfig_storage_file_java",
+ ],
+ sdk_version: "core_current",
+ host_supported: true,
+ min_sdk_version: "29",
+ apex_available: [
+ "//apex_available:platform",
+ "//apex_available:anyapex",
+ ],
+}
+
+java_library {
+ name: "aconfig_storage_reader_java_none",
+ srcs: [
+ "srcs/android/aconfig/storage/StorageInternalReader.java",
+ ],
+ libs: [
+ "unsupportedappusage-sdk-none",
+ ],
+ static_libs: [
+ "aconfig_storage_file_java_none",
+ ],
+ sdk_version: "none",
+ system_modules: "core-all-system-modules",
+ host_supported: true,
+}
diff --git a/tools/aconfig/aconfig_storage_read_api/aconfig_storage_read_api.cpp b/tools/aconfig/aconfig_storage_read_api/aconfig_storage_read_api.cpp
index 97ada3a..8e0c4e1 100644
--- a/tools/aconfig/aconfig_storage_read_api/aconfig_storage_read_api.cpp
+++ b/tools/aconfig/aconfig_storage_read_api/aconfig_storage_read_api.cpp
@@ -1,3 +1,4 @@
+#include <android-base/unique_fd.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
@@ -59,22 +60,22 @@
/// Map a storage file
Result<MappedStorageFile*> map_storage_file(std::string const& file) {
- int fd = open(file.c_str(), O_CLOEXEC | O_NOFOLLOW | O_RDONLY);
- if (fd == -1) {
+ android::base::unique_fd ufd(open(file.c_str(), O_CLOEXEC | O_NOFOLLOW | O_RDONLY));
+ if (ufd.get() == -1) {
auto result = Result<MappedStorageFile*>();
result.errmsg = std::string("failed to open ") + file + ": " + strerror(errno);
return result;
};
struct stat fd_stat;
- if (fstat(fd, &fd_stat) < 0) {
+ if (fstat(ufd.get(), &fd_stat) < 0) {
auto result = Result<MappedStorageFile*>();
result.errmsg = std::string("fstat failed: ") + strerror(errno);
return result;
}
size_t file_size = fd_stat.st_size;
- void* const map_result = mmap(nullptr, file_size, PROT_READ, MAP_SHARED, fd, 0);
+ void* const map_result = mmap(nullptr, file_size, PROT_READ, MAP_SHARED, ufd.get(), 0);
if (map_result == MAP_FAILED) {
auto result = Result<MappedStorageFile*>();
result.errmsg = std::string("mmap failed: ") + strerror(errno);
diff --git a/tools/aconfig/aconfig_storage_read_api/srcs/android/aconfig/storage/StorageInternalReader.java b/tools/aconfig/aconfig_storage_read_api/srcs/android/aconfig/storage/StorageInternalReader.java
new file mode 100644
index 0000000..07558ee
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_read_api/srcs/android/aconfig/storage/StorageInternalReader.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.aconfig.storage;
+
+import android.compat.annotation.UnsupportedAppUsage;
+
+import java.io.Closeable;
+import java.io.FileInputStream;
+import java.nio.MappedByteBuffer;
+import java.nio.channels.FileChannel;
+
+/** @hide */
+public class StorageInternalReader {
+
+ private static final String MAP_PATH = "/metadata/aconfig/maps/";
+ private static final String BOOT_PATH = "/metadata/aconfig/boot/";
+
+ private PackageTable mPackageTable;
+ private FlagValueList mFlagValueList;
+
+ private int mPackageBooleanStartOffset;
+
+ @UnsupportedAppUsage
+ public StorageInternalReader(String container, String packageName) {
+ this(packageName, MAP_PATH + container + ".package.map", BOOT_PATH + container + ".val");
+ }
+
+ @UnsupportedAppUsage
+ public StorageInternalReader(String packageName, String packageMapFile, String flagValueFile) {
+ mPackageTable = PackageTable.fromBytes(mapStorageFile(packageMapFile));
+ mFlagValueList = FlagValueList.fromBytes(mapStorageFile(flagValueFile));
+ mPackageBooleanStartOffset = getPackageBooleanStartOffset(packageName);
+ }
+
+ @UnsupportedAppUsage
+ public boolean getBooleanFlagValue(int index) {
+ index += mPackageBooleanStartOffset;
+ if (index >= mFlagValueList.size()) {
+ throw new AconfigStorageException("Fail to get boolean flag value");
+ }
+ return mFlagValueList.get(index);
+ }
+
+ private int getPackageBooleanStartOffset(String packageName) {
+ PackageTable.Node pNode = mPackageTable.get(packageName);
+ if (pNode == null) {
+ PackageTable.Header header = mPackageTable.getHeader();
+ throw new AconfigStorageException(
+ String.format(
+ "Fail to get package %s from container %s",
+ packageName, header.getContainer()));
+ }
+ return pNode.getBooleanStartIndex();
+ }
+
+ // Map a storage file given file path
+ private static MappedByteBuffer mapStorageFile(String file) {
+ FileInputStream stream = null;
+ try {
+ stream = new FileInputStream(file);
+ FileChannel channel = stream.getChannel();
+ return channel.map(FileChannel.MapMode.READ_ONLY, 0, channel.size());
+ } catch (Exception e) {
+ throw new AconfigStorageException(
+ String.format("Fail to mmap storage file %s", file), e);
+ } finally {
+ quietlyDispose(stream);
+ }
+ }
+
+ private static void quietlyDispose(Closeable closable) {
+ try {
+ if (closable != null) {
+ closable.close();
+ }
+ } catch (Exception e) {
+ // no need to care, at least as of now
+ }
+ }
+}
diff --git a/tools/aconfig/aconfig_storage_read_api/tests/java/Android.bp b/tools/aconfig/aconfig_storage_read_api/tests/java/Android.bp
index d94b2b4..11b3824 100644
--- a/tools/aconfig/aconfig_storage_read_api/tests/java/Android.bp
+++ b/tools/aconfig/aconfig_storage_read_api/tests/java/Android.bp
@@ -1,7 +1,8 @@
android_test {
name: "aconfig_storage_read_api.test.java",
- srcs: ["AconfigStorageReadAPITest.java"],
+ srcs: ["./**/*.java"],
static_libs: [
+ "aconfig_storage_reader_java",
"androidx.test.rules",
"libaconfig_storage_read_api_java",
"junit",
diff --git a/tools/aconfig/aconfig_storage_read_api/tests/java/StorageInternalReaderTest.java b/tools/aconfig/aconfig_storage_read_api/tests/java/StorageInternalReaderTest.java
new file mode 100644
index 0000000..3a1bba0
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_read_api/tests/java/StorageInternalReaderTest.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.aconfig.storage.test;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.aconfig.storage.StorageInternalReader;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class StorageInternalReaderTest {
+
+ private String mStorageDir = "/data/local/tmp/aconfig_java_api_test";
+
+ @Test
+ public void testStorageInternalReader_getFlag() {
+
+ String packageMapFile = mStorageDir + "/maps/mockup.package.map";
+ String flagValueFile = mStorageDir + "/boot/mockup.val";
+
+ StorageInternalReader reader =
+ new StorageInternalReader(
+ "com.android.aconfig.storage.test_1", packageMapFile, flagValueFile);
+ assertFalse(reader.getBooleanFlagValue(0));
+ assertTrue(reader.getBooleanFlagValue(1));
+ }
+}
diff --git a/tools/aconfig/aflags/src/aconfig_storage_source.rs b/tools/aconfig/aflags/src/aconfig_storage_source.rs
index 04140c7..0dfb956 100644
--- a/tools/aconfig/aflags/src/aconfig_storage_source.rs
+++ b/tools/aconfig/aflags/src/aconfig_storage_source.rs
@@ -1,14 +1,93 @@
use crate::{Flag, FlagPermission, FlagSource, FlagValue, ValuePickedFrom};
use anyhow::{anyhow, Result};
+use std::collections::HashMap;
use std::fs::File;
use std::io::Read;
pub struct AconfigStorageSource {}
+use aconfig_storage_file::protos::ProtoStorageFileInfo;
use aconfig_storage_file::protos::ProtoStorageFiles;
+use aconfig_storage_file::FlagValueAndInfoSummary;
-static STORAGE_INFO_FILE_PATH: &str = "/metadata/aconfig/persistent_storage_file_records.pb";
+static STORAGE_INFO_FILE_PATH: &str = "/metadata/aconfig/storage_records.pb";
+
+fn read_default_values(file_info: ProtoStorageFileInfo) -> Result<HashMap<String, FlagValue>> {
+ let package_map =
+ file_info.package_map.ok_or(anyhow!("storage file is missing package map"))?;
+ let flag_map = file_info.flag_map.ok_or(anyhow!("storage file is missing flag map"))?;
+ let flag_val = file_info.flag_val.ok_or(anyhow!("storage file is missing flag val"))?;
+
+ let mut result = HashMap::new();
+ for listed_flag in aconfig_storage_file::list_flags(&package_map, &flag_map, &flag_val)? {
+ let value = FlagValue::try_from(listed_flag.flag_value.as_str())?;
+ result.insert(listed_flag.package_name + &listed_flag.flag_name, value);
+ }
+ Ok(result)
+}
+
+fn read_next_boot_values(
+ listed_flags: &[FlagValueAndInfoSummary],
+) -> Result<HashMap<String, FlagValue>> {
+ let mut result = HashMap::new();
+ for flag in listed_flags {
+ result.insert(
+ flag.package_name.clone() + &flag.flag_name,
+ FlagValue::try_from(flag.flag_value.as_str())?,
+ );
+ }
+ Ok(result)
+}
+
+fn reconcile(
+ default_values: HashMap<String, FlagValue>,
+ next_boot_values: HashMap<String, FlagValue>,
+ flags_current_boot: &[FlagValueAndInfoSummary],
+ container: &str,
+) -> Result<Vec<Flag>> {
+ let mut result = Vec::new();
+ for listed_flag in flags_current_boot {
+ let default_value = default_values
+ .get(&(listed_flag.package_name.clone() + &listed_flag.flag_name))
+ .copied();
+
+ let name = listed_flag.flag_name.clone();
+ let package = listed_flag.package_name.clone();
+ let value = FlagValue::try_from(listed_flag.flag_value.as_str())?;
+ let container = container.to_string();
+ let staged_value = next_boot_values
+ .get(&(listed_flag.package_name.clone() + &listed_flag.flag_name))
+ .filter(|&v| value != *v)
+ .copied();
+ let permission = if listed_flag.is_readwrite {
+ FlagPermission::ReadWrite
+ } else {
+ FlagPermission::ReadOnly
+ };
+ let value_picked_from = if listed_flag.has_local_override {
+ ValuePickedFrom::Local
+ } else if Some(value) == default_value {
+ ValuePickedFrom::Default
+ } else {
+ ValuePickedFrom::Server
+ };
+
+ result.push(Flag {
+ name,
+ package,
+ value,
+ container,
+ staged_value,
+ permission,
+ value_picked_from,
+
+ // TODO(b/324436145): delete namespace field once DeviceConfig isn't in CLI.
+ namespace: "-".to_string(),
+ });
+ }
+ Ok(result)
+}
impl FlagSource for AconfigStorageSource {
fn list_flags() -> Result<Vec<Flag>> {
@@ -20,30 +99,35 @@
let storage_file_info: ProtoStorageFiles = protobuf::Message::parse_from_bytes(&bytes)?;
for file_info in storage_file_info.files {
- let package_map =
- file_info.package_map.ok_or(anyhow!("storage file is missing package map"))?;
- let flag_map = file_info.flag_map.ok_or(anyhow!("storage file is missing flag map"))?;
- let flag_val = file_info.flag_val.ok_or(anyhow!("storage file is missing flag val"))?;
+ let default_values = read_default_values(file_info.clone())?;
+
let container =
file_info.container.ok_or(anyhow!("storage file is missing container"))?;
+ let package_map = format!("/metadata/aconfig/maps/{container}.package.map");
+ let flag_map = format!("/metadata/aconfig/maps/{container}.flag.map");
+ let flag_info = format!("/metadata/aconfig/boot/{container}.info");
- for listed_flag in aconfig_storage_file::list_flags(&package_map, &flag_map, &flag_val)?
- {
- result.push(Flag {
- name: listed_flag.flag_name,
- package: listed_flag.package_name,
- value: FlagValue::try_from(listed_flag.flag_value.as_str())?,
- container: container.to_string(),
+ let flag_val_current_boot = format!("/metadata/aconfig/boot/{container}.val");
+ let flag_val_next_boot = format!("/metadata/aconfig/flags/{container}.val");
- // TODO(b/324436145): delete namespace field once DeviceConfig isn't in CLI.
- namespace: "-".to_string(),
+ let flags_next_boot = aconfig_storage_file::list_flags_with_info(
+ &package_map,
+ &flag_map,
+ &flag_val_next_boot,
+ &flag_info,
+ )?;
+ let flags_current_boot = aconfig_storage_file::list_flags_with_info(
+ &package_map,
+ &flag_map,
+ &flag_val_current_boot,
+ &flag_info,
+ )?;
- // TODO(b/324436145): Populate with real values once API is available.
- staged_value: None,
- permission: FlagPermission::ReadOnly,
- value_picked_from: ValuePickedFrom::Default,
- });
- }
+ let next_boot_values = read_next_boot_values(&flags_next_boot)?;
+ let processed_flags =
+ reconcile(default_values, next_boot_values, &flags_current_boot, &container)?;
+
+ result.extend(processed_flags);
}
Ok(result)
diff --git a/tools/aconfig/aflags/src/main.rs b/tools/aconfig/aflags/src/main.rs
index 810f2e3..0a5c989 100644
--- a/tools/aconfig/aflags/src/main.rs
+++ b/tools/aconfig/aflags/src/main.rs
@@ -50,6 +50,7 @@
enum ValuePickedFrom {
Default,
Server,
+ Local,
}
impl std::fmt::Display for ValuePickedFrom {
@@ -60,6 +61,7 @@
match &self {
Self::Default => "default",
Self::Server => "server",
+ Self::Local => "local",
}
)
}
@@ -286,7 +288,9 @@
let cli = Cli::parse();
let output = match cli.command {
Command::List { use_new_storage: true, container } => {
- list(FlagSourceType::AconfigStorage, container).map(Some)
+ list(FlagSourceType::AconfigStorage, container)
+ .map_err(|_| anyhow!("storage may not be enabled"))
+ .map(Some)
}
Command::List { use_new_storage: false, container } => {
list(FlagSourceType::DeviceConfig, container).map(Some)
diff --git a/tools/aconfig/fake_device_config/src/android/provider/Log.java b/tools/aconfig/fake_device_config/src/android/provider/Log.java
new file mode 100644
index 0000000..3e7fd0f
--- /dev/null
+++ b/tools/aconfig/fake_device_config/src/android/provider/Log.java
@@ -0,0 +1,15 @@
+package android.util;
+
+public final class Log {
+ public static int i(String tag, String msg) {
+ return 0;
+ }
+
+ public static int e(String tag, String msg) {
+ return 0;
+ }
+
+ public static int e(String tag, String msg, Throwable tr) {
+ return 0;
+ }
+}
diff --git a/tools/check-flagged-apis/src/com/android/checkflaggedapis/Main.kt b/tools/check-flagged-apis/src/com/android/checkflaggedapis/Main.kt
index 1125d39..d323c20 100644
--- a/tools/check-flagged-apis/src/com/android/checkflaggedapis/Main.kt
+++ b/tools/check-flagged-apis/src/com/android/checkflaggedapis/Main.kt
@@ -19,10 +19,10 @@
import android.aconfig.Aconfig
import com.android.tools.metalava.model.BaseItemVisitor
+import com.android.tools.metalava.model.CallableItem
import com.android.tools.metalava.model.ClassItem
import com.android.tools.metalava.model.FieldItem
import com.android.tools.metalava.model.Item
-import com.android.tools.metalava.model.MethodItem
import com.android.tools.metalava.model.text.ApiFile
import com.github.ajalt.clikt.core.CliktCommand
import com.github.ajalt.clikt.core.ProgramResult
@@ -274,15 +274,15 @@
}
}
- override fun visitMethod(method: MethodItem) {
- getFlagOrNull(method)?.let { flag ->
- val methodName = buildString {
- append(method.name())
+ override fun visitCallable(callable: CallableItem) {
+ getFlagOrNull(callable)?.let { flag ->
+ val callableSignature = buildString {
+ append(callable.name())
append("(")
- method.parameters().joinTo(this, separator = "") { it.type().internalName() }
+ callable.parameters().joinTo(this, separator = "") { it.type().internalName() }
append(")")
}
- val symbol = Symbol.createMethod(method.containingClass().qualifiedName(), methodName)
+ val symbol = Symbol.createMethod(callable.containingClass().qualifiedName(), callableSignature)
output.add(Pair(symbol, flag))
}
}
diff --git a/tools/filelistdiff/allowlist b/tools/filelistdiff/allowlist
index 0a51d0e..943f955 100644
--- a/tools/filelistdiff/allowlist
+++ b/tools/filelistdiff/allowlist
@@ -78,6 +78,10 @@
lib/libwfds.so
lib/libyuv.so
+# b/351258461
+adb_keys
+init.environ.rc
+
# Known diffs only in the Soong system image
lib/libhidcommand_jni.so
lib/libuinputcommand_jni.so
\ No newline at end of file
diff --git a/tools/ide_query/prober_scripts/jvm/Foo.java b/tools/ide_query/prober_scripts/jvm/Foo.java
new file mode 100644
index 0000000..a043f72
--- /dev/null
+++ b/tools/ide_query/prober_scripts/jvm/Foo.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2014 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 jvm;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+
+/** Foo class. */
+public final class Foo {
+
+ void testCompletion() {
+ ArrayList<Integer> list = new ArrayList<>();
+ System.out.println(list);
+
+ // ^
+
+ // step
+ // ; Test completion on the standard types.
+ // type("list.")
+ // completion.trigger()
+ // assert completion.items.filter(label="add.*")
+ }
+}
diff --git a/tools/ide_query/prober_scripts/jvm/suite.textpb b/tools/ide_query/prober_scripts/jvm/suite.textpb
new file mode 100644
index 0000000..460e08c
--- /dev/null
+++ b/tools/ide_query/prober_scripts/jvm/suite.textpb
@@ -0,0 +1,4 @@
+tests: {
+ name: "general"
+ scripts: "build/make/tools/ide_query/prober_scripts/jvm/Foo.java"
+}
diff --git a/tools/sbom/Android.bp b/tools/sbom/Android.bp
index 2b2b573..6901b06 100644
--- a/tools/sbom/Android.bp
+++ b/tools/sbom/Android.bp
@@ -33,6 +33,23 @@
],
}
+python_binary_host {
+ name: "gen_sbom",
+ srcs: [
+ "gen_sbom.py",
+ ],
+ version: {
+ py3: {
+ embedded_launcher: true,
+ },
+ },
+ libs: [
+ "metadata_file_proto_py",
+ "libprotobuf-python",
+ "sbom_lib",
+ ],
+}
+
python_library_host {
name: "sbom_lib",
srcs: [
@@ -91,4 +108,4 @@
libs: [
"sbom_lib",
],
-}
\ No newline at end of file
+}
diff --git a/tools/sbom/gen_sbom.py b/tools/sbom/gen_sbom.py
new file mode 100644
index 0000000..a203258
--- /dev/null
+++ b/tools/sbom/gen_sbom.py
@@ -0,0 +1,926 @@
+# !/usr/bin/env python3
+#
+# 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.
+
+"""
+Generate the SBOM of the current target product in SPDX format.
+Usage example:
+ gen_sbom.py --output_file out/soong/sbom/aosp_cf_x86_64_phone/sbom.spdx \
+ --metadata out/soong/metadata/aosp_cf_x86_64_phone/metadata.db \
+ --product_out out/target/vsoc_x86_64
+ --soong_out out/soong
+ --build_version $(cat out/target/product/vsoc_x86_64/build_fingerprint.txt) \
+ --product_mfr=Google
+"""
+
+import argparse
+import datetime
+import google.protobuf.text_format as text_format
+import hashlib
+import os
+import pathlib
+import queue
+import metadata_file_pb2
+import sbom_data
+import sbom_writers
+import sqlite3
+
+# Package type
+PKG_SOURCE = 'SOURCE'
+PKG_UPSTREAM = 'UPSTREAM'
+PKG_PREBUILT = 'PREBUILT'
+
+# Security tag
+NVD_CPE23 = 'NVD-CPE2.3:'
+
+# Report
+ISSUE_NO_METADATA = 'No metadata generated in Make for installed files:'
+ISSUE_NO_METADATA_FILE = 'No METADATA file found for installed file:'
+ISSUE_METADATA_FILE_INCOMPLETE = 'METADATA file incomplete:'
+ISSUE_UNKNOWN_SECURITY_TAG_TYPE = 'Unknown security tag type:'
+ISSUE_INSTALLED_FILE_NOT_EXIST = 'Non-existent installed files:'
+ISSUE_NO_MODULE_FOUND_FOR_STATIC_DEP = 'No module found for static dependency files:'
+INFO_METADATA_FOUND_FOR_PACKAGE = 'METADATA file found for packages:'
+
+SOONG_PREBUILT_MODULE_TYPES = [
+ 'android_app_import',
+ 'android_library_import',
+ 'cc_prebuilt_binary',
+ 'cc_prebuilt_library',
+ 'cc_prebuilt_library_headers',
+ 'cc_prebuilt_library_shared',
+ 'cc_prebuilt_library_static',
+ 'cc_prebuilt_object',
+ 'dex_import',
+ 'java_import',
+ 'java_sdk_library_import',
+ 'java_system_modules_import',
+ 'libclang_rt_prebuilt_library_static',
+ 'libclang_rt_prebuilt_library_shared',
+ 'llvm_prebuilt_library_static',
+ 'ndk_prebuilt_object',
+ 'ndk_prebuilt_shared_stl',
+ 'nkd_prebuilt_static_stl',
+ 'prebuilt_apex',
+ 'prebuilt_bootclasspath_fragment',
+ 'prebuilt_dsp',
+ 'prebuilt_firmware',
+ 'prebuilt_kernel_modules',
+ 'prebuilt_rfsa',
+ 'prebuilt_root',
+ 'rust_prebuilt_dylib',
+ 'rust_prebuilt_library',
+ 'rust_prebuilt_rlib',
+ 'vndk_prebuilt_shared',
+]
+
+THIRD_PARTY_IDENTIFIER_TYPES = [
+ # Types defined in metadata_file.proto
+ 'Git',
+ 'SVN',
+ 'Hg',
+ 'Darcs',
+ 'VCS',
+ 'Archive',
+ 'PrebuiltByAlphabet',
+ 'LocalSource',
+ 'Other',
+ # OSV ecosystems defined at https://ossf.github.io/osv-schema/#affectedpackage-field.
+ 'Go',
+ 'npm',
+ 'OSS-Fuzz',
+ 'PyPI',
+ 'RubyGems',
+ 'crates.io',
+ 'Hackage',
+ 'GHC',
+ 'Packagist',
+ 'Maven',
+ 'NuGet',
+ 'Linux',
+ 'Debian',
+ 'Alpine',
+ 'Hex',
+ 'Android',
+ 'GitHub Actions',
+ 'Pub',
+ 'ConanCenter',
+ 'Rocky Linux',
+ 'AlmaLinux',
+ 'Bitnami',
+ 'Photon OS',
+ 'CRAN',
+ 'Bioconductor',
+ 'SwiftURL'
+]
+
+
+def get_args():
+ parser = argparse.ArgumentParser()
+ parser.add_argument('-v', '--verbose', action='store_true', default=False, help='Print more information.')
+ parser.add_argument('-d', '--debug', action='store_true', default=False, help='Debug mode')
+ parser.add_argument('--output_file', required=True, help='The generated SBOM file in SPDX format.')
+ parser.add_argument('--metadata', required=True, help='The metadata DB file path.')
+ parser.add_argument('--product_out', required=True, help='The path of PRODUCT_OUT, e.g. out/target/product/vsoc_x86_64.')
+ parser.add_argument('--soong_out', required=True, help='The path of Soong output directory, e.g. out/soong')
+ parser.add_argument('--build_version', required=True, help='The build version.')
+ parser.add_argument('--product_mfr', required=True, help='The product manufacturer.')
+ parser.add_argument('--json', action='store_true', default=False, help='Generated SBOM file in SPDX JSON format')
+
+ return parser.parse_args()
+
+
+def log(*info):
+ if args.verbose:
+ for i in info:
+ print(i)
+
+
+def new_package_id(package_name, type):
+ return f'SPDXRef-{type}-{sbom_data.encode_for_spdxid(package_name)}'
+
+
+def new_file_id(file_path):
+ return f'SPDXRef-{sbom_data.encode_for_spdxid(file_path)}'
+
+
+def new_license_id(license_name):
+ return f'LicenseRef-{sbom_data.encode_for_spdxid(license_name)}'
+
+
+def checksum(file_path):
+ h = hashlib.sha1()
+ if os.path.islink(file_path):
+ h.update(os.readlink(file_path).encode('utf-8'))
+ else:
+ with open(file_path, 'rb') as f:
+ h.update(f.read())
+ return f'SHA1: {h.hexdigest()}'
+
+
+def is_soong_prebuilt_module(file_metadata):
+ return (file_metadata['soong_module_type'] and
+ file_metadata['soong_module_type'] in SOONG_PREBUILT_MODULE_TYPES)
+
+
+def is_source_package(file_metadata):
+ module_path = file_metadata['module_path']
+ return module_path.startswith('external/') and not is_prebuilt_package(file_metadata)
+
+
+def is_prebuilt_package(file_metadata):
+ module_path = file_metadata['module_path']
+ if module_path:
+ return (module_path.startswith('prebuilts/') or
+ is_soong_prebuilt_module(file_metadata) or
+ file_metadata['is_prebuilt_make_module'])
+
+ kernel_module_copy_files = file_metadata['kernel_module_copy_files']
+ if kernel_module_copy_files and not kernel_module_copy_files.startswith('ANDROID-GEN:'):
+ return True
+
+ return False
+
+
+def get_source_package_info(file_metadata, metadata_file_path):
+ """Return source package info exists in its METADATA file, currently including name, security tag
+ and external SBOM reference.
+
+ See go/android-spdx and go/android-sbom-gen for more details.
+ """
+ if not metadata_file_path:
+ return file_metadata['module_path'], []
+
+ metadata_proto = metadata_file_protos[metadata_file_path]
+ external_refs = []
+ for tag in metadata_proto.third_party.security.tag:
+ if tag.lower().startswith((NVD_CPE23 + 'cpe:2.3:').lower()):
+ external_refs.append(
+ sbom_data.PackageExternalRef(category=sbom_data.PackageExternalRefCategory.SECURITY,
+ type=sbom_data.PackageExternalRefType.cpe23Type,
+ locator=tag.removeprefix(NVD_CPE23)))
+ elif tag.lower().startswith((NVD_CPE23 + 'cpe:/').lower()):
+ external_refs.append(
+ sbom_data.PackageExternalRef(category=sbom_data.PackageExternalRefCategory.SECURITY,
+ type=sbom_data.PackageExternalRefType.cpe22Type,
+ locator=tag.removeprefix(NVD_CPE23)))
+
+ if metadata_proto.name:
+ return metadata_proto.name, external_refs
+ else:
+ return os.path.basename(metadata_file_path), external_refs # return the directory name only as package name
+
+
+def get_prebuilt_package_name(file_metadata, metadata_file_path):
+ """Return name of a prebuilt package, which can be from the METADATA file, metadata file path,
+ module path or kernel module's source path if the installed file is a kernel module.
+
+ See go/android-spdx and go/android-sbom-gen for more details.
+ """
+ name = None
+ if metadata_file_path:
+ metadata_proto = metadata_file_protos[metadata_file_path]
+ if metadata_proto.name:
+ name = metadata_proto.name
+ else:
+ name = metadata_file_path
+ elif file_metadata['module_path']:
+ name = file_metadata['module_path']
+ elif file_metadata['kernel_module_copy_files']:
+ src_path = file_metadata['kernel_module_copy_files'].split(':')[0]
+ name = os.path.dirname(src_path)
+
+ return name.removeprefix('prebuilts/').replace('/', '-')
+
+
+def get_metadata_file_path(file_metadata):
+ """Search for METADATA file of a package and return its path."""
+ metadata_path = ''
+ if file_metadata['module_path']:
+ metadata_path = file_metadata['module_path']
+ elif file_metadata['kernel_module_copy_files']:
+ metadata_path = os.path.dirname(file_metadata['kernel_module_copy_files'].split(':')[0])
+
+ while metadata_path and not os.path.exists(metadata_path + '/METADATA'):
+ metadata_path = os.path.dirname(metadata_path)
+
+ return metadata_path
+
+
+def get_package_version(metadata_file_path):
+ """Return a package's version in its METADATA file."""
+ if not metadata_file_path:
+ return None
+ metadata_proto = metadata_file_protos[metadata_file_path]
+ return metadata_proto.third_party.version
+
+
+def get_package_homepage(metadata_file_path):
+ """Return a package's homepage URL in its METADATA file."""
+ if not metadata_file_path:
+ return None
+ metadata_proto = metadata_file_protos[metadata_file_path]
+ if metadata_proto.third_party.homepage:
+ return metadata_proto.third_party.homepage
+ for url in metadata_proto.third_party.url:
+ if url.type == metadata_file_pb2.URL.Type.HOMEPAGE:
+ return url.value
+
+ return None
+
+
+def get_package_download_location(metadata_file_path):
+ """Return a package's code repository URL in its METADATA file."""
+ if not metadata_file_path:
+ return None
+ metadata_proto = metadata_file_protos[metadata_file_path]
+ if metadata_proto.third_party.url:
+ urls = sorted(metadata_proto.third_party.url, key=lambda url: url.type)
+ if urls[0].type != metadata_file_pb2.URL.Type.HOMEPAGE:
+ return urls[0].value
+ elif len(urls) > 1:
+ return urls[1].value
+
+ return None
+
+
+def get_license_text(license_files):
+ license_text = ''
+ for license_file in license_files:
+ if args.debug:
+ license_text += '#### Content from ' + license_file + '\n'
+ else:
+ license_text += pathlib.Path(license_file).read_text(errors='replace') + '\n\n'
+ return license_text
+
+
+def get_sbom_fragments(installed_file_metadata, metadata_file_path):
+ """Return SPDX fragment of source/prebuilt packages, which usually contains a SOURCE/PREBUILT
+ package, a UPSTREAM package and an external SBOM document reference if sbom_ref defined in its
+ METADATA file.
+
+ See go/android-spdx and go/android-sbom-gen for more details.
+ """
+ external_doc_ref = None
+ packages = []
+ relationships = []
+ licenses = []
+
+ # Info from METADATA file
+ homepage = get_package_homepage(metadata_file_path)
+ version = get_package_version(metadata_file_path)
+ download_location = get_package_download_location(metadata_file_path)
+
+ lics = db.get_package_licenses(installed_file_metadata['module_path'])
+ if not lics:
+ lics = db.get_package_licenses(metadata_file_path)
+
+ if lics:
+ for license_name, license_files in lics.items():
+ if not license_files:
+ continue
+ license_id = new_license_id(license_name)
+ if license_name not in licenses_text:
+ licenses_text[license_name] = get_license_text(license_files.split(' '))
+ licenses.append(sbom_data.License(id=license_id, name=license_name, text=licenses_text[license_name]))
+
+ if is_source_package(installed_file_metadata):
+ # Source fork packages
+ name, external_refs = get_source_package_info(installed_file_metadata, metadata_file_path)
+ source_package_id = new_package_id(name, PKG_SOURCE)
+ source_package = sbom_data.Package(id=source_package_id, name=name, version=args.build_version,
+ download_location=sbom_data.VALUE_NONE,
+ supplier='Organization: ' + args.product_mfr,
+ external_refs=external_refs)
+
+ upstream_package_id = new_package_id(name, PKG_UPSTREAM)
+ upstream_package = sbom_data.Package(id=upstream_package_id, name=name, version=version,
+ supplier=(
+ 'Organization: ' + homepage) if homepage else sbom_data.VALUE_NOASSERTION,
+ download_location=download_location)
+ packages += [source_package, upstream_package]
+ relationships.append(sbom_data.Relationship(id1=source_package_id,
+ relationship=sbom_data.RelationshipType.VARIANT_OF,
+ id2=upstream_package_id))
+
+ for license in licenses:
+ source_package.declared_license_ids.append(license.id)
+ upstream_package.declared_license_ids.append(license.id)
+
+ elif is_prebuilt_package(installed_file_metadata):
+ # Prebuilt fork packages
+ name = get_prebuilt_package_name(installed_file_metadata, metadata_file_path)
+ prebuilt_package_id = new_package_id(name, PKG_PREBUILT)
+ prebuilt_package = sbom_data.Package(id=prebuilt_package_id,
+ name=name,
+ download_location=sbom_data.VALUE_NONE,
+ version=version if version else args.build_version,
+ supplier='Organization: ' + args.product_mfr)
+
+ upstream_package_id = new_package_id(name, PKG_UPSTREAM)
+ upstream_package = sbom_data.Package(id=upstream_package_id, name=name, version=version,
+ supplier=(
+ 'Organization: ' + homepage) if homepage else sbom_data.VALUE_NOASSERTION,
+ download_location=download_location)
+ packages += [prebuilt_package, upstream_package]
+ relationships.append(sbom_data.Relationship(id1=prebuilt_package_id,
+ relationship=sbom_data.RelationshipType.VARIANT_OF,
+ id2=upstream_package_id))
+ for license in licenses:
+ prebuilt_package.declared_license_ids.append(license.id)
+ upstream_package.declared_license_ids.append(license.id)
+
+ if metadata_file_path:
+ metadata_proto = metadata_file_protos[metadata_file_path]
+ if metadata_proto.third_party.WhichOneof('sbom') == 'sbom_ref':
+ sbom_url = metadata_proto.third_party.sbom_ref.url
+ sbom_checksum = metadata_proto.third_party.sbom_ref.checksum
+ upstream_element_id = metadata_proto.third_party.sbom_ref.element_id
+ if sbom_url and sbom_checksum and upstream_element_id:
+ doc_ref_id = f'DocumentRef-{PKG_UPSTREAM}-{sbom_data.encode_for_spdxid(name)}'
+ external_doc_ref = sbom_data.DocumentExternalReference(id=doc_ref_id,
+ uri=sbom_url,
+ checksum=sbom_checksum)
+ relationships.append(
+ sbom_data.Relationship(id1=upstream_package_id,
+ relationship=sbom_data.RelationshipType.VARIANT_OF,
+ id2=doc_ref_id + ':' + upstream_element_id))
+
+ return external_doc_ref, packages, relationships, licenses
+
+
+def save_report(report_file_path, report):
+ with open(report_file_path, 'w', encoding='utf-8') as report_file:
+ for type, issues in report.items():
+ report_file.write(type + '\n')
+ for issue in issues:
+ report_file.write('\t' + issue + '\n')
+ report_file.write('\n')
+
+
+# Validate the metadata generated by Make for installed files and report if there is no metadata.
+def installed_file_has_metadata(installed_file_metadata, report):
+ installed_file = installed_file_metadata['installed_file']
+ module_path = installed_file_metadata['module_path']
+ product_copy_files = installed_file_metadata['product_copy_files']
+ kernel_module_copy_files = installed_file_metadata['kernel_module_copy_files']
+ is_platform_generated = installed_file_metadata['is_platform_generated']
+
+ if (not module_path and
+ not product_copy_files and
+ not kernel_module_copy_files and
+ not is_platform_generated and
+ not installed_file.endswith('.fsv_meta')):
+ report[ISSUE_NO_METADATA].append(installed_file)
+ return False
+
+ return True
+
+
+# Validate identifiers in a package's METADATA.
+# 1) Only known identifier type is allowed
+# 2) Only one identifier's primary_source can be true
+def validate_package_metadata(metadata_file_path, package_metadata):
+ primary_source_found = False
+ for identifier in package_metadata.third_party.identifier:
+ if identifier.type not in THIRD_PARTY_IDENTIFIER_TYPES:
+ sys.exit(f'Unknown value of third_party.identifier.type in {metadata_file_path}/METADATA: {identifier.type}.')
+ if primary_source_found and identifier.primary_source:
+ sys.exit(
+ f'Field "primary_source" is set to true in multiple third_party.identifier in {metadata_file_path}/METADATA.')
+ primary_source_found = identifier.primary_source
+
+
+def report_metadata_file(metadata_file_path, installed_file_metadata, report):
+ if metadata_file_path:
+ report[INFO_METADATA_FOUND_FOR_PACKAGE].append(
+ 'installed_file: {}, module_path: {}, METADATA file: {}'.format(
+ installed_file_metadata['installed_file'],
+ installed_file_metadata['module_path'],
+ metadata_file_path + '/METADATA'))
+
+ package_metadata = metadata_file_pb2.Metadata()
+ with open(metadata_file_path + '/METADATA', 'rt') as f:
+ text_format.Parse(f.read(), package_metadata)
+
+ validate_package_metadata(metadata_file_path, package_metadata)
+
+ if not metadata_file_path in metadata_file_protos:
+ metadata_file_protos[metadata_file_path] = package_metadata
+ if not package_metadata.name:
+ report[ISSUE_METADATA_FILE_INCOMPLETE].append(f'{metadata_file_path}/METADATA does not has "name"')
+
+ if not package_metadata.third_party.version:
+ report[ISSUE_METADATA_FILE_INCOMPLETE].append(
+ f'{metadata_file_path}/METADATA does not has "third_party.version"')
+
+ for tag in package_metadata.third_party.security.tag:
+ if not tag.startswith(NVD_CPE23):
+ report[ISSUE_UNKNOWN_SECURITY_TAG_TYPE].append(
+ f'Unknown security tag type: {tag} in {metadata_file_path}/METADATA')
+ else:
+ report[ISSUE_NO_METADATA_FILE].append(
+ "installed_file: {}, module_path: {}".format(
+ installed_file_metadata['installed_file'], installed_file_metadata['module_path']))
+
+
+# If a file is from a source fork or prebuilt fork package, add its package information to SBOM
+def add_package_of_file(file_id, file_metadata, doc, report):
+ metadata_file_path = get_metadata_file_path(file_metadata)
+ report_metadata_file(metadata_file_path, file_metadata, report)
+
+ external_doc_ref, pkgs, rels, licenses = get_sbom_fragments(file_metadata, metadata_file_path)
+ if len(pkgs) > 0:
+ if external_doc_ref:
+ doc.add_external_ref(external_doc_ref)
+ for p in pkgs:
+ doc.add_package(p)
+ for rel in rels:
+ doc.add_relationship(rel)
+ fork_package_id = pkgs[0].id # The first package should be the source/prebuilt fork package
+ doc.add_relationship(sbom_data.Relationship(id1=file_id,
+ relationship=sbom_data.RelationshipType.GENERATED_FROM,
+ id2=fork_package_id))
+ for license in licenses:
+ doc.add_license(license)
+
+
+# Add STATIC_LINK relationship for static dependencies of a file
+def add_static_deps_of_file(file_id, file_metadata, doc):
+ if not file_metadata['static_dep_files'] and not file_metadata['whole_static_dep_files']:
+ return
+ static_dep_files = []
+ if file_metadata['static_dep_files']:
+ static_dep_files += file_metadata['static_dep_files'].split(' ')
+ if file_metadata['whole_static_dep_files']:
+ static_dep_files += file_metadata['whole_static_dep_files'].split(' ')
+
+ for dep_file in static_dep_files:
+ # Static libs are not shipped on devices, so names are derived from .intermediates paths.
+ doc.add_relationship(sbom_data.Relationship(id1=file_id,
+ relationship=sbom_data.RelationshipType.STATIC_LINK,
+ id2=new_file_id(
+ dep_file.removeprefix(args.soong_out + '/.intermediates/'))))
+
+
+def add_licenses_of_file(file_id, file_metadata, doc):
+ lics = db.get_module_licenses(file_metadata.get('name', ''), file_metadata['module_path'])
+ if lics:
+ file = next(f for f in doc.files if file_id == f.id)
+ for license_name, license_files in lics.items():
+ if not license_files:
+ continue
+ license_id = new_license_id(license_name)
+ file.concluded_license_ids.append(license_id)
+ if license_name not in licenses_text:
+ license_text = get_license_text(license_files.split(' '))
+ licenses_text[license_name] = license_text
+
+ doc.add_license(sbom_data.License(id=license_id, name=license_name, text=licenses_text[license_name]))
+
+
+def get_all_transitive_static_dep_files_of_installed_files(installed_files_metadata, db, report):
+ # Find all transitive static dep files of all installed files
+ q = queue.Queue()
+ for installed_file_metadata in installed_files_metadata:
+ if installed_file_metadata['static_dep_files']:
+ for f in installed_file_metadata['static_dep_files'].split(' '):
+ q.put(f)
+ if installed_file_metadata['whole_static_dep_files']:
+ for f in installed_file_metadata['whole_static_dep_files'].split(' '):
+ q.put(f)
+
+ all_static_dep_files = {}
+ while not q.empty():
+ dep_file = q.get()
+ if dep_file in all_static_dep_files:
+ # It has been processed
+ continue
+
+ all_static_dep_files[dep_file] = True
+ soong_module = db.get_soong_module_of_built_file(dep_file)
+ if not soong_module:
+ # This should not happen, add to report[ISSUE_NO_MODULE_FOUND_FOR_STATIC_DEP]
+ report[ISSUE_NO_MODULE_FOUND_FOR_STATIC_DEP].append(f)
+ continue
+
+ if soong_module['static_dep_files']:
+ for f in soong_module['static_dep_files'].split(' '):
+ if f not in all_static_dep_files:
+ q.put(f)
+ if soong_module['whole_static_dep_files']:
+ for f in soong_module['whole_static_dep_files'].split(' '):
+ if f not in all_static_dep_files:
+ q.put(f)
+
+ return sorted(all_static_dep_files.keys())
+
+
+class MetadataDb:
+ def __init__(self, db):
+ self.conn = sqlite3.connect(':memory')
+ self.conn.row_factory = sqlite3.Row
+ with sqlite3.connect(db) as c:
+ c.backup(self.conn)
+ self.reorg()
+
+ def reorg(self):
+ # package_license table
+ self.conn.execute("create table package_license as "
+ "select name as package, pkg_default_applicable_licenses as license "
+ "from modules "
+ "where module_type = 'package' ")
+ cursor = self.conn.execute("select package,license from package_license where license like '% %'")
+ multi_licenses_packages = cursor.fetchall()
+ cursor.close()
+ rows = []
+ for p in multi_licenses_packages:
+ licenses = p['license'].strip().split(' ')
+ for lic in licenses:
+ rows.append((p['package'], lic))
+ self.conn.executemany('insert into package_license values (?, ?)', rows)
+ self.conn.commit()
+
+ self.conn.execute("delete from package_license where license like '% %'")
+ self.conn.commit()
+
+ # module_license table
+ self.conn.execute("create table module_license as "
+ "select distinct name as module, package, licenses as license "
+ "from modules "
+ "where licenses != '' ")
+ cursor = self.conn.execute("select module,package,license from module_license where license like '% %'")
+ multi_licenses_modules = cursor.fetchall()
+ cursor.close()
+ rows = []
+ for m in multi_licenses_modules:
+ licenses = m['license'].strip().split(' ')
+ for lic in licenses:
+ rows.append((m['module'], m['package'],lic))
+ self.conn.executemany('insert into module_license values (?, ?, ?)', rows)
+ self.conn.commit()
+
+ self.conn.execute("delete from module_license where license like '% %'")
+ self.conn.commit()
+
+ # module_installed_file table
+ self.conn.execute("create table module_installed_file as "
+ "select id as module_id, name as module_name, package, installed_files as installed_file "
+ "from modules "
+ "where installed_files != '' ")
+ cursor = self.conn.execute("select module_id, module_name, package, installed_file "
+ "from module_installed_file where installed_file like '% %'")
+ multi_installed_file_modules = cursor.fetchall()
+ cursor.close()
+ rows = []
+ for m in multi_installed_file_modules:
+ installed_files = m['installed_file'].strip().split(' ')
+ for f in installed_files:
+ rows.append((m['module_id'], m['module_name'], m['package'], f))
+ self.conn.executemany('insert into module_installed_file values (?, ?, ?, ?)', rows)
+ self.conn.commit()
+
+ self.conn.execute("delete from module_installed_file where installed_file like '% %'")
+ self.conn.commit()
+
+ # module_built_file table
+ self.conn.execute("create table module_built_file as "
+ "select id as module_id, name as module_name, package, built_files as built_file "
+ "from modules "
+ "where built_files != '' ")
+ cursor = self.conn.execute("select module_id, module_name, package, built_file "
+ "from module_built_file where built_file like '% %'")
+ multi_built_file_modules = cursor.fetchall()
+ cursor.close()
+ rows = []
+ for m in multi_built_file_modules:
+ built_files = m['installed_file'].strip().split(' ')
+ for f in built_files:
+ rows.append((m['module_id'], m['module_name'], m['package'], f))
+ self.conn.executemany('insert into module_built_file values (?, ?, ?, ?)', rows)
+ self.conn.commit()
+
+ self.conn.execute("delete from module_built_file where built_file like '% %'")
+ self.conn.commit()
+
+
+ # Indexes
+ self.conn.execute('create index idx_modules_id on modules (id)')
+ self.conn.execute('create index idx_modules_name on modules (name)')
+ self.conn.execute('create index idx_package_licnese_package on package_license (package)')
+ self.conn.execute('create index idx_package_licnese_license on package_license (license)')
+ self.conn.execute('create index idx_module_licnese_module on module_license (module)')
+ self.conn.execute('create index idx_module_licnese_license on module_license (license)')
+ self.conn.execute('create index idx_module_installed_file_module_id on module_installed_file (module_id)')
+ self.conn.execute('create index idx_module_installed_file_installed_file on module_installed_file (installed_file)')
+ self.conn.execute('create index idx_module_built_file_module_id on module_built_file (module_id)')
+ self.conn.execute('create index idx_module_built_file_built_file on module_built_file (built_file)')
+ self.conn.commit()
+
+ if args.debug:
+ with sqlite3.connect(os.path.dirname(args.metadata) + '/compliance-metadata-debug.db') as c:
+ self.conn.backup(c)
+
+
+ def get_installed_files(self):
+ # Get all records from table make_metadata, which contains all installed files and corresponding make modules' metadata
+ cursor = self.conn.execute('select installed_file, module_path, is_prebuilt_make_module, product_copy_files, kernel_module_copy_files, is_platform_generated, license_text from make_metadata')
+ rows = cursor.fetchall()
+ cursor.close()
+ installed_files_metadata = []
+ for row in rows:
+ metadata = dict(zip(row.keys(), row))
+ installed_files_metadata.append(metadata)
+ return installed_files_metadata
+
+ def get_soong_modules(self):
+ # Get all records from table modules, which contains metadata of all soong modules
+ cursor = self.conn.execute('select name, package, package as module_path, module_type as soong_module_type, built_files, installed_files, static_dep_files, whole_static_dep_files from modules')
+ rows = cursor.fetchall()
+ cursor.close()
+ soong_modules = []
+ for row in rows:
+ soong_module = dict(zip(row.keys(), row))
+ soong_modules.append(soong_module)
+ return soong_modules
+
+ def get_package_licenses(self, package):
+ cursor = self.conn.execute('select m.name, m.package, m.lic_license_text as license_text '
+ 'from package_license pl join modules m on pl.license = m.name '
+ 'where pl.package = ?',
+ ('//' + package,))
+ rows = cursor.fetchall()
+ licenses = {}
+ for r in rows:
+ licenses[r['name']] = r['license_text']
+ return licenses
+
+ def get_module_licenses(self, module_name, package):
+ licenses = {}
+ # If property "licenses" is defined on module
+ cursor = self.conn.execute('select m.name, m.package, m.lic_license_text as license_text '
+ 'from module_license ml join modules m on ml.license = m.name '
+ 'where ml.module = ? and ml.package = ?',
+ (module_name, package))
+ rows = cursor.fetchall()
+ for r in rows:
+ licenses[r['name']] = r['license_text']
+ if len(licenses) > 0:
+ return licenses
+
+ # Use default package license
+ cursor = self.conn.execute('select m.name, m.package, m.lic_license_text as license_text '
+ 'from package_license pl join modules m on pl.license = m.name '
+ 'where pl.package = ?',
+ ('//' + package,))
+ rows = cursor.fetchall()
+ for r in rows:
+ licenses[r['name']] = r['license_text']
+ return licenses
+
+ def get_soong_module_of_installed_file(self, installed_file):
+ cursor = self.conn.execute('select name, m.package, m.package as module_path, module_type as soong_module_type, built_files, installed_files, static_dep_files, whole_static_dep_files '
+ 'from modules m join module_installed_file mif on m.id = mif.module_id '
+ 'where mif.installed_file = ?',
+ (installed_file,))
+ rows = cursor.fetchall()
+ cursor.close()
+ if rows:
+ soong_module = dict(zip(rows[0].keys(), rows[0]))
+ return soong_module
+
+ return None
+
+ def get_soong_module_of_built_file(self, built_file):
+ cursor = self.conn.execute('select name, m.package, m.package as module_path, module_type as soong_module_type, built_files, installed_files, static_dep_files, whole_static_dep_files '
+ 'from modules m join module_built_file mbf on m.id = mbf.module_id '
+ 'where mbf.built_file = ?',
+ (built_file,))
+ rows = cursor.fetchall()
+ cursor.close()
+ if rows:
+ soong_module = dict(zip(rows[0].keys(), rows[0]))
+ return soong_module
+
+ return None
+
+
+def main():
+ global args
+ args = get_args()
+ log('Args:', vars(args))
+
+ global db
+ db = MetadataDb(args.metadata)
+ global metadata_file_protos
+ metadata_file_protos = {}
+ global licenses_text
+ licenses_text = {}
+
+ product_package_id = sbom_data.SPDXID_PRODUCT
+ product_package_name = sbom_data.PACKAGE_NAME_PRODUCT
+ product_package = sbom_data.Package(id=product_package_id,
+ name=product_package_name,
+ download_location=sbom_data.VALUE_NONE,
+ version=args.build_version,
+ supplier='Organization: ' + args.product_mfr,
+ files_analyzed=True)
+ doc_name = args.build_version
+ doc = sbom_data.Document(name=doc_name,
+ namespace=f'https://www.google.com/sbom/spdx/android/{doc_name}',
+ creators=['Organization: ' + args.product_mfr],
+ describes=product_package_id)
+
+ doc.packages.append(product_package)
+ doc.packages.append(sbom_data.Package(id=sbom_data.SPDXID_PLATFORM,
+ name=sbom_data.PACKAGE_NAME_PLATFORM,
+ download_location=sbom_data.VALUE_NONE,
+ version=args.build_version,
+ supplier='Organization: ' + args.product_mfr,
+ declared_license_ids=[sbom_data.SPDXID_LICENSE_APACHE]))
+
+ # Report on some issues and information
+ report = {
+ ISSUE_NO_METADATA: [],
+ ISSUE_NO_METADATA_FILE: [],
+ ISSUE_METADATA_FILE_INCOMPLETE: [],
+ ISSUE_UNKNOWN_SECURITY_TAG_TYPE: [],
+ ISSUE_INSTALLED_FILE_NOT_EXIST: [],
+ ISSUE_NO_MODULE_FOUND_FOR_STATIC_DEP: [],
+ INFO_METADATA_FOUND_FOR_PACKAGE: [],
+ }
+
+ # Get installed files and corresponding make modules' metadata if an installed file is from a make module.
+ installed_files_metadata = db.get_installed_files()
+
+ # Find which Soong module an installed file is from and merge metadata from Make and Soong
+ for installed_file_metadata in installed_files_metadata:
+ soong_module = db.get_soong_module_of_installed_file(installed_file_metadata['installed_file'])
+ if soong_module:
+ # Merge soong metadata to make metadata
+ installed_file_metadata.update(soong_module)
+ else:
+ # For make modules soong_module_type should be empty
+ installed_file_metadata['soong_module_type'] = ''
+ installed_file_metadata['static_dep_files'] = ''
+ installed_file_metadata['whole_static_dep_files'] = ''
+
+ # Scan the metadata and create the corresponding package and file records in SPDX
+ for installed_file_metadata in installed_files_metadata:
+ installed_file = installed_file_metadata['installed_file']
+ module_path = installed_file_metadata['module_path']
+ product_copy_files = installed_file_metadata['product_copy_files']
+ kernel_module_copy_files = installed_file_metadata['kernel_module_copy_files']
+ build_output_path = installed_file
+ installed_file = installed_file.removeprefix(args.product_out)
+
+ if not installed_file_has_metadata(installed_file_metadata, report):
+ continue
+ if not (os.path.islink(build_output_path) or os.path.isfile(build_output_path)):
+ report[ISSUE_INSTALLED_FILE_NOT_EXIST].append(installed_file)
+ continue
+
+ file_id = new_file_id(installed_file)
+ sha1 = checksum(build_output_path)
+ f = sbom_data.File(id=file_id, name=installed_file, checksum=sha1)
+ doc.files.append(f)
+ product_package.file_ids.append(file_id)
+
+ if is_source_package(installed_file_metadata) or is_prebuilt_package(installed_file_metadata):
+ add_package_of_file(file_id, installed_file_metadata, doc, report)
+
+ elif module_path or installed_file_metadata['is_platform_generated']:
+ # File from PLATFORM package
+ doc.add_relationship(sbom_data.Relationship(id1=file_id,
+ relationship=sbom_data.RelationshipType.GENERATED_FROM,
+ id2=sbom_data.SPDXID_PLATFORM))
+ if installed_file_metadata['is_platform_generated']:
+ f.concluded_license_ids = [sbom_data.SPDXID_LICENSE_APACHE]
+
+ elif product_copy_files:
+ # Format of product_copy_files: <source path>:<dest path>
+ src_path = product_copy_files.split(':')[0]
+ # So far product_copy_files are copied from directory system, kernel, hardware, frameworks and device,
+ # so process them as files from PLATFORM package
+ doc.add_relationship(sbom_data.Relationship(id1=file_id,
+ relationship=sbom_data.RelationshipType.GENERATED_FROM,
+ id2=sbom_data.SPDXID_PLATFORM))
+ if installed_file_metadata['license_text']:
+ if installed_file_metadata['license_text'] == 'build/soong/licenses/LICENSE':
+ f.concluded_license_ids = [sbom_data.SPDXID_LICENSE_APACHE]
+
+ elif installed_file.endswith('.fsv_meta'):
+ doc.add_relationship(sbom_data.Relationship(id1=file_id,
+ relationship=sbom_data.RelationshipType.GENERATED_FROM,
+ id2=sbom_data.SPDXID_PLATFORM))
+ f.concluded_license_ids = [sbom_data.SPDXID_LICENSE_APACHE]
+
+ elif kernel_module_copy_files.startswith('ANDROID-GEN'):
+ # For the four files generated for _dlkm, _ramdisk partitions
+ doc.add_relationship(sbom_data.Relationship(id1=file_id,
+ relationship=sbom_data.RelationshipType.GENERATED_FROM,
+ id2=sbom_data.SPDXID_PLATFORM))
+
+ # Process static dependencies of the installed file
+ add_static_deps_of_file(file_id, installed_file_metadata, doc)
+
+ # Add licenses of the installed file
+ add_licenses_of_file(file_id, installed_file_metadata, doc)
+
+ # Add all static library files to SBOM
+ for dep_file in get_all_transitive_static_dep_files_of_installed_files(installed_files_metadata, db, report):
+ filepath = dep_file.removeprefix(args.soong_out + '/.intermediates/')
+ file_id = new_file_id(filepath)
+ # SHA1 of empty string. Sometimes .a files might not be built.
+ sha1 = 'SHA1: da39a3ee5e6b4b0d3255bfef95601890afd80709'
+ if os.path.islink(dep_file) or os.path.isfile(dep_file):
+ sha1 = checksum(dep_file)
+ doc.files.append(sbom_data.File(id=file_id,
+ name=filepath,
+ checksum=sha1))
+ file_metadata = {
+ 'installed_file': dep_file,
+ 'is_prebuilt_make_module': False
+ }
+ file_metadata.update(db.get_soong_module_of_built_file(dep_file))
+ add_package_of_file(file_id, file_metadata, doc, report)
+
+ # Add relationships for static deps of static libraries
+ add_static_deps_of_file(file_id, file_metadata, doc)
+
+ # Add licenses of the static lib
+ add_licenses_of_file(file_id, file_metadata, doc)
+
+ # Save SBOM records to output file
+ doc.generate_packages_verification_code()
+ doc.created = datetime.datetime.now(tz=datetime.timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ')
+ prefix = args.output_file
+ if prefix.endswith('.spdx'):
+ prefix = prefix.removesuffix('.spdx')
+ elif prefix.endswith('.spdx.json'):
+ prefix = prefix.removesuffix('.spdx.json')
+
+ output_file = prefix + '.spdx'
+ with open(output_file, 'w', encoding="utf-8") as file:
+ sbom_writers.TagValueWriter.write(doc, file)
+ if args.json:
+ with open(prefix + '.spdx.json', 'w', encoding="utf-8") as file:
+ sbom_writers.JSONWriter.write(doc, file)
+
+ save_report(prefix + '-gen-report.txt', report)
+
+
+if __name__ == '__main__':
+ main()