diff options
151 files changed, 3461 insertions, 1660 deletions
diff --git a/ci/test_discovery_agent.py b/ci/test_discovery_agent.py index 89d35d71ae..4eed28dcb9 100644 --- a/ci/test_discovery_agent.py +++ b/ci/test_discovery_agent.py @@ -11,18 +11,33 @@ # 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. +"""Test discovery agent that uses TradeFed to discover test artifacts.""" +import glob +import json +import logging +import os +import subprocess +import buildbot + + class TestDiscoveryAgent: """Test discovery agent.""" - _AOSP_TRADEFED_PREBUILT_JAR_RELATIVE_PATH = ( - "tools/tradefederation/prebuilts/filegroups/tradefed/" + _TRADEFED_PREBUILT_JAR_RELATIVE_PATH = ( + "vendor/google_tradefederation/prebuilts/filegroups/google-tradefed/" ) + _TRADEFED_NO_POSSIBLE_TEST_DISCOVERY_KEY = "NoPossibleTestDiscovery" + + _TRADEFED_TEST_ZIP_REGEXES_LIST_KEY = "TestZipRegexes" + + _TRADEFED_DISCOVERY_OUTPUT_FILE_NAME = "test_discovery_agent.txt" + def __init__( self, tradefed_args: list[str], - test_mapping_zip_path: str, - tradefed_jar_revelant_files_path: str = _AOSP_TRADEFED_PREBUILT_JAR_RELATIVE_PATH, + test_mapping_zip_path: str = "", + tradefed_jar_revelant_files_path: str = _TRADEFED_PREBUILT_JAR_RELATIVE_PATH, ): self.tradefed_args = tradefed_args self.test_mapping_zip_path = test_mapping_zip_path @@ -34,7 +49,46 @@ class TestDiscoveryAgent: Returns: A list of test zip regexes that TF is going to try to pull files from. """ - return [] + test_discovery_output_file_name = os.path.join( + buildbot.OutDir(), self._TRADEFED_DISCOVERY_OUTPUT_FILE_NAME + ) + with open( + test_discovery_output_file_name, mode="w+t" + ) as test_discovery_output_file: + java_args = [] + java_args.append("prebuilts/jdk/jdk21/linux-x86/bin/java") + java_args.append("-cp") + java_args.append( + self.create_classpath(self.tradefed_jar_relevant_files_path) + ) + java_args.append( + "com.android.tradefed.observatory.TestZipDiscoveryExecutor" + ) + java_args.extend(self.tradefed_args) + env = os.environ.copy() + env.update({"DISCOVERY_OUTPUT_FILE": test_discovery_output_file.name}) + logging.info(f"Calling test discovery with args: {java_args}") + try: + result = subprocess.run(args=java_args, env=env, text=True, check=True) + logging.info(f"Test zip discovery output: {result.stdout}") + except subprocess.CalledProcessError as e: + raise TestDiscoveryError( + f"Failed to run test discovery, strout: {e.stdout}, strerr:" + f" {e.stderr}, returncode: {e.returncode}" + ) + data = json.loads(test_discovery_output_file.read()) + logging.info(f"Test discovery result file content: {data}") + if ( + self._TRADEFED_NO_POSSIBLE_TEST_DISCOVERY_KEY in data + and data[self._TRADEFED_NO_POSSIBLE_TEST_DISCOVERY_KEY] + ): + raise TestDiscoveryError("No possible test discovery") + if ( + data[self._TRADEFED_TEST_ZIP_REGEXES_LIST_KEY] is None + or data[self._TRADEFED_TEST_ZIP_REGEXES_LIST_KEY] is [] + ): + raise TestDiscoveryError("No test zip regexes returned") + return data[self._TRADEFED_TEST_ZIP_REGEXES_LIST_KEY] def discover_test_modules(self) -> list[str]: """Discover test modules from TradeFed. @@ -44,3 +98,24 @@ class TestDiscoveryAgent: TradeFed test args. """ return [] + + def create_classpath(self, directory): + """Creates a classpath string from all .jar files in the given directory. + + Args: + directory: The directory to search for .jar files. + + Returns: + A string representing the classpath, with jar files separated by the + OS-specific path separator (e.g., ':' on Linux/macOS, ';' on Windows). + """ + jar_files = glob.glob(os.path.join(directory, "*.jar")) + return os.pathsep.join(jar_files) + + +class TestDiscoveryError(Exception): + """A TestDiscoveryErrorclass.""" + + def __init__(self, message): + super().__init__(message) + self.message = message diff --git a/core/Makefile b/core/Makefile index 81ae6f784c..43618e316d 100644 --- a/core/Makefile +++ b/core/Makefile @@ -692,7 +692,7 @@ endif BOARD_KERNEL_MODULE_DIRS += top -# Default to not generating modules.dep for kernel modules on system +# Default to not generating modules.load for kernel modules on system # side. We should only load these modules if they are depended by vendor # side modules. ifeq ($(BOARD_SYSTEM_KERNEL_MODULES_LOAD),) @@ -1676,12 +1676,13 @@ ifdef INTERNAL_KERNEL_CMDLINE INTERNAL_VENDOR_BOOTIMAGE_ARGS += --vendor_cmdline "$(INTERNAL_KERNEL_CMDLINE)" endif -ifdef INTERNAL_BOOTCONFIG +ifneq (, $(INTERNAL_BOOTCONFIG)$(INTERNAL_BOOTCONFIG_FILE)) INTERNAL_VENDOR_BOOTCONFIG_TARGET := $(PRODUCT_OUT)/vendor-bootconfig.img $(INTERNAL_VENDOR_BOOTCONFIG_TARGET): rm -f $@ $(foreach param,$(INTERNAL_BOOTCONFIG), \ printf "%s\n" $(param) >> $@;) + cat $(INTERNAL_BOOTCONFIG_FILE) >> $@ INTERNAL_VENDOR_BOOTIMAGE_ARGS += --vendor_bootconfig $(INTERNAL_VENDOR_BOOTCONFIG_TARGET) endif @@ -3488,12 +3489,14 @@ endef # $(2): The partition's staging directory # $(3): Files to include in the partition define write-partition-file-list +$(1): PRIVATE_FILES := $(subst $(2)/,,$(filter $(2)/%,$(3))) +$(1): PRIVATE_EXTRA_INSTALL_ZIPS := $(call relevant-extra-install-zips,$(filter $(2)/%,$(3))) $(1): $$(HOST_OUT_EXECUTABLES)/extra_install_zips_file_list $(foreach p,$(call relevant-extra-install-zips,$(filter $(2)/%,$(3))),$(call word-colon,3,$(p))) @echo Writing $$@ rm -f $$@ echo -n > $$@ - $$(foreach f,$(subst $(2)/,,$(filter $(2)/%,$(3))),echo "$$(f)" >> $$@$$(newline)) - $$(HOST_OUT_EXECUTABLES)/extra_install_zips_file_list $(2) $(call relevant-extra-install-zips,$(filter $(2)/%,$(3))) >> $$@ + $$(foreach f,$$(PRIVATE_FILES),echo "$$(f)" >> $$@$$(newline)) + $$(HOST_OUT_EXECUTABLES)/extra_install_zips_file_list $(2) $$(PRIVATE_EXTRA_INSTALL_ZIPS) >> $$@ endef # ----------------------------------------------------------------- @@ -5295,7 +5298,7 @@ $(vintffm_log): $(HOST_OUT_EXECUTABLES)/vintffm $(check_vintf_system_deps) $(APE --dirmap /system_ext:$(TARGET_OUT_SYSTEM_EXT) \ --dirmap /product:$(TARGET_OUT_PRODUCT) \ --dirmap /apex:$(APEX_OUT) \ - $(VINTF_FRAMEWORK_MANIFEST_FROZEN_DIR) > $@ 2>&1 ) || ( cat $@ && exit 1 ) + system/libhidl/vintfdata/frozen > $@ 2>&1 ) || ( cat $@ && exit 1 ) $(call declare-1p-target,$(vintffm_log)) @@ -6380,6 +6383,10 @@ ifdef BUILDING_VENDOR_BOOT_IMAGE endif endif +ifdef BUILDING_VENDOR_KERNEL_BOOT_IMAGE + $(BUILT_TARGET_FILES_DIR): $(INTERNAL_VENDOR_KERNEL_RAMDISK_FILES) +endif + ifdef BUILDING_RECOVERY_IMAGE # TODO(b/30414428): Can't depend on INTERNAL_RECOVERYIMAGE_FILES alone like other # BUILT_TARGET_FILES_PACKAGE dependencies because currently there're cp/rsync/rm @@ -7973,7 +7980,7 @@ endif # PACK_DESKTOP_FILESYSTEM_IMAGES # ----------------------------------------------------------------- # Desktop pack recovery image hook. -ifneq (,$(strip $(PACK_DESKTOP_RECOVERY_IMAGE))) +ifeq ($(BOARD_USES_DESKTOP_RECOVERY_IMAGE),true) PACK_RECOVERY_IMAGE_TARGET := $(PRODUCT_OUT)/android-desktop_recovery_image.bin PACK_RECOVERY_IMAGE_ARGS := --noarchive --recovery @@ -7994,11 +8001,11 @@ $(call dist-for-goals,dist_files,$(PACKED_RECOVERY_IMAGE_ARCHIVE_TARGET)) .PHONY: pack-recovery-image pack-recovery-image: $(PACK_RECOVERY_IMAGE_TARGET) -endif # PACK_DESKTOP_RECOVERY_IMAGE +endif # BOARD_USES_DESKTOP_RECOVERY_IMAGE # ----------------------------------------------------------------- # Desktop pack update image hook. -ifneq (,$(strip $(PACK_DESKTOP_UPDATE_IMAGE))) +ifeq ($(BOARD_USES_DESKTOP_UPDATE_IMAGE),true) PACK_UPDATE_IMAGE_TARGET := $(PRODUCT_OUT)/android-desktop_update_image.bin PACK_UPDATE_IMAGE_ARGS := --noarchive --update @@ -8019,7 +8026,7 @@ $(call dist-for-goals,dist_files,$(PACKED_UPDATE_IMAGE_ARCHIVE_TARGET)) .PHONY: pack-update-image pack-update-image: $(PACK_UPDATE_IMAGE_TARGET) -endif # PACK_DESKTOP_UPDATE_IMAGE +endif # BOARD_USES_DESKTOP_UPDATE_IMAGE PACK_MIGRATION_IMAGE_SCRIPT := $(HOST_OUT_EXECUTABLES)/pack_migration_image diff --git a/core/android_soong_config_vars.mk b/core/android_soong_config_vars.mk index e5a8e443a0..26fe1dadb3 100644 --- a/core/android_soong_config_vars.mk +++ b/core/android_soong_config_vars.mk @@ -42,6 +42,12 @@ $(call add_soong_config_var,ANDROID,TARGET_DYNAMIC_64_32_MEDIASERVER) $(call add_soong_config_var,ANDROID,ADDITIONAL_M4DEFS,$(if $(BOARD_SEPOLICY_M4DEFS),$(addprefix -D,$(BOARD_SEPOLICY_M4DEFS)))) +# For bootable/recovery +RECOVERY_API_VERSION := 3 +RECOVERY_FSTAB_VERSION := 2 +$(call soong_config_set, recovery, recovery_api_version, $(RECOVERY_API_VERSION)) +$(call soong_config_set, recovery, recovery_fstab_version, $(RECOVERY_FSTAB_VERSION)) + # For Sanitizers $(call soong_config_set_bool,ANDROID,ASAN_ENABLED,$(if $(filter address,$(SANITIZE_TARGET)),true,false)) $(call soong_config_set_bool,ANDROID,HWASAN_ENABLED,$(if $(filter hwaddress,$(SANITIZE_TARGET)),true,false)) @@ -65,6 +71,11 @@ endif # The default value of ART_BUILD_HOST_DEBUG is true $(call soong_config_set_bool,art_module,art_build_host_debug,$(if $(filter false,$(ART_BUILD_HOST_DEBUG)),false,true)) +# For chre +$(call soong_config_set_bool,chre,chre_daemon_lama_enabled,$(if $(filter true,$(CHRE_DAEMON_LPMA_ENABLED)),true,false)) +$(call soong_config_set_bool,chre,chre_dedicated_transport_channel_enabled,$(if $(filter true,$(CHRE_DEDICATED_TRANSPORT_CHANNEL_ENABLED)),true,false)) +$(call soong_config_set_bool,chre,chre_log_atom_extension_enabled,$(if $(filter true,$(CHRE_LOG_ATOM_EXTENSION_ENABLED)),true,false)) + ifdef TARGET_BOARD_AUTO $(call add_soong_config_var_value, ANDROID, target_board_auto, $(TARGET_BOARD_AUTO)) endif @@ -96,6 +107,10 @@ ifdef PRODUCT_AVF_MICRODROID_GUEST_GKI_VERSION $(call add_soong_config_var_value,ANDROID,avf_microdroid_guest_gki_version,$(PRODUCT_AVF_MICRODROID_GUEST_GKI_VERSION)) endif +ifdef TARGET_BOOTS_16K +$(call soong_config_set_bool,ANDROID,target_boots_16k,$(filter true,$(TARGET_BOOTS_16K))) +endif + ifdef PRODUCT_MEMCG_V2_FORCE_ENABLED $(call add_soong_config_var_value,ANDROID,memcg_v2_force_enabled,$(PRODUCT_MEMCG_V2_FORCE_ENABLED)) endif @@ -219,5 +234,45 @@ $(call soong_config_set,video_codec,target_soc_name,$(TARGET_SOC_NAME)) $(call soong_config_set_bool,video_codec,board_use_codec2_hidl_1_2,$(if $(filter true,$(BOARD_USE_CODEC2_HIDL_1_2)),true,false)) $(call soong_config_set_bool,video_codec,board_support_mfc_enc_bt2020,$(if $(filter true,$(BOARD_SUPPORT_MFC_ENC_BT2020)),true,false)) $(call soong_config_set_bool,video_codec,board_support_flexible_p010,$(if $(filter true,$(BOARD_SUPPORT_FLEXIBLE_P010)),true,false)) -$(call soong_config_set,video_codec,board_support_mfc_version,$(BOARD_SUPPORT_MFC_VERSION)) $(call soong_config_set_bool,video_codec,board_use_codec2_aidl,$(if $(BOARD_USE_CODEC2_AIDL),true,false)) +$(call soong_config_set,video_codec,board_gpu_type,$(BOARD_GPU_TYPE)) +$(call soong_config_set_bool,video_codec,board_use_small_secure_memory,$(if $(filter true,$(BOARD_USE_SMALL_SECURE_MEMORY)),true,false)) +ifdef BOARD_SUPPORT_MFC_VERSION + $(call soong_config_set,video_codec,board_support_mfc_version,$(BOARD_SUPPORT_MFC_VERSION)) +endif +ifdef BOARD_USE_MAX_SECURE_RESOURCE + $(call soong_config_set,video_codec,board_use_max_secure_resource,$(BOARD_USE_MAX_SECURE_RESOURCE)) +endif + +# Export related variables to soong for hardware/google/graphics/common/libacryl:libacryl +ifdef BOARD_LIBACRYL_DEFAULT_COMPOSITOR + $(call soong_config_set,acryl,libacryl_default_compositor,$(BOARD_LIBACRYL_DEFAULT_COMPOSITOR)) +endif +ifdef BOARD_LIBACRYL_DEFAULT_SCALER + $(call soong_config_set,acryl,libacryl_default_scaler,$(BOARD_LIBACRYL_DEFAULT_SCALER)) +endif +ifdef BOARD_LIBACRYL_DEFAULT_BLTER + $(call soong_config_set,acryl,libacryl_default_blter,$(BOARD_LIBACRYL_DEFAULT_BLTER)) +endif +ifdef BOARD_LIBACRYL_G2D_HDR_PLUGIN + #BOARD_LIBACRYL_G2D_HDR_PLUGIN is set in each board config + $(call soong_config_set_bool,acryl,libacryl_use_g2d_hdr_plugin,true) +endif + +# Export related variables to soong for hardware/google/graphics/common/BoardConfigCFlags.mk +$(call soong_config_set_bool,google_graphics,hwc_no_support_skip_validate,$(if $(filter true,$(HWC_NO_SUPPORT_SKIP_VALIDATE)),true,false)) +$(call soong_config_set_bool,google_graphics,hwc_support_color_transform,$(if $(filter true,$(HWC_SUPPORT_COLOR_TRANSFORM)),true,false)) +$(call soong_config_set_bool,google_graphics,hwc_support_render_intent,$(if $(filter true,$(HWC_SUPPORT_RENDER_INTENT)),true,false)) +$(call soong_config_set_bool,google_graphics,board_uses_virtual_display,$(if $(filter true,$(BOARD_USES_VIRTUAL_DISPLAY)),true,false)) +$(call soong_config_set_bool,google_graphics,board_uses_dt,$(if $(filter true,$(BOARD_USES_DT)),true,false)) +$(call soong_config_set_bool,google_graphics,board_uses_decon_64bit_address,$(if $(filter true,$(BOARD_USES_DECON_64BIT_ADDRESS)),true,false)) +$(call soong_config_set_bool,google_graphics,board_uses_hdrui_gles_conversion,$(if $(filter true,$(BOARD_USES_HDRUI_GLES_CONVERSION)),true,false)) +$(call soong_config_set_bool,google_graphics,uses_idisplay_intf_sec,$(if $(filter true,$(USES_IDISPLAY_INTF_SEC)),true,false)) + +# Variables for fs_config +$(call soong_config_set_bool,fs_config,vendor,$(if $(BOARD_USES_VENDORIMAGE)$(BOARD_VENDORIMAGE_FILE_SYSTEM_TYPE),true,false)) +$(call soong_config_set_bool,fs_config,oem,$(if $(BOARD_USES_OEMIMAGE)$(BOARD_OEMIMAGE_FILE_SYSTEM_TYPE),true,false)) +$(call soong_config_set_bool,fs_config,odm,$(if $(BOARD_USES_ODMIMAGE)$(BOARD_ODMIMAGE_FILE_SYSTEM_TYPE),true,false)) +$(call soong_config_set_bool,fs_config,vendor_dlkm,$(if $(BOARD_USES_VENDOR_DLKMIMAGE)$(BOARD_VENDOR_DLKMIMAGE_FILE_SYSTEM_TYPE),true,false)) +$(call soong_config_set_bool,fs_config,odm_dlkm,$(if $(BOARD_USES_ODM_DLKMIMAGE)$(BOARD_ODM_DLKMIMAGE_FILE_SYSTEM_TYPE),true,false)) +$(call soong_config_set_bool,fs_config,system_dlkm,$(if $(BOARD_USES_SYSTEM_DLKMIMAGE)$(BOARD_SYSTEM_DLKMIMAGE_FILE_SYSTEM_TYPE),true,false)) diff --git a/core/binary.mk b/core/binary.mk index 34811449e9..ea862be6b4 100644 --- a/core/binary.mk +++ b/core/binary.mk @@ -174,7 +174,7 @@ my_allow_undefined_symbols := true endif endif -my_ndk_sysroot_include := +my_ndk_sysroot := my_ndk_sysroot_lib := my_api_level := 10000 @@ -207,11 +207,9 @@ ifneq ($(LOCAL_SDK_VERSION),) my_built_ndk := $(SOONG_OUT_DIR)/ndk my_ndk_triple := $($(LOCAL_2ND_ARCH_VAR_PREFIX)TARGET_NDK_TRIPLE) - my_ndk_sysroot_include := \ - $(my_built_ndk)/sysroot/usr/include \ - $(my_built_ndk)/sysroot/usr/include/$(my_ndk_triple) \ + my_ndk_sysroot := $(my_built_ndk)/sysroot - my_ndk_sysroot_lib := $(my_built_ndk)/sysroot/usr/lib/$(my_ndk_triple)/$(my_ndk_api) + my_ndk_sysroot_lib := $(my_ndk_sysroot)/usr/lib/$(my_ndk_triple)/$(my_ndk_api) # The bionic linker now has support for packed relocations and gnu style # hashes (which are much faster!), but shipping to older devices requires @@ -1628,19 +1626,6 @@ my_ldlibs += $(my_cxx_ldlibs) ########################################################### ifndef LOCAL_IS_HOST_MODULE -ifeq ($(call module-in-vendor-or-product),true) - my_target_global_c_includes := - my_target_global_c_system_includes := $(TARGET_OUT_HEADERS) -else ifdef LOCAL_SDK_VERSION - my_target_global_c_includes := - my_target_global_c_system_includes := $(my_ndk_stl_include_path) $(my_ndk_sysroot_include) -else - my_target_global_c_includes := $(SRC_HEADERS) \ - $($(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)C_INCLUDES) - my_target_global_c_system_includes := $(SRC_SYSTEM_HEADERS) \ - $($(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)C_SYSTEM_INCLUDES) -endif - my_target_global_cflags := $($(LOCAL_2ND_ARCH_VAR_PREFIX)CLANG_$(my_prefix)GLOBAL_CFLAGS) my_target_global_conlyflags := $($(LOCAL_2ND_ARCH_VAR_PREFIX)CLANG_$(my_prefix)GLOBAL_CONLYFLAGS) $(my_c_std_conlyflags) my_target_global_cppflags := $($(LOCAL_2ND_ARCH_VAR_PREFIX)CLANG_$(my_prefix)GLOBAL_CPPFLAGS) $(my_cpp_std_cppflags) @@ -1656,6 +1641,22 @@ else my_target_global_ldflags := $($(LOCAL_2ND_ARCH_VAR_PREFIX)CLANG_$(my_prefix)GLOBAL_LDFLAGS) endif # my_use_clang_lld +ifeq ($(call module-in-vendor-or-product),true) + my_target_global_c_includes := + my_target_global_c_system_includes := $(TARGET_OUT_HEADERS) + my_target_global_cflags += -nostdlibinc +else ifdef LOCAL_SDK_VERSION + my_target_global_c_includes := + my_target_global_c_system_includes := $(my_ndk_stl_include_path) + my_target_global_cflags += --sysroot $(my_ndk_sysroot) +else + my_target_global_c_includes := $(SRC_HEADERS) \ + $($(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)C_INCLUDES) + my_target_global_c_system_includes := $(SRC_SYSTEM_HEADERS) \ + $($(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)C_SYSTEM_INCLUDES) + my_target_global_cflags += -nostdlibinc +endif + my_target_triple := $($(LOCAL_2ND_ARCH_VAR_PREFIX)CLANG_$(my_prefix)TRIPLE) ifndef LOCAL_IS_HOST_MODULE my_target_triple_flag := -target $(my_target_triple)$(my_api_level) diff --git a/core/board_config.mk b/core/board_config.mk index 5606964950..ea0d02253c 100644 --- a/core/board_config.mk +++ b/core/board_config.mk @@ -27,6 +27,7 @@ _board_strip_readonly_list += BOARD_INSTALLER_CMDLINE _board_strip_readonly_list += BOARD_KERNEL_CMDLINE _board_strip_readonly_list += BOARD_BOOT_HEADER_VERSION _board_strip_readonly_list += BOARD_BOOTCONFIG +_board_strip_readonly_list += BOARD_BOOTCONFIG_FILE _board_strip_readonly_list += BOARD_KERNEL_BASE _board_strip_readonly_list += BOARD_USES_GENERIC_AUDIO _board_strip_readonly_list += BOARD_USES_RECOVERY_AS_BOOT @@ -311,9 +312,10 @@ endif .KATI_READONLY := $(_board_strip_readonly_list) INTERNAL_KERNEL_CMDLINE := $(BOARD_KERNEL_CMDLINE) -ifneq (,$(BOARD_BOOTCONFIG)) +ifneq (,$(BOARD_BOOTCONFIG)$(BOARD_BOOTCONFIG_FILE)) INTERNAL_KERNEL_CMDLINE += bootconfig INTERNAL_BOOTCONFIG := $(BOARD_BOOTCONFIG) + INTERNAL_BOOTCONFIG_FILE := $(BOARD_BOOTCONFIG_FILE) endif ifneq ($(filter %64,$(TARGET_ARCH)),) @@ -922,6 +924,18 @@ ifeq ($(PRODUCT_BUILD_PVMFW_IMAGE),true) endif .KATI_READONLY := BOARD_USES_PVMFWIMAGE +BOARD_USES_DESKTOP_RECOVERY_IMAGE := +ifeq ($(PRODUCT_BUILD_DESKTOP_RECOVERY_IMAGE),true) + BOARD_USES_DESKTOP_RECOVERY_IMAGE := true +endif +.KATI_READONLY := BOARD_USES_DESKTOP_RECOVERY_IMAGE + +BOARD_USES_DESKTOP_UPDATE_IMAGE := +ifeq ($(PRODUCT_BUILD_DESKTOP_UPDATE_IMAGE),true) + BOARD_USES_DESKTOP_UPDATE_IMAGE := true +endif +.KATI_READONLY := BOARD_USES_DESKTOP_UPDATE_IMAGE + ########################################### # Ensure consistency among TARGET_RECOVERY_UPDATER_LIBS, AB_OTA_UPDATER, and PRODUCT_OTA_FORCE_NON_AB_PACKAGE. TARGET_RECOVERY_UPDATER_LIBS ?= diff --git a/core/config.mk b/core/config.mk index 192c8b28c8..f9ba38ce28 100644 --- a/core/config.mk +++ b/core/config.mk @@ -432,13 +432,6 @@ else endif .KATI_READONLY := TARGET_MAX_PAGE_SIZE_SUPPORTED -ifdef PRODUCT_CHECK_PREBUILT_MAX_PAGE_SIZE - TARGET_CHECK_PREBUILT_MAX_PAGE_SIZE := $(PRODUCT_CHECK_PREBUILT_MAX_PAGE_SIZE) -else - TARGET_CHECK_PREBUILT_MAX_PAGE_SIZE := false -endif -.KATI_READONLY := TARGET_CHECK_PREBUILT_MAX_PAGE_SIZE - # Boolean variable determining if AOSP relies on bionic's PAGE_SIZE macro. ifdef PRODUCT_NO_BIONIC_PAGE_SIZE_MACRO TARGET_NO_BIONIC_PAGE_SIZE_MACRO := $(PRODUCT_NO_BIONIC_PAGE_SIZE_MACRO) @@ -817,6 +810,18 @@ ifneq ($(call math_gt_or_eq,$(PRODUCT_SHIPPING_API_LEVEL),36),) endif endif +ifdef PRODUCT_CHECK_PREBUILT_MAX_PAGE_SIZE + TARGET_CHECK_PREBUILT_MAX_PAGE_SIZE := $(PRODUCT_CHECK_PREBUILT_MAX_PAGE_SIZE) +else ifeq (true,$(TARGET_BUILD_UNBUNDLED)) + # unbundled builds may not have updated build sources + TARGET_CHECK_PREBUILT_MAX_PAGE_SIZE := false +else ifneq ($(call math_gt_or_eq,$(PRODUCT_SHIPPING_API_LEVEL),36),) + TARGET_CHECK_PREBUILT_MAX_PAGE_SIZE := true +else + TARGET_CHECK_PREBUILT_MAX_PAGE_SIZE := false +endif +.KATI_READONLY := TARGET_CHECK_PREBUILT_MAX_PAGE_SIZE + # Set BOARD_SYSTEMSDK_VERSIONS to the latest SystemSDK version starting from P-launching # devices if unset. ifndef BOARD_SYSTEMSDK_VERSIONS @@ -839,12 +844,6 @@ endif .KATI_READONLY := BOARD_CURRENT_API_LEVEL_FOR_VENDOR_MODULES ifdef PRODUCT_SHIPPING_API_LEVEL - board_api_level := $(firstword $(BOARD_API_LEVEL) $(BOARD_SHIPPING_API_LEVEL)) - ifneq (,$(board_api_level)) - min_systemsdk_version := $(call math_min,$(board_api_level),$(PRODUCT_SHIPPING_API_LEVEL)) - else - min_systemsdk_version := $(PRODUCT_SHIPPING_API_LEVEL) - endif ifneq ($(call math_gt_or_eq,$(PRODUCT_SHIPPING_API_LEVEL),29),) ifneq ($(BOARD_OTA_FRAMEWORK_VBMETA_VERSION_OVERRIDE),) $(error When PRODUCT_SHIPPING_API_LEVEL >= 29, BOARD_OTA_FRAMEWORK_VBMETA_VERSION_OVERRIDE cannot be set) diff --git a/core/main.mk b/core/main.mk index 60955878ed..5142adc759 100644 --- a/core/main.mk +++ b/core/main.mk @@ -83,6 +83,8 @@ $(shell mkdir -p $(EMPTY_DIRECTORY) && rm -rf $(EMPTY_DIRECTORY)/*) -include test/cts-root/tools/build/config.mk # WVTS-specific config. -include test/wvts/tools/build/config.mk +# DTS-specific config. +-include test/dts/tools/build/config.mk # Clean rules @@ -290,7 +292,7 @@ subdir_makefiles_total := $(words int $(subdir_makefiles) post finish) $(foreach mk,$(subdir_makefiles),$(info [$(call inc_and_print,subdir_makefiles_inc)/$(subdir_makefiles_total)] including $(mk) ...)$(eval include $(mk))) # Build bootloader.img/radio.img, and unpack the partitions. -include $(BUILD_SYSTEM)/tasks/tools/update_bootloader_radio_image.mk +-include vendor/google/build/tasks/tools/update_bootloader_radio_image.mk # For an unbundled image, we can skip blueprint_tools because unbundled image # aims to remove a large number framework projects from the manifest, the @@ -982,9 +984,7 @@ endef # variables being set. define auto-included-modules $(foreach vndk_ver,$(PRODUCT_EXTRA_VNDK_VERSIONS),com.android.vndk.v$(vndk_ver)) \ - $(filter-out $(LLNDK_MOVED_TO_APEX_LIBRARIES),$(LLNDK_LIBRARIES)) \ llndk.libraries.txt \ - $(if $(DEVICE_MANIFEST_FILE),vendor_manifest.xml) \ $(if $(DEVICE_MANIFEST_SKUS),$(foreach sku, $(DEVICE_MANIFEST_SKUS),vendor_manifest_$(sku).xml)) \ $(if $(ODM_MANIFEST_FILES),odm_manifest.xml) \ $(if $(ODM_MANIFEST_SKUS),$(foreach sku, $(ODM_MANIFEST_SKUS),odm_manifest_$(sku).xml)) \ diff --git a/core/product.mk b/core/product.mk index 8fc40f820c..7cf4ec26d8 100644 --- a/core/product.mk +++ b/core/product.mk @@ -366,6 +366,8 @@ _product_single_value_vars += PRODUCT_BUILD_DEBUG_VENDOR_BOOT_IMAGE _product_single_value_vars += PRODUCT_BUILD_VBMETA_IMAGE _product_single_value_vars += PRODUCT_BUILD_SUPER_EMPTY_IMAGE _product_single_value_vars += PRODUCT_BUILD_PVMFW_IMAGE +_product_single_value_vars += PRODUCT_BUILD_DESKTOP_RECOVERY_IMAGE +_product_single_value_vars += PRODUCT_BUILD_DESKTOP_UPDATE_IMAGE # List of boot jars delivered via updatable APEXes, following the same format as # PRODUCT_BOOT_JARS. diff --git a/core/product_config.mk b/core/product_config.mk index 738d4cff58..abe6e38a93 100644 --- a/core/product_config.mk +++ b/core/product_config.mk @@ -424,10 +424,12 @@ ifdef PRODUCT_DEFAULT_DEV_CERTIFICATE endif endif -$(foreach pair,$(PRODUCT_APEX_BOOT_JARS), \ - $(eval jar := $(call word-colon,2,$(pair))) \ - $(if $(findstring $(jar), $(PRODUCT_BOOT_JARS)), \ - $(error A jar in PRODUCT_APEX_BOOT_JARS must not be in PRODUCT_BOOT_JARS, but $(jar) is))) +$(foreach apexpair,$(PRODUCT_APEX_BOOT_JARS), \ + $(foreach platformpair,$(PRODUCT_BOOT_JARS), \ + $(eval apexjar := $(call word-colon,2,$(apexpair))) \ + $(eval platformjar := $(call word-colon,2,$(platformpair))) \ + $(if $(filter $(apexjar), $(platformjar)), \ + $(error A jar in PRODUCT_APEX_BOOT_JARS must not be in PRODUCT_BOOT_JARS, but $(apexjar) is)))) ENFORCE_SYSTEM_CERTIFICATE := $(PRODUCT_ENFORCE_ARTIFACT_SYSTEM_CERTIFICATE_REQUIREMENT) ENFORCE_SYSTEM_CERTIFICATE_ALLOW_LIST := $(PRODUCT_ARTIFACT_SYSTEM_CERTIFICATE_REQUIREMENT_ALLOW_LIST) @@ -602,7 +604,12 @@ else # Vendors with GRF must define BOARD_SHIPPING_API_LEVEL for the vendor API level. # In this case, the VSR API level is the minimum of the PRODUCT_SHIPPING_API_LEVEL # and RELEASE_BOARD_API_LEVEL - VSR_VENDOR_API_LEVEL := $(call math_min,$(VSR_VENDOR_API_LEVEL),$(RELEASE_BOARD_API_LEVEL)) + board_api_level := $(RELEASE_BOARD_API_LEVEL) + ifdef BOARD_API_LEVEL_PROP_OVERRIDE + board_api_level := $(BOARD_API_LEVEL_PROP_OVERRIDE) + endif + VSR_VENDOR_API_LEVEL := $(call math_min,$(VSR_VENDOR_API_LEVEL),$(board_api_level)) + board_api_level := endif endif .KATI_READONLY := VSR_VENDOR_API_LEVEL diff --git a/core/ravenwood_test_config_template.xml b/core/ravenwood_test_config_template.xml index 2f21baedf7..9e9dd762ff 100644 --- a/core/ravenwood_test_config_template.xml +++ b/core/ravenwood_test_config_template.xml @@ -22,6 +22,7 @@ <option name="use-ravenwood-resources" value="true" /> <option name="exclude-paths" value="java" /> <option name="null-device" value="true" /> + <option name="do-not-swallow-runner-errors" value="true" /> {EXTRA_CONFIGS} diff --git a/core/soong_config.mk b/core/soong_config.mk index a99bf0ef51..82be1f45ff 100644 --- a/core/soong_config.mk +++ b/core/soong_config.mk @@ -150,6 +150,7 @@ $(call add_json_bool, ArtUseReadBarrier, $(call invert_bool,$(fi $(call add_json_str, BtConfigIncludeDir, $(BOARD_BLUETOOTH_BDROID_BUILDCFG_INCLUDE_DIR)) $(call add_json_list, DeviceKernelHeaders, $(TARGET_DEVICE_KERNEL_HEADERS) $(TARGET_BOARD_KERNEL_HEADERS) $(TARGET_PRODUCT_KERNEL_HEADERS)) $(call add_json_str, VendorApiLevel, $(BOARD_API_LEVEL)) +$(call add_json_str, VendorApiLevelPropOverride, $(BOARD_API_LEVEL_PROP_OVERRIDE)) $(call add_json_list, ExtraVndkVersions, $(PRODUCT_EXTRA_VNDK_VERSIONS)) $(call add_json_list, DeviceSystemSdkVersions, $(BOARD_SYSTEMSDK_VERSIONS)) $(call add_json_list, Platform_systemsdk_versions, $(PLATFORM_SYSTEMSDK_VERSIONS)) @@ -182,9 +183,16 @@ $(call add_json_bool, Enforce_vintf_manifest, $(filter true,$(PRODUCT $(call add_json_bool, Uml, $(filter true,$(TARGET_USER_MODE_LINUX))) $(call add_json_str, VendorPath, $(TARGET_COPY_OUT_VENDOR)) +$(call add_json_str, VendorDlkmPath, $(TARGET_COPY_OUT_VENDOR_DLKM)) +$(call add_json_bool, BuildingVendorImage, $(BUILDING_VENDOR_IMAGE)) $(call add_json_str, OdmPath, $(TARGET_COPY_OUT_ODM)) +$(call add_json_bool, BuildingOdmImage, $(BUILDING_ODM_IMAGE)) +$(call add_json_str, OdmDlkmPath, $(TARGET_COPY_OUT_ODM_DLKM)) $(call add_json_str, ProductPath, $(TARGET_COPY_OUT_PRODUCT)) +$(call add_json_bool, BuildingProductImage, $(BUILDING_PRODUCT_IMAGE)) $(call add_json_str, SystemExtPath, $(TARGET_COPY_OUT_SYSTEM_EXT)) +$(call add_json_str, SystemDlkmPath, $(TARGET_COPY_OUT_SYSTEM_DLKM)) +$(call add_json_str, OemPath, $(TARGET_COPY_OUT_OEM)) $(call add_json_bool, MinimizeJavaDebugInfo, $(filter true,$(PRODUCT_MINIMIZE_JAVA_DEBUG_INFO))) $(call add_json_bool, UseGoma, $(filter-out false,$(USE_GOMA))) @@ -345,6 +353,7 @@ $(call add_json_list, SystemPropFiles, $(TARGET_SYSTEM_PROP)) $(call add_json_list, SystemExtPropFiles, $(TARGET_SYSTEM_EXT_PROP)) $(call add_json_list, ProductPropFiles, $(TARGET_PRODUCT_PROP)) $(call add_json_list, OdmPropFiles, $(TARGET_ODM_PROP)) +$(call add_json_list, VendorPropFiles, $(TARGET_VENDOR_PROP)) $(call add_json_str, ExtraAllowedDepsTxt, $(EXTRA_ALLOWED_DEPS_TXT)) @@ -412,6 +421,21 @@ $(call add_json_map, PartitionVarsForSoongMigrationOnlyDoNotUse) $(call add_json_str, BoardExt4ShareDupBlocks, $(BOARD_EXT4_SHARE_DUP_BLOCKS)) $(call add_json_str, BoardFlashLogicalBlockSize, $(BOARD_FLASH_LOGICAL_BLOCK_SIZE)) $(call add_json_str, BoardFlashEraseBlockSize, $(BOARD_FLASH_ERASE_BLOCK_SIZE)) + $(call add_json_bool, BuildingVbmetaImage, $(BUILDING_VBMETA_IMAGE)) + $(call add_json_bool, BoardAvbEnable, $(filter true,$(BOARD_AVB_ENABLE))) + $(call add_json_str, BoardAvbAlgorithm, $(BOARD_AVB_ALGORITHM)) + $(call add_json_str, BoardAvbKeyPath, $(BOARD_AVB_KEY_PATH)) + $(call add_json_str, BoardAvbRollbackIndex, $(BOARD_AVB_ROLLBACK_INDEX)) + $(call add_json_map, ChainedVbmetaPartitions) + $(foreach partition,system vendor $(BOARD_AVB_VBMETA_CUSTOM_PARTITIONS),\ + $(call add_json_map, $(partition)) \ + $(call add_json_list,Partitions,$(BOARD_AVB_VBMETA_$(call to-upper,$(partition)))) \ + $(call add_json_str,Key,$(BOARD_AVB_VBMETA_$(call to-upper,$(partition))_KEY_PATH)) \ + $(call add_json_str,Algorithm,$(BOARD_AVB_VBMETA_$(call to-upper,$(partition))_ALGORITHM)) \ + $(call add_json_str,RollbackIndex,$(BOARD_AVB_VBMETA_$(call to-upper,$(partition))_ROLLBACK_INDEX)) \ + $(call add_json_str,RollbackIndexLocation,$(BOARD_AVB_VBMETA_$(call to-upper,$(partition))_ROLLBACK_INDEX_LOCATION)) \ + $(call end_json_map)) + $(call end_json_map) $(call add_json_bool, BoardUsesRecoveryAsBoot, $(filter true,$(BOARD_USES_RECOVERY_AS_BOOT))) $(call add_json_bool, ProductUseDynamicPartitionSize, $(filter true,$(PRODUCT_USE_DYNAMIC_PARTITION_SIZE))) @@ -420,8 +444,47 @@ $(call add_json_map, PartitionVarsForSoongMigrationOnlyDoNotUse) $(call add_json_list, ProductPackages, $(PRODUCT_PACKAGES)) $(call add_json_list, ProductPackagesDebug, $(PRODUCT_PACKAGES_DEBUG)) + # Used to generate /vendor/linker.config.pb + $(call add_json_list, VendorLinkerConfigSrcs, $(PRODUCT_VENDOR_LINKER_CONFIG_FRAGMENTS)) + $(call add_json_list, ProductLinkerConfigSrcs, $(PRODUCT_PRODUCT_LINKER_CONFIG_FRAGMENTS)) + + # Used to generate _dlkm partitions + $(call add_json_bool, BuildingSystemDlkmImage, $(BUILDING_SYSTEM_DLKM_IMAGE)) + $(call add_json_list, SystemKernelModules, $(BOARD_SYSTEM_KERNEL_MODULES)) + + # Used to generate /vendor/build.prop + $(call add_json_list, BoardInfoFiles, $(if $(TARGET_BOARD_INFO_FILES),$(TARGET_BOARD_INFO_FILES),$(firstword $(TARGET_BOARD_INFO_FILE) $(wildcard $(TARGET_DEVICE_DIR)/board-info.txt)))) + $(call add_json_str, BootLoaderBoardName, $(TARGET_BOOTLOADER_BOARD_NAME)) + + $(call add_json_map, ProductCopyFiles) + $(foreach pair,$(PRODUCT_COPY_FILES),\ + $(call add_json_str,$(word 1,$(subst :, ,$(pair))),$(word 2,$(subst :, ,$(pair))))) + $(call end_json_map) + $(call end_json_map) +# For converting vintf_data +$(call add_json_list, DeviceMatrixFile, $(DEVICE_MATRIX_FILE)) +$(call add_json_list, ProductManifestFiles, $(PRODUCT_MANIFEST_FILES)) +$(call add_json_list, SystemManifestFile, $(DEVICE_FRAMEWORK_MANIFEST_FILE)) +SYSTEM_EXT_HWSERVICE_FILES := +ifeq ($(PRODUCT_HIDL_ENABLED),true) + ifneq ($(filter hwservicemanager,$(PRODUCT_PACKAGES)),) + SYSTEM_EXT_HWSERVICE_FILES += system/hwservicemanager/hwservicemanager_no_max.xml + else + $(error If PRODUCT_HIDL_ENABLED is set, hwservicemanager must be added to PRODUCT_PACKAGES explicitly) + endif +else + ifneq ($(filter hwservicemanager,$(PRODUCT_PACKAGES)),) + SYSTEM_EXT_HWSERVICE_FILES += system/hwservicemanager/hwservicemanager.xml + else ifneq ($(filter hwservicemanager,$(PRODUCT_PACKAGES_SHIPPING_API_LEVEL_34)),) + SYSTEM_EXT_HWSERVICE_FILES += system/hwservicemanager/hwservicemanager.xml + endif +endif +$(call add_json_list, SystemExtManifestFiles, $(SYSTEM_EXT_MANIFEST_FILES) $(SYSTEM_EXT_HWSERVICE_FILES)) +$(call add_json_list, DeviceManifestFiles, $(DEVICE_MANIFEST_FILE)) +$(call add_json_list, OdmManifestFiles, $(ODM_MANIFEST_FILES)) + $(call json_end) $(file >$(SOONG_VARIABLES).tmp,$(json_contents)) diff --git a/core/soong_extra_config.mk b/core/soong_extra_config.mk index 00b5c0fd63..2ff83a1b77 100644 --- a/core/soong_extra_config.mk +++ b/core/soong_extra_config.mk @@ -43,6 +43,7 @@ $(call add_json_list, PRODUCT_VENDOR_PROPERTIES, $(call collapse-prop-pa $(call add_json_list, PRODUCT_PRODUCT_PROPERTIES, $(call collapse-prop-pairs,PRODUCT_PRODUCT_PROPERTIES)) $(call add_json_list, PRODUCT_ODM_PROPERTIES, $(call collapse-prop-pairs,PRODUCT_ODM_PROPERTIES)) $(call add_json_list, PRODUCT_PROPERTY_OVERRIDES, $(call collapse-prop-pairs,PRODUCT_PROPERTY_OVERRIDES)) +$(call add_json_list, PRODUCT_DEFAULT_PROPERTY_OVERRIDES, $(call collapse-prop-pairs,PRODUCT_DEFAULT_PROPERTY_OVERRIDES)) $(call add_json_str, BootloaderBoardName, $(TARGET_BOOTLOADER_BOARD_NAME)) diff --git a/core/sysprop.mk b/core/sysprop.mk index dc6f2c4ac6..9b70d1ce9a 100644 --- a/core/sysprop.mk +++ b/core/sysprop.mk @@ -281,51 +281,17 @@ INSTALLED_ODM_BUILD_PROP_TARGET := $(TARGET_OUT_ODM)/etc/build.prop # ---------------------------------------------------------------- # vendor_dlkm/etc/build.prop -# - -INSTALLED_VENDOR_DLKM_BUILD_PROP_TARGET := $(TARGET_OUT_VENDOR_DLKM)/etc/build.prop -$(eval $(call build-properties,\ - vendor_dlkm,\ - $(INSTALLED_VENDOR_DLKM_BUILD_PROP_TARGET),\ - $(empty),\ - $(empty),\ - $(empty),\ - $(empty),\ - $(empty))) - -$(eval $(call declare-1p-target,$(INSTALLED_VENDOR_DLKM_BUILD_PROP_TARGET))) - -# ---------------------------------------------------------------- # odm_dlkm/etc/build.prop -# - -INSTALLED_ODM_DLKM_BUILD_PROP_TARGET := $(TARGET_OUT_ODM_DLKM)/etc/build.prop -$(eval $(call build-properties,\ - odm_dlkm,\ - $(INSTALLED_ODM_DLKM_BUILD_PROP_TARGET),\ - $(empty),\ - $(empty),\ - $(empty),\ - $(empty),\ - $(empty))) - -$(eval $(call declare-1p-target,$(INSTALLED_ODM_DLKM_BUILD_PROP_TARGET))) - -# ---------------------------------------------------------------- # system_dlkm/build.prop -# +# These are built by Soong. See build/soong/Android.bp +INSTALLED_VENDOR_DLKM_BUILD_PROP_TARGET := $(TARGET_OUT_VENDOR_DLKM)/etc/build.prop +INSTALLED_ODM_DLKM_BUILD_PROP_TARGET := $(TARGET_OUT_ODM_DLKM)/etc/build.prop INSTALLED_SYSTEM_DLKM_BUILD_PROP_TARGET := $(TARGET_OUT_SYSTEM_DLKM)/etc/build.prop -$(eval $(call build-properties,\ - system_dlkm,\ - $(INSTALLED_SYSTEM_DLKM_BUILD_PROP_TARGET),\ - $(empty),\ - $(empty),\ - $(empty),\ - $(empty),\ - $(empty))) - -$(eval $(call declare-1p-target,$(INSTALLED_SYSTEM_DLKM_BUILD_PROP_TARGET))) +ALL_DEFAULT_INSTALLED_MODULES += \ + $(INSTALLED_VENDOR_DLKM_BUILD_PROP_TARGET) \ + $(INSTALLED_ODM_DLKM_BUILD_PROP_TARGET) \ + $(INSTALLED_SYSTEM_DLKM_BUILD_PROP_TARGET) \ # ----------------------------------------------------------------- # system_ext/etc/build.prop diff --git a/core/sysprop_config.mk b/core/sysprop_config.mk index 69066117a3..199150347c 100644 --- a/core/sysprop_config.mk +++ b/core/sysprop_config.mk @@ -91,8 +91,12 @@ endif # Build system set BOARD_API_LEVEL to show the api level of the vendor API surface. # This must not be altered outside of build system. ifdef BOARD_API_LEVEL -ADDITIONAL_VENDOR_PROPERTIES += \ - ro.board.api_level=$(BOARD_API_LEVEL) + ADDITIONAL_VENDOR_PROPERTIES += \ + ro.board.api_level?=$(BOARD_API_LEVEL) + ifdef BOARD_API_LEVEL_PROP_OVERRIDE + ADDITIONAL_VENDOR_PROPERTIES += \ + ro.board.api_level=$(BOARD_API_LEVEL_PROP_OVERRIDE) + endif endif # RELEASE_BOARD_API_LEVEL_FROZEN is true when the vendor API surface is frozen. ifdef RELEASE_BOARD_API_LEVEL_FROZEN diff --git a/core/tasks/autorepro.mk b/core/tasks/autorepro.mk new file mode 100644 index 0000000000..2f81f9bf85 --- /dev/null +++ b/core/tasks/autorepro.mk @@ -0,0 +1,39 @@ +# Copyright (C) 2022 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. + +ifneq ($(wildcard test/sts/README-autorepro.md),) +test_suite_name := autorepro +test_suite_tradefed := sts-tradefed +test_suite_readme := test/sts/README-autorepro.md +autorepro_zip := $(HOST_OUT)/$(test_suite_name)/autorepro.zip + +include $(BUILD_SYSTEM)/tasks/tools/compatibility.mk + +autorepro_plugin_skel := $(call intermediates-dir-for,ETC,autorepro-plugin-skel.zip)/autorepro-plugin-skel.zip + +$(autorepro_zip): AUTOREPRO_ZIP := $(compatibility_zip) +$(autorepro_zip): AUTOREPRO_PLUGIN_SKEL := $(autorepro_plugin_skel) +$(autorepro_zip): $(MERGE_ZIPS) $(ZIP2ZIP) $(compatibility_zip) $(autorepro_plugin_skel) + rm -f $@ $(AUTOREPRO_ZIP)_filtered + $(ZIP2ZIP) -i $(AUTOREPRO_ZIP) -o $(AUTOREPRO_ZIP)_filtered \ + -x android-autorepro/tools/sts-tradefed-tests.jar \ + 'android-autorepro/tools/*:autorepro/src/main/resources/sts-tradefed-tools/' + $(MERGE_ZIPS) $@ $(AUTOREPRO_ZIP)_filtered $(AUTOREPRO_PLUGIN_SKEL) + rm -f $(AUTOREPRO_ZIP)_filtered + +.PHONY: autorepro +autorepro: $(autorepro_zip) +$(call dist-for-goals, autorepro, $(autorepro_zip)) + +endif diff --git a/core/tasks/dts.mk b/core/tasks/dts.mk new file mode 100644 index 0000000000..8f090828d9 --- /dev/null +++ b/core/tasks/dts.mk @@ -0,0 +1,28 @@ +# 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. + +# Desktop test suite +ifneq ($(wildcard test/dts/tools/dts-tradefed/README),) +test_suite_name := dts +test_suite_tradefed := dts-tradefed +test_suite_readme := test/dts/tools/dts-tradefed/README +test_suite_tools := $(HOST_OUT_JAVA_LIBRARIES)/ats_console_deploy.jar \ + $(HOST_OUT_JAVA_LIBRARIES)/ats_olc_server_local_mode_deploy.jar + +include $(BUILD_SYSTEM)/tasks/tools/compatibility.mk + +.PHONY: dts +dts: $(compatibility_zip) $(compatibility_tests_list_zip) +$(call dist-for-goals, dts, $(compatibility_zip) $(compatibility_tests_list_zip)) +endif diff --git a/core/tasks/sts-sdk.mk b/core/tasks/sts-sdk.mk deleted file mode 100644 index 4abbc29c5e..0000000000 --- a/core/tasks/sts-sdk.mk +++ /dev/null @@ -1,39 +0,0 @@ -# Copyright (C) 2022 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. - -ifneq ($(wildcard test/sts/README-sts-sdk.md),) -test_suite_name := sts-sdk -test_suite_tradefed := sts-tradefed -test_suite_readme := test/sts/README-sts-sdk.md -sts_sdk_zip := $(HOST_OUT)/$(test_suite_name)/sts-sdk.zip - -include $(BUILD_SYSTEM)/tasks/tools/compatibility.mk - -sts_sdk_plugin_skel := $(call intermediates-dir-for,ETC,sts-sdk-plugin-skel.zip)/sts-sdk-plugin-skel.zip - -$(sts_sdk_zip): STS_SDK_ZIP := $(compatibility_zip) -$(sts_sdk_zip): STS_SDK_PLUGIN_SKEL := $(sts_sdk_plugin_skel) -$(sts_sdk_zip): $(MERGE_ZIPS) $(ZIP2ZIP) $(compatibility_zip) $(sts_sdk_plugin_skel) - rm -f $@ $(STS_SDK_ZIP)_filtered - $(ZIP2ZIP) -i $(STS_SDK_ZIP) -o $(STS_SDK_ZIP)_filtered \ - -x android-sts-sdk/tools/sts-tradefed-tests.jar \ - 'android-sts-sdk/tools/*:sts-sdk/src/main/resources/sts-tradefed-tools/' - $(MERGE_ZIPS) $@ $(STS_SDK_ZIP)_filtered $(STS_SDK_PLUGIN_SKEL) - rm -f $(STS_SDK_ZIP)_filtered - -.PHONY: sts-sdk -sts-sdk: $(sts_sdk_zip) -$(call dist-for-goals, sts-sdk, $(sts_sdk_zip)) - -endif diff --git a/core/tasks/tools/update_bootloader_radio_image.mk b/core/tasks/tools/update_bootloader_radio_image.mk deleted file mode 100644 index adb86ea11a..0000000000 --- a/core/tasks/tools/update_bootloader_radio_image.mk +++ /dev/null @@ -1,26 +0,0 @@ -# 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. - -ifeq ($(USES_DEVICE_GOOGLE_ZUMA),true) - -include vendor/google_devices/zuma/prebuilts/misc_bins/update_bootloader_radio_image.mk -endif -ifeq ($(USES_DEVICE_GOOGLE_ZUMAPRO),true) - -include vendor/google_devices/zumapro/prebuilts/misc_bins/update_bootloader_radio_image.mk -endif -ifeq ($(USES_DEVICE_GOOGLE_LAGUNA),true) - -include vendor/google_devices/laguna/prebuilts/misc_bins/update_bootloader_radio_image.mk -endif -ifeq ($(USES_DEVICE_GOOGLE_MALIBU),true) - -include vendor/google_devices/malibu/prebuilts/misc_bins/update_bootloader_radio_image.mk -endif diff --git a/envsetup.sh b/envsetup.sh index 3fed5aed6d..554a220f1d 100644 --- a/envsetup.sh +++ b/envsetup.sh @@ -362,7 +362,6 @@ function addcompletions() packages/modules/adb/adb.bash system/core/fastboot/fastboot.bash tools/asuite/asuite.sh - prebuilts/bazel/common/bazel-complete.bash ) # Completion can be disabled selectively to allow users to use non-standard completion. # e.g. diff --git a/target/board/Android.mk b/target/board/Android.mk index 8133af9a7f..36be0025ad 100644 --- a/target/board/Android.mk +++ b/target/board/Android.mk @@ -51,29 +51,6 @@ $(call declare-0p-target,$(INSTALLED_ANDROID_INFO_TXT_TARGET)) # Copy compatibility metadata to the device. -# Device Manifest -ifdef DEVICE_MANIFEST_FILE -# $(DEVICE_MANIFEST_FILE) can be a list of files -include $(CLEAR_VARS) -LOCAL_MODULE := vendor_manifest.xml -LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 legacy_not_a_contribution -LOCAL_LICENSE_CONDITIONS := by_exception_only not_allowed notice -LOCAL_MODULE_STEM := manifest.xml -LOCAL_MODULE_CLASS := ETC -LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR)/etc/vintf - -GEN := $(local-generated-sources-dir)/manifest.xml -$(GEN): PRIVATE_DEVICE_MANIFEST_FILE := $(DEVICE_MANIFEST_FILE) -$(GEN): $(DEVICE_MANIFEST_FILE) $(HOST_OUT_EXECUTABLES)/assemble_vintf - BOARD_SEPOLICY_VERS=$(BOARD_SEPOLICY_VERS) \ - PRODUCT_ENFORCE_VINTF_MANIFEST=$(PRODUCT_ENFORCE_VINTF_MANIFEST) \ - $(HOST_OUT_EXECUTABLES)/assemble_vintf -o $@ \ - -i $(call normalize-path-list,$(PRIVATE_DEVICE_MANIFEST_FILE)) - -LOCAL_PREBUILT_MODULE_FILE := $(GEN) -include $(BUILD_PREBUILT) -endif - # DEVICE_MANIFEST_SKUS: a list of SKUS where DEVICE_MANIFEST_<sku>_FILES is defined. ifdef DEVICE_MANIFEST_SKUS @@ -112,30 +89,6 @@ _add_device_sku_manifest := endif # DEVICE_MANIFEST_SKUS -# ODM manifest -ifdef ODM_MANIFEST_FILES -# ODM_MANIFEST_FILES is a list of files that is combined and installed as the default ODM manifest. -include $(CLEAR_VARS) -LOCAL_MODULE := odm_manifest.xml -LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 legacy_not_a_contribution -LOCAL_LICENSE_CONDITIONS := by_exception_only not_allowed notice -LOCAL_MODULE_STEM := manifest.xml -LOCAL_MODULE_CLASS := ETC -LOCAL_MODULE_RELATIVE_PATH := vintf -LOCAL_ODM_MODULE := true - -GEN := $(local-generated-sources-dir)/manifest.xml -$(GEN): PRIVATE_SRC_FILES := $(ODM_MANIFEST_FILES) -$(GEN): $(ODM_MANIFEST_FILES) $(HOST_OUT_EXECUTABLES)/assemble_vintf - # Set VINTF_IGNORE_TARGET_FCM_VERSION to true because it should only be in device manifest. - VINTF_IGNORE_TARGET_FCM_VERSION=true \ - $(HOST_OUT_EXECUTABLES)/assemble_vintf -o $@ \ - -i $(call normalize-path-list,$(PRIVATE_SRC_FILES)) - -LOCAL_PREBUILT_MODULE_FILE := $(GEN) -include $(BUILD_PREBUILT) -endif # ODM_MANIFEST_FILES - # ODM_MANIFEST_SKUS: a list of SKUS where ODM_MANIFEST_<sku>_FILES are defined. ifdef ODM_MANIFEST_SKUS diff --git a/target/product/base_system.mk b/target/product/base_system.mk index 74ed82d3ca..9d6402e042 100644 --- a/target/product/base_system.mk +++ b/target/product/base_system.mk @@ -96,6 +96,7 @@ PRODUCT_PACKAGES += \ enhanced-confirmation.xml \ ExtShared \ flags_health_check \ + framework-connectivity-b \ framework-graphics \ framework-location \ framework-minus-apex \ @@ -211,6 +212,7 @@ PRODUCT_PACKAGES += \ libwilhelm \ linker \ llkd \ + llndk_libs \ lmkd \ LocalTransport \ locksettings \ @@ -274,7 +276,6 @@ PRODUCT_PACKAGES += \ Shell \ shell_and_utilities_system \ sm \ - snapshotctl \ snapuserd \ storaged \ surfaceflinger \ @@ -287,6 +288,7 @@ PRODUCT_PACKAGES += \ tombstoned \ traced \ traced_probes \ + tradeinmode \ tune2fs \ uiautomator \ uinput \ @@ -350,6 +352,11 @@ ifneq (,$(RELEASE_RANGING_STACK)) com.android.ranging endif +ifeq ($(RELEASE_MEMORY_MANAGEMENT_DAEMON),true) + PRODUCT_PACKAGES += \ + mm_daemon +endif + # VINTF data for system image PRODUCT_PACKAGES += \ system_manifest.xml \ @@ -424,6 +431,7 @@ PRODUCT_HOST_PACKAGES += \ lpdump \ mke2fs \ mkfs.erofs \ + pbtombstone \ resize2fs \ sgdisk \ sqlite3 \ @@ -491,6 +499,7 @@ PRODUCT_PACKAGES_DEBUG := \ record_binder \ servicedispatcher \ showmap \ + snapshotctl \ sqlite3 \ ss \ start_with_lockagent \ @@ -504,10 +513,6 @@ PRODUCT_PACKAGES_DEBUG := \ unwind_reg_info \ unwind_symbols \ -# For Remotely Provisioned Certificate Processor -PRODUCT_SYSTEM_PROPERTIES += \ - remote_provisioning.use_cert_processor=false - # The set of packages whose code can be loaded by the system server. PRODUCT_SYSTEM_SERVER_APPS += \ SettingsProvider \ diff --git a/target/product/base_vendor.mk b/target/product/base_vendor.mk index 3f859417b6..16fc7fd906 100644 --- a/target/product/base_vendor.mk +++ b/target/product/base_vendor.mk @@ -71,6 +71,8 @@ PRODUCT_PACKAGES += \ passwd_odm \ passwd_vendor \ selinux_policy_nonsystem \ + selinux_policy_vendor \ + selinux_policy_odm \ shell_and_utilities_vendor \ odm-build.prop \ @@ -104,6 +106,7 @@ PRODUCT_PACKAGES_SHIPPING_API_LEVEL_29 += \ # VINTF data for vendor image PRODUCT_PACKAGES += \ vendor_compatibility_matrix.xml \ + vendor_manifest.xml \ # Base modules and settings for the debug ramdisk, which is then packed # into a boot-debug.img and a vendor_boot-debug.img. diff --git a/target/product/build_variables.mk b/target/product/build_variables.mk index 9fc9ff9dc1..7661e063a7 100644 --- a/target/product/build_variables.mk +++ b/target/product/build_variables.mk @@ -22,3 +22,6 @@ $(call soong_config_set, libbinder, release_libbinder_client_cache, $(RELEASE_LI # Use the configured release of sqlite $(call soong_config_set, libsqlite3, release_package_libsqlite3, $(RELEASE_PACKAGE_LIBSQLITE3)) + +# Use the configured MessageQueue implementation +$(call soong_config_set, messagequeue, release_package_messagequeue_implementation, $(RELEASE_PACKAGE_MESSAGEQUEUE_IMPLEMENTATION)) diff --git a/target/product/default_art_config.mk b/target/product/default_art_config.mk index 668f054773..83d9215a9b 100644 --- a/target/product/default_art_config.mk +++ b/target/product/default_art_config.mk @@ -51,6 +51,7 @@ PRODUCT_BOOT_JARS += \ framework-minus-apex \ framework-graphics \ framework-location \ + framework-connectivity-b \ ext \ telephony-common \ voip-common \ diff --git a/target/product/generic/Android.bp b/target/product/generic/Android.bp index f86774b066..c81e66c01a 100644 --- a/target/product/generic/Android.bp +++ b/target/product/generic/Android.bp @@ -385,7 +385,6 @@ android_system_image { "android.software.webview.prebuilt.xml", // media_system "android.software.window_magnification.prebuilt.xml", // handheld_system "android.system.suspend-service", - "prebuilt_vintf_manifest", "apexd", "appops", "approved-ogki-builds.xml", // base_system @@ -530,12 +529,14 @@ android_system_image { "storaged", // base_system "surfaceflinger", // base_system "svc", // base_system + "system_manifest.xml", // base_system "task_profiles.json", // base_system "tc", // base_system "telecom", // base_system "tombstoned", // base_system "traced", // base_system "traced_probes", // base_system + "tradeinmode", // base_system "tune2fs", // base_system "uiautomator", // base_system "uinput", // base_system @@ -562,6 +563,11 @@ android_system_image { "trace_redactor", // base_system (RELEASE_PACKAGE_PROFILING_MODULE) ], default: [], + }) + select(release_flag("RELEASE_MEMORY_MANAGEMENT_DAEMON"), { + true: [ + "mm_daemon", // base_system (RELEASE_MEMORY_MANAGEMENT_DAEMON) + ], + default: [], }) + select(product_variable("debuggable"), { true: [ "adevice_fingerprint", @@ -695,6 +701,7 @@ android_system_image { "framework-graphics", // base_system "framework-location", // base_system "framework-minus-apex-install-dependencies", // base_system + "framework-connectivity-b", // base_system "framework_compatibility_matrix.device.xml", "generic_system_fonts", // ok "hwservicemanager_compat_symlink_module", // base_system @@ -785,6 +792,7 @@ android_system_image { "libbinder_ndk", "libbinder_rpc_unstable", "libcamera2ndk", + "libcgrouprc", // llndk library "libclang_rt.asan", "libcompiler_rt", "libcutils", // used by many libs @@ -861,11 +869,3 @@ android_system_image { }, }, } - -prebuilt_etc { - name: "prebuilt_vintf_manifest", - src: "manifest.xml", - filename: "manifest.xml", - relative_install_path: "vintf", - no_full_install: true, -} diff --git a/target/product/generic/manifest.xml b/target/product/generic/manifest.xml deleted file mode 100644 index 1df2c0d0cf..0000000000 --- a/target/product/generic/manifest.xml +++ /dev/null @@ -1,54 +0,0 @@ -<!-- - Input: - system/libhidl/vintfdata/manifest.xml ---> -<manifest version="8.0" type="framework"> - <hal format="hidl" max-level="6"> - <name>android.frameworks.displayservice</name> - <transport>hwbinder</transport> - <fqname>@1.0::IDisplayService/default</fqname> - </hal> - <hal format="hidl" max-level="5"> - <name>android.frameworks.schedulerservice</name> - <transport>hwbinder</transport> - <fqname>@1.0::ISchedulingPolicyService/default</fqname> - </hal> - <hal format="aidl"> - <name>android.frameworks.sensorservice</name> - <fqname>ISensorManager/default</fqname> - </hal> - <hal format="hidl" max-level="8"> - <name>android.frameworks.sensorservice</name> - <transport>hwbinder</transport> - <fqname>@1.0::ISensorManager/default</fqname> - </hal> - <hal format="hidl" max-level="8"> - <name>android.hidl.memory</name> - <transport arch="32+64">passthrough</transport> - <fqname>@1.0::IMapper/ashmem</fqname> - </hal> - <hal format="hidl" max-level="7"> - <name>android.system.net.netd</name> - <transport>hwbinder</transport> - <fqname>@1.1::INetd/default</fqname> - </hal> - <hal format="hidl" max-level="7"> - <name>android.system.wifi.keystore</name> - <transport>hwbinder</transport> - <fqname>@1.0::IKeystore/default</fqname> - </hal> - <hal format="native"> - <name>netutils-wrapper</name> - <version>1.0</version> - </hal> - <system-sdk> - <version>29</version> - <version>30</version> - <version>31</version> - <version>32</version> - <version>33</version> - <version>34</version> - <version>35</version> - <version>VanillaIceCream</version> - </system-sdk> -</manifest> diff --git a/target/product/generic_ramdisk.mk b/target/product/generic_ramdisk.mk index ebac62fd6b..388b180120 100644 --- a/target/product/generic_ramdisk.mk +++ b/target/product/generic_ramdisk.mk @@ -35,8 +35,6 @@ PRODUCT_PACKAGES += \ _my_paths := \ $(TARGET_COPY_OUT_RAMDISK)/ \ $(TARGET_COPY_OUT_DEBUG_RAMDISK)/ \ - system/usr/share/zoneinfo/tz_version \ - system/usr/share/zoneinfo/tzdata \ $(TARGET_COPY_OUT_RECOVERY)/root/first_stage_ramdisk/system \ diff --git a/target/product/security/Android.bp b/target/product/security/Android.bp index 69d19a3e40..ffbec0616e 100644 --- a/target/product/security/Android.bp +++ b/target/product/security/Android.bp @@ -40,4 +40,5 @@ otacerts_zip { adb_keys { name: "adb_keys", + product_specific: true, } diff --git a/target/product/virtual_ab_ota/compression.mk b/target/product/virtual_ab_ota/compression.mk index dc1ee3e028..e77c36fb78 100644 --- a/target/product/virtual_ab_ota/compression.mk +++ b/target/product/virtual_ab_ota/compression.mk @@ -18,9 +18,12 @@ $(call inherit-product, $(SRC_TARGET_DIR)/product/virtual_ab_ota/launch_with_ven PRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.compression.enabled=true PRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.userspace.snapshots.enabled=true -PRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.io_uring.enabled=true PRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.batch_writes=true +# Optional assignment. On low memory devices, disabling io_uring can relieve cpu and memory +# pressure during an OTA. +PRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.io_uring.enabled?=true + # Enabling this property, will improve OTA install time # but will use an additional CPU core # PRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.compression.threads=true diff --git a/target/product/virtual_ab_ota/vabc_features.mk b/target/product/virtual_ab_ota/vabc_features.mk index e2745a1356..d092699a47 100644 --- a/target/product/virtual_ab_ota/vabc_features.mk +++ b/target/product/virtual_ab_ota/vabc_features.mk @@ -31,14 +31,15 @@ PRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.enabled=true PRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.compression.enabled=true PRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.userspace.snapshots.enabled=true -PRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.io_uring.enabled=true -PRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.compression.xor.enabled=true PRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.batch_writes=true + +# Optional assignments, low memory devices may benefit from overriding these. +PRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.io_uring.enabled?=true +PRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.compression.xor.enabled?=true + # Low memory device configurations. If memory usage and cpu utilization is # a bottleneck during OTA, the below configurations can be added to a -# device's .mk file improve performance for low mem devices. Disabling -# ro.virtual_ab.compression.xor.enabled and ro.virtual_ab.io_uring.enabled -# is also recommended +# device's .mk file improve performance for low mem devices. # # PRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.read_ahead_size=16 # PRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.o_direct.enabled=true diff --git a/teams/Android.bp b/teams/Android.bp index 4c40287d6d..21f52224e1 100644 --- a/teams/Android.bp +++ b/teams/Android.bp @@ -13,6 +13,9 @@ // See the License for the specific language governing permissions and // limitations under the License. +// DON'T ADD NEW RULES HERE. For more details refer to +// go/new-android-ownership-model + package { default_applicable_licenses: ["Android-Apache-2.0"], } @@ -4475,3 +4478,13 @@ team { // go/trendy/manage/engineers/6303298703949824 trendy_team_id: "6303298703949824", } + +team { + name: "trendy_team_desktop_stats", + + // go/trendy/manage/engineers/5440764114206720 + trendy_team_id: "5440764114206720", +} + +// DON'T ADD NEW RULES HERE. For more details refer to +// go/new-android-ownership-model diff --git a/tools/aconfig/aconfig/Android.bp b/tools/aconfig/aconfig/Android.bp index f4dd10399b..5e3eb12f3b 100644 --- a/tools/aconfig/aconfig/Android.bp +++ b/tools/aconfig/aconfig/Android.bp @@ -68,6 +68,14 @@ aconfig_values { ], } +aconfig_values { + name: "aconfig.test.flag.second_values", + package: "com.android.aconfig.test", + srcs: [ + "tests/third.values", + ], +} + aconfig_value_set { name: "aconfig.test.flag.value_set", values: [ diff --git a/tools/aconfig/aconfig/src/codegen/java.rs b/tools/aconfig/aconfig/src/codegen/java.rs index 47d4042a2e..bfdf1a73c2 100644 --- a/tools/aconfig/aconfig/src/codegen/java.rs +++ b/tools/aconfig/aconfig/src/codegen/java.rs @@ -501,7 +501,7 @@ mod tests { modified_parsed_flags.into_iter(), mode, flag_ids, - false, + true, ) .unwrap(); let expect_flags_content = EXPECTED_FLAG_COMMON_CONTENT.to_string() @@ -513,6 +513,7 @@ mod tests { package com.android.aconfig.test; // TODO(b/303773055): Remove the annotation after access issue is resolved. import android.compat.annotation.UnsupportedAppUsage; + import android.os.Binder; import android.provider.DeviceConfig; import android.provider.DeviceConfig.Properties; import android.aconfig.storage.StorageInternalReader; @@ -544,6 +545,7 @@ mod tests { isCached = true; } private void load_overrides_aconfig_test() { + final long ident = Binder.clearCallingIdentity(); try { Properties properties = DeviceConfig.getProperties("aconfig_test"); disabledRw = @@ -561,11 +563,16 @@ mod tests { + "flag declaration.", e ); + } catch (SecurityException e) { + // for isolated process case, skip loading flag value from the storage, use the default + } finally { + Binder.restoreCallingIdentity(ident); } aconfig_test_is_cached = true; } private void load_overrides_other_namespace() { + final long ident = Binder.clearCallingIdentity(); try { Properties properties = DeviceConfig.getProperties("other_namespace"); disabledRwInOtherNamespace = @@ -579,6 +586,10 @@ mod tests { + "flag declaration.", e ); + } catch (SecurityException e) { + // for isolated process case, skip loading flag value from the storage, use the default + } finally { + Binder.restoreCallingIdentity(ident); } other_namespace_is_cached = true; } @@ -760,6 +771,7 @@ mod tests { let expect_feature_flags_impl_content = r#" package com.android.aconfig.test; + import android.os.Binder; import android.provider.DeviceConfig; import android.provider.DeviceConfig.Properties; /** @hide */ @@ -770,6 +782,7 @@ mod tests { private static boolean enabledRoExported = false; private void load_overrides_aconfig_test() { + final long ident = Binder.clearCallingIdentity(); try { Properties properties = DeviceConfig.getProperties("aconfig_test"); disabledRwExported = @@ -787,6 +800,10 @@ mod tests { + "flag declaration.", e ); + } catch (SecurityException e) { + // for isolated process case, skip loading flag value from the storage, use the default + } finally { + Binder.restoreCallingIdentity(ident); } aconfig_test_is_cached = true; } diff --git a/tools/aconfig/aconfig/src/codegen/rust.rs b/tools/aconfig/aconfig/src/codegen/rust.rs index d318b9652f..569a34b5f5 100644 --- a/tools/aconfig/aconfig/src/codegen/rust.rs +++ b/tools/aconfig/aconfig/src/codegen/rust.rs @@ -297,7 +297,7 @@ static CACHED_disabled_rw: LazyLock<bool> = LazyLock::new(|| { }, None => { log!(Level::Error, "no context found for package com.android.aconfig.test"); - Ok(false) + Err(format!("failed to flag package com.android.aconfig.test")) } } }) @@ -309,7 +309,7 @@ static CACHED_disabled_rw: LazyLock<bool> = LazyLock::new(|| { }, Err(err) => { log!(Level::Error, "aconfig_rust_codegen: error: {err}"); - panic!("failed to read flag value: {err}"); + return false; } } } else { @@ -344,7 +344,7 @@ static CACHED_disabled_rw_exported: LazyLock<bool> = LazyLock::new(|| { }, None => { log!(Level::Error, "no context found for package com.android.aconfig.test"); - Ok(false) + Err(format!("failed to flag package com.android.aconfig.test")) } } }) @@ -356,7 +356,7 @@ static CACHED_disabled_rw_exported: LazyLock<bool> = LazyLock::new(|| { }, Err(err) => { log!(Level::Error, "aconfig_rust_codegen: error: {err}"); - panic!("failed to read flag value: {err}"); + return false; } } } else { @@ -391,7 +391,7 @@ static CACHED_disabled_rw_in_other_namespace: LazyLock<bool> = LazyLock::new(|| }, None => { log!(Level::Error, "no context found for package com.android.aconfig.test"); - Ok(false) + Err(format!("failed to flag package com.android.aconfig.test")) } } }) @@ -403,7 +403,7 @@ static CACHED_disabled_rw_in_other_namespace: LazyLock<bool> = LazyLock::new(|| }, Err(err) => { log!(Level::Error, "aconfig_rust_codegen: error: {err}"); - panic!("failed to read flag value: {err}"); + return false; } } } else { @@ -439,7 +439,7 @@ static CACHED_enabled_rw: LazyLock<bool> = LazyLock::new(|| { }, None => { log!(Level::Error, "no context found for package com.android.aconfig.test"); - Ok(true) + Err(format!("failed to flag package com.android.aconfig.test")) } } }) @@ -451,7 +451,7 @@ static CACHED_enabled_rw: LazyLock<bool> = LazyLock::new(|| { }, Err(err) => { log!(Level::Error, "aconfig_rust_codegen: error: {err}"); - panic!("failed to read flag value: {err}"); + return true; } } } else { diff --git a/tools/aconfig/aconfig/src/commands.rs b/tools/aconfig/aconfig/src/commands.rs index 496876e08f..0ad3d97422 100644 --- a/tools/aconfig/aconfig/src/commands.rs +++ b/tools/aconfig/aconfig/src/commands.rs @@ -17,7 +17,7 @@ use anyhow::{bail, ensure, Context, Result}; use itertools::Itertools; use protobuf::Message; -use std::collections::{BTreeMap, HashMap}; +use std::collections::HashMap; use std::hash::Hasher; use std::io::Read; use std::path::PathBuf; @@ -425,23 +425,34 @@ where #[allow(dead_code)] // TODO: b/316357686 - Use fingerprint in codegen to // protect hardcoded offset reads. -pub fn compute_flag_offsets_fingerprint(flags_map: &HashMap<String, u16>) -> Result<u64> { - let mut hasher = SipHasher13::new(); - - // Need to sort to ensure the data is added to the hasher in the same order - // each run. - let sorted_map: BTreeMap<&String, &u16> = flags_map.iter().collect(); + // Creates a fingerprint of the flag names (which requires sorting the vector). + // Fingerprint is used by both codegen and storage files. +pub fn compute_flags_fingerprint(flag_names: &mut Vec<String>) -> Result<u64> { + flag_names.sort(); - for (flag, offset) in sorted_map { - // See https://docs.rs/siphasher/latest/siphasher/#note for use of write - // over write_i16. Similarly, use to_be_bytes rather than to_ne_bytes to - // ensure consistency. + let mut hasher = SipHasher13::new(); + for flag in flag_names { hasher.write(flag.as_bytes()); - hasher.write(&offset.to_be_bytes()); } Ok(hasher.finish()) } +#[allow(dead_code)] // TODO: b/316357686 - Use fingerprint in codegen to + // protect hardcoded offset reads. + // Converts ProtoParsedFlags into a vector of strings containing all of the flag + // names. Helper fn for creating fingerprint for codegen files. Flags must all + // belong to the same package. +fn extract_flag_names(flags: ProtoParsedFlags) -> Result<Vec<String>> { + let separated_flags: Vec<ProtoParsedFlag> = flags.parsed_flag.into_iter().collect::<Vec<_>>(); + + // All flags must belong to the same package as the fingerprint is per-package. + let Some(_package) = find_unique_package(&separated_flags) else { + bail!("No parsed flags, or the parsed flags use different packages."); + }; + + Ok(separated_flags.into_iter().map(|flag| flag.name.unwrap()).collect::<Vec<_>>()) +} + #[cfg(test)] mod tests { use super::*; @@ -450,16 +461,51 @@ mod tests { #[test] fn test_offset_fingerprint() { let parsed_flags = crate::test::parse_test_flags(); - let package = find_unique_package(&parsed_flags.parsed_flag).unwrap().to_string(); - let flag_ids = assign_flag_ids(&package, parsed_flags.parsed_flag.iter()).unwrap(); - let expected_fingerprint = 10709892481002252132u64; + let expected_fingerprint: u64 = 5801144784618221668; - let hash_result = compute_flag_offsets_fingerprint(&flag_ids); + let mut extracted_flags = extract_flag_names(parsed_flags).unwrap(); + let hash_result = compute_flags_fingerprint(&mut extracted_flags); assert_eq!(hash_result.unwrap(), expected_fingerprint); } #[test] + fn test_offset_fingerprint_matches_from_package() { + let parsed_flags: ProtoParsedFlags = crate::test::parse_test_flags(); + + // All test flags are in the same package, so fingerprint from all of them. + let mut extracted_flags = extract_flag_names(parsed_flags.clone()).unwrap(); + let result_from_parsed_flags = compute_flags_fingerprint(&mut extracted_flags); + + let mut flag_names_vec = parsed_flags + .parsed_flag + .clone() + .into_iter() + .map(|flag| flag.name.unwrap()) + .map(String::from) + .collect::<Vec<_>>(); + let result_from_names = compute_flags_fingerprint(&mut flag_names_vec); + + // Assert the same hash is generated for each case. + assert_eq!(result_from_parsed_flags.unwrap(), result_from_names.unwrap()); + } + + #[test] + fn test_offset_fingerprint_different_packages_does_not_match() { + // Parse flags from two packages. + let parsed_flags: ProtoParsedFlags = crate::test::parse_test_flags(); + let second_parsed_flags = crate::test::parse_second_package_flags(); + + let mut extracted_flags = extract_flag_names(parsed_flags).unwrap(); + let result_from_parsed_flags = compute_flags_fingerprint(&mut extracted_flags).unwrap(); + let mut second_extracted_flags = extract_flag_names(second_parsed_flags).unwrap(); + let second_result = compute_flags_fingerprint(&mut second_extracted_flags).unwrap(); + + // Different flags should have a different fingerprint. + assert_ne!(result_from_parsed_flags, second_result); + } + + #[test] fn test_parse_flags() { let parsed_flags = crate::test::parse_test_flags(); // calls parse_flags aconfig_protos::parsed_flags::verify_fields(&parsed_flags).unwrap(); diff --git a/tools/aconfig/aconfig/src/main.rs b/tools/aconfig/aconfig/src/main.rs index edb4fd373b..e184efed58 100644 --- a/tools/aconfig/aconfig/src/main.rs +++ b/tools/aconfig/aconfig/src/main.rs @@ -51,8 +51,7 @@ fn cli() -> Command { .subcommand( Command::new("create-cache") .arg(Arg::new("package").long("package").required(true)) - // TODO(b/312769710): Make this argument required. - .arg(Arg::new("container").long("container")) + .arg(Arg::new("container").long("container").required(true)) .arg(Arg::new("declarations").long("declarations").action(ArgAction::Append)) .arg(Arg::new("values").long("values").action(ArgAction::Append)) .arg( diff --git a/tools/aconfig/aconfig/src/storage/flag_info.rs b/tools/aconfig/aconfig/src/storage/flag_info.rs index 25326094da..5d565e81e5 100644 --- a/tools/aconfig/aconfig/src/storage/flag_info.rs +++ b/tools/aconfig/aconfig/src/storage/flag_info.rs @@ -83,7 +83,7 @@ mod tests { let flag_info_list = create_test_flag_info_list_from_source(); assert!(flag_info_list.is_ok()); let expected_flag_info_list = - aconfig_storage_file::test_utils::create_test_flag_info_list(); + aconfig_storage_file::test_utils::create_test_flag_info_list(DEFAULT_FILE_VERSION); assert_eq!(flag_info_list.unwrap(), expected_flag_info_list); } } diff --git a/tools/aconfig/aconfig/src/storage/flag_table.rs b/tools/aconfig/aconfig/src/storage/flag_table.rs index 6046d7ef18..8856eb6e10 100644 --- a/tools/aconfig/aconfig/src/storage/flag_table.rs +++ b/tools/aconfig/aconfig/src/storage/flag_table.rs @@ -157,7 +157,8 @@ mod tests { fn test_table_contents() { let flag_table = create_test_flag_table_from_source(); assert!(flag_table.is_ok()); - let expected_flag_table = aconfig_storage_file::test_utils::create_test_flag_table(); + let expected_flag_table = + aconfig_storage_file::test_utils::create_test_flag_table(DEFAULT_FILE_VERSION); assert_eq!(flag_table.unwrap(), expected_flag_table); } } diff --git a/tools/aconfig/aconfig/src/storage/flag_value.rs b/tools/aconfig/aconfig/src/storage/flag_value.rs index 6a655b9a92..0dd5a9de96 100644 --- a/tools/aconfig/aconfig/src/storage/flag_value.rs +++ b/tools/aconfig/aconfig/src/storage/flag_value.rs @@ -82,7 +82,7 @@ mod tests { let flag_value_list = create_test_flag_value_list_from_source(); assert!(flag_value_list.is_ok()); let expected_flag_value_list = - aconfig_storage_file::test_utils::create_test_flag_value_list(); + aconfig_storage_file::test_utils::create_test_flag_value_list(DEFAULT_FILE_VERSION); assert_eq!(flag_value_list.unwrap(), expected_flag_value_list); } } diff --git a/tools/aconfig/aconfig/src/storage/mod.rs b/tools/aconfig/aconfig/src/storage/mod.rs index 9e5dad5955..c7fd55a7f8 100644 --- a/tools/aconfig/aconfig/src/storage/mod.rs +++ b/tools/aconfig/aconfig/src/storage/mod.rs @@ -32,6 +32,7 @@ use aconfig_storage_file::StorageFileType; pub struct FlagPackage<'a> { pub package_name: &'a str, pub package_id: u32, + pub fingerprint: u64, pub flag_names: HashSet<&'a str>, pub boolean_flags: Vec<&'a ProtoParsedFlag>, // The index of the first boolean flag in this aconfig package among all boolean @@ -44,6 +45,7 @@ impl<'a> FlagPackage<'a> { FlagPackage { package_name, package_id, + fingerprint: 0, flag_names: HashSet::new(), boolean_flags: vec![], boolean_start_index: 0, @@ -79,6 +81,8 @@ where for p in packages.iter_mut() { p.boolean_start_index = boolean_start_index; boolean_start_index += p.boolean_flags.len() as u32; + + // TODO: b/316357686 - Calculate fingerprint and add to package. } packages diff --git a/tools/aconfig/aconfig/src/storage/package_table.rs b/tools/aconfig/aconfig/src/storage/package_table.rs index 56559f8daa..e46607b3fc 100644 --- a/tools/aconfig/aconfig/src/storage/package_table.rs +++ b/tools/aconfig/aconfig/src/storage/package_table.rs @@ -47,6 +47,7 @@ impl PackageTableNodeWrapper { let node = PackageTableNode { package_name: String::from(package.package_name), package_id: package.package_id, + fingerprint: package.fingerprint, boolean_start_index: package.boolean_start_index, next_offset: None, }; @@ -127,7 +128,8 @@ mod tests { fn test_table_contents() { let package_table = create_test_package_table_from_source(); assert!(package_table.is_ok()); - let expected_package_table = aconfig_storage_file::test_utils::create_test_package_table(); + let expected_package_table = + aconfig_storage_file::test_utils::create_test_package_table(DEFAULT_FILE_VERSION); assert_eq!(package_table.unwrap(), expected_package_table); } } diff --git a/tools/aconfig/aconfig/src/test.rs b/tools/aconfig/aconfig/src/test.rs index 7409cda6e8..a19b372aac 100644 --- a/tools/aconfig/aconfig/src/test.rs +++ b/tools/aconfig/aconfig/src/test.rs @@ -295,6 +295,24 @@ parsed_flag { aconfig_protos::parsed_flags::try_from_binary_proto(&bytes).unwrap() } + pub fn parse_second_package_flags() -> ProtoParsedFlags { + let bytes = crate::commands::parse_flags( + "com.android.aconfig.second_test", + Some("system"), + vec![Input { + source: "tests/test_second_package.aconfig".to_string(), + reader: Box::new(include_bytes!("../tests/test_second_package.aconfig").as_slice()), + }], + vec![Input { + source: "tests/third.values".to_string(), + reader: Box::new(include_bytes!("../tests/third.values").as_slice()), + }], + crate::commands::DEFAULT_FLAG_PERMISSION, + ) + .unwrap(); + aconfig_protos::parsed_flags::try_from_binary_proto(&bytes).unwrap() + } + pub fn first_significant_code_diff(a: &str, b: &str) -> Option<String> { let a = a.lines().map(|line| line.trim_start()).filter(|line| !line.is_empty()); let b = b.lines().map(|line| line.trim_start()).filter(|line| !line.is_empty()); diff --git a/tools/aconfig/aconfig/templates/FeatureFlagsImpl.java.template b/tools/aconfig/aconfig/templates/FeatureFlagsImpl.java.template index 26d3069fd8..cb52150f02 100644 --- a/tools/aconfig/aconfig/templates/FeatureFlagsImpl.java.template +++ b/tools/aconfig/aconfig/templates/FeatureFlagsImpl.java.template @@ -1,11 +1,13 @@ package {package_name}; {{ -if not is_test_mode }} +{{ -if allow_instrumentation }} {{ if not library_exported- }} // TODO(b/303773055): Remove the annotation after access issue is resolved. import android.compat.annotation.UnsupportedAppUsage; {{ -endif }} {{ -if runtime_lookup_required }} +import android.os.Binder; import android.provider.DeviceConfig; import android.provider.DeviceConfig.Properties; @@ -56,6 +58,7 @@ public final class FeatureFlagsImpl implements FeatureFlags \{ {{ for namespace_with_flags in namespace_flags }} private void load_overrides_{namespace_with_flags.namespace}() \{ + final long ident = Binder.clearCallingIdentity(); try \{ Properties properties = DeviceConfig.getProperties("{namespace_with_flags.namespace}"); {{ -for flag in namespace_with_flags.flags }} @@ -73,6 +76,10 @@ public final class FeatureFlagsImpl implements FeatureFlags \{ + "flag declaration.", e ); + } catch (SecurityException e) \{ + // for isolated process case, skip loading flag value from the storage, use the default + } finally \{ + Binder.restoreCallingIdentity(ident); } {namespace_with_flags.namespace}_is_cached = true; } @@ -110,6 +117,76 @@ public final class FeatureFlagsImpl implements FeatureFlags \{ } {{ endfor }} } + +{{ else }} {#- else for allow_instrumentation is not enabled #} +{{ if not library_exported- }} +// TODO(b/303773055): Remove the annotation after access issue is resolved. +import android.compat.annotation.UnsupportedAppUsage; +{{ -endif }} + +{{ -if runtime_lookup_required }} +import android.os.Binder; +import android.provider.DeviceConfig; +import android.provider.DeviceConfig.Properties; +{{ -endif }} +/** @hide */ +public final class FeatureFlagsImpl implements FeatureFlags \{ +{{ -if runtime_lookup_required }} +{{ -for namespace_with_flags in namespace_flags }} + private static volatile boolean {namespace_with_flags.namespace}_is_cached = false; +{{ -endfor- }} + +{{ for flag in flag_elements }} +{{- if flag.is_read_write }} + private static boolean {flag.method_name} = {flag.default_value}; +{{ -endif }} +{{ -endfor }} +{{ for namespace_with_flags in namespace_flags }} + private void load_overrides_{namespace_with_flags.namespace}() \{ + final long ident = Binder.clearCallingIdentity(); + try \{ + Properties properties = DeviceConfig.getProperties("{namespace_with_flags.namespace}"); +{{ -for flag in namespace_with_flags.flags }} +{{ -if flag.is_read_write }} + {flag.method_name} = + properties.getBoolean(Flags.FLAG_{flag.flag_name_constant_suffix}, {flag.default_value}); +{{ -endif }} +{{ -endfor }} + } catch (NullPointerException e) \{ + throw new RuntimeException( + "Cannot read value from namespace {namespace_with_flags.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 + ); + } finally \{ + Binder.restoreCallingIdentity(ident); + } + {namespace_with_flags.namespace}_is_cached = true; +} +{{ endfor- }} +{{ -endif }}{#- end of runtime_lookup_required #} +{{ -for flag in flag_elements }} + @Override +{{ -if not library_exported }} + @com.android.aconfig.annotations.AconfigFlagAccessor + @UnsupportedAppUsage +{{ -endif }} + public boolean {flag.method_name}() \{ +{{ -if flag.is_read_write }} + if (!{flag.device_config_namespace}_is_cached) \{ + load_overrides_{flag.device_config_namespace}(); + } + return {flag.method_name}; +{{ -else }} + return {flag.default_value}; +{{ -endif }} + } +{{ endfor }} +} +{{ endif}} {#- endif for allow_instrumentation #} {{ else }} {#- Generate only stub if in test mode #} /** @hide */ public final class FeatureFlagsImpl implements FeatureFlags \{ diff --git a/tools/aconfig/aconfig/templates/cpp_source_file.template b/tools/aconfig/aconfig/templates/cpp_source_file.template index eaaf86f527..df3b10dc65 100644 --- a/tools/aconfig/aconfig/templates/cpp_source_file.template +++ b/tools/aconfig/aconfig/templates/cpp_source_file.template @@ -92,17 +92,21 @@ namespace {cpp_namespace} \{ aconfig_storage::StorageFileType::package_map); if (!package_map_file.ok()) \{ ALOGE("error: failed to get package map file: %s", package_map_file.error().c_str()); + package_exists_in_storage_ = false; + return; } auto context = aconfig_storage::get_package_read_context( **package_map_file, "{package}"); if (!context.ok()) \{ ALOGE("error: failed to get package read context: %s", context.error().c_str()); + package_exists_in_storage_ = false; + return; } if (!(context->package_exists)) \{ - package_exists_in_storage_ = false; - return; + package_exists_in_storage_ = false; + return; } // cache package boolean flag start index @@ -116,6 +120,8 @@ namespace {cpp_namespace} \{ aconfig_storage::StorageFileType::flag_val); if (!flag_value_file.ok()) \{ ALOGE("error: failed to get flag value file: %s", flag_value_file.error().c_str()); + package_exists_in_storage_ = false; + return; } // cache flag value file diff --git a/tools/aconfig/aconfig/templates/rust.template b/tools/aconfig/aconfig/templates/rust.template index 6456360734..d0079d45d4 100644 --- a/tools/aconfig/aconfig/templates/rust.template +++ b/tools/aconfig/aconfig/templates/rust.template @@ -53,7 +53,7 @@ static CACHED_{flag.name}: LazyLock<bool> = LazyLock::new(|| \{ }, None => \{ log!(Level::Error, "no context found for package {package}"); - Ok({flag.default_value}) + Err(format!("failed to flag package {package}")) } } }) @@ -65,7 +65,7 @@ static CACHED_{flag.name}: LazyLock<bool> = LazyLock::new(|| \{ }, Err(err) => \{ log!(Level::Error, "aconfig_rust_codegen: error: \{err}"); - panic!("failed to read flag value: \{err}"); + return {flag.default_value}; } } } else \{ diff --git a/tools/aconfig/aconfig/tests/test_second_package.aconfig b/tools/aconfig/aconfig/tests/test_second_package.aconfig new file mode 100644 index 0000000000..188bc96cfb --- /dev/null +++ b/tools/aconfig/aconfig/tests/test_second_package.aconfig @@ -0,0 +1,10 @@ +package: "com.android.aconfig.second_test" +container: "system" + +flag { + name: "testing_flag" + namespace: "another_namespace" + description: "This is a flag for testing." + bug: "123" +} + diff --git a/tools/aconfig/aconfig/tests/third.values b/tools/aconfig/aconfig/tests/third.values new file mode 100644 index 0000000000..675832a4bc --- /dev/null +++ b/tools/aconfig/aconfig/tests/third.values @@ -0,0 +1,6 @@ +flag_value { + package: "com.android.aconfig.second_test" + name: "testing_flag" + state: DISABLED + permission: READ_WRITE +} diff --git a/tools/aconfig/aconfig_flags/flags.aconfig b/tools/aconfig/aconfig_flags/flags.aconfig index 0a004ca4e1..b66d282f0e 100644 --- a/tools/aconfig/aconfig_flags/flags.aconfig +++ b/tools/aconfig/aconfig_flags/flags.aconfig @@ -14,3 +14,11 @@ flag { bug: "369808805" description: "When enabled, launch aconfigd from config infra module." } + +flag { + name: "enable_system_aconfigd_rust" + namespace: "core_experiments_team_internal" + bug: "378079539" + description: "When enabled, the aconfigd cc_binary target becomes a no-op, and the rust_binary aconfigd-system target starts up." + is_fixed_read_only: true +} diff --git a/tools/aconfig/aconfig_flags/src/lib.rs b/tools/aconfig/aconfig_flags/src/lib.rs index 2e891273ed..b413c62b12 100644 --- a/tools/aconfig/aconfig_flags/src/lib.rs +++ b/tools/aconfig/aconfig_flags/src/lib.rs @@ -39,6 +39,11 @@ pub mod auto_generated { pub fn enable_aconfigd_from_mainline() -> bool { aconfig_flags_rust::enable_only_new_storage() } + + /// Returns the value for the enable_system_aconfigd_rust flag. + pub fn enable_system_aconfigd_rust() -> bool { + aconfig_flags_rust::enable_system_aconfigd_rust() + } } /// Module used when building with cargo @@ -55,4 +60,10 @@ pub mod auto_generated { // Used only to enable typechecking and testing with cargo true } + + /// Returns a placeholder value for the enable_system_aconfigd_rust flag. + pub fn enable_system_aconfigd_rust() -> bool { + // Used only to enable typechecking and testing with cargo + true + } } diff --git a/tools/aconfig/aconfig_storage_file/src/flag_info.rs b/tools/aconfig/aconfig_storage_file/src/flag_info.rs index f090396901..cf16834be2 100644 --- a/tools/aconfig/aconfig_storage_file/src/flag_info.rs +++ b/tools/aconfig/aconfig_storage_file/src/flag_info.rs @@ -194,12 +194,15 @@ impl FlagInfoList { #[cfg(test)] mod tests { use super::*; - use crate::test_utils::create_test_flag_info_list; + use crate::{ + test_utils::create_test_flag_info_list, DEFAULT_FILE_VERSION, MAX_SUPPORTED_FILE_VERSION, + }; - #[test] // this test point locks down the value list serialization - fn test_serialization() { - let flag_info_list = create_test_flag_info_list(); + // TODO: b/376108268 - Use parameterized tests. + #[test] + fn test_serialization_default() { + let flag_info_list = create_test_flag_info_list(DEFAULT_FILE_VERSION); let header: &FlagInfoHeader = &flag_info_list.header; let reinterpreted_header = FlagInfoHeader::from_bytes(&header.into_bytes()); @@ -220,20 +223,42 @@ mod tests { } #[test] + fn test_serialization_max() { + let flag_info_list = create_test_flag_info_list(MAX_SUPPORTED_FILE_VERSION); + + let header: &FlagInfoHeader = &flag_info_list.header; + let reinterpreted_header = FlagInfoHeader::from_bytes(&header.into_bytes()); + assert!(reinterpreted_header.is_ok()); + assert_eq!(header, &reinterpreted_header.unwrap()); + + let nodes: &Vec<FlagInfoNode> = &flag_info_list.nodes; + for node in nodes.iter() { + let reinterpreted_node = FlagInfoNode::from_bytes(&node.into_bytes()).unwrap(); + assert_eq!(node, &reinterpreted_node); + } + + let flag_info_bytes = flag_info_list.into_bytes(); + let reinterpreted_info_list = FlagInfoList::from_bytes(&flag_info_bytes); + assert!(reinterpreted_info_list.is_ok()); + assert_eq!(&flag_info_list, &reinterpreted_info_list.unwrap()); + assert_eq!(flag_info_bytes.len() as u32, header.file_size); + } + // this test point locks down that version number should be at the top of serialized // bytes + #[test] fn test_version_number() { - let flag_info_list = create_test_flag_info_list(); + let flag_info_list = create_test_flag_info_list(DEFAULT_FILE_VERSION); let bytes = &flag_info_list.into_bytes(); let mut head = 0; - let version = read_u32_from_bytes(bytes, &mut head).unwrap(); - assert_eq!(version, 1); + let version_from_file = read_u32_from_bytes(bytes, &mut head).unwrap(); + assert_eq!(version_from_file, DEFAULT_FILE_VERSION); } - #[test] // this test point locks down file type check + #[test] fn test_file_type_check() { - let mut flag_info_list = create_test_flag_info_list(); + let mut flag_info_list = create_test_flag_info_list(DEFAULT_FILE_VERSION); flag_info_list.header.file_type = 123u8; let error = FlagInfoList::from_bytes(&flag_info_list.into_bytes()).unwrap_err(); assert_eq!( diff --git a/tools/aconfig/aconfig_storage_file/src/flag_table.rs b/tools/aconfig/aconfig_storage_file/src/flag_table.rs index 0588fe5039..6fbee023ce 100644 --- a/tools/aconfig/aconfig_storage_file/src/flag_table.rs +++ b/tools/aconfig/aconfig_storage_file/src/flag_table.rs @@ -220,12 +220,15 @@ impl FlagTable { #[cfg(test)] mod tests { use super::*; - use crate::test_utils::create_test_flag_table; + use crate::{ + test_utils::create_test_flag_table, DEFAULT_FILE_VERSION, MAX_SUPPORTED_FILE_VERSION, + }; - #[test] // this test point locks down the table serialization - fn test_serialization() { - let flag_table = create_test_flag_table(); + // TODO: b/376108268 - Use parameterized tests. + #[test] + fn test_serialization_default() { + let flag_table = create_test_flag_table(DEFAULT_FILE_VERSION); let header: &FlagTableHeader = &flag_table.header; let reinterpreted_header = FlagTableHeader::from_bytes(&header.into_bytes()); @@ -246,20 +249,42 @@ mod tests { } #[test] + fn test_serialization_max() { + let flag_table = create_test_flag_table(MAX_SUPPORTED_FILE_VERSION); + + let header: &FlagTableHeader = &flag_table.header; + let reinterpreted_header = FlagTableHeader::from_bytes(&header.into_bytes()); + assert!(reinterpreted_header.is_ok()); + assert_eq!(header, &reinterpreted_header.unwrap()); + + let nodes: &Vec<FlagTableNode> = &flag_table.nodes; + for node in nodes.iter() { + let reinterpreted_node = FlagTableNode::from_bytes(&node.into_bytes()).unwrap(); + assert_eq!(node, &reinterpreted_node); + } + + let flag_table_bytes = flag_table.into_bytes(); + let reinterpreted_table = FlagTable::from_bytes(&flag_table_bytes); + assert!(reinterpreted_table.is_ok()); + assert_eq!(&flag_table, &reinterpreted_table.unwrap()); + assert_eq!(flag_table_bytes.len() as u32, header.file_size); + } + // this test point locks down that version number should be at the top of serialized // bytes + #[test] fn test_version_number() { - let flag_table = create_test_flag_table(); + let flag_table = create_test_flag_table(DEFAULT_FILE_VERSION); let bytes = &flag_table.into_bytes(); let mut head = 0; - let version = read_u32_from_bytes(bytes, &mut head).unwrap(); - assert_eq!(version, 1); + let version_from_file = read_u32_from_bytes(bytes, &mut head).unwrap(); + assert_eq!(version_from_file, DEFAULT_FILE_VERSION); } - #[test] // this test point locks down file type check + #[test] fn test_file_type_check() { - let mut flag_table = create_test_flag_table(); + let mut flag_table = create_test_flag_table(DEFAULT_FILE_VERSION); flag_table.header.file_type = 123u8; let error = FlagTable::from_bytes(&flag_table.into_bytes()).unwrap_err(); assert_eq!( diff --git a/tools/aconfig/aconfig_storage_file/src/flag_value.rs b/tools/aconfig/aconfig_storage_file/src/flag_value.rs index b64c10ecdd..9a14bec7de 100644 --- a/tools/aconfig/aconfig_storage_file/src/flag_value.rs +++ b/tools/aconfig/aconfig_storage_file/src/flag_value.rs @@ -132,12 +132,32 @@ impl FlagValueList { #[cfg(test)] mod tests { use super::*; - use crate::test_utils::create_test_flag_value_list; + use crate::{ + test_utils::create_test_flag_value_list, DEFAULT_FILE_VERSION, MAX_SUPPORTED_FILE_VERSION, + }; #[test] // this test point locks down the value list serialization - fn test_serialization() { - let flag_value_list = create_test_flag_value_list(); + // TODO: b/376108268 - Use parameterized tests. + fn test_serialization_default() { + let flag_value_list = create_test_flag_value_list(DEFAULT_FILE_VERSION); + + let header: &FlagValueHeader = &flag_value_list.header; + let reinterpreted_header = FlagValueHeader::from_bytes(&header.into_bytes()); + assert!(reinterpreted_header.is_ok()); + assert_eq!(header, &reinterpreted_header.unwrap()); + + let flag_value_bytes = flag_value_list.into_bytes(); + let reinterpreted_value_list = FlagValueList::from_bytes(&flag_value_bytes); + assert!(reinterpreted_value_list.is_ok()); + assert_eq!(&flag_value_list, &reinterpreted_value_list.unwrap()); + assert_eq!(flag_value_bytes.len() as u32, header.file_size); + } + + #[test] + // this test point locks down the value list serialization + fn test_serialization_max() { + let flag_value_list = create_test_flag_value_list(MAX_SUPPORTED_FILE_VERSION); let header: &FlagValueHeader = &flag_value_list.header; let reinterpreted_header = FlagValueHeader::from_bytes(&header.into_bytes()); @@ -155,17 +175,17 @@ mod tests { // this test point locks down that version number should be at the top of serialized // bytes fn test_version_number() { - let flag_value_list = create_test_flag_value_list(); + let flag_value_list = create_test_flag_value_list(DEFAULT_FILE_VERSION); let bytes = &flag_value_list.into_bytes(); let mut head = 0; - let version = read_u32_from_bytes(bytes, &mut head).unwrap(); - assert_eq!(version, 1); + let version_from_file = read_u32_from_bytes(bytes, &mut head).unwrap(); + assert_eq!(version_from_file, DEFAULT_FILE_VERSION); } #[test] // this test point locks down file type check fn test_file_type_check() { - let mut flag_value_list = create_test_flag_value_list(); + let mut flag_value_list = create_test_flag_value_list(DEFAULT_FILE_VERSION); flag_value_list.header.file_type = 123u8; let error = FlagValueList::from_bytes(&flag_value_list.into_bytes()).unwrap_err(); assert_eq!( diff --git a/tools/aconfig/aconfig_storage_file/src/lib.rs b/tools/aconfig/aconfig_storage_file/src/lib.rs index 1d92ba49ab..1e5b001f8b 100644 --- a/tools/aconfig/aconfig_storage_file/src/lib.rs +++ b/tools/aconfig/aconfig_storage_file/src/lib.rs @@ -228,10 +228,14 @@ pub(crate) fn get_bucket_index(val: &[u8], num_buckets: u32) -> u32 { /// Read and parse bytes as u8 pub fn read_u8_from_bytes(buf: &[u8], head: &mut usize) -> Result<u8, AconfigStorageError> { - let val = - u8::from_le_bytes(buf[*head..*head + 1].try_into().map_err(|errmsg| { - BytesParseFail(anyhow!("fail to parse u8 from bytes: {}", errmsg)) - })?); + let val = u8::from_le_bytes( + buf.get(*head..*head + 1) + .ok_or(AconfigStorageError::BytesParseFail(anyhow!( + "fail to parse u8 from bytes: access out of bounds" + )))? + .try_into() + .map_err(|errmsg| BytesParseFail(anyhow!("fail to parse u8 from bytes: {}", errmsg)))?, + ); *head += 1; Ok(val) } @@ -241,10 +245,16 @@ pub(crate) fn read_u16_from_bytes( buf: &[u8], head: &mut usize, ) -> Result<u16, AconfigStorageError> { - let val = - u16::from_le_bytes(buf[*head..*head + 2].try_into().map_err(|errmsg| { - BytesParseFail(anyhow!("fail to parse u16 from bytes: {}", errmsg)) - })?); + let val = u16::from_le_bytes( + buf.get(*head..*head + 2) + .ok_or(AconfigStorageError::BytesParseFail(anyhow!( + "fail to parse u16 from bytes: access out of bounds" + )))? + .try_into() + .map_err(|errmsg| { + BytesParseFail(anyhow!("fail to parse u16 from bytes: {}", errmsg)) + })?, + ); *head += 2; Ok(val) } @@ -256,20 +266,32 @@ pub fn read_u32_from_start_of_bytes(buf: &[u8]) -> Result<u32, AconfigStorageErr /// Read and parse bytes as u32 pub fn read_u32_from_bytes(buf: &[u8], head: &mut usize) -> Result<u32, AconfigStorageError> { - let val = - u32::from_le_bytes(buf[*head..*head + 4].try_into().map_err(|errmsg| { - BytesParseFail(anyhow!("fail to parse u32 from bytes: {}", errmsg)) - })?); + let val = u32::from_le_bytes( + buf.get(*head..*head + 4) + .ok_or(AconfigStorageError::BytesParseFail(anyhow!( + "fail to parse u32 from bytes: access out of bounds" + )))? + .try_into() + .map_err(|errmsg| { + BytesParseFail(anyhow!("fail to parse u32 from bytes: {}", errmsg)) + })?, + ); *head += 4; Ok(val) } // Read and parse bytes as u64 pub fn read_u64_from_bytes(buf: &[u8], head: &mut usize) -> Result<u64, AconfigStorageError> { - let val = - u64::from_le_bytes(buf[*head..*head + 8].try_into().map_err(|errmsg| { - BytesParseFail(anyhow!("fail to parse u64 from bytes: {}", errmsg)) - })?); + let val = u64::from_le_bytes( + buf.get(*head..*head + 8) + .ok_or(AconfigStorageError::BytesParseFail(anyhow!( + "fail to parse u64 from bytes: access out of bounds" + )))? + .try_into() + .map_err(|errmsg| { + BytesParseFail(anyhow!("fail to parse u64 from bytes: {}", errmsg)) + })?, + ); *head += 8; Ok(val) } @@ -280,8 +302,21 @@ pub(crate) fn read_str_from_bytes( head: &mut usize, ) -> Result<String, AconfigStorageError> { let num_bytes = read_u32_from_bytes(buf, head)? as usize; - let val = String::from_utf8(buf[*head..*head + num_bytes].to_vec()) - .map_err(|errmsg| BytesParseFail(anyhow!("fail to parse string from bytes: {}", errmsg)))?; + // TODO(opg): Document this limitation and check it when creating files. + if num_bytes > 1024 { + return Err(AconfigStorageError::BytesParseFail(anyhow!( + "fail to parse string from bytes, string is too long (found {}, max is 1024)", + num_bytes + ))); + } + let val = String::from_utf8( + buf.get(*head..*head + num_bytes) + .ok_or(AconfigStorageError::BytesParseFail(anyhow!( + "fail to parse string from bytes: access out of bounds" + )))? + .to_vec(), + ) + .map_err(|errmsg| BytesParseFail(anyhow!("fail to parse string from bytes: {}", errmsg)))?; *head += num_bytes; Ok(val) } @@ -533,13 +568,46 @@ mod tests { }; #[test] + fn test_list_flags_with_missing_files_error() { + let flag_list_error = list_flags("does", "not", "exist").unwrap_err(); + assert_eq!( + format!("{:?}", flag_list_error), + format!( + "FileReadFail(Failed to open file does: No such file or directory (os error 2))" + ) + ); + } + + #[test] + fn test_list_flags_with_invalid_files_error() { + let invalid_bytes: [u8; 3] = [0; 3]; + let package_table = write_bytes_to_temp_file(&invalid_bytes).unwrap(); + let flag_table = write_bytes_to_temp_file(&invalid_bytes).unwrap(); + let flag_value_list = write_bytes_to_temp_file(&invalid_bytes).unwrap(); + let package_table_path = package_table.path().display().to_string(); + let flag_table_path = flag_table.path().display().to_string(); + let flag_value_list_path = flag_value_list.path().display().to_string(); + let flag_list_error = + list_flags(&package_table_path, &flag_table_path, &flag_value_list_path).unwrap_err(); + assert_eq!( + format!("{:?}", flag_list_error), + format!("BytesParseFail(fail to parse u32 from bytes: access out of bounds)") + ); + } + + #[test] // this test point locks down the flag list api fn test_list_flag() { let package_table = - write_bytes_to_temp_file(&create_test_package_table().into_bytes()).unwrap(); - let flag_table = write_bytes_to_temp_file(&create_test_flag_table().into_bytes()).unwrap(); - let flag_value_list = - write_bytes_to_temp_file(&create_test_flag_value_list().into_bytes()).unwrap(); + write_bytes_to_temp_file(&create_test_package_table(DEFAULT_FILE_VERSION).into_bytes()) + .unwrap(); + let flag_table = + write_bytes_to_temp_file(&create_test_flag_table(DEFAULT_FILE_VERSION).into_bytes()) + .unwrap(); + let flag_value_list = write_bytes_to_temp_file( + &create_test_flag_value_list(DEFAULT_FILE_VERSION).into_bytes(), + ) + .unwrap(); let package_table_path = package_table.path().display().to_string(); let flag_table_path = flag_table.path().display().to_string(); @@ -604,12 +672,19 @@ mod tests { // this test point locks down the flag list with info api fn test_list_flag_with_info() { let package_table = - write_bytes_to_temp_file(&create_test_package_table().into_bytes()).unwrap(); - let flag_table = write_bytes_to_temp_file(&create_test_flag_table().into_bytes()).unwrap(); - let flag_value_list = - write_bytes_to_temp_file(&create_test_flag_value_list().into_bytes()).unwrap(); - let flag_info_list = - write_bytes_to_temp_file(&create_test_flag_info_list().into_bytes()).unwrap(); + write_bytes_to_temp_file(&create_test_package_table(DEFAULT_FILE_VERSION).into_bytes()) + .unwrap(); + let flag_table = + write_bytes_to_temp_file(&create_test_flag_table(DEFAULT_FILE_VERSION).into_bytes()) + .unwrap(); + let flag_value_list = write_bytes_to_temp_file( + &create_test_flag_value_list(DEFAULT_FILE_VERSION).into_bytes(), + ) + .unwrap(); + let flag_info_list = write_bytes_to_temp_file( + &create_test_flag_info_list(DEFAULT_FILE_VERSION).into_bytes(), + ) + .unwrap(); let package_table_path = package_table.path().display().to_string(); let flag_table_path = flag_table.path().display().to_string(); diff --git a/tools/aconfig/aconfig_storage_file/src/package_table.rs b/tools/aconfig/aconfig_storage_file/src/package_table.rs index af39fbc783..21357c7e4a 100644 --- a/tools/aconfig/aconfig_storage_file/src/package_table.rs +++ b/tools/aconfig/aconfig_storage_file/src/package_table.rs @@ -100,6 +100,7 @@ impl PackageTableHeader { pub struct PackageTableNode { pub package_name: String, pub package_id: u32, + pub fingerprint: u64, // The index of the first boolean flag in this aconfig package among all boolean // flags in this container. pub boolean_start_index: u32, @@ -111,8 +112,12 @@ impl fmt::Debug for PackageTableNode { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { writeln!( f, - "Package: {}, Id: {}, Boolean flag start index: {}, Next: {:?}", - self.package_name, self.package_id, self.boolean_start_index, self.next_offset + "Package: {}, Id: {}, Fingerprint: {}, Boolean flag start index: {}, Next: {:?}", + self.package_name, + self.package_id, + self.fingerprint, + self.boolean_start_index, + self.next_offset )?; Ok(()) } @@ -146,9 +151,7 @@ impl PackageTableNode { result.extend_from_slice(&(name_bytes.len() as u32).to_le_bytes()); result.extend_from_slice(name_bytes); result.extend_from_slice(&self.package_id.to_le_bytes()); - // V2 storage files have a fingerprint. Current struct (v1) does not, so - // we write 0. - result.extend_from_slice(&0u64.to_le_bytes()); + result.extend_from_slice(&self.fingerprint.to_le_bytes()); result.extend_from_slice(&self.boolean_start_index.to_le_bytes()); result.extend_from_slice(&self.next_offset.unwrap_or(0).to_le_bytes()); result @@ -172,13 +175,15 @@ impl PackageTableNode { let mut head = 0; let package_name = read_str_from_bytes(bytes, &mut head)?; let package_id = read_u32_from_bytes(bytes, &mut head)?; + // v1 does not have fingerprint, so just set to 0. + let fingerprint: u64 = 0; let boolean_start_index = read_u32_from_bytes(bytes, &mut head)?; let next_offset = match read_u32_from_bytes(bytes, &mut head)? { 0 => None, val => Some(val), }; - let node = Self { package_name, package_id, boolean_start_index, next_offset }; + let node = Self { package_name, package_id, fingerprint, boolean_start_index, next_offset }; Ok(node) } @@ -186,18 +191,14 @@ impl PackageTableNode { let mut head = 0; let package_name = read_str_from_bytes(bytes, &mut head)?; let package_id = read_u32_from_bytes(bytes, &mut head)?; - - // Fingerprint is unused in the current struct (v1), but we need to read - // the bytes if the storage file type is v2 or else the subsequent - // fields will be inaccurate. - let _fingerprint = read_u64_from_bytes(bytes, &mut head)?; + let fingerprint = read_u64_from_bytes(bytes, &mut head)?; let boolean_start_index = read_u32_from_bytes(bytes, &mut head)?; let next_offset = match read_u32_from_bytes(bytes, &mut head)? { 0 => None, val => Some(val), }; - let node = Self { package_name, package_id, boolean_start_index, next_offset }; + let node = Self { package_name, package_id, fingerprint, boolean_start_index, next_offset }; Ok(node) } @@ -281,13 +282,37 @@ impl PackageTable { #[cfg(test)] mod tests { use super::*; - use crate::read_u32_from_start_of_bytes; - use crate::{test_utils::create_test_package_table, DEFAULT_FILE_VERSION}; + use crate::test_utils::create_test_package_table; + use crate::{read_u32_from_start_of_bytes, DEFAULT_FILE_VERSION, MAX_SUPPORTED_FILE_VERSION}; #[test] // this test point locks down the table serialization - fn test_serialization() { - let package_table = create_test_package_table(); + // TODO: b/376108268 - Use parameterized tests. + fn test_serialization_default() { + let package_table = create_test_package_table(DEFAULT_FILE_VERSION); + let header: &PackageTableHeader = &package_table.header; + let reinterpreted_header = PackageTableHeader::from_bytes(&header.into_bytes()); + assert!(reinterpreted_header.is_ok()); + assert_eq!(header, &reinterpreted_header.unwrap()); + + let nodes: &Vec<PackageTableNode> = &package_table.nodes; + for node in nodes.iter() { + let reinterpreted_node = + PackageTableNode::from_bytes(&node.into_bytes(header.version), header.version) + .unwrap(); + assert_eq!(node, &reinterpreted_node); + } + + let package_table_bytes = package_table.into_bytes(); + let reinterpreted_table = PackageTable::from_bytes(&package_table_bytes); + assert!(reinterpreted_table.is_ok()); + assert_eq!(&package_table, &reinterpreted_table.unwrap()); + assert_eq!(package_table_bytes.len() as u32, header.file_size); + } + + #[test] + fn test_serialization_max() { + let package_table = create_test_package_table(MAX_SUPPORTED_FILE_VERSION); let header: &PackageTableHeader = &package_table.header; let reinterpreted_header = PackageTableHeader::from_bytes(&header.into_bytes()); assert!(reinterpreted_header.is_ok()); @@ -312,44 +337,36 @@ mod tests { // this test point locks down that version number should be at the top of serialized // bytes fn test_version_number() { - let package_table = create_test_package_table(); + let package_table = create_test_package_table(DEFAULT_FILE_VERSION); let bytes = &package_table.into_bytes(); - let version = read_u32_from_start_of_bytes(bytes).unwrap(); - assert_eq!(version, DEFAULT_FILE_VERSION); + let unpacked_version = read_u32_from_start_of_bytes(bytes).unwrap(); + assert_eq!(unpacked_version, DEFAULT_FILE_VERSION); } #[test] - fn test_round_trip_v1() { - let table_v1: PackageTable = create_test_package_table(); - let table_bytes_v1 = table_v1.into_bytes(); + fn test_round_trip_default() { + let table: PackageTable = create_test_package_table(DEFAULT_FILE_VERSION); + let table_bytes = table.into_bytes(); - // Will automatically read from version 2 as the version code is encoded - // into the bytes. - let reinterpreted_table = PackageTable::from_bytes(&table_bytes_v1).unwrap(); + let reinterpreted_table = PackageTable::from_bytes(&table_bytes).unwrap(); - assert_eq!(table_v1, reinterpreted_table); + assert_eq!(table, reinterpreted_table); } #[test] - fn test_round_trip_v2() { - // Have to fake v2 because though we will set the version to v2 - // and write the bytes as v2, we don't have the ability to actually set - // the fingerprint yet. - let mut fake_table_v2 = create_test_package_table(); - fake_table_v2.header.version = 2; - let table_bytes_v2 = fake_table_v2.into_bytes(); - - // Will automatically read from version 2 as the version code is encoded - // into the bytes. - let reinterpreted_table = PackageTable::from_bytes(&table_bytes_v2).unwrap(); - - assert_eq!(fake_table_v2, reinterpreted_table); + fn test_round_trip_max() { + let table: PackageTable = create_test_package_table(MAX_SUPPORTED_FILE_VERSION); + let table_bytes = table.into_bytes(); + + let reinterpreted_table = PackageTable::from_bytes(&table_bytes).unwrap(); + + assert_eq!(table, reinterpreted_table); } #[test] // this test point locks down file type check fn test_file_type_check() { - let mut package_table = create_test_package_table(); + let mut package_table = create_test_package_table(DEFAULT_FILE_VERSION); package_table.header.file_type = 123u8; let error = PackageTable::from_bytes(&package_table.into_bytes()).unwrap_err(); assert_eq!( diff --git a/tools/aconfig/aconfig_storage_file/src/test_utils.rs b/tools/aconfig/aconfig_storage_file/src/test_utils.rs index 5c364f6bc2..55780eda24 100644 --- a/tools/aconfig/aconfig_storage_file/src/test_utils.rs +++ b/tools/aconfig/aconfig_storage_file/src/test_utils.rs @@ -18,38 +18,53 @@ use crate::flag_info::{FlagInfoHeader, FlagInfoList, FlagInfoNode}; use crate::flag_table::{FlagTable, FlagTableHeader, FlagTableNode}; use crate::flag_value::{FlagValueHeader, FlagValueList}; use crate::package_table::{PackageTable, PackageTableHeader, PackageTableNode}; -use crate::{AconfigStorageError, StorageFileType, StoredFlagType, DEFAULT_FILE_VERSION}; +use crate::{AconfigStorageError, StorageFileType, StoredFlagType}; use anyhow::anyhow; use std::io::Write; use tempfile::NamedTempFile; -pub fn create_test_package_table() -> PackageTable { +pub fn create_test_package_table(version: u32) -> PackageTable { let header = PackageTableHeader { - version: DEFAULT_FILE_VERSION, + version: version, container: String::from("mockup"), file_type: StorageFileType::PackageMap as u8, - file_size: 209, + file_size: match version { + 1 => 209, + 2 => 233, + _ => panic!("Unsupported version."), + }, num_packages: 3, bucket_offset: 31, node_offset: 59, }; - let buckets: Vec<Option<u32>> = vec![Some(59), None, None, Some(109), None, None, None]; + let buckets: Vec<Option<u32>> = match version { + 1 => vec![Some(59), None, None, Some(109), None, None, None], + 2 => vec![Some(59), None, None, Some(117), None, None, None], + _ => panic!("Unsupported version."), + }; let first_node = PackageTableNode { package_name: String::from("com.android.aconfig.storage.test_2"), package_id: 1, + fingerprint: 0, boolean_start_index: 3, next_offset: None, }; let second_node = PackageTableNode { package_name: String::from("com.android.aconfig.storage.test_1"), package_id: 0, + fingerprint: 0, boolean_start_index: 0, - next_offset: Some(159), + next_offset: match version { + 1 => Some(159), + 2 => Some(175), + _ => panic!("Unsupported version."), + }, }; let third_node = PackageTableNode { package_name: String::from("com.android.aconfig.storage.test_4"), package_id: 2, + fingerprint: 0, boolean_start_index: 6, next_offset: None, }; @@ -76,9 +91,9 @@ impl FlagTableNode { } } -pub fn create_test_flag_table() -> FlagTable { +pub fn create_test_flag_table(version: u32) -> FlagTable { let header = FlagTableHeader { - version: DEFAULT_FILE_VERSION, + version: version, container: String::from("mockup"), file_type: StorageFileType::FlagMap as u8, file_size: 321, @@ -118,9 +133,9 @@ pub fn create_test_flag_table() -> FlagTable { FlagTable { header, buckets, nodes } } -pub fn create_test_flag_value_list() -> FlagValueList { +pub fn create_test_flag_value_list(version: u32) -> FlagValueList { let header = FlagValueHeader { - version: DEFAULT_FILE_VERSION, + version: version, container: String::from("mockup"), file_type: StorageFileType::FlagVal as u8, file_size: 35, @@ -131,9 +146,9 @@ pub fn create_test_flag_value_list() -> FlagValueList { FlagValueList { header, booleans } } -pub fn create_test_flag_info_list() -> FlagInfoList { +pub fn create_test_flag_info_list(version: u32) -> FlagInfoList { let header = FlagInfoHeader { - version: DEFAULT_FILE_VERSION, + version: version, container: String::from("mockup"), file_type: StorageFileType::FlagInfo as u8, file_size: 35, 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 index 86a75f2f65..b1c7ee77a6 100644 --- a/tools/aconfig/aconfig_storage_file/srcs/android/aconfig/storage/AconfigStorageException.java +++ b/tools/aconfig/aconfig_storage_file/srcs/android/aconfig/storage/AconfigStorageException.java @@ -16,12 +16,118 @@ package android.aconfig.storage; +/** + * Exception thrown when an error occurs while accessing Aconfig Storage. + * + * <p>This exception indicates a general problem with Aconfig Storage, such as an inability to read + * or write data. + */ public class AconfigStorageException extends RuntimeException { + + /** Generic error code indicating an unspecified Aconfig Storage error. */ + public static final int ERROR_GENERIC = 0; + + /** Error code indicating that the Aconfig Storage system is not found on the device. */ + public static final int ERROR_STORAGE_SYSTEM_NOT_FOUND = 1; + + /** Error code indicating that the requested configuration package is not found. */ + public static final int ERROR_PACKAGE_NOT_FOUND = 2; + + /** Error code indicating that the specified container is not found. */ + public static final int ERROR_CONTAINER_NOT_FOUND = 3; + + /** Error code indicating that there was an error reading the Aconfig Storage file. */ + public static final int ERROR_CANNOT_READ_STORAGE_FILE = 4; + + private final int mErrorCode; + + /** + * Constructs a new {@code AconfigStorageException} with a generic error code and the specified + * detail message. + * + * @param msg The detail message for this exception. + */ public AconfigStorageException(String msg) { super(msg); + mErrorCode = ERROR_GENERIC; } + /** + * Constructs a new {@code AconfigStorageException} with a generic error code, the specified + * detail message, and cause. + * + * @param msg The detail message for this exception. + * @param cause The cause of this exception. + */ public AconfigStorageException(String msg, Throwable cause) { super(msg, cause); + mErrorCode = ERROR_GENERIC; + } + + /** + * Constructs a new {@code AconfigStorageException} with the specified error code and detail + * message. + * + * @param errorCode The error code for this exception. + * @param msg The detail message for this exception. + */ + public AconfigStorageException(int errorCode, String msg) { + super(msg); + mErrorCode = errorCode; + } + + /** + * Constructs a new {@code AconfigStorageException} with the specified error code, detail + * message, and cause. + * + * @param errorCode The error code for this exception. + * @param msg The detail message for this exception. + * @param cause The cause of this exception. + */ + public AconfigStorageException(int errorCode, String msg, Throwable cause) { + super(msg, cause); + mErrorCode = errorCode; + } + + /** + * Returns the error code associated with this exception. + * + * @return The error code. + */ + public int getErrorCode() { + return mErrorCode; + } + + /** + * Returns the error message for this exception, including the error code and the original + * message. + * + * @return The error message. + */ + @Override + public String getMessage() { + return errorString() + ": " + super.getMessage(); + } + + /** + * Returns a string representation of the error code. + * + * @return The error code string. + */ + private String errorString() { + switch (mErrorCode) { + case ERROR_GENERIC: + return "ERROR_GENERIC"; + case ERROR_STORAGE_SYSTEM_NOT_FOUND: + return "ERROR_STORAGE_SYSTEM_NOT_FOUND"; + case ERROR_PACKAGE_NOT_FOUND: + return "ERROR_PACKAGE_NOT_FOUND"; + case ERROR_CONTAINER_NOT_FOUND: + return "ERROR_CONTAINER_NOT_FOUND"; + case ERROR_CANNOT_READ_STORAGE_FILE: + return "ERROR_CANNOT_READ_STORAGE_FILE"; + default: + return "<Unknown error code " + mErrorCode + ">"; + } } } 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 index 4bea0836f0..957156876d 100644 --- a/tools/aconfig/aconfig_storage_file/srcs/android/aconfig/storage/ByteBufferReader.java +++ b/tools/aconfig/aconfig_storage_file/srcs/android/aconfig/storage/ByteBufferReader.java @@ -41,8 +41,16 @@ public class ByteBufferReader { return this.mByteBuffer.getInt(); } + public long readLong() { + return this.mByteBuffer.getLong(); + } + public String readString() { int length = readInt(); + if (length > 1024) { + throw new AconfigStorageException( + "String length exceeds maximum allowed size (1024 bytes): " + length); + } byte[] bytes = new byte[length]; mByteBuffer.get(bytes, 0, length); return new String(bytes, StandardCharsets.UTF_8); 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 index 39b7e59d7e..a45d12a0b3 100644 --- a/tools/aconfig/aconfig_storage_file/srcs/android/aconfig/storage/PackageTable.java +++ b/tools/aconfig/aconfig_storage_file/srcs/android/aconfig/storage/PackageTable.java @@ -124,8 +124,10 @@ public class PackageTable { private String mPackageName; private int mPackageId; + private long mPackageFingerprint; private int mBooleanStartIndex; private int mNextOffset; + private boolean mHasPackageFingerprint; private static Node fromBytes(ByteBufferReader reader, int version) { switch (version) { @@ -153,9 +155,11 @@ public class PackageTable { Node node = new Node(); node.mPackageName = reader.readString(); node.mPackageId = reader.readInt(); + node.mPackageFingerprint = reader.readLong(); node.mBooleanStartIndex = reader.readInt(); node.mNextOffset = reader.readInt(); node.mNextOffset = node.mNextOffset == 0 ? -1 : node.mNextOffset; + node.mHasPackageFingerprint = true; return node; } @@ -189,6 +193,10 @@ public class PackageTable { return mPackageId; } + public long getPackageFingerprint() { + return mPackageFingerprint; + } + public int getBooleanStartIndex() { return mBooleanStartIndex; } @@ -196,5 +204,9 @@ public class PackageTable { public int getNextOffset() { return mNextOffset; } + + public boolean hasPackageFingerprint() { + return mHasPackageFingerprint; + } } } diff --git a/tools/aconfig/aconfig_storage_file/tests/Android.bp b/tools/aconfig/aconfig_storage_file/tests/Android.bp index 13d321408b..bd46d5f0ab 100644 --- a/tools/aconfig/aconfig_storage_file/tests/Android.bp +++ b/tools/aconfig/aconfig_storage_file/tests/Android.bp @@ -10,10 +10,14 @@ cc_test { "libbase", ], data: [ - "data/v1/package.map", - "data/v1/flag.map", - "data/v1/flag.val", - "data/v1/flag.info", + "data/v1/package_v1.map", + "data/v1/flag_v1.map", + "data/v1/flag_v1.val", + "data/v1/flag_v1.info", + "data/v2/package_v2.map", + "data/v2/flag_v2.map", + "data/v2/flag_v2.val", + "data/v2/flag_v2.info", ], test_suites: [ "device-tests", @@ -35,10 +39,14 @@ android_test { test_config: "AndroidStorageJaveTest.xml", sdk_version: "test_current", data: [ - "data/v1/package.map", - "data/v1/flag.map", - "data/v1/flag.val", - "data/v1/flag.info", + "data/v1/package_v1.map", + "data/v1/flag_v1.map", + "data/v1/flag_v1.val", + "data/v1/flag_v1.info", + "data/v2/package_v2.map", + "data/v2/flag_v2.map", + "data/v2/flag_v2.val", + "data/v2/flag_v2.info", ], test_suites: [ "general-tests", diff --git a/tools/aconfig/aconfig_storage_file/tests/AndroidStorageJaveTest.xml b/tools/aconfig/aconfig_storage_file/tests/AndroidStorageJaveTest.xml index 2d52d44c57..20fbfdb6be 100644 --- a/tools/aconfig/aconfig_storage_file/tests/AndroidStorageJaveTest.xml +++ b/tools/aconfig/aconfig_storage_file/tests/AndroidStorageJaveTest.xml @@ -21,13 +21,17 @@ </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" /> + <option name="push" value="package_v1.map->/data/local/tmp/aconfig_storage_file_test_java/testdata/package_v1.map" /> + <option name="push" value="flag_v1.map->/data/local/tmp/aconfig_storage_file_test_java/testdata/flag_v1.map" /> + <option name="push" value="flag_v1.val->/data/local/tmp/aconfig_storage_file_test_java/testdata/flag_v1.val" /> + <option name="push" value="flag_v1.info->/data/local/tmp/aconfig_storage_file_test_java/testdata/flag_v1.info" /> + <option name="push" value="package_v2.map->/data/local/tmp/aconfig_storage_file_test_java/testdata/package_v2.map" /> + <option name="push" value="flag_v2.map->/data/local/tmp/aconfig_storage_file_test_java/testdata/flag_v2.map" /> + <option name="push" value="flag_v2.val->/data/local/tmp/aconfig_storage_file_test_java/testdata/flag_v2.val" /> + <option name="push" value="flag_v2.info->/data/local/tmp/aconfig_storage_file_test_java/testdata/flag_v2.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 +</configuration> diff --git a/tools/aconfig/aconfig_storage_file/tests/data/v1/flag.info b/tools/aconfig/aconfig_storage_file/tests/data/v1/flag_v1.info Binary files differindex 6223edf369..6223edf369 100644 --- a/tools/aconfig/aconfig_storage_file/tests/data/v1/flag.info +++ b/tools/aconfig/aconfig_storage_file/tests/data/v1/flag_v1.info diff --git a/tools/aconfig/aconfig_storage_file/tests/data/v1/flag.map b/tools/aconfig/aconfig_storage_file/tests/data/v1/flag_v1.map Binary files differindex e868f53d7e..e868f53d7e 100644 --- a/tools/aconfig/aconfig_storage_file/tests/data/v1/flag.map +++ b/tools/aconfig/aconfig_storage_file/tests/data/v1/flag_v1.map diff --git a/tools/aconfig/aconfig_storage_file/tests/data/v1/flag.val b/tools/aconfig/aconfig_storage_file/tests/data/v1/flag_v1.val Binary files differindex ed203d4d13..ed203d4d13 100644 --- a/tools/aconfig/aconfig_storage_file/tests/data/v1/flag.val +++ b/tools/aconfig/aconfig_storage_file/tests/data/v1/flag_v1.val diff --git a/tools/aconfig/aconfig_storage_file/tests/data/v1/package.map b/tools/aconfig/aconfig_storage_file/tests/data/v1/package_v1.map Binary files differindex 6c46a0339c..6c46a0339c 100644 --- a/tools/aconfig/aconfig_storage_file/tests/data/v1/package.map +++ b/tools/aconfig/aconfig_storage_file/tests/data/v1/package_v1.map diff --git a/tools/aconfig/aconfig_storage_file/tests/data/v2/flag_v2.info b/tools/aconfig/aconfig_storage_file/tests/data/v2/flag_v2.info Binary files differnew file mode 100644 index 0000000000..9db7fde7ae --- /dev/null +++ b/tools/aconfig/aconfig_storage_file/tests/data/v2/flag_v2.info diff --git a/tools/aconfig/aconfig_storage_file/tests/data/v2/flag_v2.map b/tools/aconfig/aconfig_storage_file/tests/data/v2/flag_v2.map Binary files differnew file mode 100644 index 0000000000..cf4685ceb4 --- /dev/null +++ b/tools/aconfig/aconfig_storage_file/tests/data/v2/flag_v2.map diff --git a/tools/aconfig/aconfig_storage_file/tests/data/v2/flag_v2.val b/tools/aconfig/aconfig_storage_file/tests/data/v2/flag_v2.val Binary files differnew file mode 100644 index 0000000000..37d4750206 --- /dev/null +++ b/tools/aconfig/aconfig_storage_file/tests/data/v2/flag_v2.val diff --git a/tools/aconfig/aconfig_storage_file/tests/data/v2/package_v2.map b/tools/aconfig/aconfig_storage_file/tests/data/v2/package_v2.map Binary files differnew file mode 100644 index 0000000000..16f4054879 --- /dev/null +++ b/tools/aconfig/aconfig_storage_file/tests/data/v2/package_v2.map diff --git a/tools/aconfig/aconfig_storage_file/tests/srcs/FlagTableTest.java b/tools/aconfig/aconfig_storage_file/tests/srcs/FlagTableTest.java index fd40d4c4ef..dc465b658d 100644 --- a/tools/aconfig/aconfig_storage_file/tests/srcs/FlagTableTest.java +++ b/tools/aconfig/aconfig_storage_file/tests/srcs/FlagTableTest.java @@ -31,7 +31,7 @@ public class FlagTableTest { @Test public void testFlagTable_rightHeader() throws Exception { - FlagTable flagTable = FlagTable.fromBytes(TestDataUtils.getTestFlagMapByteBuffer()); + FlagTable flagTable = FlagTable.fromBytes(TestDataUtils.getTestFlagMapByteBuffer(1)); FlagTable.Header header = flagTable.getHeader(); assertEquals(1, header.getVersion()); assertEquals("mockup", header.getContainer()); @@ -44,7 +44,7 @@ public class FlagTableTest { @Test public void testFlagTable_rightNode() throws Exception { - FlagTable flagTable = FlagTable.fromBytes(TestDataUtils.getTestFlagMapByteBuffer()); + FlagTable flagTable = FlagTable.fromBytes(TestDataUtils.getTestFlagMapByteBuffer(1)); FlagTable.Node node1 = flagTable.get(0, "enabled_ro"); FlagTable.Node node2 = flagTable.get(0, "enabled_rw"); diff --git a/tools/aconfig/aconfig_storage_file/tests/srcs/FlagValueListTest.java b/tools/aconfig/aconfig_storage_file/tests/srcs/FlagValueListTest.java index 1b0de630c7..306df7da5f 100644 --- a/tools/aconfig/aconfig_storage_file/tests/srcs/FlagValueListTest.java +++ b/tools/aconfig/aconfig_storage_file/tests/srcs/FlagValueListTest.java @@ -34,7 +34,7 @@ public class FlagValueListTest { @Test public void testFlagValueList_rightHeader() throws Exception { FlagValueList flagValueList = - FlagValueList.fromBytes(TestDataUtils.getTestFlagValByteBuffer()); + FlagValueList.fromBytes(TestDataUtils.getTestFlagValByteBuffer(1)); FlagValueList.Header header = flagValueList.getHeader(); assertEquals(1, header.getVersion()); assertEquals("mockup", header.getContainer()); @@ -47,7 +47,7 @@ public class FlagValueListTest { @Test public void testFlagValueList_rightNode() throws Exception { FlagValueList flagValueList = - FlagValueList.fromBytes(TestDataUtils.getTestFlagValByteBuffer()); + FlagValueList.fromBytes(TestDataUtils.getTestFlagValByteBuffer(1)); boolean[] expected = new boolean[] {false, true, true, false, true, true, true, true}; assertEquals(expected.length, flagValueList.size()); @@ -60,11 +60,11 @@ public class FlagValueListTest { @Test public void testFlagValueList_getValue() throws Exception { PackageTable packageTable = - PackageTable.fromBytes(TestDataUtils.getTestPackageMapByteBuffer()); - FlagTable flagTable = FlagTable.fromBytes(TestDataUtils.getTestFlagMapByteBuffer()); + PackageTable.fromBytes(TestDataUtils.getTestPackageMapByteBuffer(1)); + FlagTable flagTable = FlagTable.fromBytes(TestDataUtils.getTestFlagMapByteBuffer(1)); FlagValueList flagValueList = - FlagValueList.fromBytes(TestDataUtils.getTestFlagValByteBuffer()); + FlagValueList.fromBytes(TestDataUtils.getTestFlagValByteBuffer(1)); PackageTable.Node pNode = packageTable.get("com.android.aconfig.storage.test_1"); FlagTable.Node fNode = flagTable.get(pNode.getPackageId(), "enabled_rw"); diff --git a/tools/aconfig/aconfig_storage_file/tests/srcs/PackageTableTest.java b/tools/aconfig/aconfig_storage_file/tests/srcs/PackageTableTest.java index e7e19d8d51..4d7ab2a699 100644 --- a/tools/aconfig/aconfig_storage_file/tests/srcs/PackageTableTest.java +++ b/tools/aconfig/aconfig_storage_file/tests/srcs/PackageTableTest.java @@ -17,6 +17,8 @@ package android.aconfig.storage.test; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; import android.aconfig.storage.FileType; import android.aconfig.storage.PackageTable; @@ -31,7 +33,7 @@ public class PackageTableTest { @Test public void testPackageTable_rightHeader() throws Exception { PackageTable packageTable = - PackageTable.fromBytes(TestDataUtils.getTestPackageMapByteBuffer()); + PackageTable.fromBytes(TestDataUtils.getTestPackageMapByteBuffer(1)); PackageTable.Header header = packageTable.getHeader(); assertEquals(1, header.getVersion()); assertEquals("mockup", header.getContainer()); @@ -43,9 +45,23 @@ public class PackageTableTest { } @Test + public void testPackageTable_rightHeader_v2() throws Exception { + PackageTable packageTable = + PackageTable.fromBytes(TestDataUtils.getTestPackageMapByteBuffer(2)); + PackageTable.Header header = packageTable.getHeader(); + assertEquals(2, header.getVersion()); + assertEquals("mockup", header.getContainer()); + assertEquals(FileType.PACKAGE_MAP, header.getFileType()); + assertEquals(233, 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.fromBytes(TestDataUtils.getTestPackageMapByteBuffer(1)); PackageTable.Node node1 = packageTable.get("com.android.aconfig.storage.test_1"); PackageTable.Node node2 = packageTable.get("com.android.aconfig.storage.test_2"); @@ -66,5 +82,39 @@ public class PackageTableTest { assertEquals(159, node1.getNextOffset()); assertEquals(-1, node2.getNextOffset()); assertEquals(-1, node4.getNextOffset()); + + assertFalse(node1.hasPackageFingerprint()); + assertFalse(node2.hasPackageFingerprint()); + assertFalse(node4.hasPackageFingerprint()); + } + + @Test + public void testPackageTable_rightNode_v2() throws Exception { + PackageTable packageTable = + PackageTable.fromBytes(TestDataUtils.getTestPackageMapByteBuffer(2)); + + 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(175, node1.getNextOffset()); + assertEquals(-1, node2.getNextOffset()); + assertEquals(-1, node4.getNextOffset()); + + assertTrue(node1.hasPackageFingerprint()); + assertTrue(node2.hasPackageFingerprint()); + assertTrue(node4.hasPackageFingerprint()); } } diff --git a/tools/aconfig/aconfig_storage_file/tests/srcs/TestDataUtils.java b/tools/aconfig/aconfig_storage_file/tests/srcs/TestDataUtils.java index f35952d392..0643154e1d 100644 --- a/tools/aconfig/aconfig_storage_file/tests/srcs/TestDataUtils.java +++ b/tools/aconfig/aconfig_storage_file/tests/srcs/TestDataUtils.java @@ -21,28 +21,28 @@ 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 TEST_PACKAGE_MAP_PATH = "package_v%d.map"; + private static final String TEST_FLAG_MAP_PATH = "flag_v%d.map"; + private static final String TEST_FLAG_VAL_PATH = "flag_v%d.val"; + private static final String TEST_FLAG_INFO_PATH = "flag_v%d.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 getTestPackageMapByteBuffer(int version) throws Exception { + return readFile(TESTDATA_PATH + String.format(TEST_PACKAGE_MAP_PATH, version)); } - public static ByteBuffer getTestFlagMapByteBuffer() throws Exception { - return readFile(TESTDATA_PATH + TEST_FLAG_MAP_PATH); + public static ByteBuffer getTestFlagMapByteBuffer(int version) throws Exception { + return readFile(TESTDATA_PATH + String.format(TEST_FLAG_MAP_PATH, version)); } - public static ByteBuffer getTestFlagValByteBuffer() throws Exception { - return readFile(TESTDATA_PATH + TEST_FLAG_VAL_PATH); + public static ByteBuffer getTestFlagValByteBuffer(int version) throws Exception { + return readFile(TESTDATA_PATH + String.format(TEST_FLAG_VAL_PATH, version)); } - public static ByteBuffer getTestFlagInfoByteBuffer() throws Exception { - return readFile(TESTDATA_PATH + TEST_FLAG_INFO_PATH); + public static ByteBuffer getTestFlagInfoByteBuffer(int version) throws Exception { + return readFile(TESTDATA_PATH + String.format(TEST_FLAG_INFO_PATH, version)); } private static ByteBuffer readFile(String fileName) throws Exception { diff --git a/tools/aconfig/aconfig_storage_file/tests/storage_file_test.cpp b/tools/aconfig/aconfig_storage_file/tests/storage_file_test.cpp index 3626f72e6c..5c008afbf1 100644 --- a/tools/aconfig/aconfig_storage_file/tests/storage_file_test.cpp +++ b/tools/aconfig/aconfig_storage_file/tests/storage_file_test.cpp @@ -24,10 +24,8 @@ using namespace android::base; using namespace aconfig_storage; -void verify_value(const FlagValueSummary& flag, - const std::string& package_name, - const std::string& flag_name, - const std::string& flag_val, +void verify_value(const FlagValueSummary& flag, const std::string& package_name, + const std::string& flag_name, const std::string& flag_val, const std::string& value_type) { ASSERT_EQ(flag.package_name, package_name); ASSERT_EQ(flag.flag_name, flag_name); @@ -39,10 +37,8 @@ void verify_value_info(const FlagValueAndInfoSummary& flag, const std::string& package_name, const std::string& flag_name, const std::string& flag_val, - const std::string& value_type, - bool is_readwrite, - bool has_server_override, - bool has_local_override) { + const std::string& value_type, bool is_readwrite, + bool has_server_override, bool has_local_override) { ASSERT_EQ(flag.package_name, package_name); ASSERT_EQ(flag.flag_name, flag_name); ASSERT_EQ(flag.flag_value, flag_val); @@ -52,63 +48,137 @@ void verify_value_info(const FlagValueAndInfoSummary& flag, ASSERT_EQ(flag.has_local_override, has_local_override); } -TEST(AconfigStorageFileTest, test_list_flag) { +Result<std::vector<FlagValueSummary>> get_flag_list_result( + const std::string version) { + auto const test_base_dir = GetExecutableDirectory(); + auto const test_dir = test_base_dir + "/data/v" + version; + auto const package_map = test_dir + "/package_v" + version + ".map"; + auto const flag_map = test_dir + "/flag_v" + version + ".map"; + auto const flag_val = test_dir + "/flag_v" + version + ".val"; + return aconfig_storage::list_flags(package_map, flag_map, flag_val); +} + +Result<std::vector<FlagValueAndInfoSummary>> get_flag_list_result_with_info( + const std::string version) { auto const test_base_dir = GetExecutableDirectory(); - auto const test_dir = test_base_dir + "/data/v1"; - auto const package_map = test_dir + "/package.map"; - auto const flag_map = test_dir + "/flag.map"; - auto const flag_val = test_dir + "/flag.val"; - auto flag_list_result = aconfig_storage::list_flags( - package_map, flag_map, flag_val); + auto const test_dir = test_base_dir + "/data/v" + version; + auto const package_map = test_dir + "/package_v" + version + ".map"; + auto const flag_map = test_dir + "/flag_v" + version + ".map"; + auto const flag_val = test_dir + "/flag_v" + version + ".val"; + auto const flag_info = test_dir + "/flag_v" + version + ".info"; + return aconfig_storage::list_flags_with_info(package_map, flag_map, flag_val, + flag_info); +} + +TEST(AconfigStorageFileTest, test_list_flag) { + auto flag_list_result = get_flag_list_result("1"); ASSERT_TRUE(flag_list_result.ok()); auto const& flag_list = *flag_list_result; ASSERT_EQ(flag_list.size(), 8); - verify_value(flag_list[0], "com.android.aconfig.storage.test_1", "disabled_rw", - "false", "ReadWriteBoolean"); + verify_value(flag_list[0], "com.android.aconfig.storage.test_1", + "disabled_rw", "false", "ReadWriteBoolean"); verify_value(flag_list[1], "com.android.aconfig.storage.test_1", "enabled_ro", "true", "ReadOnlyBoolean"); verify_value(flag_list[2], "com.android.aconfig.storage.test_1", "enabled_rw", "true", "ReadWriteBoolean"); - verify_value(flag_list[3], "com.android.aconfig.storage.test_2", "disabled_rw", - "false", "ReadWriteBoolean"); - verify_value(flag_list[4], "com.android.aconfig.storage.test_2", "enabled_fixed_ro", - "true", "FixedReadOnlyBoolean"); + verify_value(flag_list[3], "com.android.aconfig.storage.test_2", + "disabled_rw", "false", "ReadWriteBoolean"); + verify_value(flag_list[4], "com.android.aconfig.storage.test_2", + "enabled_fixed_ro", "true", "FixedReadOnlyBoolean"); verify_value(flag_list[5], "com.android.aconfig.storage.test_2", "enabled_ro", "true", "ReadOnlyBoolean"); - verify_value(flag_list[6], "com.android.aconfig.storage.test_4", "enabled_fixed_ro", - "true", "FixedReadOnlyBoolean"); + verify_value(flag_list[6], "com.android.aconfig.storage.test_4", + "enabled_fixed_ro", "true", "FixedReadOnlyBoolean"); + verify_value(flag_list[7], "com.android.aconfig.storage.test_4", "enabled_rw", + "true", "ReadWriteBoolean"); +} + +// TODO: b/376256472 - Use parameterized tests. +TEST(AconfigStorageFileTest, test_list_flag_v2) { + auto flag_list_result = get_flag_list_result("2"); + ASSERT_TRUE(flag_list_result.ok()); + + auto const& flag_list = *flag_list_result; + ASSERT_EQ(flag_list.size(), 8); + verify_value(flag_list[0], "com.android.aconfig.storage.test_1", + "disabled_rw", "false", "ReadWriteBoolean"); + verify_value(flag_list[1], "com.android.aconfig.storage.test_1", "enabled_ro", + "true", "ReadOnlyBoolean"); + verify_value(flag_list[2], "com.android.aconfig.storage.test_1", "enabled_rw", + "true", "ReadWriteBoolean"); + verify_value(flag_list[3], "com.android.aconfig.storage.test_2", + "disabled_rw", "false", "ReadWriteBoolean"); + verify_value(flag_list[4], "com.android.aconfig.storage.test_2", + "enabled_fixed_ro", "true", "FixedReadOnlyBoolean"); + verify_value(flag_list[5], "com.android.aconfig.storage.test_2", "enabled_ro", + "true", "ReadOnlyBoolean"); + verify_value(flag_list[6], "com.android.aconfig.storage.test_4", + "enabled_fixed_ro", "true", "FixedReadOnlyBoolean"); verify_value(flag_list[7], "com.android.aconfig.storage.test_4", "enabled_rw", "true", "ReadWriteBoolean"); } TEST(AconfigStorageFileTest, test_list_flag_with_info) { - auto const base_test_dir = GetExecutableDirectory(); - auto const test_dir = base_test_dir + "/data/v1"; - auto const package_map = test_dir + "/package.map"; - auto const flag_map = test_dir + "/flag.map"; - auto const flag_val = test_dir + "/flag.val"; - auto const flag_info = test_dir + "/flag.info"; - auto flag_list_result = aconfig_storage::list_flags_with_info( - package_map, flag_map, flag_val, flag_info); + auto flag_list_result = get_flag_list_result_with_info("1"); + ASSERT_TRUE(flag_list_result.ok()); + + auto const& flag_list = *flag_list_result; + ASSERT_EQ(flag_list.size(), 8); + verify_value_info(flag_list[0], "com.android.aconfig.storage.test_1", + "disabled_rw", "false", "ReadWriteBoolean", true, false, + false); + verify_value_info(flag_list[1], "com.android.aconfig.storage.test_1", + "enabled_ro", "true", "ReadOnlyBoolean", false, false, + false); + verify_value_info(flag_list[2], "com.android.aconfig.storage.test_1", + "enabled_rw", "true", "ReadWriteBoolean", true, false, + false); + verify_value_info(flag_list[3], "com.android.aconfig.storage.test_2", + "disabled_rw", "false", "ReadWriteBoolean", true, false, + false); + verify_value_info(flag_list[4], "com.android.aconfig.storage.test_2", + "enabled_fixed_ro", "true", "FixedReadOnlyBoolean", false, + false, false); + verify_value_info(flag_list[5], "com.android.aconfig.storage.test_2", + "enabled_ro", "true", "ReadOnlyBoolean", false, false, + false); + verify_value_info(flag_list[6], "com.android.aconfig.storage.test_4", + "enabled_fixed_ro", "true", "FixedReadOnlyBoolean", false, + false, false); + verify_value_info(flag_list[7], "com.android.aconfig.storage.test_4", + "enabled_rw", "true", "ReadWriteBoolean", true, false, + false); +} + +TEST(AconfigStorageFileTest, test_list_flag_with_info_v2) { + auto flag_list_result = get_flag_list_result_with_info("2"); ASSERT_TRUE(flag_list_result.ok()); auto const& flag_list = *flag_list_result; ASSERT_EQ(flag_list.size(), 8); - verify_value_info(flag_list[0], "com.android.aconfig.storage.test_1", "disabled_rw", - "false", "ReadWriteBoolean", true, false, false); - verify_value_info(flag_list[1], "com.android.aconfig.storage.test_1", "enabled_ro", - "true", "ReadOnlyBoolean", false, false, false); - verify_value_info(flag_list[2], "com.android.aconfig.storage.test_1", "enabled_rw", - "true", "ReadWriteBoolean", true, false, false); - verify_value_info(flag_list[3], "com.android.aconfig.storage.test_2", "disabled_rw", - "false", "ReadWriteBoolean", true, false, false); - verify_value_info(flag_list[4], "com.android.aconfig.storage.test_2", "enabled_fixed_ro", - "true", "FixedReadOnlyBoolean", false, false, false); - verify_value_info(flag_list[5], "com.android.aconfig.storage.test_2", "enabled_ro", - "true", "ReadOnlyBoolean", false, false, false); - verify_value_info(flag_list[6], "com.android.aconfig.storage.test_4", "enabled_fixed_ro", - "true", "FixedReadOnlyBoolean", false, false, false); - verify_value_info(flag_list[7], "com.android.aconfig.storage.test_4", "enabled_rw", - "true", "ReadWriteBoolean", true, false, false); + verify_value_info(flag_list[0], "com.android.aconfig.storage.test_1", + "disabled_rw", "false", "ReadWriteBoolean", true, false, + false); + verify_value_info(flag_list[1], "com.android.aconfig.storage.test_1", + "enabled_ro", "true", "ReadOnlyBoolean", false, false, + false); + verify_value_info(flag_list[2], "com.android.aconfig.storage.test_1", + "enabled_rw", "true", "ReadWriteBoolean", true, false, + false); + verify_value_info(flag_list[3], "com.android.aconfig.storage.test_2", + "disabled_rw", "false", "ReadWriteBoolean", true, false, + false); + verify_value_info(flag_list[4], "com.android.aconfig.storage.test_2", + "enabled_fixed_ro", "true", "FixedReadOnlyBoolean", false, + false, false); + verify_value_info(flag_list[5], "com.android.aconfig.storage.test_2", + "enabled_ro", "true", "ReadOnlyBoolean", false, false, + false); + verify_value_info(flag_list[6], "com.android.aconfig.storage.test_4", + "enabled_fixed_ro", "true", "FixedReadOnlyBoolean", false, + false, false); + verify_value_info(flag_list[7], "com.android.aconfig.storage.test_4", + "enabled_rw", "true", "ReadWriteBoolean", true, false, + false); } diff --git a/tools/aconfig/aconfig_storage_read_api/Android.bp b/tools/aconfig/aconfig_storage_read_api/Android.bp index 80b8ece1be..7f7dd5a428 100644 --- a/tools/aconfig/aconfig_storage_read_api/Android.bp +++ b/tools/aconfig/aconfig_storage_read_api/Android.bp @@ -36,10 +36,10 @@ rust_test_host { "librand", ], data: [ - "tests/data/v1/package.map", - "tests/data/v1/flag.map", - "tests/data/v1/flag.val", - "tests/data/v1/flag.info", + "tests/data/v1/package_v1.map", + "tests/data/v1/flag_v1.map", + "tests/data/v1/flag_v1.val", + "tests/data/v1/flag_v1.info", ], } @@ -154,6 +154,8 @@ java_library { java_library { name: "aconfig_storage_reader_java", srcs: [ + "srcs/android/aconfig/storage/AconfigPackageImpl.java", + "srcs/android/aconfig/storage/StorageFileProvider.java", "srcs/android/aconfig/storage/StorageInternalReader.java", ], libs: [ @@ -175,6 +177,8 @@ java_library { java_library { name: "aconfig_storage_reader_java_none", srcs: [ + "srcs/android/aconfig/storage/AconfigPackageImpl.java", + "srcs/android/aconfig/storage/StorageFileProvider.java", "srcs/android/aconfig/storage/StorageInternalReader.java", ], libs: [ diff --git a/tools/aconfig/aconfig_storage_read_api/src/flag_info_query.rs b/tools/aconfig/aconfig_storage_read_api/src/flag_info_query.rs index fe57a6dd78..68b6193079 100644 --- a/tools/aconfig/aconfig_storage_read_api/src/flag_info_query.rs +++ b/tools/aconfig/aconfig_storage_read_api/src/flag_info_query.rs @@ -55,12 +55,14 @@ pub fn find_flag_attribute( #[cfg(test)] mod tests { use super::*; - use aconfig_storage_file::{test_utils::create_test_flag_info_list, FlagInfoBit}; + use aconfig_storage_file::{ + test_utils::create_test_flag_info_list, FlagInfoBit, DEFAULT_FILE_VERSION, + }; #[test] // this test point locks down query if flag has server override fn test_is_flag_sticky() { - let flag_info_list = create_test_flag_info_list().into_bytes(); + let flag_info_list = create_test_flag_info_list(DEFAULT_FILE_VERSION).into_bytes(); for offset in 0..8 { let attribute = find_flag_attribute(&flag_info_list[..], FlagValueType::Boolean, offset).unwrap(); @@ -71,7 +73,7 @@ mod tests { #[test] // this test point locks down query if flag is readwrite fn test_is_flag_readwrite() { - let flag_info_list = create_test_flag_info_list().into_bytes(); + let flag_info_list = create_test_flag_info_list(DEFAULT_FILE_VERSION).into_bytes(); let baseline: Vec<bool> = vec![true, false, true, true, false, false, false, true]; for offset in 0..8 { let attribute = @@ -86,7 +88,7 @@ mod tests { #[test] // this test point locks down query if flag has local override fn test_flag_has_override() { - let flag_info_list = create_test_flag_info_list().into_bytes(); + let flag_info_list = create_test_flag_info_list(DEFAULT_FILE_VERSION).into_bytes(); for offset in 0..8 { let attribute = find_flag_attribute(&flag_info_list[..], FlagValueType::Boolean, offset).unwrap(); @@ -97,7 +99,7 @@ mod tests { #[test] // this test point locks down query beyond the end of boolean section fn test_boolean_out_of_range() { - let flag_info_list = create_test_flag_info_list().into_bytes(); + let flag_info_list = create_test_flag_info_list(DEFAULT_FILE_VERSION).into_bytes(); let error = find_flag_attribute(&flag_info_list[..], FlagValueType::Boolean, 8).unwrap_err(); assert_eq!( @@ -109,7 +111,7 @@ mod tests { #[test] // this test point locks down query error when file has a higher version fn test_higher_version_storage_file() { - let mut info_list = create_test_flag_info_list(); + let mut info_list = create_test_flag_info_list(DEFAULT_FILE_VERSION); info_list.header.version = MAX_SUPPORTED_FILE_VERSION + 1; let flag_info = info_list.into_bytes(); let error = find_flag_attribute(&flag_info[..], FlagValueType::Boolean, 4).unwrap_err(); diff --git a/tools/aconfig/aconfig_storage_read_api/src/flag_table_query.rs b/tools/aconfig/aconfig_storage_read_api/src/flag_table_query.rs index e9bc6041cf..3e87acc43b 100644 --- a/tools/aconfig/aconfig_storage_read_api/src/flag_table_query.rs +++ b/tools/aconfig/aconfig_storage_read_api/src/flag_table_query.rs @@ -74,12 +74,12 @@ pub fn find_flag_read_context( #[cfg(test)] mod tests { use super::*; - use aconfig_storage_file::test_utils::create_test_flag_table; + use aconfig_storage_file::{test_utils::create_test_flag_table, DEFAULT_FILE_VERSION}; #[test] // this test point locks down table query fn test_flag_query() { - let flag_table = create_test_flag_table().into_bytes(); + let flag_table = create_test_flag_table(DEFAULT_FILE_VERSION).into_bytes(); let baseline = vec![ (0, "enabled_ro", StoredFlagType::ReadOnlyBoolean, 1u16), (0, "enabled_rw", StoredFlagType::ReadWriteBoolean, 2u16), @@ -101,7 +101,7 @@ mod tests { #[test] // this test point locks down table query of a non exist flag fn test_not_existed_flag_query() { - let flag_table = create_test_flag_table().into_bytes(); + let flag_table = create_test_flag_table(DEFAULT_FILE_VERSION).into_bytes(); let flag_context = find_flag_read_context(&flag_table[..], 1, "disabled_fixed_ro").unwrap(); assert_eq!(flag_context, None); let flag_context = find_flag_read_context(&flag_table[..], 2, "disabled_rw").unwrap(); @@ -111,7 +111,7 @@ mod tests { #[test] // this test point locks down query error when file has a higher version fn test_higher_version_storage_file() { - let mut table = create_test_flag_table(); + let mut table = create_test_flag_table(DEFAULT_FILE_VERSION); table.header.version = MAX_SUPPORTED_FILE_VERSION + 1; let flag_table = table.into_bytes(); let error = find_flag_read_context(&flag_table[..], 0, "enabled_ro").unwrap_err(); diff --git a/tools/aconfig/aconfig_storage_read_api/src/flag_value_query.rs b/tools/aconfig/aconfig_storage_read_api/src/flag_value_query.rs index 12c1e83628..35f56929a9 100644 --- a/tools/aconfig/aconfig_storage_read_api/src/flag_value_query.rs +++ b/tools/aconfig/aconfig_storage_read_api/src/flag_value_query.rs @@ -48,12 +48,12 @@ pub fn find_boolean_flag_value(buf: &[u8], flag_index: u32) -> Result<bool, Acon #[cfg(test)] mod tests { use super::*; - use aconfig_storage_file::test_utils::create_test_flag_value_list; + use aconfig_storage_file::{test_utils::create_test_flag_value_list, DEFAULT_FILE_VERSION}; #[test] // this test point locks down flag value query fn test_flag_value_query() { - let flag_value_list = create_test_flag_value_list().into_bytes(); + let flag_value_list = create_test_flag_value_list(DEFAULT_FILE_VERSION).into_bytes(); let baseline: Vec<bool> = vec![false, true, true, false, true, true, true, true]; for (offset, expected_value) in baseline.into_iter().enumerate() { let flag_value = find_boolean_flag_value(&flag_value_list[..], offset as u32).unwrap(); @@ -64,7 +64,7 @@ mod tests { #[test] // this test point locks down query beyond the end of boolean section fn test_boolean_out_of_range() { - let flag_value_list = create_test_flag_value_list().into_bytes(); + let flag_value_list = create_test_flag_value_list(DEFAULT_FILE_VERSION).into_bytes(); let error = find_boolean_flag_value(&flag_value_list[..], 8).unwrap_err(); assert_eq!( format!("{:?}", error), @@ -75,7 +75,7 @@ mod tests { #[test] // this test point locks down query error when file has a higher version fn test_higher_version_storage_file() { - let mut value_list = create_test_flag_value_list(); + let mut value_list = create_test_flag_value_list(DEFAULT_FILE_VERSION); value_list.header.version = MAX_SUPPORTED_FILE_VERSION + 1; let flag_value = value_list.into_bytes(); let error = find_boolean_flag_value(&flag_value[..], 4).unwrap_err(); diff --git a/tools/aconfig/aconfig_storage_read_api/src/lib.rs b/tools/aconfig/aconfig_storage_read_api/src/lib.rs index 884f148de9..6e98fe97ad 100644 --- a/tools/aconfig/aconfig_storage_read_api/src/lib.rs +++ b/tools/aconfig/aconfig_storage_read_api/src/lib.rs @@ -44,6 +44,7 @@ pub mod package_table_query; pub use aconfig_storage_file::{AconfigStorageError, FlagValueType, StorageFileType}; pub use flag_table_query::FlagReadContext; +pub use mapped_file::map_file; pub use package_table_query::PackageReadContext; use aconfig_storage_file::read_u32_from_bytes; @@ -114,13 +115,13 @@ pub fn get_flag_read_context( /// Get the boolean flag value. /// -/// \input file: mapped flag file +/// \input file: a byte slice, can be either &Mmap or &MapMut /// \input index: boolean flag offset /// /// \return /// If the provide offset is valid, it returns the boolean flag value, otherwise it /// returns the error message. -pub fn get_boolean_flag_value(file: &Mmap, index: u32) -> Result<bool, AconfigStorageError> { +pub fn get_boolean_flag_value(file: &[u8], index: u32) -> Result<bool, AconfigStorageError> { find_boolean_flag_value(file, index) } @@ -148,7 +149,7 @@ pub fn get_storage_file_version(file_path: &str) -> Result<u32, AconfigStorageEr /// Get the flag attribute. /// -/// \input file: mapped flag info file +/// \input file: a byte slice, can be either &Mmap or &MapMut /// \input flag_type: flag value type /// \input flag_index: flag index /// @@ -156,7 +157,7 @@ pub fn get_storage_file_version(file_path: &str) -> Result<u32, AconfigStorageEr /// If the provide offset is valid, it returns the flag attribute bitfiled, otherwise it /// returns the error message. pub fn get_flag_attribute( - file: &Mmap, + file: &[u8], flag_type: FlagValueType, flag_index: u32, ) -> Result<u8, AconfigStorageError> { @@ -412,10 +413,10 @@ mod tests { let flag_map = storage_dir.clone() + "/maps/mockup.flag.map"; let flag_val = storage_dir.clone() + "/boot/mockup.val"; let flag_info = storage_dir.clone() + "/boot/mockup.info"; - fs::copy("./tests/data/v1/package.map", &package_map).unwrap(); - fs::copy("./tests/data/v1/flag.map", &flag_map).unwrap(); - fs::copy("./tests/data/v1/flag.val", &flag_val).unwrap(); - fs::copy("./tests/data/v1/flag.info", &flag_info).unwrap(); + fs::copy("./tests/data/v1/package_v1.map", &package_map).unwrap(); + fs::copy("./tests/data/v1/flag_v1.map", &flag_map).unwrap(); + fs::copy("./tests/data/v1/flag_v1.val", &flag_val).unwrap(); + fs::copy("./tests/data/v1/flag_v1.info", &flag_info).unwrap(); return storage_dir; } @@ -507,9 +508,9 @@ mod tests { #[test] // this test point locks down flag storage file version number query api fn test_storage_version_query() { - assert_eq!(get_storage_file_version("./tests/data/v1/package.map").unwrap(), 1); - assert_eq!(get_storage_file_version("./tests/data/v1/flag.map").unwrap(), 1); - assert_eq!(get_storage_file_version("./tests/data/v1/flag.val").unwrap(), 1); - assert_eq!(get_storage_file_version("./tests/data/v1/flag.info").unwrap(), 1); + assert_eq!(get_storage_file_version("./tests/data/v1/package_v1.map").unwrap(), 1); + assert_eq!(get_storage_file_version("./tests/data/v1/flag_v1.map").unwrap(), 1); + assert_eq!(get_storage_file_version("./tests/data/v1/flag_v1.val").unwrap(), 1); + assert_eq!(get_storage_file_version("./tests/data/v1/flag_v1.info").unwrap(), 1); } } diff --git a/tools/aconfig/aconfig_storage_read_api/src/mapped_file.rs b/tools/aconfig/aconfig_storage_read_api/src/mapped_file.rs index 32dbed88e3..f4e269e68b 100644 --- a/tools/aconfig/aconfig_storage_read_api/src/mapped_file.rs +++ b/tools/aconfig/aconfig_storage_read_api/src/mapped_file.rs @@ -28,7 +28,7 @@ use crate::StorageFileType; /// The memory mapped file may have undefined behavior if there are writes to this /// file after being mapped. Ensure no writes can happen to this file while this /// mapping stays alive. -unsafe fn map_file(file_path: &str) -> Result<Mmap, AconfigStorageError> { +pub unsafe fn map_file(file_path: &str) -> Result<Mmap, AconfigStorageError> { let file = File::open(file_path) .map_err(|errmsg| FileReadFail(anyhow!("Failed to open file {}: {}", file_path, errmsg)))?; unsafe { @@ -97,10 +97,10 @@ mod tests { let flag_map = storage_dir.clone() + "/maps/mockup.flag.map"; let flag_val = storage_dir.clone() + "/boot/mockup.val"; let flag_info = storage_dir.clone() + "/boot/mockup.info"; - fs::copy("./tests/data/v1/package.map", &package_map).unwrap(); - fs::copy("./tests/data/v1/flag.map", &flag_map).unwrap(); - fs::copy("./tests/data/v1/flag.val", &flag_val).unwrap(); - fs::copy("./tests/data/v1/flag.info", &flag_info).unwrap(); + fs::copy("./tests/data/v1/package_v1.map", &package_map).unwrap(); + fs::copy("./tests/data/v1/flag_v1.map", &flag_map).unwrap(); + fs::copy("./tests/data/v1/flag_v1.val", &flag_val).unwrap(); + fs::copy("./tests/data/v1/flag_v1.info", &flag_info).unwrap(); return storage_dir; } @@ -108,9 +108,9 @@ mod tests { #[test] fn test_mapped_file_contents() { let storage_dir = create_test_storage_files(); - map_and_verify(&storage_dir, StorageFileType::PackageMap, "./tests/data/v1/package.map"); - map_and_verify(&storage_dir, StorageFileType::FlagMap, "./tests/data/v1/flag.map"); - map_and_verify(&storage_dir, StorageFileType::FlagVal, "./tests/data/v1/flag.val"); - map_and_verify(&storage_dir, StorageFileType::FlagInfo, "./tests/data/v1/flag.info"); + map_and_verify(&storage_dir, StorageFileType::PackageMap, "./tests/data/v1/package_v1.map"); + map_and_verify(&storage_dir, StorageFileType::FlagMap, "./tests/data/v1/flag_v1.map"); + map_and_verify(&storage_dir, StorageFileType::FlagVal, "./tests/data/v1/flag_v1.val"); + map_and_verify(&storage_dir, StorageFileType::FlagInfo, "./tests/data/v1/flag_v1.info"); } } diff --git a/tools/aconfig/aconfig_storage_read_api/src/package_table_query.rs b/tools/aconfig/aconfig_storage_read_api/src/package_table_query.rs index acb60f6b3a..a4b63ab050 100644 --- a/tools/aconfig/aconfig_storage_read_api/src/package_table_query.rs +++ b/tools/aconfig/aconfig_storage_read_api/src/package_table_query.rs @@ -74,12 +74,12 @@ pub fn find_package_read_context( #[cfg(test)] mod tests { use super::*; - use aconfig_storage_file::test_utils::create_test_package_table; + use aconfig_storage_file::{test_utils::create_test_package_table, DEFAULT_FILE_VERSION}; #[test] // this test point locks down table query fn test_package_query() { - let package_table = create_test_package_table().into_bytes(); + let package_table = create_test_package_table(DEFAULT_FILE_VERSION).into_bytes(); let package_context = find_package_read_context(&package_table[..], "com.android.aconfig.storage.test_1") .unwrap() @@ -104,7 +104,7 @@ mod tests { // this test point locks down table query of a non exist package fn test_not_existed_package_query() { // this will land at an empty bucket - let package_table = create_test_package_table().into_bytes(); + let package_table = create_test_package_table(DEFAULT_FILE_VERSION).into_bytes(); let package_context = find_package_read_context(&package_table[..], "com.android.aconfig.storage.test_3") .unwrap(); @@ -119,7 +119,7 @@ mod tests { #[test] // this test point locks down query error when file has a higher version fn test_higher_version_storage_file() { - let mut table = create_test_package_table(); + let mut table = create_test_package_table(DEFAULT_FILE_VERSION); table.header.version = MAX_SUPPORTED_FILE_VERSION + 1; let package_table = table.into_bytes(); let error = diff --git a/tools/aconfig/aconfig_storage_read_api/srcs/android/aconfig/storage/AconfigPackageImpl.java b/tools/aconfig/aconfig_storage_read_api/srcs/android/aconfig/storage/AconfigPackageImpl.java new file mode 100644 index 0000000000..c2bef31c2b --- /dev/null +++ b/tools/aconfig/aconfig_storage_read_api/srcs/android/aconfig/storage/AconfigPackageImpl.java @@ -0,0 +1,137 @@ +/* + * 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.os.StrictMode; + +import java.nio.file.Path; + +/** @hide */ +public class AconfigPackageImpl { + private FlagTable mFlagTable; + private FlagValueList mFlagValueList; + private PackageTable.Node mPNode; + + /** @hide */ + private AconfigPackageImpl() {} + + /** @hide */ + public static AconfigPackageImpl load(String packageName, StorageFileProvider fileProvider) { + AconfigPackageImpl impl = new AconfigPackageImpl(); + impl.init(null, packageName, fileProvider); + return impl; + } + + /** @hide */ + public static AconfigPackageImpl load( + String container, String packageName, StorageFileProvider fileProvider) { + if (container == null) { + throw new AconfigStorageException( + AconfigStorageException.ERROR_CONTAINER_NOT_FOUND, + "container null cannot be found on the device"); + } + AconfigPackageImpl impl = new AconfigPackageImpl(); + impl.init(container, packageName, fileProvider); + return impl; + } + + /** @hide */ + public boolean getBooleanFlagValue(String flagName, boolean defaultValue) { + FlagTable.Node fNode = mFlagTable.get(mPNode.getPackageId(), flagName); + // no such flag in this package + if (fNode == null) return defaultValue; + int index = fNode.getFlagIndex() + mPNode.getBooleanStartIndex(); + return mFlagValueList.getBoolean(index); + } + + /** @hide */ + public boolean getBooleanFlagValue(int index) { + return mFlagValueList.getBoolean(index + mPNode.getBooleanStartIndex()); + } + + /** @hide */ + public long getPackageFingerprint() { + return mPNode.getPackageFingerprint(); + } + + /** @hide */ + public boolean hasPackageFingerprint() { + return mPNode.hasPackageFingerprint(); + } + + private void init(String containerName, String packageName, StorageFileProvider fileProvider) { + StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads(); + String container = containerName; + try { + // for devices don't have new storage directly return + if (!fileProvider.containerFileExists(null)) { + throw new AconfigStorageException( + AconfigStorageException.ERROR_STORAGE_SYSTEM_NOT_FOUND, + "aconfig storage cannot be found on the device"); + } + PackageTable.Node pNode = null; + + if (container == null) { + // Check if the device has flag files on the system partition. + // If the device does, search the system partition first. + container = "system"; + if (fileProvider.containerFileExists(container)) { + pNode = fileProvider.getPackageTable(container).get(packageName); + } + + if (pNode == null) { + // Search all package map files if not found in the system partition. + for (Path p : fileProvider.listPackageMapFiles()) { + PackageTable pTable = StorageFileProvider.getPackageTable(p); + pNode = pTable.get(packageName); + if (pNode != null) { + container = pTable.getHeader().getContainer(); + break; + } + } + } + } else { + if (!fileProvider.containerFileExists(container)) { + throw new AconfigStorageException( + AconfigStorageException.ERROR_CONTAINER_NOT_FOUND, + "container " + container + " cannot be found on the device"); + } + pNode = fileProvider.getPackageTable(container).get(packageName); + } + + if (pNode == null) { + // for the case package is not found in all container, return instead of throwing + // error + throw new AconfigStorageException( + AconfigStorageException.ERROR_PACKAGE_NOT_FOUND, + "package " + + packageName + + " in container " + + container + + " cannot be found on the device"); + } + + mFlagTable = fileProvider.getFlagTable(container); + mFlagValueList = fileProvider.getFlagValueList(container); + mPNode = pNode; + } catch (Exception e) { + throw e; + } finally { + StrictMode.setThreadPolicy(oldPolicy); + } + } +} diff --git a/tools/aconfig/aconfig_storage_read_api/srcs/android/aconfig/storage/StorageFileProvider.java b/tools/aconfig/aconfig_storage_read_api/srcs/android/aconfig/storage/StorageFileProvider.java new file mode 100644 index 0000000000..b28341ecd2 --- /dev/null +++ b/tools/aconfig/aconfig_storage_read_api/srcs/android/aconfig/storage/StorageFileProvider.java @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.aconfig.storage; + +import java.io.Closeable; +import java.nio.MappedByteBuffer; +import java.nio.channels.FileChannel; +import java.nio.file.DirectoryStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardOpenOption; +import java.util.ArrayList; +import java.util.List; + +/** @hide */ +public class StorageFileProvider { + + private static final String DEFAULT_MAP_PATH = "/metadata/aconfig/maps/"; + private static final String DEFAULT_BOOT_PATH = "/metadata/aconfig/boot/"; + private static final String PMAP_FILE_EXT = ".package.map"; + private static final String FMAP_FILE_EXT = ".flag.map"; + private static final String VAL_FILE_EXT = ".val"; + + private final String mMapPath; + private final String mBootPath; + + /** @hide */ + public static StorageFileProvider getDefaultProvider() { + return new StorageFileProvider(DEFAULT_MAP_PATH, DEFAULT_BOOT_PATH); + } + + /** @hide */ + public StorageFileProvider(String mapPath, String bootPath) { + mMapPath = mapPath; + mBootPath = bootPath; + } + + /** @hide */ + public boolean containerFileExists(String container) { + if (container == null) { + return Files.exists(Paths.get(mMapPath)); + } + return Files.exists(Paths.get(mMapPath, container + PMAP_FILE_EXT)); + } + + /** @hide */ + public List<Path> listPackageMapFiles() { + List<Path> result = new ArrayList<>(); + try { + DirectoryStream<Path> stream = + Files.newDirectoryStream(Paths.get(mMapPath), "*" + PMAP_FILE_EXT); + for (Path entry : stream) { + result.add(entry); + // sb.append(entry. toString()); + } + } catch (Exception e) { + throw new AconfigStorageException( + String.format("Fail to list map files in path %s", mMapPath), e); + } + + return result; + } + + /** @hide */ + public PackageTable getPackageTable(String container) { + return getPackageTable(Paths.get(mMapPath, container + PMAP_FILE_EXT)); + } + + /** @hide */ + public FlagTable getFlagTable(String container) { + return FlagTable.fromBytes(mapStorageFile(Paths.get(mMapPath, container + FMAP_FILE_EXT))); + } + + /** @hide */ + public FlagValueList getFlagValueList(String container) { + return FlagValueList.fromBytes( + mapStorageFile(Paths.get(mBootPath, container + VAL_FILE_EXT))); + } + + /** @hide */ + public static PackageTable getPackageTable(Path path) { + return PackageTable.fromBytes(mapStorageFile(path)); + } + + // Map a storage file given file path + private static MappedByteBuffer mapStorageFile(Path file) { + FileChannel channel = null; + try { + channel = FileChannel.open(file, StandardOpenOption.READ); + return channel.map(FileChannel.MapMode.READ_ONLY, 0, channel.size()); + } catch (Exception e) { + throw new AconfigStorageException( + AconfigStorageException.ERROR_CANNOT_READ_STORAGE_FILE, + String.format("Fail to mmap storage file %s", file), + e); + } finally { + quietlyDispose(channel); + } + } + + 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/Android.bp b/tools/aconfig/aconfig_storage_read_api/tests/Android.bp index b8e510d68c..6b8942b389 100644 --- a/tools/aconfig/aconfig_storage_read_api/tests/Android.bp +++ b/tools/aconfig/aconfig_storage_read_api/tests/Android.bp @@ -1,10 +1,14 @@ filegroup { name: "read_api_test_storage_files", srcs: [ - "data/v1/package.map", - "data/v1/flag.map", - "data/v1/flag.val", - "data/v1/flag.info", + "data/v1/package_v1.map", + "data/v1/flag_v1.map", + "data/v1/flag_v1.val", + "data/v1/flag_v1.info", + "data/v2/package_v2.map", + "data/v2/flag_v2.map", + "data/v2/flag_v2.val", + "data/v2/flag_v2.info", ], } diff --git a/tools/aconfig/aconfig_storage_read_api/tests/data/v1/flag.info b/tools/aconfig/aconfig_storage_read_api/tests/data/v1/flag_v1.info Binary files differindex 6223edf369..6223edf369 100644 --- a/tools/aconfig/aconfig_storage_read_api/tests/data/v1/flag.info +++ b/tools/aconfig/aconfig_storage_read_api/tests/data/v1/flag_v1.info diff --git a/tools/aconfig/aconfig_storage_read_api/tests/data/v1/flag.map b/tools/aconfig/aconfig_storage_read_api/tests/data/v1/flag_v1.map Binary files differindex e868f53d7e..e868f53d7e 100644 --- a/tools/aconfig/aconfig_storage_read_api/tests/data/v1/flag.map +++ b/tools/aconfig/aconfig_storage_read_api/tests/data/v1/flag_v1.map diff --git a/tools/aconfig/aconfig_storage_read_api/tests/data/v1/flag.val b/tools/aconfig/aconfig_storage_read_api/tests/data/v1/flag_v1.val Binary files differindex ed203d4d13..ed203d4d13 100644 --- a/tools/aconfig/aconfig_storage_read_api/tests/data/v1/flag.val +++ b/tools/aconfig/aconfig_storage_read_api/tests/data/v1/flag_v1.val diff --git a/tools/aconfig/aconfig_storage_read_api/tests/data/v1/package.map b/tools/aconfig/aconfig_storage_read_api/tests/data/v1/package_v1.map Binary files differindex 6c46a0339c..6c46a0339c 100644 --- a/tools/aconfig/aconfig_storage_read_api/tests/data/v1/package.map +++ b/tools/aconfig/aconfig_storage_read_api/tests/data/v1/package_v1.map diff --git a/tools/aconfig/aconfig_storage_read_api/tests/data/v2/flag_v2.info b/tools/aconfig/aconfig_storage_read_api/tests/data/v2/flag_v2.info Binary files differnew file mode 100644 index 0000000000..9db7fde7ae --- /dev/null +++ b/tools/aconfig/aconfig_storage_read_api/tests/data/v2/flag_v2.info diff --git a/tools/aconfig/aconfig_storage_read_api/tests/data/v2/flag_v2.map b/tools/aconfig/aconfig_storage_read_api/tests/data/v2/flag_v2.map Binary files differnew file mode 100644 index 0000000000..cf4685ceb4 --- /dev/null +++ b/tools/aconfig/aconfig_storage_read_api/tests/data/v2/flag_v2.map diff --git a/tools/aconfig/aconfig_storage_read_api/tests/data/v2/flag_v2.val b/tools/aconfig/aconfig_storage_read_api/tests/data/v2/flag_v2.val Binary files differnew file mode 100644 index 0000000000..37d4750206 --- /dev/null +++ b/tools/aconfig/aconfig_storage_read_api/tests/data/v2/flag_v2.val diff --git a/tools/aconfig/aconfig_storage_read_api/tests/data/v2/package_v2.map b/tools/aconfig/aconfig_storage_read_api/tests/data/v2/package_v2.map Binary files differnew file mode 100644 index 0000000000..16f4054879 --- /dev/null +++ b/tools/aconfig/aconfig_storage_read_api/tests/data/v2/package_v2.map diff --git a/tools/aconfig/aconfig_storage_read_api/tests/java/AconfigPackageImplTest.java b/tools/aconfig/aconfig_storage_read_api/tests/java/AconfigPackageImplTest.java new file mode 100644 index 0000000000..18f70e827e --- /dev/null +++ b/tools/aconfig/aconfig_storage_read_api/tests/java/AconfigPackageImplTest.java @@ -0,0 +1,134 @@ +/* + * 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.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertThrows; +import static org.junit.Assert.assertTrue; + +import android.aconfig.storage.AconfigPackageImpl; +import android.aconfig.storage.AconfigStorageException; +import android.aconfig.storage.StorageFileProvider; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@RunWith(JUnit4.class) +public class AconfigPackageImplTest { + + private StorageFileProvider pr; + + @Before + public void setup() { + pr = new StorageFileProvider(TestDataUtils.TESTDATA_PATH, TestDataUtils.TESTDATA_PATH); + } + + @Test + public void testLoad_onlyPackageName() throws Exception { + AconfigPackageImpl p = AconfigPackageImpl.load("com.android.aconfig.storage.test_1", pr); + assertNotNull(p); + } + + @Test + public void testLoad_groupNameFingerprint() throws Exception { + AconfigPackageImpl p = + AconfigPackageImpl.load("mockup", "com.android.aconfig.storage.test_1", pr); + assertNotNull(p); + } + + @Test + public void testLoad_error() throws Exception { + AconfigPackageImpl p; + AconfigStorageException e = + assertThrows( + AconfigStorageException.class, + () -> + AconfigPackageImpl.load( + "mockup", "com.android.aconfig.storage.test_10", pr)); + // cannot find package + assertEquals(AconfigStorageException.ERROR_PACKAGE_NOT_FOUND, e.getErrorCode()); + // cannot find package + e = + assertThrows( + AconfigStorageException.class, + () -> AconfigPackageImpl.load("com.android.aconfig.storage.test_10", pr)); + assertEquals(AconfigStorageException.ERROR_PACKAGE_NOT_FOUND, e.getErrorCode()); + // cannot find container + e = + assertThrows( + AconfigStorageException.class, + () -> + AconfigPackageImpl.load( + null, "com.android.aconfig.storage.test_1", pr)); + assertEquals(AconfigStorageException.ERROR_CONTAINER_NOT_FOUND, e.getErrorCode()); + e = + assertThrows( + AconfigStorageException.class, + () -> + AconfigPackageImpl.load( + "test", "com.android.aconfig.storage.test_1", pr)); + assertEquals(AconfigStorageException.ERROR_CONTAINER_NOT_FOUND, e.getErrorCode()); + + // new storage doesn't exist + pr = new StorageFileProvider("fake/path/", "fake/path/"); + e = + assertThrows( + AconfigStorageException.class, + () -> AconfigPackageImpl.load("fake_package", pr)); + assertEquals(AconfigStorageException.ERROR_STORAGE_SYSTEM_NOT_FOUND, e.getErrorCode()); + + // file read issue + pr = new StorageFileProvider(TestDataUtils.TESTDATA_PATH, "fake/path/"); + e = + assertThrows( + AconfigStorageException.class, + () -> + AconfigPackageImpl.load( + "mockup", "com.android.aconfig.storage.test_1", pr)); + assertEquals(AconfigStorageException.ERROR_CANNOT_READ_STORAGE_FILE, e.getErrorCode()); + } + + @Test + public void testGetBooleanFlagValue_flagName() throws Exception { + AconfigPackageImpl p = + AconfigPackageImpl.load("mockup", "com.android.aconfig.storage.test_1", pr); + assertFalse(p.getBooleanFlagValue("disabled_rw", true)); + assertTrue(p.getBooleanFlagValue("enabled_ro", false)); + assertTrue(p.getBooleanFlagValue("enabled_rw", false)); + assertFalse(p.getBooleanFlagValue("fake", false)); + } + + @Test + public void testGetBooleanFlagValue_index() throws Exception { + AconfigPackageImpl p = + AconfigPackageImpl.load("mockup", "com.android.aconfig.storage.test_1", pr); + assertFalse(p.getBooleanFlagValue(0)); + assertTrue(p.getBooleanFlagValue(1)); + assertTrue(p.getBooleanFlagValue(2)); + } + + @Test + public void testHasPackageFingerprint() throws Exception { + AconfigPackageImpl p = + AconfigPackageImpl.load("mockup", "com.android.aconfig.storage.test_1", pr); + assertFalse(p.hasPackageFingerprint()); + } +} 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 3d4e9ad218..9c881228b1 100644 --- a/tools/aconfig/aconfig_storage_read_api/tests/java/Android.bp +++ b/tools/aconfig/aconfig_storage_read_api/tests/java/Android.bp @@ -22,3 +22,28 @@ android_test { ], team: "trendy_team_android_core_experiments", } + +android_test { + name: "aconfig_storage_package", + team: "trendy_team_android_core_experiments", + srcs: [ + "AconfigPackageImplTest.java", + "StorageFileProviderTest.java", + "TestDataUtils.java", + ], + static_libs: [ + "androidx.test.runner", + "junit", + "aconfig_storage_reader_java", + ], + test_config: "AndroidStorageJaveTest.xml", + manifest: "AndroidPackageTestManifest.xml", + sdk_version: "test_current", + data: [ + ":read_api_test_storage_files", + ], + test_suites: [ + "general-tests", + ], + jarjar_rules: "jarjar.txt", +} diff --git a/tools/aconfig/aconfig_storage_read_api/tests/java/AndroidPackageTestManifest.xml b/tools/aconfig/aconfig_storage_read_api/tests/java/AndroidPackageTestManifest.xml new file mode 100644 index 0000000000..5e01879157 --- /dev/null +++ b/tools/aconfig/aconfig_storage_read_api/tests/java/AndroidPackageTestManifest.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_read_api/tests/java/AndroidStorageJaveTest.xml b/tools/aconfig/aconfig_storage_read_api/tests/java/AndroidStorageJaveTest.xml new file mode 100644 index 0000000000..861b9b5706 --- /dev/null +++ b/tools/aconfig/aconfig_storage_read_api/tests/java/AndroidStorageJaveTest.xml @@ -0,0 +1,34 @@ +<?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_package.apk" /> + </target_preparer> + <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher"> + <option name="cleanup" value="true" /> + <option name="push" value="package_v1.map->/data/local/tmp/aconfig_storage_package/testdata/mockup.package.map" /> + <option name="push" value="flag_v1.map->/data/local/tmp/aconfig_storage_package/testdata/mockup.flag.map" /> + <option name="push" value="flag_v1.val->/data/local/tmp/aconfig_storage_package/testdata/mockup.val" /> + <option name="push" value="flag_v1.info->/data/local/tmp/aconfig_storage_package/testdata/mockup.info" /> + <option name="post-push" value="chmod +r /data/local/tmp/aconfig_storage_package/testdata/" /> + </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> diff --git a/tools/aconfig/aconfig_storage_read_api/tests/java/AndroidTest.xml b/tools/aconfig/aconfig_storage_read_api/tests/java/AndroidTest.xml index 99c9e2566e..7ffa18cfc5 100644 --- a/tools/aconfig/aconfig_storage_read_api/tests/java/AndroidTest.xml +++ b/tools/aconfig/aconfig_storage_read_api/tests/java/AndroidTest.xml @@ -35,11 +35,11 @@ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer"> <option name="cleanup" value="true" /> <option name="abort-on-push-failure" value="true" /> - <option name="push-file" key="package.map" + <option name="push-file" key="package_v1.map" value="/data/local/tmp/aconfig_java_api_test/maps/mockup.package.map" /> - <option name="push-file" key="flag.map" + <option name="push-file" key="flag_v1.map" value="/data/local/tmp/aconfig_java_api_test/maps/mockup.flag.map" /> - <option name="push-file" key="flag.val" + <option name="push-file" key="flag_v1.val" value="/data/local/tmp/aconfig_java_api_test/boot/mockup.val" /> </target_preparer> diff --git a/tools/aconfig/aconfig_storage_read_api/tests/java/StorageFileProviderTest.java b/tools/aconfig/aconfig_storage_read_api/tests/java/StorageFileProviderTest.java new file mode 100644 index 0000000000..ba1ae9ed3d --- /dev/null +++ b/tools/aconfig/aconfig_storage_read_api/tests/java/StorageFileProviderTest.java @@ -0,0 +1,80 @@ +/* + * 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.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import android.aconfig.storage.FlagTable; +import android.aconfig.storage.FlagValueList; +import android.aconfig.storage.PackageTable; +import android.aconfig.storage.StorageFileProvider; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; + +@RunWith(JUnit4.class) +public class StorageFileProviderTest { + + @Test + public void testContainerFileExists() throws Exception { + StorageFileProvider p = + new StorageFileProvider(TestDataUtils.TESTDATA_PATH, TestDataUtils.TESTDATA_PATH); + assertTrue(p.containerFileExists(null)); + assertTrue(p.containerFileExists("mockup")); + assertFalse(p.containerFileExists("fake")); + } + + @Test + public void testListpackageMapFiles() throws Exception { + StorageFileProvider p = + new StorageFileProvider(TestDataUtils.TESTDATA_PATH, TestDataUtils.TESTDATA_PATH); + // throw new Exception(Environment.getExternalStorageDirectory().getAbsolutePath()); + List<Path> file = p.listPackageMapFiles(); + assertEquals(1, file.size()); + assertTrue( + file.get(0) + .equals( + Paths.get( + TestDataUtils.TESTDATA_PATH, + TestDataUtils.TEST_PACKAGE_MAP_PATH))); + } + + @Test + public void testLoadFiles() throws Exception { + StorageFileProvider p = + new StorageFileProvider(TestDataUtils.TESTDATA_PATH, TestDataUtils.TESTDATA_PATH); + PackageTable pt = p.getPackageTable("mockup"); + assertNotNull(pt); + pt = + StorageFileProvider.getPackageTable( + Paths.get( + TestDataUtils.TESTDATA_PATH, TestDataUtils.TEST_PACKAGE_MAP_PATH)); + assertNotNull(pt); + FlagTable f = p.getFlagTable("mockup"); + assertNotNull(f); + FlagValueList v = p.getFlagValueList("mockup"); + assertNotNull(v); + } +} diff --git a/tools/aconfig/aconfig_storage_read_api/tests/java/TestDataUtils.java b/tools/aconfig/aconfig_storage_read_api/tests/java/TestDataUtils.java new file mode 100644 index 0000000000..d5cddc732a --- /dev/null +++ b/tools/aconfig/aconfig_storage_read_api/tests/java/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 { + public static final String TEST_PACKAGE_MAP_PATH = "mockup.package.map"; + public static final String TEST_FLAG_MAP_PATH = "mockup.flag.map"; + public static final String TEST_FLAG_VAL_PATH = "mockup.val"; + public static final String TEST_FLAG_INFO_PATH = "mockup.info"; + + public static final String TESTDATA_PATH = + "/data/local/tmp/aconfig_storage_package/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/tests/java/jarjar.txt b/tools/aconfig/aconfig_storage_read_api/tests/java/jarjar.txt new file mode 100644 index 0000000000..24952ecfdf --- /dev/null +++ b/tools/aconfig/aconfig_storage_read_api/tests/java/jarjar.txt @@ -0,0 +1,17 @@ +rule android.aconfig.storage.AconfigStorageException android.aconfig.storage.test.AconfigStorageException +rule android.aconfig.storage.FlagTable android.aconfig.storage.test.FlagTable +rule android.aconfig.storage.PackageTable android.aconfig.storage.test.PackageTable +rule android.aconfig.storage.ByteBufferReader android.aconfig.storage.test.ByteBufferReader +rule android.aconfig.storage.FlagType android.aconfig.storage.test.FlagType +rule android.aconfig.storage.SipHasher13 android.aconfig.storage.test.SipHasher13 +rule android.aconfig.storage.FileType android.aconfig.storage.test.FileType +rule android.aconfig.storage.FlagValueList android.aconfig.storage.test.FlagValueList +rule android.aconfig.storage.TableUtils android.aconfig.storage.test.TableUtils +rule android.aconfig.storage.AconfigPackageImpl android.aconfig.storage.test.AconfigPackageImpl +rule android.aconfig.storage.StorageFileProvider android.aconfig.storage.test.StorageFileProvider + + +rule android.aconfig.storage.FlagTable$* android.aconfig.storage.test.FlagTable$@1 +rule android.aconfig.storage.PackageTable$* android.aconfig.storage.test.PackageTable$@1 +rule android.aconfig.storage.FlagValueList$* android.aconfig.storage.test.FlagValueList@1 +rule android.aconfig.storage.SipHasher13$* android.aconfig.storage.test.SipHasher13@1 diff --git a/tools/aconfig/aconfig_storage_read_api/tests/storage_read_api_test.cpp b/tools/aconfig/aconfig_storage_read_api/tests/storage_read_api_test.cpp index 753764369d..5289faa6de 100644 --- a/tools/aconfig/aconfig_storage_read_api/tests/storage_read_api_test.cpp +++ b/tools/aconfig/aconfig_storage_read_api/tests/storage_read_api_test.cpp @@ -56,10 +56,10 @@ class AconfigStorageTest : public ::testing::Test { flag_map = std::string(maps_dir) + "/mockup.flag.map"; flag_val = std::string(boot_dir) + "/mockup.val"; flag_info = std::string(boot_dir) + "/mockup.info"; - copy_file(test_dir + "/package.map", package_map); - copy_file(test_dir + "/flag.map", flag_map); - copy_file(test_dir + "/flag.val", flag_val); - copy_file(test_dir + "/flag.info", flag_info); + copy_file(test_dir + "/package_v1.map", package_map); + copy_file(test_dir + "/flag_v1.map", flag_map); + copy_file(test_dir + "/flag_v1.val", flag_val); + copy_file(test_dir + "/flag_v1.info", flag_info); } void TearDown() override { diff --git a/tools/aconfig/aconfig_storage_read_api/tests/storage_read_api_test.rs b/tools/aconfig/aconfig_storage_read_api/tests/storage_read_api_test.rs index 0d943f8981..5605a4118d 100644 --- a/tools/aconfig/aconfig_storage_read_api/tests/storage_read_api_test.rs +++ b/tools/aconfig/aconfig_storage_read_api/tests/storage_read_api_test.rs @@ -9,7 +9,7 @@ mod aconfig_storage_rust_test { use rand::Rng; use std::fs; - fn create_test_storage_files() -> String { + fn create_test_storage_files(version: u32) -> String { let mut rng = rand::thread_rng(); let number: u32 = rng.gen(); let storage_dir = String::from("/tmp/") + &number.to_string(); @@ -26,17 +26,17 @@ mod aconfig_storage_rust_test { let flag_map = storage_dir.clone() + "/maps/mockup.flag.map"; let flag_val = storage_dir.clone() + "/boot/mockup.val"; let flag_info = storage_dir.clone() + "/boot/mockup.info"; - fs::copy("./data/v1/package.map", package_map).unwrap(); - fs::copy("./data/v1/flag.map", flag_map).unwrap(); - fs::copy("./data/v1/flag.val", flag_val).unwrap(); - fs::copy("./data/v1/flag.info", flag_info).unwrap(); + fs::copy(format!("./data/v{0}/package_v{0}.map", version), package_map).unwrap(); + fs::copy(format!("./data/v{0}/flag_v{0}.map", version), flag_map).unwrap(); + fs::copy(format!("./data/v{}/flag_v{0}.val", version), flag_val).unwrap(); + fs::copy(format!("./data/v{}/flag_v{0}.info", version), flag_info).unwrap(); storage_dir } #[test] - fn test_unavailable_stoarge() { - let storage_dir = create_test_storage_files(); + fn test_unavailable_storage() { + let storage_dir = create_test_storage_files(1); // SAFETY: // The safety here is ensured as the test process will not write to temp storage file let err = unsafe { @@ -53,7 +53,7 @@ mod aconfig_storage_rust_test { #[test] fn test_package_context_query() { - let storage_dir = create_test_storage_files(); + let storage_dir = create_test_storage_files(1); // SAFETY: // The safety here is ensured as the test process will not write to temp storage file let package_mapped_file = unsafe { @@ -84,7 +84,7 @@ mod aconfig_storage_rust_test { #[test] fn test_none_exist_package_context_query() { - let storage_dir = create_test_storage_files(); + let storage_dir = create_test_storage_files(1); // SAFETY: // The safety here is ensured as the test process will not write to temp storage file let package_mapped_file = unsafe { @@ -99,7 +99,7 @@ mod aconfig_storage_rust_test { #[test] fn test_flag_context_query() { - let storage_dir = create_test_storage_files(); + let storage_dir = create_test_storage_files(1); // SAFETY: // The safety here is ensured as the test process will not write to temp storage file let flag_mapped_file = @@ -125,7 +125,7 @@ mod aconfig_storage_rust_test { #[test] fn test_none_exist_flag_context_query() { - let storage_dir = create_test_storage_files(); + let storage_dir = create_test_storage_files(1); // SAFETY: // The safety here is ensured as the test process will not write to temp storage file let flag_mapped_file = @@ -141,7 +141,7 @@ mod aconfig_storage_rust_test { #[test] fn test_boolean_flag_value_query() { - let storage_dir = create_test_storage_files(); + let storage_dir = create_test_storage_files(1); // SAFETY: // The safety here is ensured as the test process will not write to temp storage file let flag_value_file = @@ -155,7 +155,7 @@ mod aconfig_storage_rust_test { #[test] fn test_invalid_boolean_flag_value_query() { - let storage_dir = create_test_storage_files(); + let storage_dir = create_test_storage_files(1); // SAFETY: // The safety here is ensured as the test process will not write to temp storage file let flag_value_file = @@ -169,7 +169,7 @@ mod aconfig_storage_rust_test { #[test] fn test_flag_info_query() { - let storage_dir = create_test_storage_files(); + let storage_dir = create_test_storage_files(1); // SAFETY: // The safety here is ensured as the test process will not write to temp storage file let flag_info_file = @@ -186,7 +186,7 @@ mod aconfig_storage_rust_test { #[test] fn test_invalid_boolean_flag_info_query() { - let storage_dir = create_test_storage_files(); + let storage_dir = create_test_storage_files(1); // SAFETY: // The safety here is ensured as the test process will not write to temp storage file let flag_info_file = @@ -199,10 +199,18 @@ mod aconfig_storage_rust_test { } #[test] - fn test_storage_version_query() { - assert_eq!(get_storage_file_version("./data/v1/package.map").unwrap(), 1); - assert_eq!(get_storage_file_version("./data/v1/flag.map").unwrap(), 1); - assert_eq!(get_storage_file_version("./data/v1/flag.val").unwrap(), 1); - assert_eq!(get_storage_file_version("./data/v1/flag.info").unwrap(), 1); + fn test_storage_version_query_v1() { + assert_eq!(get_storage_file_version("./data/v1/package_v1.map").unwrap(), 1); + assert_eq!(get_storage_file_version("./data/v1/flag_v1.map").unwrap(), 1); + assert_eq!(get_storage_file_version("./data/v1/flag_v1.val").unwrap(), 1); + assert_eq!(get_storage_file_version("./data/v1/flag_v1.info").unwrap(), 1); + } + + #[test] + fn test_storage_version_query_v2() { + assert_eq!(get_storage_file_version("./data/v2/package_v2.map").unwrap(), 2); + assert_eq!(get_storage_file_version("./data/v2/flag_v2.map").unwrap(), 2); + assert_eq!(get_storage_file_version("./data/v2/flag_v2.val").unwrap(), 2); + assert_eq!(get_storage_file_version("./data/v2/flag_v2.info").unwrap(), 2); } } diff --git a/tools/aconfig/aconfig_storage_write_api/Android.bp b/tools/aconfig/aconfig_storage_write_api/Android.bp index 0f1962c3ac..4c882b4b9a 100644 --- a/tools/aconfig/aconfig_storage_write_api/Android.bp +++ b/tools/aconfig/aconfig_storage_write_api/Android.bp @@ -16,6 +16,11 @@ rust_defaults { "libaconfig_storage_file", "libaconfig_storage_read_api", ], + min_sdk_version: "34", + apex_available: [ + "//apex_available:anyapex", + "//apex_available:platform", + ], } rust_library { diff --git a/tools/aconfig/aconfig_storage_write_api/src/flag_info_update.rs b/tools/aconfig/aconfig_storage_write_api/src/flag_info_update.rs index 5640922f57..5721105d86 100644 --- a/tools/aconfig/aconfig_storage_write_api/src/flag_info_update.rs +++ b/tools/aconfig/aconfig_storage_write_api/src/flag_info_update.rs @@ -94,13 +94,13 @@ pub fn update_flag_has_local_override( #[cfg(test)] mod tests { use super::*; - use aconfig_storage_file::test_utils::create_test_flag_info_list; + use aconfig_storage_file::{test_utils::create_test_flag_info_list, DEFAULT_FILE_VERSION}; use aconfig_storage_read_api::flag_info_query::find_flag_attribute; #[test] // this test point locks down has server override update fn test_update_flag_has_server_override() { - let flag_info_list = create_test_flag_info_list(); + let flag_info_list = create_test_flag_info_list(DEFAULT_FILE_VERSION); let mut buf = flag_info_list.into_bytes(); for i in 0..flag_info_list.header.num_flags { update_flag_has_server_override(&mut buf, FlagValueType::Boolean, i, true).unwrap(); @@ -115,7 +115,7 @@ mod tests { #[test] // this test point locks down has local override update fn test_update_flag_has_local_override() { - let flag_info_list = create_test_flag_info_list(); + let flag_info_list = create_test_flag_info_list(DEFAULT_FILE_VERSION); let mut buf = flag_info_list.into_bytes(); for i in 0..flag_info_list.header.num_flags { update_flag_has_local_override(&mut buf, FlagValueType::Boolean, i, true).unwrap(); diff --git a/tools/aconfig/aconfig_storage_write_api/src/flag_value_update.rs b/tools/aconfig/aconfig_storage_write_api/src/flag_value_update.rs index 06a9b15241..9772db9ee8 100644 --- a/tools/aconfig/aconfig_storage_write_api/src/flag_value_update.rs +++ b/tools/aconfig/aconfig_storage_write_api/src/flag_value_update.rs @@ -49,12 +49,12 @@ pub fn update_boolean_flag_value( #[cfg(test)] mod tests { use super::*; - use aconfig_storage_file::test_utils::create_test_flag_value_list; + use aconfig_storage_file::{test_utils::create_test_flag_value_list, DEFAULT_FILE_VERSION}; #[test] // this test point locks down flag value update fn test_boolean_flag_value_update() { - let flag_value_list = create_test_flag_value_list(); + let flag_value_list = create_test_flag_value_list(DEFAULT_FILE_VERSION); let value_offset = flag_value_list.header.boolean_value_offset; let mut content = flag_value_list.into_bytes(); let true_byte = u8::from(true).to_le_bytes()[0]; @@ -72,7 +72,7 @@ mod tests { #[test] // this test point locks down update beyond the end of boolean section fn test_boolean_out_of_range() { - let mut flag_value_list = create_test_flag_value_list().into_bytes(); + let mut flag_value_list = create_test_flag_value_list(DEFAULT_FILE_VERSION).into_bytes(); let error = update_boolean_flag_value(&mut flag_value_list[..], 8, true).unwrap_err(); assert_eq!( format!("{:?}", error), @@ -83,7 +83,7 @@ mod tests { #[test] // this test point locks down query error when file has a higher version fn test_higher_version_storage_file() { - let mut value_list = create_test_flag_value_list(); + let mut value_list = create_test_flag_value_list(DEFAULT_FILE_VERSION); value_list.header.version = MAX_SUPPORTED_FILE_VERSION + 1; let mut flag_value = value_list.into_bytes(); let error = update_boolean_flag_value(&mut flag_value[..], 4, true).unwrap_err(); diff --git a/tools/aconfig/aflags/src/aconfig_storage_source.rs b/tools/aconfig/aflags/src/aconfig_storage_source.rs index 68edf7d3ac..3f593fe77e 100644 --- a/tools/aconfig/aflags/src/aconfig_storage_source.rs +++ b/tools/aconfig/aflags/src/aconfig_storage_source.rs @@ -93,7 +93,12 @@ fn read_from_socket() -> Result<Vec<ProtoFlagQueryReturnMessage>> { special_fields: SpecialFields::new(), }; - let mut socket = UnixStream::connect("/dev/socket/aconfigd")?; + let socket_name = if aconfig_flags::auto_generated::enable_system_aconfigd_rust() { + "/dev/socket/aconfigd_system" + } else { + "/dev/socket/aconfigd" + }; + let mut socket = UnixStream::connect(socket_name)?; let message_buffer = messages.write_to_bytes()?; let mut message_length_buffer: [u8; 4] = [0; 4]; diff --git a/tools/aconfig/aflags/src/load_protos.rs b/tools/aconfig/aflags/src/load_protos.rs index 90d8599145..c5ac8ff9dc 100644 --- a/tools/aconfig/aflags/src/load_protos.rs +++ b/tools/aconfig/aflags/src/load_protos.rs @@ -51,7 +51,10 @@ pub(crate) fn load() -> Result<Vec<Flag>> { let paths = aconfig_device_paths::parsed_flags_proto_paths()?; for path in paths { - let bytes = fs::read(path.clone())?; + let Ok(bytes) = fs::read(&path) else { + eprintln!("warning: failed to read {:?}", path); + continue; + }; let parsed_flags: ProtoParsedFlags = protobuf::Message::parse_from_bytes(&bytes)?; for flag in parsed_flags.parsed_flag { // TODO(b/334954748): enforce one-container-per-flag invariant. @@ -60,3 +63,10 @@ pub(crate) fn load() -> Result<Vec<Flag>> { } Ok(result) } + +pub(crate) fn list_containers() -> Result<Vec<String>> { + Ok(aconfig_device_paths::parsed_flags_proto_paths()? + .into_iter() + .map(|p| infer_container(&p)) + .collect()) +} diff --git a/tools/aconfig/aflags/src/main.rs b/tools/aconfig/aflags/src/main.rs index 07b7243ab4..8173bc24da 100644 --- a/tools/aconfig/aflags/src/main.rs +++ b/tools/aconfig/aflags/src/main.rs @@ -253,6 +253,14 @@ fn list(source_type: FlagSourceType, container: Option<String>) -> Result<String FlagSourceType::DeviceConfig => DeviceConfigSource::list_flags()?, FlagSourceType::AconfigStorage => AconfigStorageSource::list_flags()?, }; + + if let Some(ref c) = container { + ensure!( + load_protos::list_containers()?.contains(c), + format!("container '{}' not found", &c) + ); + } + let flags = (Filter { container }).apply(&flags_unfiltered); let padding_info = PaddingInfo { longest_flag_col: flags.iter().map(|f| f.qualified_name().len()).max().unwrap_or(0), @@ -298,7 +306,7 @@ fn main() -> Result<()> { Command::List { container } => { if aconfig_flags::auto_generated::enable_only_new_storage() { list(FlagSourceType::AconfigStorage, container) - .map_err(|err| anyhow!("storage may not be enabled: {err}")) + .map_err(|err| anyhow!("could not list flags: {err}")) .map(Some) } else { list(FlagSourceType::DeviceConfig, container).map(Some) diff --git a/tools/aconfig/fake_device_config/src/android/os/Binder.java b/tools/aconfig/fake_device_config/src/android/os/Binder.java new file mode 100644 index 0000000000..8a2313dfda --- /dev/null +++ b/tools/aconfig/fake_device_config/src/android/os/Binder.java @@ -0,0 +1,26 @@ +/* + * 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.os; + +public class Binder { + public static final long clearCallingIdentity() { + throw new UnsupportedOperationException("Stub!"); + } + public static final void restoreCallingIdentity(long token) { + throw new UnsupportedOperationException("Stub!"); + } +} diff --git a/tools/edit_monitor/Android.bp b/tools/edit_monitor/Android.bp index 34978214a9..b8ac5bff53 100644 --- a/tools/edit_monitor/Android.bp +++ b/tools/edit_monitor/Android.bp @@ -35,6 +35,13 @@ python_library_host { pkg_path: "edit_monitor", srcs: [ "daemon_manager.py", + "edit_monitor.py", + "utils.py", + ], + libs: [ + "asuite_cc_client", + "edit_event_proto", + "watchdog", ], } @@ -53,6 +60,51 @@ python_test_host { }, } +python_test_host { + name: "edit_monitor_test", + main: "edit_monitor_test.py", + pkg_path: "edit_monitor", + srcs: [ + "edit_monitor_test.py", + ], + libs: [ + "edit_monitor_lib", + ], + test_options: { + unit_test: true, + }, +} + +python_test_host { + name: "edit_monitor_utils_test", + main: "utils_test.py", + pkg_path: "edit_monitor", + srcs: [ + "utils_test.py", + ], + libs: [ + "edit_monitor_lib", + ], + test_options: { + unit_test: true, + }, +} + +python_test_host { + name: "edit_monitor_integration_test", + main: "edit_monitor_integration_test.py", + pkg_path: "testdata", + srcs: [ + "edit_monitor_integration_test.py", + ], + test_options: { + unit_test: true, + }, + data: [ + ":edit_monitor", + ], +} + python_binary_host { name: "edit_monitor", pkg_path: "edit_monitor", diff --git a/tools/edit_monitor/daemon_manager.py b/tools/edit_monitor/daemon_manager.py index 892c292a71..afdefc54e9 100644 --- a/tools/edit_monitor/daemon_manager.py +++ b/tools/edit_monitor/daemon_manager.py @@ -13,24 +13,33 @@ # limitations under the License. +import getpass import hashlib import logging import multiprocessing import os import pathlib +import platform import signal import subprocess import sys import tempfile import time +from atest.metrics import clearcut_client +from atest.proto import clientanalytics_pb2 +from edit_monitor import utils +from proto import edit_event_pb2 -DEFAULT_PROCESS_TERMINATION_TIMEOUT_SECONDS = 1 +DEFAULT_PROCESS_TERMINATION_TIMEOUT_SECONDS = 5 DEFAULT_MONITOR_INTERVAL_SECONDS = 5 -DEFAULT_MEMORY_USAGE_THRESHOLD = 2000 +DEFAULT_MEMORY_USAGE_THRESHOLD = 2 * 1024 # 2GB DEFAULT_CPU_USAGE_THRESHOLD = 200 DEFAULT_REBOOT_TIMEOUT_SECONDS = 60 * 60 * 24 BLOCK_SIGN_FILE = "edit_monitor_block_sign" +# Enum of the Clearcut log source defined under +# /google3/wireless/android/play/playlog/proto/log_source_enum.proto +LOG_SOURCE = 2524 def default_daemon_target(): @@ -46,11 +55,16 @@ class DaemonManager: binary_path: str, daemon_target: callable = default_daemon_target, daemon_args: tuple = (), + cclient: clearcut_client.Clearcut | None = None, ): self.binary_path = binary_path self.daemon_target = daemon_target self.daemon_args = daemon_args + self.cclient = cclient or clearcut_client.Clearcut(LOG_SOURCE) + self.user_name = getpass.getuser() + self.host_name = platform.node() + self.source_root = os.environ.get("ANDROID_BUILD_TOP", "") self.pid = os.getpid() self.daemon_process = None @@ -66,17 +80,33 @@ class DaemonManager: def start(self): """Writes the pidfile and starts the daemon proces.""" + if not utils.is_feature_enabled( + "edit_monitor", + self.user_name, + "ENABLE_ANDROID_EDIT_MONITOR", + 10, + ): + logging.warning("Edit monitor is disabled, exiting...") + return + if self.block_sign.exists(): logging.warning("Block sign found, exiting...") return - if self.binary_path.startswith('/google/cog/'): + if self.binary_path.startswith("/google/cog/"): logging.warning("Edit monitor for cog is not supported, exiting...") return - self._stop_any_existing_instance() - self._write_pid_to_pidfile() - self._start_daemon_process() + try: + self._stop_any_existing_instance() + self._write_pid_to_pidfile() + self._start_daemon_process() + except Exception as e: + logging.exception("Failed to start daemon manager with error %s", e) + self._send_error_event_to_clearcut( + edit_event_pb2.EditEvent.FAILED_TO_START_EDIT_MONITOR + ) + raise e def monitor_daemon( self, @@ -118,6 +148,9 @@ class DaemonManager: logging.error( "Daemon process is consuming too much resource, killing..." ), + self._send_error_event_to_clearcut( + edit_event_pb2.EditEvent.KILLED_DUE_TO_EXCEEDED_RESOURCE_USAGE + ) self._terminate_process(self.daemon_process.pid) logging.info( @@ -131,14 +164,24 @@ class DaemonManager: def stop(self): """Stops the daemon process and removes the pidfile.""" - logging.debug("in daemon manager cleanup.") + logging.info("in daemon manager cleanup.") try: - if self.daemon_process and self.daemon_process.is_alive(): - self._terminate_process(self.daemon_process.pid) + if self.daemon_process: + # The daemon process might already in termination process, + # wait some time before kill it explicitly. + self._wait_for_process_terminate(self.daemon_process.pid, 1) + if self.daemon_process.is_alive(): + self._terminate_process(self.daemon_process.pid) self._remove_pidfile() - logging.debug("Successfully stopped daemon manager.") + logging.info("Successfully stopped daemon manager.") except Exception as e: logging.exception("Failed to stop daemon manager with error %s", e) + self._send_error_event_to_clearcut( + edit_event_pb2.EditEvent.FAILED_TO_STOP_EDIT_MONITOR + ) + sys.exit(1) + finally: + self.cclient.flush_events() def reboot(self): """Reboots the current process. @@ -146,7 +189,7 @@ class DaemonManager: Stops the current daemon manager and reboots the entire process based on the binary file. Exits directly If the binary file no longer exists. """ - logging.debug("Rebooting process based on binary %s.", self.binary_path) + logging.info("Rebooting process based on binary %s.", self.binary_path) # Stop the current daemon manager first. self.stop() @@ -160,6 +203,9 @@ class DaemonManager: os.execv(self.binary_path, sys.argv) except OSError as e: logging.exception("Failed to reboot process with error: %s.", e) + self._send_error_event_to_clearcut( + edit_event_pb2.EditEvent.FAILED_TO_REBOOT_EDIT_MONITOR + ) sys.exit(1) # Indicate an error occurred def cleanup(self): @@ -171,6 +217,7 @@ class DaemonManager: that requires immediate cleanup to prevent damanger to the system. """ logging.debug("Start cleaning up all existing instances.") + self._send_error_event_to_clearcut(edit_event_pb2.EditEvent.FORCE_CLEANUP) try: # First places a block sign to prevent any edit monitor process to start. @@ -227,6 +274,7 @@ class DaemonManager: p = multiprocessing.Process( target=self.daemon_target, args=self.daemon_args ) + p.daemon = True p.start() logging.info("Start subprocess with PID %d", p.pid) @@ -299,36 +347,28 @@ class DaemonManager: return pid_file_path def _get_process_memory_percent(self, pid: int) -> float: - try: - with open(f"/proc/{pid}/stat", "r") as f: - stat_data = f.readline().split() - # RSS is the 24th field in /proc/[pid]/stat - rss_pages = int(stat_data[23]) - return rss_pages * 4 / 1024 # Covert to MB - except (FileNotFoundError, IndexError, ValueError, IOError) as e: - logging.exception("Failed to get memory usage.") - raise e + with open(f"/proc/{pid}/stat", "r") as f: + stat_data = f.readline().split() + # RSS is the 24th field in /proc/[pid]/stat + rss_pages = int(stat_data[23]) + return rss_pages * 4 / 1024 # Covert to MB def _get_process_cpu_percent(self, pid: int, interval: int = 1) -> float: - try: - total_start_time = self._get_total_cpu_time(pid) - with open("/proc/uptime", "r") as f: - uptime_start = float(f.readline().split()[0]) + total_start_time = self._get_total_cpu_time(pid) + with open("/proc/uptime", "r") as f: + uptime_start = float(f.readline().split()[0]) - time.sleep(interval) + time.sleep(interval) - total_end_time = self._get_total_cpu_time(pid) - with open("/proc/uptime", "r") as f: - uptime_end = float(f.readline().split()[0]) + total_end_time = self._get_total_cpu_time(pid) + with open("/proc/uptime", "r") as f: + uptime_end = float(f.readline().split()[0]) - return ( - (total_end_time - total_start_time) - / (uptime_end - uptime_start) - * 100 - ) - except (FileNotFoundError, IndexError, ValueError, IOError) as e: - logging.exception("Failed to get CPU usage.") - raise e + return ( + (total_end_time - total_start_time) + / (uptime_end - uptime_start) + * 100 + ) def _get_total_cpu_time(self, pid: int) -> float: with open(f"/proc/{str(pid)}/stat", "r") as f: @@ -350,4 +390,19 @@ class DaemonManager: except (FileNotFoundError, IOError, ValueError, TypeError): logging.exception("Failed to get pid from file path: %s", file) - return pids
\ No newline at end of file + return pids + + def _send_error_event_to_clearcut(self, error_type): + edit_monitor_error_event_proto = edit_event_pb2.EditEvent( + user_name=self.user_name, + host_name=self.host_name, + source_root=self.source_root, + ) + edit_monitor_error_event_proto.edit_monitor_error_event.CopyFrom( + edit_event_pb2.EditEvent.EditMonitorErrorEvent(error_type=error_type) + ) + log_event = clientanalytics_pb2.LogEvent( + event_time_ms=int(time.time() * 1000), + source_extension=edit_monitor_error_event_proto.SerializeToString(), + ) + self.cclient.log(log_event) diff --git a/tools/edit_monitor/daemon_manager_test.py b/tools/edit_monitor/daemon_manager_test.py index 72442c61d4..8d183889f5 100644 --- a/tools/edit_monitor/daemon_manager_test.py +++ b/tools/edit_monitor/daemon_manager_test.py @@ -26,6 +26,7 @@ import time import unittest from unittest import mock from edit_monitor import daemon_manager +from proto import edit_event_pb2 TEST_BINARY_FILE = '/path/to/test_binary' @@ -80,6 +81,9 @@ class DaemonManagerTest(unittest.TestCase): # Sets the tempdir under the working dir so any temp files created during # tests will be cleaned. tempfile.tempdir = self.working_dir.name + self.patch = mock.patch.dict( + os.environ, {'ENABLE_ANDROID_EDIT_MONITOR': 'true'}) + self.patch.start() def tearDown(self): # Cleans up any child processes left by the tests. @@ -87,6 +91,7 @@ class DaemonManagerTest(unittest.TestCase): self.working_dir.cleanup() # Restores tempdir. tempfile.tempdir = self.original_tempdir + self.patch.stop() super().tearDown() def test_start_success_with_no_existing_instance(self): @@ -128,13 +133,24 @@ class DaemonManagerTest(unittest.TestCase): dm = daemon_manager.DaemonManager(TEST_BINARY_FILE) dm.start() + + # Verify no daemon process is started. + self.assertIsNone(dm.daemon_process) + + @mock.patch.dict(os.environ, {'ENABLE_ANDROID_EDIT_MONITOR': 'false'}, clear=True) + def test_start_return_directly_if_disabled(self): + dm = daemon_manager.DaemonManager(TEST_BINARY_FILE) + dm.start() + # Verify no daemon process is started. self.assertIsNone(dm.daemon_process) def test_start_return_directly_if_in_cog_env(self): dm = daemon_manager.DaemonManager( - '/google/cog/cloud/user/workspace/edit_monitor') + '/google/cog/cloud/user/workspace/edit_monitor' + ) dm.start() + # Verify no daemon process is started. self.assertIsNone(dm.daemon_process) @@ -148,9 +164,13 @@ class DaemonManagerTest(unittest.TestCase): with open(pid_file_path_dir.joinpath(TEST_PID_FILE_PATH), 'w') as f: f.write('123456') - with self.assertRaises(OSError) as error: - dm = daemon_manager.DaemonManager(TEST_BINARY_FILE) + fake_cclient = FakeClearcutClient() + with self.assertRaises(OSError): + dm = daemon_manager.DaemonManager(TEST_BINARY_FILE, cclient=fake_cclient) dm.start() + self._assert_error_event_logged( + fake_cclient, edit_event_pb2.EditEvent.FAILED_TO_START_EDIT_MONITOR + ) def test_start_failed_to_write_pidfile(self): pid_file_path_dir = pathlib.Path(self.working_dir.name).joinpath( @@ -160,40 +180,63 @@ class DaemonManagerTest(unittest.TestCase): # Makes the directory read-only so write pidfile will fail. os.chmod(pid_file_path_dir, 0o555) - with self.assertRaises(PermissionError) as error: - dm = daemon_manager.DaemonManager(TEST_BINARY_FILE) + fake_cclient = FakeClearcutClient() + with self.assertRaises(PermissionError): + dm = daemon_manager.DaemonManager(TEST_BINARY_FILE, cclient=fake_cclient) dm.start() + self._assert_error_event_logged( + fake_cclient, edit_event_pb2.EditEvent.FAILED_TO_START_EDIT_MONITOR + ) def test_start_failed_to_start_daemon_process(self): - with self.assertRaises(TypeError) as error: + fake_cclient = FakeClearcutClient() + with self.assertRaises(TypeError): dm = daemon_manager.DaemonManager( - TEST_BINARY_FILE, daemon_target='wrong_target', daemon_args=(1) + TEST_BINARY_FILE, + daemon_target='wrong_target', + daemon_args=(1), + cclient=fake_cclient, ) dm.start() + self._assert_error_event_logged( + fake_cclient, edit_event_pb2.EditEvent.FAILED_TO_START_EDIT_MONITOR + ) def test_monitor_daemon_subprocess_killed_high_memory_usage(self): + fake_cclient = FakeClearcutClient() dm = daemon_manager.DaemonManager( TEST_BINARY_FILE, daemon_target=memory_consume_daemon_target, daemon_args=(2,), + cclient=fake_cclient, ) dm.start() dm.monitor_daemon(interval=1, memory_threshold=2) self.assertTrue(dm.max_memory_usage >= 2) self.assert_no_subprocess_running() + self._assert_error_event_logged( + fake_cclient, + edit_event_pb2.EditEvent.KILLED_DUE_TO_EXCEEDED_RESOURCE_USAGE, + ) def test_monitor_daemon_subprocess_killed_high_cpu_usage(self): + fake_cclient = FakeClearcutClient() dm = daemon_manager.DaemonManager( TEST_BINARY_FILE, daemon_target=cpu_consume_daemon_target, daemon_args=(20,), + cclient=fake_cclient, ) dm.start() dm.monitor_daemon(interval=1, cpu_threshold=20) self.assertTrue(dm.max_cpu_usage >= 20) self.assert_no_subprocess_running() + self._assert_error_event_logged( + fake_cclient, + edit_event_pb2.EditEvent.KILLED_DUE_TO_EXCEEDED_RESOURCE_USAGE, + ) @mock.patch('subprocess.check_output') def test_monitor_daemon_failed_does_not_matter(self, mock_output): @@ -207,7 +250,8 @@ class DaemonManagerTest(unittest.TestCase): ) dm = daemon_manager.DaemonManager( - binary_file.name, daemon_target=long_running_daemon + binary_file.name, + daemon_target=long_running_daemon, ) dm.start() dm.monitor_daemon(reboot_timeout=0.5) @@ -226,27 +270,42 @@ class DaemonManagerTest(unittest.TestCase): @mock.patch('os.kill') def test_stop_failed_to_kill_daemon_process(self, mock_kill): mock_kill.side_effect = OSError('Unknown OSError') + fake_cclient = FakeClearcutClient() dm = daemon_manager.DaemonManager( - TEST_BINARY_FILE, daemon_target=long_running_daemon + TEST_BINARY_FILE, + daemon_target=long_running_daemon, + cclient=fake_cclient, ) - dm.start() - dm.stop() - self.assertTrue(dm.daemon_process.is_alive()) - self.assertTrue(dm.pid_file_path.exists()) + with self.assertRaises(SystemExit): + dm.start() + dm.stop() + self.assertTrue(dm.daemon_process.is_alive()) + self.assertTrue(dm.pid_file_path.exists()) + self._assert_error_event_logged( + fake_cclient, edit_event_pb2.EditEvent.FAILED_TO_STOP_EDIT_MONITOR + ) @mock.patch('os.remove') def test_stop_failed_to_remove_pidfile(self, mock_remove): mock_remove.side_effect = OSError('Unknown OSError') + fake_cclient = FakeClearcutClient() dm = daemon_manager.DaemonManager( - TEST_BINARY_FILE, daemon_target=long_running_daemon + TEST_BINARY_FILE, + daemon_target=long_running_daemon, + cclient=fake_cclient, ) - dm.start() - dm.stop() - self.assert_no_subprocess_running() - self.assertTrue(dm.pid_file_path.exists()) + with self.assertRaises(SystemExit): + dm.start() + dm.stop() + self.assert_no_subprocess_running() + self.assertTrue(dm.pid_file_path.exists()) + + self._assert_error_event_logged( + fake_cclient, edit_event_pb2.EditEvent.FAILED_TO_STOP_EDIT_MONITOR + ) @mock.patch('os.execv') def test_reboot_success(self, mock_execv): @@ -273,7 +332,7 @@ class DaemonManagerTest(unittest.TestCase): ) dm.start() - with self.assertRaises(SystemExit) as cm: + with self.assertRaises(SystemExit): dm.reboot() mock_execv.assert_not_called() self.assertEqual(cm.exception.code, 0) @@ -281,18 +340,24 @@ class DaemonManagerTest(unittest.TestCase): @mock.patch('os.execv') def test_reboot_failed(self, mock_execv): mock_execv.side_effect = OSError('Unknown OSError') + fake_cclient = FakeClearcutClient() binary_file = tempfile.NamedTemporaryFile( dir=self.working_dir.name, delete=False ) dm = daemon_manager.DaemonManager( - binary_file.name, daemon_target=long_running_daemon + binary_file.name, + daemon_target=long_running_daemon, + cclient=fake_cclient, ) dm.start() - with self.assertRaises(SystemExit) as cm: + with self.assertRaises(SystemExit): dm.reboot() self.assertEqual(cm.exception.code, 1) + self._assert_error_event_logged( + fake_cclient, edit_event_pb2.EditEvent.FAILED_TO_REBOOT_EDIT_MONITOR + ) def assert_run_simple_daemon_success(self): damone_output_file = tempfile.NamedTemporaryFile( @@ -374,6 +439,33 @@ class DaemonManagerTest(unittest.TestCase): f.write(str(p.pid)) return p + def _assert_error_event_logged(self, fake_cclient, error_type): + error_events = fake_cclient.get_sent_events() + self.assertEquals(len(error_events), 1) + self.assertEquals( + edit_event_pb2.EditEvent.FromString( + error_events[0].source_extension + ).edit_monitor_error_event.error_type, + error_type, + ) + + +class FakeClearcutClient: + + def __init__(self): + self.pending_log_events = [] + self.sent_log_event = [] + + def log(self, log_event): + self.pending_log_events.append(log_event) + + def flush_events(self): + self.sent_log_event.extend(self.pending_log_events) + self.pending_log_events.clear() + + def get_sent_events(self): + return self.sent_log_event + self.pending_log_events + if __name__ == '__main__': unittest.main() diff --git a/tools/edit_monitor/edit_monitor.py b/tools/edit_monitor/edit_monitor.py new file mode 100644 index 0000000000..ab528e870f --- /dev/null +++ b/tools/edit_monitor/edit_monitor.py @@ -0,0 +1,220 @@ +# Copyright 2024, The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +import getpass +import logging +import multiprocessing.connection +import os +import pathlib +import platform +import threading +import time + +from atest.metrics import clearcut_client +from atest.proto import clientanalytics_pb2 +from proto import edit_event_pb2 +from watchdog.events import FileSystemEvent +from watchdog.events import PatternMatchingEventHandler +from watchdog.observers import Observer + +# Enum of the Clearcut log source defined under +# /google3/wireless/android/play/playlog/proto/log_source_enum.proto +LOG_SOURCE = 2524 +DEFAULT_FLUSH_INTERVAL_SECONDS = 5 +DEFAULT_SINGLE_EVENTS_SIZE_THRESHOLD = 100 + + +class ClearcutEventHandler(PatternMatchingEventHandler): + + def __init__( + self, + path: str, + flush_interval_sec: int, + single_events_size_threshold: int, + is_dry_run: bool = False, + cclient: clearcut_client.Clearcut | None = None, + ): + + super().__init__(patterns=["*"], ignore_directories=True) + self.root_monitoring_path = path + self.flush_interval_sec = flush_interval_sec + self.single_events_size_threshold = single_events_size_threshold + self.is_dry_run = is_dry_run + self.cclient = cclient or clearcut_client.Clearcut(LOG_SOURCE) + + self.user_name = getpass.getuser() + self.host_name = platform.node() + self.source_root = os.environ.get("ANDROID_BUILD_TOP", "") + + self.pending_events = [] + self._scheduled_log_thread = None + self._pending_events_lock = threading.Lock() + + def on_moved(self, event: FileSystemEvent): + self._log_edit_event(event, edit_event_pb2.EditEvent.MOVE) + + def on_created(self, event: FileSystemEvent): + self._log_edit_event(event, edit_event_pb2.EditEvent.CREATE) + + def on_deleted(self, event: FileSystemEvent): + self._log_edit_event(event, edit_event_pb2.EditEvent.DELETE) + + def on_modified(self, event: FileSystemEvent): + self._log_edit_event(event, edit_event_pb2.EditEvent.MODIFY) + + def flushall(self): + logging.info("flushing all pending events.") + if self._scheduled_log_thread: + logging.info("canceling log thread") + self._scheduled_log_thread.cancel() + self._scheduled_log_thread = None + + self._log_clearcut_events() + self.cclient.flush_events() + + def _log_edit_event( + self, event: FileSystemEvent, edit_type: edit_event_pb2.EditEvent.EditType + ): + try: + event_time = time.time() + + if self._is_hidden_file(pathlib.Path(event.src_path)): + logging.debug("ignore hidden file: %s.", event.src_path) + return + + if not self._is_under_git_project(pathlib.Path(event.src_path)): + logging.debug( + "ignore file %s which does not belong to a git project", + event.src_path, + ) + return + + logging.info("%s: %s", event.event_type, event.src_path) + + event_proto = edit_event_pb2.EditEvent( + user_name=self.user_name, + host_name=self.host_name, + source_root=self.source_root, + ) + event_proto.single_edit_event.CopyFrom( + edit_event_pb2.EditEvent.SingleEditEvent( + file_path=event.src_path, edit_type=edit_type + ) + ) + with self._pending_events_lock: + self.pending_events.append((event_proto, event_time)) + if not self._scheduled_log_thread: + logging.debug( + "Scheduling thread to run in %d seconds", self.flush_interval_sec + ) + self._scheduled_log_thread = threading.Timer( + self.flush_interval_sec, self._log_clearcut_events + ) + self._scheduled_log_thread.start() + + except Exception: + logging.exception("Failed to log edit event.") + + def _is_hidden_file(self, file_path: pathlib.Path) -> bool: + return any( + part.startswith(".") + for part in file_path.relative_to(self.root_monitoring_path).parts + ) + + def _is_under_git_project(self, file_path: pathlib.Path) -> bool: + root_path = pathlib.Path(self.root_monitoring_path).resolve() + return any( + root_path.joinpath(dir).joinpath('.git').exists() + for dir in file_path.relative_to(root_path).parents + ) + + def _log_clearcut_events(self): + with self._pending_events_lock: + self._scheduled_log_thread = None + edit_events = self.pending_events + self.pending_events = [] + + pending_events_size = len(edit_events) + if pending_events_size > self.single_events_size_threshold: + logging.info( + "got %d events in %d seconds, sending aggregated events instead", + pending_events_size, + self.flush_interval_sec, + ) + aggregated_event_time = edit_events[0][1] + aggregated_event_proto = edit_event_pb2.EditEvent( + user_name=self.user_name, + host_name=self.host_name, + source_root=self.source_root, + ) + aggregated_event_proto.aggregated_edit_event.CopyFrom( + edit_event_pb2.EditEvent.AggregatedEditEvent( + num_edits=pending_events_size + ) + ) + edit_events = [(aggregated_event_proto, aggregated_event_time)] + + if self.is_dry_run: + logging.info("Sent %d edit events in dry run.", len(edit_events)) + return + + for event_proto, event_time in edit_events: + log_event = clientanalytics_pb2.LogEvent( + event_time_ms=int(event_time * 1000), + source_extension=event_proto.SerializeToString(), + ) + self.cclient.log(log_event) + + logging.info("sent %d edit events", len(edit_events)) + + +def start( + path: str, + is_dry_run: bool = False, + flush_interval_sec: int = DEFAULT_FLUSH_INTERVAL_SECONDS, + single_events_size_threshold: int = DEFAULT_SINGLE_EVENTS_SIZE_THRESHOLD, + cclient: clearcut_client.Clearcut | None = None, + pipe_sender: multiprocessing.connection.Connection | None = None, +): + """Method to start the edit monitor. + + This is the entry point to start the edit monitor as a subprocess of + the daemon manager. + + params: + path: The root path to monitor + cclient: The clearcut client to send the edit logs. + conn: the sender of the pipe to communicate with the deamon manager. + """ + event_handler = ClearcutEventHandler( + path, flush_interval_sec, single_events_size_threshold, is_dry_run, cclient) + observer = Observer() + + logging.info("Starting observer on path %s.", path) + observer.schedule(event_handler, path, recursive=True) + observer.start() + logging.info("Observer started.") + if pipe_sender: + pipe_sender.send("Observer started.") + + try: + while True: + time.sleep(1) + finally: + event_handler.flushall() + observer.stop() + observer.join() + if pipe_sender: + pipe_sender.close() diff --git a/tools/edit_monitor/edit_monitor_integration_test.py b/tools/edit_monitor/edit_monitor_integration_test.py new file mode 100644 index 0000000000..3d28274da4 --- /dev/null +++ b/tools/edit_monitor/edit_monitor_integration_test.py @@ -0,0 +1,141 @@ +# Copyright 2024, The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Integration tests for Edit Monitor.""" + +import glob +import logging +import os +import pathlib +import shutil +import signal +import subprocess +import sys +import tempfile +import time +import unittest + +from importlib import resources +from unittest import mock + + +class EditMonitorIntegrationTest(unittest.TestCase): + + @classmethod + def setUpClass(cls): + super().setUpClass() + # Configure to print logging to stdout. + logging.basicConfig(filename=None, level=logging.DEBUG) + console = logging.StreamHandler(sys.stdout) + logging.getLogger("").addHandler(console) + + def setUp(self): + super().setUp() + self.working_dir = tempfile.TemporaryDirectory() + self.root_monitoring_path = pathlib.Path(self.working_dir.name).joinpath( + "files" + ) + self.root_monitoring_path.mkdir() + self.edit_monitor_binary_path = self._import_executable("edit_monitor") + self.patch = mock.patch.dict( + os.environ, {'ENABLE_ANDROID_EDIT_MONITOR': 'true'}) + self.patch.start() + + def tearDown(self): + self.patch.stop() + self.working_dir.cleanup() + super().tearDown() + + def test_log_single_edit_event_success(self): + p = self._start_edit_monitor_process() + + # Create the .git file under the monitoring dir. + self.root_monitoring_path.joinpath(".git").touch() + + # Create and modify a file. + test_file = self.root_monitoring_path.joinpath("test.txt") + with open(test_file, "w") as f: + f.write("something") + + # Move the file. + test_file_moved = self.root_monitoring_path.joinpath("new_test.txt") + test_file.rename(test_file_moved) + + # Delete the file. + test_file_moved.unlink() + + # Give some time for the edit monitor to receive the edit event. + time.sleep(1) + # Stop the edit monitor and flush all events. + os.kill(p.pid, signal.SIGINT) + p.communicate() + + self.assertEqual(self._get_logged_events_num(), 4) + + def _start_edit_monitor_process(self): + command = f""" + export TMPDIR="{self.working_dir.name}" + {self.edit_monitor_binary_path} --path={self.root_monitoring_path} --dry_run""" + p = subprocess.Popen( + command, + shell=True, + text=True, + start_new_session=True, + executable="/bin/bash", + ) + self._wait_for_observer_start(time_out=5) + return p + + def _wait_for_observer_start(self, time_out): + start_time = time.time() + + while time.time() < start_time + time_out: + log_files = glob.glob(self.working_dir.name + "/edit_monitor_*/*.log") + if log_files: + with open(log_files[0], "r") as f: + for line in f: + logging.debug("initial log: %s", line) + if line.rstrip("\n").endswith("Observer started."): + return + else: + time.sleep(1) + + self.fail(f"Observer not started in {time_out} seconds.") + + def _get_logged_events_num(self): + log_files = glob.glob(self.working_dir.name + "/edit_monitor_*/*.log") + self.assertEqual(len(log_files), 1) + + with open(log_files[0], "r") as f: + for line in f: + logging.debug("complete log: %s", line) + if line.rstrip("\n").endswith("in dry run."): + return int(line.split(":")[-1].split(" ")[2]) + + return 0 + + def _import_executable(self, executable_name: str) -> pathlib.Path: + binary_dir = pathlib.Path(self.working_dir.name).joinpath("binary") + binary_dir.mkdir() + executable_path = binary_dir.joinpath(executable_name) + with resources.as_file( + resources.files("testdata").joinpath(executable_name) + ) as binary: + shutil.copy(binary, executable_path) + executable_path.chmod(0o755) + return executable_path + + +if __name__ == "__main__": + unittest.main() diff --git a/tools/edit_monitor/edit_monitor_test.py b/tools/edit_monitor/edit_monitor_test.py new file mode 100644 index 0000000000..64a3871b22 --- /dev/null +++ b/tools/edit_monitor/edit_monitor_test.py @@ -0,0 +1,301 @@ +# Copyright 2024, The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Unittests for Edit Monitor.""" + +import logging +import multiprocessing +import os +import pathlib +import signal +import sys +import tempfile +import time +import unittest + +from atest.proto import clientanalytics_pb2 +from edit_monitor import edit_monitor +from proto import edit_event_pb2 + + +class EditMonitorTest(unittest.TestCase): + + @classmethod + def setUpClass(cls): + super().setUpClass() + # Configure to print logging to stdout. + logging.basicConfig(filename=None, level=logging.DEBUG) + console = logging.StreamHandler(sys.stdout) + logging.getLogger('').addHandler(console) + + def setUp(self): + super().setUp() + self.working_dir = tempfile.TemporaryDirectory() + self.root_monitoring_path = pathlib.Path(self.working_dir.name).joinpath( + 'files' + ) + self.root_monitoring_path.mkdir() + self.log_event_dir = pathlib.Path(self.working_dir.name).joinpath('logs') + self.log_event_dir.mkdir() + + def tearDown(self): + self.working_dir.cleanup() + super().tearDown() + + def test_log_single_edit_event_success(self): + # Create the .git file under the monitoring dir. + self.root_monitoring_path.joinpath('.git').touch() + fake_cclient = FakeClearcutClient( + log_output_file=self.log_event_dir.joinpath('logs.output') + ) + p = self._start_test_edit_monitor_process(fake_cclient) + + # Create and modify a file. + test_file = self.root_monitoring_path.joinpath('test.txt') + with open(test_file, 'w') as f: + f.write('something') + # Move the file. + test_file_moved = self.root_monitoring_path.joinpath('new_test.txt') + test_file.rename(test_file_moved) + # Delete the file. + test_file_moved.unlink() + # Give some time for the edit monitor to receive the edit event. + time.sleep(1) + # Stop the edit monitor and flush all events. + os.kill(p.pid, signal.SIGINT) + p.join() + + logged_events = self._get_logged_events() + self.assertEqual(len(logged_events), 4) + expected_create_event = edit_event_pb2.EditEvent.SingleEditEvent( + file_path=str( + self.root_monitoring_path.joinpath('test.txt').resolve() + ), + edit_type=edit_event_pb2.EditEvent.CREATE, + ) + expected_modify_event = edit_event_pb2.EditEvent.SingleEditEvent( + file_path=str( + self.root_monitoring_path.joinpath('test.txt').resolve() + ), + edit_type=edit_event_pb2.EditEvent.MODIFY, + ) + expected_move_event = edit_event_pb2.EditEvent.SingleEditEvent( + file_path=str( + self.root_monitoring_path.joinpath('test.txt').resolve() + ), + edit_type=edit_event_pb2.EditEvent.MOVE, + ) + expected_delete_event = edit_event_pb2.EditEvent.SingleEditEvent( + file_path=str( + self.root_monitoring_path.joinpath('new_test.txt').resolve() + ), + edit_type=edit_event_pb2.EditEvent.DELETE, + ) + self.assertEqual( + expected_create_event, + edit_event_pb2.EditEvent.FromString( + logged_events[0].source_extension + ).single_edit_event, + ) + self.assertEqual( + expected_modify_event, + edit_event_pb2.EditEvent.FromString( + logged_events[1].source_extension + ).single_edit_event, + ) + self.assertEqual( + expected_move_event, + edit_event_pb2.EditEvent.FromString( + logged_events[2].source_extension + ).single_edit_event, + ) + self.assertEqual( + expected_delete_event, + edit_event_pb2.EditEvent.FromString( + logged_events[3].source_extension + ).single_edit_event, + ) + + + def test_log_aggregated_edit_event_success(self): + # Create the .git file under the monitoring dir. + self.root_monitoring_path.joinpath('.git').touch() + fake_cclient = FakeClearcutClient( + log_output_file=self.log_event_dir.joinpath('logs.output') + ) + p = self._start_test_edit_monitor_process(fake_cclient) + + # Create 6 test files + for i in range(6): + test_file = self.root_monitoring_path.joinpath('test_' + str(i)) + test_file.touch() + + # Give some time for the edit monitor to receive the edit event. + time.sleep(1) + # Stop the edit monitor and flush all events. + os.kill(p.pid, signal.SIGINT) + p.join() + + logged_events = self._get_logged_events() + self.assertEqual(len(logged_events), 1) + + expected_aggregated_edit_event = ( + edit_event_pb2.EditEvent.AggregatedEditEvent( + num_edits=6, + ) + ) + + self.assertEqual( + expected_aggregated_edit_event, + edit_event_pb2.EditEvent.FromString( + logged_events[0].source_extension + ).aggregated_edit_event, + ) + + def test_do_not_log_edit_event_for_directory_change(self): + # Create the .git file under the monitoring dir. + self.root_monitoring_path.joinpath('.git').touch() + fake_cclient = FakeClearcutClient( + log_output_file=self.log_event_dir.joinpath('logs.output') + ) + p = self._start_test_edit_monitor_process(fake_cclient) + + # Create a sub directory + self.root_monitoring_path.joinpath('test_dir').mkdir() + # Give some time for the edit monitor to receive the edit event. + time.sleep(1) + # Stop the edit monitor and flush all events. + os.kill(p.pid, signal.SIGINT) + p.join() + + logged_events = self._get_logged_events() + self.assertEqual(len(logged_events), 0) + + def test_do_not_log_edit_event_for_hidden_file(self): + # Create the .git file under the monitoring dir. + self.root_monitoring_path.joinpath('.git').touch() + fake_cclient = FakeClearcutClient( + log_output_file=self.log_event_dir.joinpath('logs.output') + ) + p = self._start_test_edit_monitor_process(fake_cclient) + + # Create a hidden file. + self.root_monitoring_path.joinpath('.test.txt').touch() + # Create a hidden dir. + hidden_dir = self.root_monitoring_path.joinpath('.test') + hidden_dir.mkdir() + hidden_dir.joinpath('test.txt').touch() + # Give some time for the edit monitor to receive the edit event. + time.sleep(1) + # Stop the edit monitor and flush all events. + os.kill(p.pid, signal.SIGINT) + p.join() + + logged_events = self._get_logged_events() + self.assertEqual(len(logged_events), 0) + + def test_do_not_log_edit_event_for_non_git_project_file(self): + fake_cclient = FakeClearcutClient( + log_output_file=self.log_event_dir.joinpath('logs.output') + ) + p = self._start_test_edit_monitor_process(fake_cclient) + + # Create a file. + self.root_monitoring_path.joinpath('test.txt').touch() + # Create a file under a sub dir. + sub_dir = self.root_monitoring_path.joinpath('.test') + sub_dir.mkdir() + sub_dir.joinpath('test.txt').touch() + # Give some time for the edit monitor to receive the edit event. + time.sleep(1) + # Stop the edit monitor and flush all events. + os.kill(p.pid, signal.SIGINT) + p.join() + + logged_events = self._get_logged_events() + self.assertEqual(len(logged_events), 0) + + def test_log_edit_event_fail(self): + # Create the .git file under the monitoring dir. + self.root_monitoring_path.joinpath('.git').touch() + fake_cclient = FakeClearcutClient( + log_output_file=self.log_event_dir.joinpath('logs.output'), + raise_log_exception=True, + ) + p = self._start_test_edit_monitor_process(fake_cclient) + + # Create a file. + self.root_monitoring_path.joinpath('test.txt').touch() + # Give some time for the edit monitor to receive the edit event. + time.sleep(1) + # Stop the edit monitor and flush all events. + os.kill(p.pid, signal.SIGINT) + p.join() + + logged_events = self._get_logged_events() + self.assertEqual(len(logged_events), 0) + + def _start_test_edit_monitor_process( + self, cclient + ) -> multiprocessing.Process: + receiver, sender = multiprocessing.Pipe() + # Start edit monitor in a subprocess. + p = multiprocessing.Process( + target=edit_monitor.start, + args=(str(self.root_monitoring_path.resolve()), False, 0.5, 5, cclient, sender), + ) + p.daemon = True + p.start() + + # Wait until observer started. + received_data = receiver.recv() + self.assertEquals(received_data, 'Observer started.') + + receiver.close() + return p + + def _get_logged_events(self): + with open(self.log_event_dir.joinpath('logs.output'), 'rb') as f: + data = f.read() + + return [ + clientanalytics_pb2.LogEvent.FromString(record) + for record in data.split(b'\x00') + if record + ] + + +class FakeClearcutClient: + + def __init__(self, log_output_file, raise_log_exception=False): + self.pending_log_events = [] + self.raise_log_exception = raise_log_exception + self.log_output_file = log_output_file + + def log(self, log_event): + if self.raise_log_exception: + raise Exception('unknown exception') + self.pending_log_events.append(log_event) + + def flush_events(self): + delimiter = b'\x00' # Use a null byte as the delimiter + with open(self.log_output_file, 'wb') as f: + for log_event in self.pending_log_events: + f.write(log_event.SerializeToString() + delimiter) + + self.pending_log_events.clear() + + +if __name__ == '__main__': + unittest.main() diff --git a/tools/edit_monitor/main.py b/tools/edit_monitor/main.py index e69de29bb2..49385f1cc5 100644 --- a/tools/edit_monitor/main.py +++ b/tools/edit_monitor/main.py @@ -0,0 +1,118 @@ +# Copyright 2024, The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import argparse +import logging +import os +import signal +import sys +import tempfile + +from edit_monitor import daemon_manager +from edit_monitor import edit_monitor + + +def create_arg_parser(): + """Creates an instance of the default arg parser.""" + + parser = argparse.ArgumentParser( + description=( + 'Monitors edits in Android source code and uploads the edit logs.' + ), + add_help=True, + formatter_class=argparse.RawDescriptionHelpFormatter, + ) + + parser.add_argument( + '--path', + type=str, + required=True, + help='Root path to monitor the edit events.', + ) + + parser.add_argument( + '--dry_run', + action='store_true', + help='Dry run the edit monitor. This starts the edit monitor process without actually send the edit logs to clearcut.', + ) + + parser.add_argument( + '--force_cleanup', + action='store_true', + help=( + 'Instead of start a new edit monitor, force stop all existing edit' + ' monitors in the system. This option is only used in emergent cases' + ' when we want to prevent user damage by the edit monitor.' + ), + ) + + parser.add_argument( + '--verbose', + action='store_true', + help=( + 'Log verbose info in the log file for debugging purpose.' + ), + ) + + return parser + + +def configure_logging(verbose=False): + root_logging_dir = tempfile.mkdtemp(prefix='edit_monitor_') + _, log_path = tempfile.mkstemp(dir=root_logging_dir, suffix='.log') + + log_fmt = '%(asctime)s %(filename)s:%(lineno)s:%(levelname)s: %(message)s' + date_fmt = '%Y-%m-%d %H:%M:%S' + log_level = logging.DEBUG if verbose else logging.INFO + + logging.basicConfig( + filename=log_path, level=log_level, format=log_fmt, datefmt=date_fmt + ) + # Filter out logs from inotify_buff to prevent log pollution. + logging.getLogger('watchdog.observers.inotify_buffer').addFilter( + lambda record: record.filename != 'inotify_buffer.py') + print(f'logging to file {log_path}') + + +def term_signal_handler(_signal_number, _frame): + logging.info('Process %d received SIGTERM, Terminating...', os.getpid()) + sys.exit(0) + + +def main(argv: list[str]): + args = create_arg_parser().parse_args(argv[1:]) + configure_logging(args.verbose) + if args.dry_run: + logging.info('This is a dry run.') + dm = daemon_manager.DaemonManager( + binary_path=argv[0], + daemon_target=edit_monitor.start, + daemon_args=(args.path, args.dry_run), + ) + + if args.force_cleanup: + dm.cleanup() + + try: + dm.start() + dm.monitor_daemon() + except Exception: + logging.exception('Unexpected exception raised when run daemon.') + finally: + dm.stop() + + +if __name__ == '__main__': + signal.signal(signal.SIGTERM, term_signal_handler) + main(sys.argv) diff --git a/tools/edit_monitor/proto/edit_event.proto b/tools/edit_monitor/proto/edit_event.proto index b3630bc944..dc3d3f68bd 100644 --- a/tools/edit_monitor/proto/edit_event.proto +++ b/tools/edit_monitor/proto/edit_event.proto @@ -36,8 +36,6 @@ message EditEvent { // Event that logs errors happened in the edit monitor. message EditMonitorErrorEvent { ErrorType error_type = 1; - string error_msg = 2; - string stack_trace = 3; } // ------------------------ diff --git a/tools/edit_monitor/utils.py b/tools/edit_monitor/utils.py new file mode 100644 index 0000000000..b88949d300 --- /dev/null +++ b/tools/edit_monitor/utils.py @@ -0,0 +1,53 @@ +# Copyright 2024, The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import hashlib +import logging +import os + + +def is_feature_enabled( + feature_name: str, + user_name: str, + enable_flag: str = None, + rollout_percent: int = 100, +) -> bool: + """Determine whether the given feature is enabled. + + Whether a given feature is enabled or not depends on two flags: 1) the + enable_flag that explicitly enable/disable the feature and 2) the rollout_flag + that controls the rollout percentage. + + Args: + feature_name: name of the feature. + user_name: system user name. + enable_flag: name of the env var that enables/disables the feature + explicitly. + rollout_flg: name of the env var that controls the rollout percentage, the + value stored in the env var should be an int between 0 and 100 string + """ + if enable_flag: + if os.environ.get(enable_flag, "") == "false": + logging.info("feature: %s is disabled", feature_name) + return False + + if os.environ.get(enable_flag, "") == "true": + logging.info("feature: %s is enabled", feature_name) + return True + + hash_object = hashlib.sha256() + hash_object.update((user_name + feature_name).encode("utf-8")) + hash_number = int(hash_object.hexdigest(), 16) % 100 + + return hash_number < rollout_percent diff --git a/tools/edit_monitor/utils_test.py b/tools/edit_monitor/utils_test.py new file mode 100644 index 0000000000..1c30aa1acc --- /dev/null +++ b/tools/edit_monitor/utils_test.py @@ -0,0 +1,71 @@ +# Copyright 2024, The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Unittests for edit monitor utils.""" +import os +import unittest +from unittest import mock + +from edit_monitor import utils + +TEST_USER = 'test_user' +TEST_FEATURE = 'test_feature' +ENABLE_TEST_FEATURE_FLAG = 'ENABLE_TEST_FEATURE' +ROLLOUT_TEST_FEATURE_FLAG = 'ROLLOUT_TEST_FEATURE' + + +class EnableFeatureTest(unittest.TestCase): + + def test_feature_enabled_without_flag(self): + self.assertTrue(utils.is_feature_enabled(TEST_FEATURE, TEST_USER)) + + @mock.patch.dict(os.environ, {ENABLE_TEST_FEATURE_FLAG: 'false'}, clear=True) + def test_feature_disabled_with_flag(self): + self.assertFalse( + utils.is_feature_enabled( + TEST_FEATURE, TEST_USER, ENABLE_TEST_FEATURE_FLAG + ) + ) + + @mock.patch.dict(os.environ, {ENABLE_TEST_FEATURE_FLAG: 'true'}, clear=True) + def test_feature_enabled_with_flag(self): + self.assertTrue( + utils.is_feature_enabled( + TEST_FEATURE, TEST_USER, ENABLE_TEST_FEATURE_FLAG + ) + ) + + def test_feature_enabled_with_rollout_percentage(self): + self.assertTrue( + utils.is_feature_enabled( + TEST_FEATURE, + TEST_USER, + ENABLE_TEST_FEATURE_FLAG, + 90, + ) + ) + + def test_feature_disabled_with_rollout_percentage(self): + self.assertFalse( + utils.is_feature_enabled( + TEST_FEATURE, + TEST_USER, + ENABLE_TEST_FEATURE_FLAG, + 10, + ) + ) + + +if __name__ == '__main__': + unittest.main() diff --git a/tools/filelistdiff/file_list_diff.py b/tools/filelistdiff/file_list_diff.py index fbbfedf09c..a6408e87cc 100644 --- a/tools/filelistdiff/file_list_diff.py +++ b/tools/filelistdiff/file_list_diff.py @@ -76,7 +76,7 @@ if __name__ == '__main__': parser.add_argument('kati_installed_file_list') parser.add_argument('soong_installed_file_list') parser.add_argument('system_module_name') - parser.add_argument('--allowlists', nargs='+') + parser.add_argument('--allowlists', nargs='*', default=[]) args = parser.parse_args() find_unique_items(args.kati_installed_file_list, args.soong_installed_file_list, args.system_module_name, args.allowlists)
\ No newline at end of file diff --git a/tools/fs_config/Android.bp b/tools/fs_config/Android.bp index 6aa528963d..a5b6fd0a4c 100644 --- a/tools/fs_config/Android.bp +++ b/tools/fs_config/Android.bp @@ -277,6 +277,7 @@ genrule_defaults { out: ["out"], } +// system genrule { name: "fs_config_dirs_system_gen", defaults: ["fs_config_defaults"], @@ -307,6 +308,7 @@ prebuilt_etc { src: ":fs_config_files_system_gen", } +// system_ext genrule { name: "fs_config_dirs_system_ext_gen", defaults: ["fs_config_defaults"], @@ -337,6 +339,7 @@ prebuilt_etc { system_ext_specific: true, } +// product genrule { name: "fs_config_dirs_product_gen", defaults: ["fs_config_defaults"], @@ -367,6 +370,7 @@ prebuilt_etc { product_specific: true, } +// vendor genrule { name: "fs_config_dirs_vendor_gen", defaults: ["fs_config_defaults"], @@ -397,6 +401,7 @@ prebuilt_etc { vendor: true, } +// odm genrule { name: "fs_config_dirs_odm_gen", defaults: ["fs_config_defaults"], @@ -427,4 +432,214 @@ prebuilt_etc { device_specific: true, } -// TODO(jiyong): add fs_config for oem, system_dlkm, vendor_dlkm, odm_dlkm partitions +// system_dlkm +genrule { + name: "fs_config_dirs_system_dlkm_gen", + defaults: ["fs_config_defaults"], + cmd: fs_config_cmd_dirs + + "--partition system_dlkm " + + "$(locations :target_fs_config_gen)", +} + +prebuilt_etc { + name: "fs_config_dirs_system_dlkm", + filename: "fs_config_dirs", + src: ":fs_config_dirs_system_dlkm_gen", + system_dlkm_specific: true, +} + +genrule { + name: "fs_config_files_system_dlkm_gen", + defaults: ["fs_config_defaults"], + cmd: fs_config_cmd_files + + "--partition system_dlkm " + + "$(locations :target_fs_config_gen)", +} + +prebuilt_etc { + name: "fs_config_files_system_dlkm", + filename: "fs_config_files", + src: ":fs_config_files_system_dlkm_gen", + system_dlkm_specific: true, +} + +// vendor_dlkm +genrule { + name: "fs_config_dirs_vendor_dlkm_gen", + defaults: ["fs_config_defaults"], + cmd: fs_config_cmd_dirs + + "--partition vendor_dlkm " + + "$(locations :target_fs_config_gen)", +} + +prebuilt_etc { + name: "fs_config_dirs_vendor_dlkm", + filename: "fs_config_dirs", + src: ":fs_config_dirs_vendor_dlkm_gen", + vendor_dlkm_specific: true, +} + +genrule { + name: "fs_config_files_vendor_dlkm_gen", + defaults: ["fs_config_defaults"], + cmd: fs_config_cmd_files + + "--partition vendor_dlkm " + + "$(locations :target_fs_config_gen)", +} + +prebuilt_etc { + name: "fs_config_files_vendor_dlkm", + filename: "fs_config_files", + src: ":fs_config_files_vendor_dlkm_gen", + vendor_dlkm_specific: true, +} + +// odm_dlkm +genrule { + name: "fs_config_dirs_odm_dlkm_gen", + defaults: ["fs_config_defaults"], + cmd: fs_config_cmd_dirs + + "--partition odm_dlkm " + + "$(locations :target_fs_config_gen)", +} + +prebuilt_etc { + name: "fs_config_dirs_odm_dlkm", + filename: "fs_config_dirs", + src: ":fs_config_dirs_odm_dlkm_gen", + odm_dlkm_specific: true, +} + +genrule { + name: "fs_config_files_odm_dlkm_gen", + defaults: ["fs_config_defaults"], + cmd: fs_config_cmd_files + + "--partition odm_dlkm " + + "$(locations :target_fs_config_gen)", +} + +prebuilt_etc { + name: "fs_config_files_odm_dlkm", + filename: "fs_config_files", + src: ":fs_config_files_odm_dlkm_gen", + odm_dlkm_specific: true, +} + +// oem +genrule { + name: "fs_config_dirs_oem_gen", + defaults: ["fs_config_defaults"], + cmd: fs_config_cmd_dirs + + "--partition oem " + + "$(locations :target_fs_config_gen)", +} + +prebuilt_etc { + name: "fs_config_dirs_oem", + filename: "fs_config_dirs", + src: ":fs_config_dirs_oem_gen", + oem_specific: true, +} + +genrule { + name: "fs_config_files_oem_gen", + defaults: ["fs_config_defaults"], + cmd: fs_config_cmd_files + + "--partition oem " + + "$(locations :target_fs_config_gen)", +} + +prebuilt_etc { + name: "fs_config_files_oem", + filename: "fs_config_files", + src: ":fs_config_files_oem_gen", + oem_specific: true, +} + +// Generate the <p>/etc/fs_config_dirs binary files for each partition. +// Add fs_config_dirs to PRODUCT_PACKAGES in the device make file to enable. +phony { + name: "fs_config_dirs", + required: [ + "fs_config_dirs_system", + "fs_config_dirs_system_ext", + "fs_config_dirs_product", + "fs_config_dirs_nonsystem", + ], +} + +// Generate the <p>/etc/fs_config_files binary files for each partition. +// Add fs_config_files to PRODUCT_PACKAGES in the device make file to enable. +phony { + name: "fs_config_files", + required: [ + "fs_config_files_system", + "fs_config_files_system_ext", + "fs_config_files_product", + "fs_config_files_nonsystem", + ], +} + +// Generate the <p>/etc/fs_config_dirs binary files for all enabled partitions +// excluding /system, /system_ext and /product. Add fs_config_dirs_nonsystem to +// PRODUCT_PACKAGES in the device make file to enable. +phony { + name: "fs_config_dirs_nonsystem", + required: [] + + select(soong_config_variable("fs_config", "vendor"), { + true: ["fs_config_dirs_vendor"], + default: [], + }) + + select(soong_config_variable("fs_config", "oem"), { + true: ["fs_config_dirs_oem"], + default: [], + }) + + select(soong_config_variable("fs_config", "odm"), { + true: ["fs_config_dirs_odm"], + default: [], + }) + + select(soong_config_variable("fs_config", "vendor_dlkm"), { + true: ["fs_config_dirs_vendor_dlkm"], + default: [], + }) + + select(soong_config_variable("fs_config", "odm_dlkm"), { + true: ["fs_config_dirs_odm_dlkm"], + default: [], + }) + + select(soong_config_variable("fs_config", "system_dlkm"), { + true: ["fs_config_dirs_system_dlkm"], + default: [], + }), +} + +// Generate the <p>/etc/fs_config_files binary files for all enabled partitions +// excluding /system, /system_ext and /product. Add fs_config_files_nonsystem to +// PRODUCT_PACKAGES in the device make file to enable. +phony { + name: "fs_config_files_nonsystem", + required: [] + + select(soong_config_variable("fs_config", "vendor"), { + true: ["fs_config_files_vendor"], + default: [], + }) + + select(soong_config_variable("fs_config", "oem"), { + true: ["fs_config_files_oem"], + default: [], + }) + + select(soong_config_variable("fs_config", "odm"), { + true: ["fs_config_files_odm"], + default: [], + }) + + select(soong_config_variable("fs_config", "vendor_dlkm"), { + true: ["fs_config_files_vendor_dlkm"], + default: [], + }) + + select(soong_config_variable("fs_config", "odm_dlkm"), { + true: ["fs_config_files_odm_dlkm"], + default: [], + }) + + select(soong_config_variable("fs_config", "system_dlkm"), { + true: ["fs_config_files_system_dlkm"], + default: [], + }), +} diff --git a/tools/fs_config/Android.mk b/tools/fs_config/Android.mk deleted file mode 100644 index e4c362630f..0000000000 --- a/tools/fs_config/Android.mk +++ /dev/null @@ -1,328 +0,0 @@ -# Copyright (C) 2008 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. - -LOCAL_PATH := $(call my-dir) - -# One can override the default android_filesystem_config.h file by using TARGET_FS_CONFIG_GEN. -# Set TARGET_FS_CONFIG_GEN to contain a list of intermediate format files -# for generating the android_filesystem_config.h file. -# -# More information can be found in the README - -ifneq ($(wildcard $(TARGET_DEVICE_DIR)/android_filesystem_config.h),) -$(error Using $(TARGET_DEVICE_DIR)/android_filesystem_config.h is deprecated, please use TARGET_FS_CONFIG_GEN instead) -endif - -android_filesystem_config := system/core/libcutils/include/private/android_filesystem_config.h -capability_header := bionic/libc/kernel/uapi/linux/capability.h - -# List of supported vendor, oem, odm, vendor_dlkm, odm_dlkm, and system_dlkm Partitions -fs_config_generate_extra_partition_list := $(strip \ - $(if $(BOARD_USES_VENDORIMAGE)$(BOARD_VENDORIMAGE_FILE_SYSTEM_TYPE),vendor) \ - $(if $(BOARD_USES_OEMIMAGE)$(BOARD_OEMIMAGE_FILE_SYSTEM_TYPE),oem) \ - $(if $(BOARD_USES_ODMIMAGE)$(BOARD_ODMIMAGE_FILE_SYSTEM_TYPE),odm) \ - $(if $(BOARD_USES_VENDOR_DLKMIMAGE)$(BOARD_VENDOR_DLKMIMAGE_FILE_SYSTEM_TYPE),vendor_dlkm) \ - $(if $(BOARD_USES_ODM_DLKMIMAGE)$(BOARD_ODM_DLKMIMAGE_FILE_SYSTEM_TYPE),odm_dlkm) \ - $(if $(BOARD_USES_SYSTEM_DLKMIMAGE)$(BOARD_SYSTEM_DLKMIMAGE_FILE_SYSTEM_TYPE),system_dlkm) \ -) - -################################## -# Generate the <p>/etc/fs_config_dirs binary files for each partition. -# Add fs_config_dirs to PRODUCT_PACKAGES in the device make file to enable. -include $(CLEAR_VARS) - -LOCAL_MODULE := fs_config_dirs -LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 -LOCAL_LICENSE_CONDITIONS := notice -LOCAL_NOTICE_FILE := build/soong/licenses/LICENSE -LOCAL_REQUIRED_MODULES := \ - fs_config_dirs_system \ - fs_config_dirs_system_ext \ - fs_config_dirs_product \ - fs_config_dirs_nonsystem -include $(BUILD_PHONY_PACKAGE) - -################################## -# Generate the <p>/etc/fs_config_files binary files for each partition. -# Add fs_config_files to PRODUCT_PACKAGES in the device make file to enable. -include $(CLEAR_VARS) - -LOCAL_MODULE := fs_config_files -LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 -LOCAL_LICENSE_CONDITIONS := notice -LOCAL_NOTICE_FILE := build/soong/licenses/LICENSE -LOCAL_REQUIRED_MODULES := \ - fs_config_files_system \ - fs_config_files_system_ext \ - fs_config_files_product \ - fs_config_files_nonsystem -include $(BUILD_PHONY_PACKAGE) - -################################## -# Generate the <p>/etc/fs_config_dirs binary files for all enabled partitions -# excluding /system, /system_ext and /product. Add fs_config_dirs_nonsystem to -# PRODUCT_PACKAGES in the device make file to enable. -include $(CLEAR_VARS) - -LOCAL_MODULE := fs_config_dirs_nonsystem -LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 -LOCAL_LICENSE_CONDITIONS := notice -LOCAL_NOTICE_FILE := build/soong/licenses/LICENSE -LOCAL_REQUIRED_MODULES := $(foreach t,$(fs_config_generate_extra_partition_list),fs_config_dirs_$(t)) -include $(BUILD_PHONY_PACKAGE) - -################################## -# Generate the <p>/etc/fs_config_files binary files for all enabled partitions -# excluding /system, /system_ext and /product. Add fs_config_files_nonsystem to -# PRODUCT_PACKAGES in the device make file to enable. -include $(CLEAR_VARS) - -LOCAL_MODULE := fs_config_files_nonsystem -LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 -LOCAL_LICENSE_CONDITIONS := notice -LOCAL_NOTICE_FILE := build/soong/licenses/LICENSE -LOCAL_REQUIRED_MODULES := $(foreach t,$(fs_config_generate_extra_partition_list),fs_config_files_$(t)) -include $(BUILD_PHONY_PACKAGE) - -ifneq ($(filter oem,$(fs_config_generate_extra_partition_list)),) -################################## -# Generate the oem/etc/fs_config_dirs binary file for the target -# Add fs_config_dirs or fs_config_dirs_nonsystem to PRODUCT_PACKAGES -# in the device make file to enable -include $(CLEAR_VARS) - -LOCAL_MODULE := fs_config_dirs_oem -LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 -LOCAL_LICENSE_CONDITIONS := notice -LOCAL_NOTICE_FILE := build/soong/licenses/LICENSE -LOCAL_MODULE_CLASS := ETC -LOCAL_INSTALLED_MODULE_STEM := fs_config_dirs -LOCAL_MODULE_PATH := $(TARGET_OUT_OEM)/etc -include $(BUILD_SYSTEM)/base_rules.mk -$(LOCAL_BUILT_MODULE): PRIVATE_ANDROID_FS_HDR := $(android_filesystem_config) -$(LOCAL_BUILT_MODULE): PRIVATE_ANDROID_CAP_HDR := $(capability_header) -$(LOCAL_BUILT_MODULE): PRIVATE_TARGET_FS_CONFIG_GEN := $(TARGET_FS_CONFIG_GEN) -$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/fs_config_generator.py $(TARGET_FS_CONFIG_GEN) $(android_filesystem_config) $(capability_header) - @mkdir -p $(dir $@) - $< fsconfig \ - --aid-header $(PRIVATE_ANDROID_FS_HDR) \ - --capability-header $(PRIVATE_ANDROID_CAP_HDR) \ - --partition oem \ - --dirs \ - --out_file $@ \ - $(or $(PRIVATE_TARGET_FS_CONFIG_GEN),/dev/null) - -################################## -# Generate the oem/etc/fs_config_files binary file for the target -# Add fs_config_files or fs_config_files_nonsystem to PRODUCT_PACKAGES -# in the device make file to enable -include $(CLEAR_VARS) - -LOCAL_MODULE := fs_config_files_oem -LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 -LOCAL_LICENSE_CONDITIONS := notice -LOCAL_NOTICE_FILE := build/soong/licenses/LICENSE -LOCAL_MODULE_CLASS := ETC -LOCAL_INSTALLED_MODULE_STEM := fs_config_files -LOCAL_MODULE_PATH := $(TARGET_OUT_OEM)/etc -include $(BUILD_SYSTEM)/base_rules.mk -$(LOCAL_BUILT_MODULE): PRIVATE_ANDROID_FS_HDR := $(android_filesystem_config) -$(LOCAL_BUILT_MODULE): PRIVATE_ANDROID_CAP_HDR := $(capability_header) -$(LOCAL_BUILT_MODULE): PRIVATE_TARGET_FS_CONFIG_GEN := $(TARGET_FS_CONFIG_GEN) -$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/fs_config_generator.py $(TARGET_FS_CONFIG_GEN) $(android_filesystem_config) $(capability_header) - @mkdir -p $(dir $@) - $< fsconfig \ - --aid-header $(PRIVATE_ANDROID_FS_HDR) \ - --capability-header $(PRIVATE_ANDROID_CAP_HDR) \ - --partition oem \ - --files \ - --out_file $@ \ - $(or $(PRIVATE_TARGET_FS_CONFIG_GEN),/dev/null) - -endif - -ifneq ($(filter vendor_dlkm,$(fs_config_generate_extra_partition_list)),) -################################## -# Generate the vendor_dlkm/etc/fs_config_dirs binary file for the target -# Add fs_config_dirs or fs_config_dirs_nonsystem to PRODUCT_PACKAGES in -# the device make file to enable -include $(CLEAR_VARS) - -LOCAL_MODULE := fs_config_dirs_vendor_dlkm -LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 -LOCAL_LICENSE_CONDITIONS := notice -LOCAL_NOTICE_FILE := build/soong/licenses/LICENSE -LOCAL_MODULE_CLASS := ETC -LOCAL_INSTALLED_MODULE_STEM := fs_config_dirs -LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR_DLKM)/etc -include $(BUILD_SYSTEM)/base_rules.mk -$(LOCAL_BUILT_MODULE): PRIVATE_ANDROID_FS_HDR := $(android_filesystem_config) -$(LOCAL_BUILT_MODULE): PRIVATE_ANDROID_CAP_HDR := $(capability_header) -$(LOCAL_BUILT_MODULE): PRIVATE_TARGET_FS_CONFIG_GEN := $(TARGET_FS_CONFIG_GEN) -$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/fs_config_generator.py $(TARGET_FS_CONFIG_GEN) $(android_filesystem_config) $(capability_header) - @mkdir -p $(dir $@) - $< fsconfig \ - --aid-header $(PRIVATE_ANDROID_FS_HDR) \ - --capability-header $(PRIVATE_ANDROID_CAP_HDR) \ - --partition vendor_dlkm \ - --dirs \ - --out_file $@ \ - $(or $(PRIVATE_TARGET_FS_CONFIG_GEN),/dev/null) - -################################## -# Generate the vendor_dlkm/etc/fs_config_files binary file for the target -# Add fs_config_files or fs_config_files_nonsystem to PRODUCT_PACKAGES in -# the device make file to enable -include $(CLEAR_VARS) - -LOCAL_MODULE := fs_config_files_vendor_dlkm -LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 -LOCAL_LICENSE_CONDITIONS := notice -LOCAL_NOTICE_FILE := build/soong/licenses/LICENSE -LOCAL_MODULE_CLASS := ETC -LOCAL_INSTALLED_MODULE_STEM := fs_config_files -LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR_DLKM)/etc -include $(BUILD_SYSTEM)/base_rules.mk -$(LOCAL_BUILT_MODULE): PRIVATE_ANDROID_FS_HDR := $(android_filesystem_config) -$(LOCAL_BUILT_MODULE): PRIVATE_ANDROID_CAP_HDR := $(capability_header) -$(LOCAL_BUILT_MODULE): PRIVATE_TARGET_FS_CONFIG_GEN := $(TARGET_FS_CONFIG_GEN) -$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/fs_config_generator.py $(TARGET_FS_CONFIG_GEN) $(android_filesystem_config) $(capability_header) - @mkdir -p $(dir $@) - $< fsconfig \ - --aid-header $(PRIVATE_ANDROID_FS_HDR) \ - --capability-header $(PRIVATE_ANDROID_CAP_HDR) \ - --partition vendor_dlkm \ - --files \ - --out_file $@ \ - $(or $(PRIVATE_TARGET_FS_CONFIG_GEN),/dev/null) - -endif - -ifneq ($(filter odm_dlkm,$(fs_config_generate_extra_partition_list)),) -################################## -# Generate the odm_dlkm/etc/fs_config_dirs binary file for the target -# Add fs_config_dirs or fs_config_dirs_nonsystem to PRODUCT_PACKAGES -# in the device make file to enable -include $(CLEAR_VARS) - -LOCAL_MODULE := fs_config_dirs_odm_dlkm -LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 -LOCAL_LICENSE_CONDITIONS := notice -LOCAL_NOTICE_FILE := build/soong/licenses/LICENSE -LOCAL_MODULE_CLASS := ETC -LOCAL_INSTALLED_MODULE_STEM := fs_config_dirs -LOCAL_MODULE_PATH := $(TARGET_OUT_ODM_DLKM)/etc -include $(BUILD_SYSTEM)/base_rules.mk -$(LOCAL_BUILT_MODULE): PRIVATE_ANDROID_FS_HDR := $(android_filesystem_config) -$(LOCAL_BUILT_MODULE): PRIVATE_ANDROID_CAP_HDR := $(capability_header) -$(LOCAL_BUILT_MODULE): PRIVATE_TARGET_FS_CONFIG_GEN := $(TARGET_FS_CONFIG_GEN) -$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/fs_config_generator.py $(TARGET_FS_CONFIG_GEN) $(android_filesystem_config) $(capability_header) - @mkdir -p $(dir $@) - $< fsconfig \ - --aid-header $(PRIVATE_ANDROID_FS_HDR) \ - --capability-header $(PRIVATE_ANDROID_CAP_HDR) \ - --partition odm_dlkm \ - --dirs \ - --out_file $@ \ - $(or $(PRIVATE_TARGET_FS_CONFIG_GEN),/dev/null) - -################################## -# Generate the odm_dlkm/etc/fs_config_files binary file for the target -# Add fs_config_files or fs_config_files_nonsystem to PRODUCT_PACKAGES -# in the device make file to enable -include $(CLEAR_VARS) - -LOCAL_MODULE := fs_config_files_odm_dlkm -LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 -LOCAL_LICENSE_CONDITIONS := notice -LOCAL_NOTICE_FILE := build/soong/licenses/LICENSE -LOCAL_MODULE_CLASS := ETC -LOCAL_INSTALLED_MODULE_STEM := fs_config_files -LOCAL_MODULE_PATH := $(TARGET_OUT_ODM_DLKM)/etc -include $(BUILD_SYSTEM)/base_rules.mk -$(LOCAL_BUILT_MODULE): PRIVATE_ANDROID_FS_HDR := $(android_filesystem_config) -$(LOCAL_BUILT_MODULE): PRIVATE_ANDROID_CAP_HDR := $(capability_header) -$(LOCAL_BUILT_MODULE): PRIVATE_TARGET_FS_CONFIG_GEN := $(TARGET_FS_CONFIG_GEN) -$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/fs_config_generator.py $(TARGET_FS_CONFIG_GEN) $(android_filesystem_config) $(capability_header) - @mkdir -p $(dir $@) - $< fsconfig \ - --aid-header $(PRIVATE_ANDROID_FS_HDR) \ - --capability-header $(PRIVATE_ANDROID_CAP_HDR) \ - --partition odm_dlkm \ - --files \ - --out_file $@ \ - $(or $(PRIVATE_TARGET_FS_CONFIG_GEN),/dev/null) - -endif - -ifneq ($(filter system_dlkm,$(fs_config_generate_extra_partition_list)),) -################################## -# Generate the system_dlkm/etc/fs_config_dirs binary file for the target -# Add fs_config_dirs or fs_config_dirs_nonsystem to PRODUCT_PACKAGES -# in the device make file to enable -include $(CLEAR_VARS) - -LOCAL_MODULE := fs_config_dirs_system_dlkm -LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 -LOCAL_LICENSE_CONDITIONS := notice -LOCAL_NOTICE_FILE := build/soong/licenses/LICENSE -LOCAL_MODULE_CLASS := ETC -LOCAL_INSTALLED_MODULE_STEM := fs_config_dirs -LOCAL_MODULE_PATH := $(TARGET_OUT_SYSTEM_DLKM)/etc -include $(BUILD_SYSTEM)/base_rules.mk -$(LOCAL_BUILT_MODULE): PRIVATE_ANDROID_FS_HDR := $(android_filesystem_config) -$(LOCAL_BUILT_MODULE): PRIVATE_ANDROID_CAP_HDR := $(capability_header) -$(LOCAL_BUILT_MODULE): PRIVATE_TARGET_FS_CONFIG_GEN := $(TARGET_FS_CONFIG_GEN) -$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/fs_config_generator.py $(TARGET_FS_CONFIG_GEN) $(android_filesystem_config) $(capability_header) - @mkdir -p $(dir $@) - $< fsconfig \ - --aid-header $(PRIVATE_ANDROID_FS_HDR) \ - --capability-header $(PRIVATE_ANDROID_CAP_HDR) \ - --partition system_dlkm \ - --dirs \ - --out_file $@ \ - $(or $(PRIVATE_TARGET_FS_CONFIG_GEN),/dev/null) - -################################## -# Generate the system_dlkm/etc/fs_config_files binary file for the target -# Add fs_config_files or fs_config_files_nonsystem to PRODUCT_PACKAGES -# in the device make file to enable -include $(CLEAR_VARS) - -LOCAL_MODULE := fs_config_files_system_dlkm -LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 -LOCAL_LICENSE_CONDITIONS := notice -LOCAL_NOTICE_FILE := build/soong/licenses/LICENSE -LOCAL_MODULE_CLASS := ETC -LOCAL_INSTALLED_MODULE_STEM := fs_config_files -LOCAL_MODULE_PATH := $(TARGET_OUT_SYSTEM_DLKM)/etc -include $(BUILD_SYSTEM)/base_rules.mk -$(LOCAL_BUILT_MODULE): PRIVATE_ANDROID_FS_HDR := $(android_filesystem_config) -$(LOCAL_BUILT_MODULE): PRIVATE_ANDROID_CAP_HDR := $(capability_header) -$(LOCAL_BUILT_MODULE): PRIVATE_TARGET_FS_CONFIG_GEN := $(TARGET_FS_CONFIG_GEN) -$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/fs_config_generator.py $(TARGET_FS_CONFIG_GEN) $(android_filesystem_config) $(capability_header) - @mkdir -p $(dir $@) - $< fsconfig \ - --aid-header $(PRIVATE_ANDROID_FS_HDR) \ - --capability-header $(PRIVATE_ANDROID_CAP_HDR) \ - --partition system_dlkm \ - --files \ - --out_file $@ \ - $(or $(PRIVATE_TARGET_FS_CONFIG_GEN),/dev/null) - -endif - -android_filesystem_config := -capability_header := -fs_config_generate_extra_partition_list := diff --git a/tools/ide_query/ide_query.go b/tools/ide_query/ide_query.go index ec937fe630..c7cf5ed49a 100644 --- a/tools/ide_query/ide_query.go +++ b/tools/ide_query/ide_query.go @@ -293,11 +293,19 @@ func getCCInputs(ctx context.Context, env Env, filePaths []string) ([]*pb.Analys // If a file is covered by multiple modules, the first module is returned. func findJavaModules(paths []string, modules map[string]*javaModule) map[string]string { ret := make(map[string]string) - for name, module := range modules { + // A file may be part of multiple modules. To make the result deterministic, + // check the modules in sorted order. + keys := make([]string, 0, len(modules)) + for name := range modules { + keys = append(keys, name) + } + slices.Sort(keys) + for _, name := range keys { if strings.HasSuffix(name, ".impl") { continue } + module := modules[name] for i, p := range paths { if slices.Contains(module.Srcs, p) { ret[p] = name diff --git a/tools/ide_query/prober_scripts/ide_query.out b/tools/ide_query/prober_scripts/ide_query.out index cd7ce6d258..be48da1424 100644 --- a/tools/ide_query/prober_scripts/ide_query.out +++ b/tools/ide_query/prober_scripts/ide_query.out @@ -1,7 +1,9 @@ -out–a -8build/make/tools/ide_query/prober_scripts/cpp/general.cc8prebuilts/clang/host/linux-x86/clang-r522817/bin/clang++-mthumb-Os-fomit-frame-pointer-mllvm-enable-shrink-wrap=false-O2-Wall-Wextra-Winit-self-Wpointer-arith-Wunguarded-availability-Werror=date-time-Werror=int-conversion-Werror=pragma-pack&-Werror=pragma-pack-suspicious-include-Werror=sizeof-array-div-Werror=string-plus-int'-Werror=unreachable-code-loop-increment"-Wno-error=deprecated-declarations-Wno-c99-designator-Wno-gnu-folding-constant"-Wno-inconsistent-missing-override-Wno-error=reorder-init-list-Wno-reorder-init-list-Wno-sign-compare-Wno-unused -DANDROID-DNDEBUG-UDEBUG(-D__compiler_offsetof=__builtin_offsetof*-D__ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__ -faddrsig-fdebug-default-version=5-fcolor-diagnostics-ffp-contract=off-fno-exceptions-fno-strict-aliasing-fmessage-length=0#-fno-relaxed-template-template-args-gsimple-template-names-gz=zstd-no-canonical-prefixes-Wno-error=format"-fdebug-prefix-map=/proc/self/cwd=-ftrivial-auto-var-init=zero-g-ffunction-sections-fdata-sections-fno-short-enums-funwind-tables-fstack-protector-strong-Wa,--noexecstack-D_FORTIFY_SOURCE=2-Wstrict-aliasing=2-Werror=return-type-Werror=non-virtual-dtor-Werror=address-Werror=sequence-point-Werror=format-security-nostdlibinc-fdebug-info-for-profiling-msoft-float-march=armv7-a-mfloat-abi=softfp --mfpu=neon/-Ibuild/make/tools/ide_query/prober_scripts/cpp³-Iout/soong/.intermediates/build/make/tools/ide_query/prober_scripts/cpp/ide_query_proberscript_cc/android_arm_armv7-a-neon/gen/proto/build/make/tools/ide_query/prober_scripts/cpp…-Iout/soong/.intermediates/build/make/tools/ide_query/prober_scripts/cpp/ide_query_proberscript_cc/android_arm_armv7-a-neon/gen/proto-D__LIBC_API__=10000-D__LIBM_API__=10000-D__LIBDL_API__=10000-Iexternal/protobuf/srcY-Iprebuilts/clang/host/linux-x86/clang-r522817/android_libc++/platform/arm/include/c++/v1=-Iprebuilts/clang/host/linux-x86/clang-r522817/include/c++/v1 -Ibionic/libc/async_safe/include-Isystem/logging/liblog/include'-Ibionic/libc/system_properties/include<-Isystem/core/property_service/libpropertyinfoparser/include-isystembionic/libc/include-isystembionic/libc/kernel/uapi/asm-arm-isystembionic/libc/kernel/uapi-isystembionic/libc/kernel/android/scsi-isystembionic/libc/kernel/android/uapi-targetarmv7a-linux-androideabi10000-DANDROID_STRICT-fPIE-Werror-Wno-unused-parameter-DGOOGLE_PROTOBUF_NO_RTTI-Wimplicit-fallthrough*-D_LIBCPP_ENABLE_THREAD_SAFETY_ANNOTATIONS-Wno-gnu-include-next-fvisibility-inlines-hidden-mllvm-enable-shrink-wrap=false-std=gnu++20 -fno-rtti-Isystem/core/include-Isystem/logging/liblog/include-Isystem/media/audio/include-Ihardware/libhardware/include%-Ihardware/libhardware_legacy/include-Ihardware/ril/include-Iframeworks/native/include"-Iframeworks/native/opengl/include-Iframeworks/av/include-Werror=bool-operation -Werror=format-insufficient-args%-Werror=implicit-int-float-conversion-Werror=int-in-bool-context-Werror=int-to-pointer-cast-Werror=pointer-to-int-cast-Werror=xor-used-as-pow-Wno-void-pointer-to-enum-cast-Wno-void-pointer-to-int-cast-Wno-pointer-to-int-cast-Werror=fortify-source-Wno-unused-variable-Wno-missing-field-initializers-Wno-packed-non-pod-Werror=address-of-temporary+-Werror=incompatible-function-pointer-types-Werror=null-dereference-Werror=return-type"-Wno-tautological-constant-compare$-Wno-tautological-type-limit-compare"-Wno-implicit-int-float-conversion!-Wno-tautological-overlap-compare-Wno-deprecated-copy-Wno-range-loop-construct"-Wno-zero-as-null-pointer-constant)-Wno-deprecated-anon-enum-enum-conversion$-Wno-deprecated-enum-enum-conversion-Wno-pessimizing-move-Wno-non-c-typedef-for-linkage-Wno-align-mismatch"-Wno-error=unused-but-set-variable#-Wno-error=unused-but-set-parameter-Wno-error=deprecated-builtins-Wno-error=deprecated2-Wno-error=single-bit-bitfield-constant-conversion$-Wno-error=enum-constexpr-conversion-Wno-error=invalid-offsetof&-Wno-deprecated-dynamic-exception-spec8build/make/tools/ide_query/prober_scripts/cpp/general.cc"Õ? +out2x +8build/make/tools/ide_query/prober_scripts/cpp/general.cc8build/make/tools/ide_query/prober_scripts/cpp/general.cc:—" +8build/make/tools/ide_query/prober_scripts/cpp/general.cc8build/make/tools/ide_query/prober_scripts/cpp/general.cc"8prebuilts/clang/host/linux-x86/clang-r530567/bin/clang++"-nostdlibinc"-mthumb"-Os"-fomit-frame-pointer"-mllvm"-enable-shrink-wrap=false"-O2"-Wall"-Wextra"-Winit-self"-Wpointer-arith"-Wunguarded-availability"-Werror=date-time"-Werror=int-conversion"-Werror=pragma-pack"&-Werror=pragma-pack-suspicious-include"-Werror=sizeof-array-div"-Werror=string-plus-int"'-Werror=unreachable-code-loop-increment""-Wno-error=deprecated-declarations"-Wno-c23-extensions"-Wno-c99-designator"-Wno-gnu-folding-constant""-Wno-inconsistent-missing-override"-Wno-error=reorder-init-list"-Wno-reorder-init-list"-Wno-sign-compare"-Wno-unused" -DANDROID"-DNDEBUG"-UDEBUG"(-D__compiler_offsetof=__builtin_offsetof"*-D__ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__" -faddrsig"-fdebug-default-version=5"-fcolor-diagnostics"-ffp-contract=off"-fno-exceptions"-fno-strict-aliasing"-fmessage-length=0"-gsimple-template-names"-gz=zstd"-no-canonical-prefixes""-fdebug-prefix-map=/proc/self/cwd="-ftrivial-auto-var-init=zero"-g"-ffunction-sections"-fdata-sections"-fno-short-enums"-funwind-tables"-fstack-protector-strong"-Wa,--noexecstack"-D_FORTIFY_SOURCE=2"-Wstrict-aliasing=2"-Werror=return-type"-Werror=non-virtual-dtor"-Werror=address"-Werror=sequence-point"-Werror=format-security"-msoft-float"-march=armv7-a"-mfloat-abi=softfp" +-mfpu=neon"/-Ibuild/make/tools/ide_query/prober_scripts/cpp"³-Iout/soong/.intermediates/build/make/tools/ide_query/prober_scripts/cpp/ide_query_proberscript_cc/android_arm_armv7-a-neon/gen/proto/build/make/tools/ide_query/prober_scripts/cpp"…-Iout/soong/.intermediates/build/make/tools/ide_query/prober_scripts/cpp/ide_query_proberscript_cc/android_arm_armv7-a-neon/gen/proto"-D__LIBC_API__=10000"-D__LIBM_API__=10000"-D__LIBDL_API__=10000"-Iexternal/protobuf/src"Y-Iprebuilts/clang/host/linux-x86/clang-r530567/android_libc++/platform/arm/include/c++/v1"=-Iprebuilts/clang/host/linux-x86/clang-r530567/include/c++/v1" -Ibionic/libc/async_safe/include"-Isystem/logging/liblog/include"'-Ibionic/libc/system_properties/include"<-Isystem/core/property_service/libpropertyinfoparser/include"-isystem"bionic/libc/include"-isystem"bionic/libc/kernel/uapi/asm-arm"-isystem"bionic/libc/kernel/uapi"-isystem"bionic/libc/kernel/android/scsi"-isystem"bionic/libc/kernel/android/uapi"-target"armv7a-linux-androideabi10000"-DANDROID_STRICT"-fPIE"-Werror"-Wno-unused-parameter"-DGOOGLE_PROTOBUF_NO_RTTI"-Wimplicit-fallthrough"*-D_LIBCPP_ENABLE_THREAD_SAFETY_ANNOTATIONS"-Wno-gnu-include-next"-fvisibility-inlines-hidden"-mllvm"-enable-shrink-wrap=false"-std=gnu++20" -fno-rtti"-Isystem/core/include"-Isystem/logging/liblog/include"-Isystem/media/audio/include"-Ihardware/libhardware/include"%-Ihardware/libhardware_legacy/include"-Ihardware/ril/include"-Iframeworks/native/include""-Iframeworks/native/opengl/include"-Iframeworks/av/include"-Werror=bool-operation" -Werror=format-insufficient-args"%-Werror=implicit-int-float-conversion"-Werror=int-in-bool-context"-Werror=int-to-pointer-cast"-Werror=pointer-to-int-cast"-Werror=xor-used-as-pow"-Wno-void-pointer-to-enum-cast"-Wno-void-pointer-to-int-cast"-Wno-pointer-to-int-cast"-Werror=fortify-source"-Wno-unused-variable"-Wno-missing-field-initializers"-Wno-packed-non-pod"-Werror=address-of-temporary"+-Werror=incompatible-function-pointer-types"-Werror=null-dereference"-Werror=return-type""-Wno-tautological-constant-compare"$-Wno-tautological-type-limit-compare""-Wno-implicit-int-float-conversion"!-Wno-tautological-overlap-compare"-Wno-deprecated-copy"-Wno-range-loop-construct""-Wno-zero-as-null-pointer-constant")-Wno-deprecated-anon-enum-enum-conversion"$-Wno-deprecated-enum-enum-conversion"-Wno-error=pessimizing-move"-Wno-non-c-typedef-for-linkage"-Wno-align-mismatch""-Wno-error=unused-but-set-variable"#-Wno-error=unused-but-set-parameter"-Wno-error=deprecated-builtins"-Wno-error=deprecated"&-Wno-deprecated-dynamic-exception-spec"$-Wno-error=enum-constexpr-conversion"-Wno-error=invalid-offsetof")-Wno-error=thread-safety-reference-return"-Wno-vla-cxx-extension"8build/make/tools/ide_query/prober_scripts/cpp/general.cc2Egenfiles_for_build/make/tools/ide_query/prober_scripts/cpp/general.cc:Ÿ@ +Egenfiles_for_build/make/tools/ide_query/prober_scripts/cpp/general.cc*Õ? ¶soong/.intermediates/build/make/tools/ide_query/prober_scripts/cpp/ide_query_proberscript_cc/android_arm_armv7-a-neon/gen/proto/build/make/tools/ide_query/prober_scripts/cpp/foo.pb.h™>// Generated by the protocol buffer compiler. DO NOT EDIT! // source: build/make/tools/ide_query/prober_scripts/cpp/foo.proto diff --git a/tools/ide_query/prober_scripts/regen.sh b/tools/ide_query/prober_scripts/regen.sh index 2edfe53ec3..04a02640d7 100755 --- a/tools/ide_query/prober_scripts/regen.sh +++ b/tools/ide_query/prober_scripts/regen.sh @@ -21,13 +21,8 @@ # ide_query.sh. The prober doesn't check-out the full source code, so it # can't run ide_query.sh itself. -cd $(dirname $BASH_SOURCE) -source $(pwd)/../../../shell_utils.sh -require_top - files_to_build=( build/make/tools/ide_query/prober_scripts/cpp/general.cc ) -cd ${TOP} build/make/tools/ide_query/ide_query.sh --lunch_target=aosp_arm-trunk_staging-eng ${files_to_build[@]} > build/make/tools/ide_query/prober_scripts/ide_query.out diff --git a/tools/metadata/Android.bp b/tools/metadata/Android.bp deleted file mode 100644 index 77d106d705..0000000000 --- a/tools/metadata/Android.bp +++ /dev/null @@ -1,16 +0,0 @@ -package { - default_applicable_licenses: ["Android-Apache-2.0"], -} - -blueprint_go_binary { - name: "metadata", - deps: [ - "soong-testing-test_spec_proto", - "soong-testing-code_metadata_proto", - "soong-testing-code_metadata_internal_proto", - "golang-protobuf-proto", - ], - srcs: [ - "generator.go", - ] -}
\ No newline at end of file diff --git a/tools/metadata/OWNERS b/tools/metadata/OWNERS deleted file mode 100644 index 03bcdf1c40..0000000000 --- a/tools/metadata/OWNERS +++ /dev/null @@ -1,4 +0,0 @@ -dariofreni@google.com -joeo@google.com -ronish@google.com -caditya@google.com diff --git a/tools/metadata/generator.go b/tools/metadata/generator.go deleted file mode 100644 index b7668be44f..0000000000 --- a/tools/metadata/generator.go +++ /dev/null @@ -1,328 +0,0 @@ -package main - -import ( - "flag" - "fmt" - "io" - "log" - "os" - "sort" - "strings" - "sync" - - "android/soong/testing/code_metadata_internal_proto" - "android/soong/testing/code_metadata_proto" - "android/soong/testing/test_spec_proto" - "google.golang.org/protobuf/proto" -) - -type keyToLocksMap struct { - locks sync.Map -} - -func (kl *keyToLocksMap) GetLockForKey(key string) *sync.Mutex { - mutex, _ := kl.locks.LoadOrStore(key, &sync.Mutex{}) - return mutex.(*sync.Mutex) -} - -// Define a struct to hold the combination of team ID and multi-ownership flag for validation -type sourceFileAttributes struct { - TeamID string - MultiOwnership bool - Path string -} - -func getSortedKeys(syncMap *sync.Map) []string { - var allKeys []string - syncMap.Range( - func(key, _ interface{}) bool { - allKeys = append(allKeys, key.(string)) - return true - }, - ) - - sort.Strings(allKeys) - return allKeys -} - -// writeProtoToFile marshals a protobuf message and writes it to a file -func writeProtoToFile(outputFile string, message proto.Message) { - data, err := proto.Marshal(message) - if err != nil { - log.Fatal(err) - } - file, err := os.Create(outputFile) - if err != nil { - log.Fatal(err) - } - defer file.Close() - - _, err = file.Write(data) - if err != nil { - log.Fatal(err) - } -} - -func readFileToString(filePath string) string { - file, err := os.Open(filePath) - if err != nil { - log.Fatal(err) - } - defer file.Close() - - data, err := io.ReadAll(file) - if err != nil { - log.Fatal(err) - } - return string(data) -} - -func writeEmptyOutputProto(outputFile string, metadataRule string) { - file, err := os.Create(outputFile) - if err != nil { - log.Fatal(err) - } - var message proto.Message - if metadataRule == "test_spec" { - message = &test_spec_proto.TestSpec{} - } else if metadataRule == "code_metadata" { - message = &code_metadata_proto.CodeMetadata{} - } - data, err := proto.Marshal(message) - if err != nil { - log.Fatal(err) - } - defer file.Close() - - _, err = file.Write([]byte(data)) - if err != nil { - log.Fatal(err) - } -} - -func processTestSpecProtobuf( - filePath string, ownershipMetadataMap *sync.Map, keyLocks *keyToLocksMap, - errCh chan error, wg *sync.WaitGroup, -) { - defer wg.Done() - - fileContent := strings.TrimRight(readFileToString(filePath), "\n") - testData := test_spec_proto.TestSpec{} - err := proto.Unmarshal([]byte(fileContent), &testData) - if err != nil { - errCh <- err - return - } - - ownershipMetadata := testData.GetOwnershipMetadataList() - for _, metadata := range ownershipMetadata { - key := metadata.GetTargetName() - lock := keyLocks.GetLockForKey(key) - lock.Lock() - - value, loaded := ownershipMetadataMap.LoadOrStore( - key, []*test_spec_proto.TestSpec_OwnershipMetadata{metadata}, - ) - if loaded { - existingMetadata := value.([]*test_spec_proto.TestSpec_OwnershipMetadata) - isDuplicate := false - for _, existing := range existingMetadata { - if metadata.GetTrendyTeamId() != existing.GetTrendyTeamId() { - errCh <- fmt.Errorf( - "Conflicting trendy team IDs found for %s at:\n%s with teamId"+ - ": %s,\n%s with teamId: %s", - key, - metadata.GetPath(), metadata.GetTrendyTeamId(), existing.GetPath(), - existing.GetTrendyTeamId(), - ) - - lock.Unlock() - return - } - if metadata.GetTrendyTeamId() == existing.GetTrendyTeamId() && metadata.GetPath() == existing.GetPath() { - isDuplicate = true - break - } - } - if !isDuplicate { - existingMetadata = append(existingMetadata, metadata) - ownershipMetadataMap.Store(key, existingMetadata) - } - } - - lock.Unlock() - } -} - -// processCodeMetadataProtobuf processes CodeMetadata protobuf files -func processCodeMetadataProtobuf( - filePath string, ownershipMetadataMap *sync.Map, sourceFileMetadataMap *sync.Map, keyLocks *keyToLocksMap, - errCh chan error, wg *sync.WaitGroup, -) { - defer wg.Done() - - fileContent := strings.TrimRight(readFileToString(filePath), "\n") - internalCodeData := code_metadata_internal_proto.CodeMetadataInternal{} - err := proto.Unmarshal([]byte(fileContent), &internalCodeData) - if err != nil { - errCh <- err - return - } - - // Process each TargetOwnership entry - for _, internalMetadata := range internalCodeData.GetTargetOwnershipList() { - key := internalMetadata.GetTargetName() - lock := keyLocks.GetLockForKey(key) - lock.Lock() - - for _, srcFile := range internalMetadata.GetSourceFiles() { - srcFileKey := srcFile - srcFileLock := keyLocks.GetLockForKey(srcFileKey) - srcFileLock.Lock() - attributes := sourceFileAttributes{ - TeamID: internalMetadata.GetTrendyTeamId(), - MultiOwnership: internalMetadata.GetMultiOwnership(), - Path: internalMetadata.GetPath(), - } - - existingAttributes, exists := sourceFileMetadataMap.Load(srcFileKey) - if exists { - existing := existingAttributes.(sourceFileAttributes) - if attributes.TeamID != existing.TeamID && (!attributes.MultiOwnership || !existing.MultiOwnership) { - errCh <- fmt.Errorf( - "Conflict found for source file %s covered at %s with team ID: %s. Existing team ID: %s and path: %s."+ - " If multi-ownership is required, multiOwnership should be set to true in all test_spec modules using this target. "+ - "Multiple-ownership in general is discouraged though as it make infrastructure around android relying on this information pick up a random value when it needs only one.", - srcFile, internalMetadata.GetPath(), attributes.TeamID, existing.TeamID, existing.Path, - ) - srcFileLock.Unlock() - lock.Unlock() - return - } - } else { - // Store the metadata if no conflict - sourceFileMetadataMap.Store(srcFileKey, attributes) - } - srcFileLock.Unlock() - } - - value, loaded := ownershipMetadataMap.LoadOrStore( - key, []*code_metadata_internal_proto.CodeMetadataInternal_TargetOwnership{internalMetadata}, - ) - if loaded { - existingMetadata := value.([]*code_metadata_internal_proto.CodeMetadataInternal_TargetOwnership) - isDuplicate := false - for _, existing := range existingMetadata { - if internalMetadata.GetTrendyTeamId() == existing.GetTrendyTeamId() && internalMetadata.GetPath() == existing.GetPath() { - isDuplicate = true - break - } - } - if !isDuplicate { - existingMetadata = append(existingMetadata, internalMetadata) - ownershipMetadataMap.Store(key, existingMetadata) - } - } - - lock.Unlock() - } -} - -func main() { - inputFile := flag.String("inputFile", "", "Input file path") - outputFile := flag.String("outputFile", "", "Output file path") - rule := flag.String( - "rule", "", "Metadata rule (Hint: test_spec or code_metadata)", - ) - flag.Parse() - - if *inputFile == "" || *outputFile == "" || *rule == "" { - fmt.Println("Usage: metadata -rule <rule> -inputFile <input file path> -outputFile <output file path>") - os.Exit(1) - } - - inputFileData := strings.TrimRight(readFileToString(*inputFile), "\n") - filePaths := strings.Split(inputFileData, " ") - if len(filePaths) == 1 && filePaths[0] == "" { - writeEmptyOutputProto(*outputFile, *rule) - return - } - ownershipMetadataMap := &sync.Map{} - keyLocks := &keyToLocksMap{} - errCh := make(chan error, len(filePaths)) - var wg sync.WaitGroup - - switch *rule { - case "test_spec": - for _, filePath := range filePaths { - wg.Add(1) - go processTestSpecProtobuf( - filePath, ownershipMetadataMap, keyLocks, errCh, &wg, - ) - } - - wg.Wait() - close(errCh) - - for err := range errCh { - log.Fatal(err) - } - - allKeys := getSortedKeys(ownershipMetadataMap) - var allMetadata []*test_spec_proto.TestSpec_OwnershipMetadata - - for _, key := range allKeys { - value, _ := ownershipMetadataMap.Load(key) - metadataList := value.([]*test_spec_proto.TestSpec_OwnershipMetadata) - allMetadata = append(allMetadata, metadataList...) - } - - testSpec := &test_spec_proto.TestSpec{ - OwnershipMetadataList: allMetadata, - } - writeProtoToFile(*outputFile, testSpec) - break - case "code_metadata": - sourceFileMetadataMap := &sync.Map{} - for _, filePath := range filePaths { - wg.Add(1) - go processCodeMetadataProtobuf( - filePath, ownershipMetadataMap, sourceFileMetadataMap, keyLocks, errCh, &wg, - ) - } - - wg.Wait() - close(errCh) - - for err := range errCh { - log.Fatal(err) - } - - sortedKeys := getSortedKeys(ownershipMetadataMap) - allMetadata := make([]*code_metadata_proto.CodeMetadata_TargetOwnership, 0) - for _, key := range sortedKeys { - value, _ := ownershipMetadataMap.Load(key) - metadata := value.([]*code_metadata_internal_proto.CodeMetadataInternal_TargetOwnership) - for _, m := range metadata { - targetName := m.GetTargetName() - path := m.GetPath() - trendyTeamId := m.GetTrendyTeamId() - - allMetadata = append(allMetadata, &code_metadata_proto.CodeMetadata_TargetOwnership{ - TargetName: &targetName, - Path: &path, - TrendyTeamId: &trendyTeamId, - SourceFiles: m.GetSourceFiles(), - }) - } - } - - finalMetadata := &code_metadata_proto.CodeMetadata{ - TargetOwnershipList: allMetadata, - } - writeProtoToFile(*outputFile, finalMetadata) - break - default: - log.Fatalf("No specific processing implemented for rule '%s'.\n", *rule) - } -} diff --git a/tools/metadata/go.mod b/tools/metadata/go.mod deleted file mode 100644 index e9d04b16f6..0000000000 --- a/tools/metadata/go.mod +++ /dev/null @@ -1,7 +0,0 @@ -module android/soong/tools/metadata - -require google.golang.org/protobuf v0.0.0 - -replace google.golang.org/protobuf v0.0.0 => ../../../external/golang-protobuf - -go 1.18
\ No newline at end of file diff --git a/tools/metadata/go.work b/tools/metadata/go.work deleted file mode 100644 index f2cdf8ec98..0000000000 --- a/tools/metadata/go.work +++ /dev/null @@ -1,11 +0,0 @@ -go 1.18 - -use ( - . - ../../../../external/golang-protobuf - ../../../soong/testing/test_spec_proto - ../../../soong/testing/code_metadata_proto - ../../../soong/testing/code_metadata_proto_internal -) - -replace google.golang.org/protobuf v0.0.0 => ../../../../external/golang-protobuf diff --git a/tools/metadata/testdata/emptyInputFile.txt b/tools/metadata/testdata/emptyInputFile.txt deleted file mode 100644 index 8b13789179..0000000000 --- a/tools/metadata/testdata/emptyInputFile.txt +++ /dev/null @@ -1 +0,0 @@ - diff --git a/tools/metadata/testdata/expectedCodeMetadataOutput.txt b/tools/metadata/testdata/expectedCodeMetadataOutput.txt deleted file mode 100644 index 755cf40a30..0000000000 --- a/tools/metadata/testdata/expectedCodeMetadataOutput.txt +++ /dev/null @@ -1,7 +0,0 @@ - - -bar -Android.bp12346"b.java - -foo -Android.bp12345"a.java
\ No newline at end of file diff --git a/tools/metadata/testdata/expectedOutputFile.txt b/tools/metadata/testdata/expectedOutputFile.txt deleted file mode 100644 index b0d382f279..0000000000 --- a/tools/metadata/testdata/expectedOutputFile.txt +++ /dev/null @@ -1,22 +0,0 @@ - -. -java-test-module-name-one -Android.bp12345 -. -java-test-module-name-six -Android.bp12346 -. -java-test-module-name-six -Aqwerty.bp12346 -. -java-test-module-name-six -Apoiuyt.bp12346 -. -java-test-module-name-two -Android.bp12345 -. -java-test-module-name-two -Asdfghj.bp12345 -. -java-test-module-name-two -Azxcvbn.bp12345
\ No newline at end of file diff --git a/tools/metadata/testdata/file1.txt b/tools/metadata/testdata/file1.txt deleted file mode 100644 index 81beed00ab..0000000000 --- a/tools/metadata/testdata/file1.txt +++ /dev/null @@ -1,13 +0,0 @@ - -. -java-test-module-name-one -Android.bp12345 -. -java-test-module-name-two -Android.bp12345 -. -java-test-module-name-two -Asdfghj.bp12345 -. -java-test-module-name-two -Azxcvbn.bp12345 diff --git a/tools/metadata/testdata/file2.txt b/tools/metadata/testdata/file2.txt deleted file mode 100644 index 32a753fef5..0000000000 --- a/tools/metadata/testdata/file2.txt +++ /dev/null @@ -1,25 +0,0 @@ - -. -java-test-module-name-one -Android.bp12345 -. -java-test-module-name-six -Android.bp12346 -. -java-test-module-name-one -Android.bp12345 -. -java-test-module-name-six -Aqwerty.bp12346 -. -java-test-module-name-six -Apoiuyt.bp12346 -. -java-test-module-name-six -Apoiuyt.bp12346 -. -java-test-module-name-six -Apoiuyt.bp12346 -. -java-test-module-name-six -Apoiuyt.bp12346 diff --git a/tools/metadata/testdata/file3.txt b/tools/metadata/testdata/file3.txt deleted file mode 100644 index 81beed00ab..0000000000 --- a/tools/metadata/testdata/file3.txt +++ /dev/null @@ -1,13 +0,0 @@ - -. -java-test-module-name-one -Android.bp12345 -. -java-test-module-name-two -Android.bp12345 -. -java-test-module-name-two -Asdfghj.bp12345 -. -java-test-module-name-two -Azxcvbn.bp12345 diff --git a/tools/metadata/testdata/file4.txt b/tools/metadata/testdata/file4.txt deleted file mode 100644 index 6a7590021d..0000000000 --- a/tools/metadata/testdata/file4.txt +++ /dev/null @@ -1,25 +0,0 @@ - -. -java-test-module-name-one -Android.bp12345 -. -java-test-module-name-six -Android.bp12346 -. -java-test-module-name-one -Android.bp12346 -. -java-test-module-name-six -Aqwerty.bp12346 -. -java-test-module-name-six -Apoiuyt.bp12346 -. -java-test-module-name-six -Apoiuyt.bp12346 -. -java-test-module-name-six -Apoiuyt.bp12346 -. -java-test-module-name-six -Apoiuyt.bp12346 diff --git a/tools/metadata/testdata/file5.txt b/tools/metadata/testdata/file5.txt deleted file mode 100644 index d8de06457d..0000000000 --- a/tools/metadata/testdata/file5.txt +++ /dev/null @@ -1,4 +0,0 @@ - - -foo -Android.bp12345"a.java diff --git a/tools/metadata/testdata/file6.txt b/tools/metadata/testdata/file6.txt deleted file mode 100644 index 9c7cdcd505..0000000000 --- a/tools/metadata/testdata/file6.txt +++ /dev/null @@ -1,4 +0,0 @@ - - -bar -Android.bp12346"b.java diff --git a/tools/metadata/testdata/file7.txt b/tools/metadata/testdata/file7.txt deleted file mode 100644 index d8de06457d..0000000000 --- a/tools/metadata/testdata/file7.txt +++ /dev/null @@ -1,4 +0,0 @@ - - -foo -Android.bp12345"a.java diff --git a/tools/metadata/testdata/file8.txt b/tools/metadata/testdata/file8.txt deleted file mode 100644 index a931690022..0000000000 --- a/tools/metadata/testdata/file8.txt +++ /dev/null @@ -1,4 +0,0 @@ - - -foo -Android.gp12346"a.java diff --git a/tools/metadata/testdata/generatedCodeMetadataOutput.txt b/tools/metadata/testdata/generatedCodeMetadataOutput.txt deleted file mode 100644 index 755cf40a30..0000000000 --- a/tools/metadata/testdata/generatedCodeMetadataOutput.txt +++ /dev/null @@ -1,7 +0,0 @@ - - -bar -Android.bp12346"b.java - -foo -Android.bp12345"a.java
\ No newline at end of file diff --git a/tools/metadata/testdata/generatedCodeMetadataOutputFile.txt b/tools/metadata/testdata/generatedCodeMetadataOutputFile.txt deleted file mode 100644 index 755cf40a30..0000000000 --- a/tools/metadata/testdata/generatedCodeMetadataOutputFile.txt +++ /dev/null @@ -1,7 +0,0 @@ - - -bar -Android.bp12346"b.java - -foo -Android.bp12345"a.java
\ No newline at end of file diff --git a/tools/metadata/testdata/generatedEmptyOutputFile.txt b/tools/metadata/testdata/generatedEmptyOutputFile.txt deleted file mode 100644 index e69de29bb2..0000000000 --- a/tools/metadata/testdata/generatedEmptyOutputFile.txt +++ /dev/null diff --git a/tools/metadata/testdata/generatedOutputFile.txt b/tools/metadata/testdata/generatedOutputFile.txt deleted file mode 100644 index b0d382f279..0000000000 --- a/tools/metadata/testdata/generatedOutputFile.txt +++ /dev/null @@ -1,22 +0,0 @@ - -. -java-test-module-name-one -Android.bp12345 -. -java-test-module-name-six -Android.bp12346 -. -java-test-module-name-six -Aqwerty.bp12346 -. -java-test-module-name-six -Apoiuyt.bp12346 -. -java-test-module-name-two -Android.bp12345 -. -java-test-module-name-two -Asdfghj.bp12345 -. -java-test-module-name-two -Azxcvbn.bp12345
\ No newline at end of file diff --git a/tools/metadata/testdata/inputCodeMetadata.txt b/tools/metadata/testdata/inputCodeMetadata.txt deleted file mode 100644 index 7a81b7d523..0000000000 --- a/tools/metadata/testdata/inputCodeMetadata.txt +++ /dev/null @@ -1 +0,0 @@ -file5.txt file6.txt
\ No newline at end of file diff --git a/tools/metadata/testdata/inputCodeMetadataNegative.txt b/tools/metadata/testdata/inputCodeMetadataNegative.txt deleted file mode 100644 index 26668e44a9..0000000000 --- a/tools/metadata/testdata/inputCodeMetadataNegative.txt +++ /dev/null @@ -1 +0,0 @@ -file7.txt file8.txt
\ No newline at end of file diff --git a/tools/metadata/testdata/inputFiles.txt b/tools/metadata/testdata/inputFiles.txt deleted file mode 100644 index e44bc94d32..0000000000 --- a/tools/metadata/testdata/inputFiles.txt +++ /dev/null @@ -1 +0,0 @@ -file1.txt file2.txt
\ No newline at end of file diff --git a/tools/metadata/testdata/inputFilesNegativeCase.txt b/tools/metadata/testdata/inputFilesNegativeCase.txt deleted file mode 100644 index a37aa3fd5d..0000000000 --- a/tools/metadata/testdata/inputFilesNegativeCase.txt +++ /dev/null @@ -1 +0,0 @@ -file3.txt file4.txt
\ No newline at end of file diff --git a/tools/metadata/testdata/metadata_test.go b/tools/metadata/testdata/metadata_test.go deleted file mode 100644 index 314add352f..0000000000 --- a/tools/metadata/testdata/metadata_test.go +++ /dev/null @@ -1,119 +0,0 @@ -package main - -import ( - "fmt" - "io/ioutil" - "os/exec" - "strings" - "testing" -) - -func TestMetadata(t *testing.T) { - cmd := exec.Command( - "metadata", "-rule", "test_spec", "-inputFile", "./inputFiles.txt", "-outputFile", - "./generatedOutputFile.txt", - ) - stderr, err := cmd.CombinedOutput() - if err != nil { - t.Fatalf("Error running metadata command: %s. Error: %v", stderr, err) - } - - // Read the contents of the expected output file - expectedOutput, err := ioutil.ReadFile("./expectedOutputFile.txt") - if err != nil { - t.Fatalf("Error reading expected output file: %s", err) - } - - // Read the contents of the generated output file - generatedOutput, err := ioutil.ReadFile("./generatedOutputFile.txt") - if err != nil { - t.Fatalf("Error reading generated output file: %s", err) - } - - fmt.Println() - - // Compare the contents - if string(expectedOutput) != string(generatedOutput) { - t.Errorf("Generated file contents do not match the expected output") - } -} - -func TestMetadataNegativeCase(t *testing.T) { - cmd := exec.Command( - "metadata", "-rule", "test_spec", "-inputFile", "./inputFilesNegativeCase.txt", "-outputFile", - "./generatedOutputFileNegativeCase.txt", - ) - stderr, err := cmd.CombinedOutput() - if err == nil { - t.Fatalf( - "Expected an error, but the metadata command executed successfully. Output: %s", - stderr, - ) - } - - expectedError := "Conflicting trendy team IDs found for java-test-module" + - "-name-one at:\nAndroid.bp with teamId: 12346," + - "\nAndroid.bp with teamId: 12345" - if !strings.Contains( - strings.TrimSpace(string(stderr)), strings.TrimSpace(expectedError), - ) { - t.Errorf( - "Unexpected error message. Expected to contain: %s, Got: %s", - expectedError, stderr, - ) - } -} - -func TestEmptyInputFile(t *testing.T) { - cmd := exec.Command( - "metadata", "-rule", "test_spec", "-inputFile", "./emptyInputFile.txt", "-outputFile", - "./generatedEmptyOutputFile.txt", - ) - stderr, err := cmd.CombinedOutput() - if err != nil { - t.Fatalf("Error running metadata command: %s. Error: %v", stderr, err) - } - - // Read the contents of the generated output file - generatedOutput, err := ioutil.ReadFile("./generatedEmptyOutputFile.txt") - if err != nil { - t.Fatalf("Error reading generated output file: %s", err) - } - - fmt.Println() - - // Compare the contents - if string(generatedOutput) != "\n" { - t.Errorf("Generated file contents do not match the expected output") - } -} - -func TestCodeMetadata(t *testing.T) { - cmd := exec.Command( - "metadata", "-rule", "code_metadata", "-inputFile", "./inputCodeMetadata.txt", "-outputFile", - "./generatedCodeMetadataOutputFile.txt", - ) - stderr, err := cmd.CombinedOutput() - if err != nil { - t.Fatalf("Error running metadata command: %s. Error: %v", stderr, err) - } - - // Read the contents of the expected output file - expectedOutput, err := ioutil.ReadFile("./expectedCodeMetadataOutput.txt") - if err != nil { - t.Fatalf("Error reading expected output file: %s", err) - } - - // Read the contents of the generated output file - generatedOutput, err := ioutil.ReadFile("./generatedCodeMetadataOutputFile.txt") - if err != nil { - t.Fatalf("Error reading generated output file: %s", err) - } - - fmt.Println() - - // Compare the contents - if string(expectedOutput) != string(generatedOutput) { - t.Errorf("Generated file contents do not match the expected output") - } -} diff --git a/tools/metadata/testdata/outputFile.txt b/tools/metadata/testdata/outputFile.txt deleted file mode 100644 index b0d382f279..0000000000 --- a/tools/metadata/testdata/outputFile.txt +++ /dev/null @@ -1,22 +0,0 @@ - -. -java-test-module-name-one -Android.bp12345 -. -java-test-module-name-six -Android.bp12346 -. -java-test-module-name-six -Aqwerty.bp12346 -. -java-test-module-name-six -Apoiuyt.bp12346 -. -java-test-module-name-two -Android.bp12345 -. -java-test-module-name-two -Asdfghj.bp12345 -. -java-test-module-name-two -Azxcvbn.bp12345
\ No newline at end of file diff --git a/tools/releasetools/Android.bp b/tools/releasetools/Android.bp index 8c710449f9..e371b2354c 100644 --- a/tools/releasetools/Android.bp +++ b/tools/releasetools/Android.bp @@ -637,6 +637,8 @@ python_defaults { ], data: [ "testdata/**/*", + ], + device_common_data: [ ":com.android.apex.compressed.v1", ":com.android.apex.vendor.foo.with_vintf", ], |