diff options
| -rw-r--r-- | CleanSpec.mk | 3 | ||||
| -rw-r--r-- | core/Makefile | 92 | ||||
| -rw-r--r-- | core/clear_vars.mk | 1 | ||||
| -rw-r--r-- | core/install_jni_libs_internal.mk | 16 | ||||
| -rw-r--r-- | core/instrumentation_test_config_template.xml | 1 | ||||
| -rw-r--r-- | core/soong_android_app_set.mk | 32 | ||||
| -rw-r--r-- | target/board/BoardConfigGsiCommon.mk | 6 | ||||
| -rw-r--r-- | target/product/gsi/current.txt | 2 | ||||
| -rwxr-xr-x | tools/auto_gen_test_config.py | 12 | ||||
| -rwxr-xr-x | tools/releasetools/add_img_to_target_files.py | 8 | ||||
| -rwxr-xr-x | tools/releasetools/build_image.py | 6 | ||||
| -rwxr-xr-x | tools/releasetools/check_target_files_vintf.py | 3 | ||||
| -rw-r--r-- | tools/releasetools/common.py | 205 | ||||
| -rwxr-xr-x | tools/releasetools/ota_from_target_files.py | 37 | ||||
| -rw-r--r-- | tools/releasetools/test_add_img_to_target_files.py | 66 | ||||
| -rw-r--r-- | tools/releasetools/test_common.py | 480 | ||||
| -rw-r--r-- | tools/releasetools/test_ota_from_target_files.py | 208 |
17 files changed, 863 insertions, 315 deletions
diff --git a/CleanSpec.mk b/CleanSpec.mk index 97dc31f71e..6352e38c98 100644 --- a/CleanSpec.mk +++ b/CleanSpec.mk @@ -722,6 +722,9 @@ $(call add-clean-step, rm -rf $(HOST_OUT)/framework/vts10-tradefed.jar) $(call add-clean-step, rm -rf $(HOST_OUT)/vts/*) $(call add-clean-step, rm -rf $(HOST_OUT)/framework/vts-tradefed.jar) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/vendor/default.prop) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/etc/prop.default) + # ************************************************ # NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST # ************************************************ diff --git a/core/Makefile b/core/Makefile index f09c75bd7c..5e81549c49 100644 --- a/core/Makefile +++ b/core/Makefile @@ -169,9 +169,9 @@ ifeq ($(BOARD_PROPERTY_OVERRIDES_SPLIT_ENABLED), true) endif # ----------------------------------------------------------------- -# FINAL_VENDOR_DEFAULT_PROPERTIES will be installed in vendor/default.prop if +# FINAL_VENDOR_DEFAULT_PROPERTIES will be installed in vendor/build.prop if # property_overrides_split_enabled is true. Otherwise it will be installed in -# ROOT/default.prop. +# /system/build.prop ifdef BOARD_VNDK_VERSION ifeq ($(BOARD_VNDK_VERSION),current) FINAL_VENDOR_DEFAULT_PROPERTIES := ro.vndk.version=$(PLATFORM_VNDK_VERSION) @@ -224,9 +224,6 @@ endif FINAL_VENDOR_DEFAULT_PROPERTIES := $(call uniq-pairs-by-first-component, \ $(FINAL_VENDOR_DEFAULT_PROPERTIES),=) -# ----------------------------------------------------------------- -# prop.default - BUILDINFO_SH := build/make/tools/buildinfo.sh BUILDINFO_COMMON_SH := build/make/tools/buildinfo_common.sh POST_PROCESS_PROPS :=$= build/make/tools/post_process_props.py @@ -257,66 +254,19 @@ define generate-common-build-props-with-product-vars-set bash $(BUILDINFO_COMMON_SH) "$(1)" >> $(2) endef -ifdef property_overrides_split_enabled -INSTALLED_DEFAULT_PROP_TARGET := $(TARGET_OUT)/etc/prop.default -INSTALLED_DEFAULT_PROP_OLD_TARGET := $(TARGET_ROOT_OUT)/default.prop -ALL_DEFAULT_INSTALLED_MODULES += $(INSTALLED_DEFAULT_PROP_OLD_TARGET) -$(INSTALLED_DEFAULT_PROP_TARGET): .KATI_IMPLICIT_OUTPUTS := $(INSTALLED_DEFAULT_PROP_OLD_TARGET) -else -# legacy path -INSTALLED_DEFAULT_PROP_TARGET := $(TARGET_ROOT_OUT)/default.prop -endif -ALL_DEFAULT_INSTALLED_MODULES += $(INSTALLED_DEFAULT_PROP_TARGET) +# ----------------------------------------------------------------- +# build.prop +intermediate_system_build_prop := $(call intermediates-dir-for,ETC,system_build_prop)/build.prop +INSTALLED_BUILD_PROP_TARGET := $(TARGET_OUT)/build.prop +ALL_DEFAULT_INSTALLED_MODULES += $(INSTALLED_BUILD_PROP_TARGET) + +# TODO(b/117892318) merge DEFAULT into BUILD FINAL_DEFAULT_PROPERTIES := \ $(call collapse-pairs, $(ADDITIONAL_DEFAULT_PROPERTIES)) \ $(call collapse-pairs, $(PRODUCT_SYSTEM_DEFAULT_PROPERTIES)) -ifndef property_overrides_split_enabled - FINAL_DEFAULT_PROPERTIES += \ - $(call collapse-pairs, $(FINAL_VENDOR_DEFAULT_PROPERTIES)) -endif FINAL_DEFAULT_PROPERTIES := $(call uniq-pairs-by-first-component, \ $(FINAL_DEFAULT_PROPERTIES),=) -intermediate_system_build_prop := $(call intermediates-dir-for,ETC,system_build_prop)/build.prop - -$(INSTALLED_DEFAULT_PROP_TARGET): $(BUILDINFO_COMMON_SH) $(POST_PROCESS_PROPS) $(intermediate_system_build_prop) - @echo Target buildinfo: $@ - @mkdir -p $(dir $@) - @rm -f $@ - $(hide) echo "#" > $@; \ - echo "# ADDITIONAL_DEFAULT_PROPERTIES" >> $@; \ - echo "#" >> $@; - $(hide) $(foreach line,$(FINAL_DEFAULT_PROPERTIES), \ - echo "$(line)" >> $@;) - $(hide) $(POST_PROCESS_PROPS) $@ -ifdef property_overrides_split_enabled - $(hide) mkdir -p $(TARGET_ROOT_OUT) - $(hide) ln -sf system/etc/prop.default $(INSTALLED_DEFAULT_PROP_OLD_TARGET) -endif - -# ----------------------------------------------------------------- -# vendor default.prop -INSTALLED_VENDOR_DEFAULT_PROP_TARGET := -ifdef property_overrides_split_enabled -INSTALLED_VENDOR_DEFAULT_PROP_TARGET := $(TARGET_OUT_VENDOR)/default.prop -ALL_DEFAULT_INSTALLED_MODULES += $(INSTALLED_VENDOR_DEFAULT_PROP_TARGET) - -$(INSTALLED_VENDOR_DEFAULT_PROP_TARGET): $(INSTALLED_DEFAULT_PROP_TARGET) $(POST_PROCESS_PROPS) - @echo Target buildinfo: $@ - @mkdir -p $(dir $@) - $(hide) echo "#" > $@; \ - echo "# ADDITIONAL VENDOR DEFAULT PROPERTIES" >> $@; \ - echo "#" >> $@; - $(hide) $(foreach line,$(FINAL_VENDOR_DEFAULT_PROPERTIES), \ - echo "$(line)" >> $@;) - $(hide) $(POST_PROCESS_PROPS) $@ - -endif # property_overrides_split_enabled - -# ----------------------------------------------------------------- -# build.prop -INSTALLED_BUILD_PROP_TARGET := $(TARGET_OUT)/build.prop -ALL_DEFAULT_INSTALLED_MODULES += $(INSTALLED_BUILD_PROP_TARGET) FINAL_BUILD_PROPERTIES := \ $(call collapse-pairs, $(ADDITIONAL_BUILD_PROPERTIES)) FINAL_BUILD_PROPERTIES := $(call uniq-pairs-by-first-component, \ @@ -430,6 +380,12 @@ $(intermediate_system_build_prop): $(BUILDINFO_SH) $(BUILDINFO_COMMON_SH) $(INTE @echo Target buildinfo: $@ @mkdir -p $(dir $@) $(hide) rm -f $@ && touch $@ + $(hide) $(foreach line,$(FINAL_DEFAULT_PROPERTIES), \ + echo "$(line)" >> $@;) +ifndef property_overrides_split_enabled + $(hide) $(foreach line,$(FINAL_VENDOR_DEFAULT_PROPERTIES), \ + echo "$(line)" >> $@;) +endif ifneq ($(PRODUCT_OEM_PROPERTIES),) $(hide) echo "#" >> $@; \ echo "# PRODUCT_OEM_PROPERTIES" >> $@; \ @@ -529,6 +485,10 @@ $(INSTALLED_VENDOR_BUILD_PROP_TARGET): $(BUILDINFO_COMMON_SH) $(POST_PROCESS_PRO @echo Target vendor buildinfo: $@ @mkdir -p $(dir $@) $(hide) rm -f $@ && touch $@ +ifdef property_overrides_split_enabled + $(hide) $(foreach line,$(FINAL_VENDOR_DEFAULT_PROPERTIES), \ + echo "$(line)" >> $@;) +endif ifeq ($(PRODUCT_USE_DYNAMIC_PARTITIONS),true) $(hide) echo ro.boot.dynamic_partitions=true >> $@ endif @@ -1304,7 +1264,7 @@ $(INSTALLED_VENDOR_BOOTIMAGE_TARGET): $(MKBOOTIMG) $(INTERNAL_VENDOR_RAMDISK_TAR ifeq ($(BOARD_AVB_ENABLE),true) $(INSTALLED_VENDOR_BOOTIMAGE_TARGET): $(AVBTOOL) $(BOARD_AVB_VENDOR_BOOTIMAGE_KEY_PATH) $(call pretty,"Target vendor_boot image: $@") - $(MKBOOTIMG) $(INTERNAL_VENDOR_BOOTIMAGE_ARGS) $(INTERNAL_MKBOOTIMG_VERSION_ARGS) $(BOARD_MKBOOTIMG_ARGS) --vendor_ramdisk $(INTERNAL_VENDOR_RAMDISK_TARGET) --vendor_boot $@ + $(MKBOOTIMG) $(INTERNAL_VENDOR_BOOTIMAGE_ARGS) $(BOARD_MKBOOTIMG_ARGS) --vendor_ramdisk $(INTERNAL_VENDOR_RAMDISK_TARGET) --vendor_boot $@ $(call assert-max-image-size,$@,$(BOARD_VENDOR_BOOTIMAGE_PARTITION_SIZE)) $(AVBTOOL) add_hash_footer \ --image $@ \ @@ -1314,7 +1274,7 @@ $(INSTALLED_VENDOR_BOOTIMAGE_TARGET): $(AVBTOOL) $(BOARD_AVB_VENDOR_BOOTIMAGE_KE else $(INSTALLED_VENDOR_BOOTIMAGE_TARGET): $(call pretty,"Target vendor_boot image: $@") - $(MKBOOTIMG) $(INTERNAL_VENDOR_BOOTIMAGE_ARGS) $(INTERNAL_MKBOOTIMG_VERSION_ARGS) $(BOARD_MKBOOTIMG_ARGS) --vendor_ramdisk $(INTERNAL_VENDOR_RAMDISK_TARGET) --vendor_boot $@ + $(MKBOOTIMG) $(INTERNAL_VENDOR_BOOTIMAGE_ARGS) $(BOARD_MKBOOTIMG_ARGS) --vendor_ramdisk $(INTERNAL_VENDOR_RAMDISK_TARGET) --vendor_boot $@ $(call assert-max-image-size,$@,$(BOARD_VENDOR_BOOTIMAGE_PARTITION_SIZE)) endif endif # BUILDING_VENDOR_BOOT_IMAGE @@ -2113,8 +2073,6 @@ $(foreach prop,$(1), \ endef $(INSTALLED_RECOVERY_BUILD_PROP_TARGET): \ - $(INSTALLED_DEFAULT_PROP_TARGET) \ - $(INSTALLED_VENDOR_DEFAULT_PROP_TARGET) \ $(intermediate_system_build_prop) \ $(INSTALLED_VENDOR_BUILD_PROP_TARGET) \ $(INSTALLED_ODM_BUILD_PROP_TARGET) \ @@ -2123,8 +2081,6 @@ $(INSTALLED_RECOVERY_BUILD_PROP_TARGET): \ @echo "Target recovery buildinfo: $@" $(hide) mkdir -p $(dir $@) $(hide) rm -f $@ - $(hide) cat $(INSTALLED_DEFAULT_PROP_TARGET) > $@ - $(hide) cat $(INSTALLED_VENDOR_DEFAULT_PROP_TARGET) >> $@ $(hide) cat $(intermediate_system_build_prop) >> $@ $(hide) cat $(INSTALLED_VENDOR_BUILD_PROP_TARGET) >> $@ $(hide) cat $(INSTALLED_ODM_BUILD_PROP_TARGET) >> $@ @@ -2512,7 +2468,7 @@ endif # Depends on vendor_boot.img and vendor-ramdisk-debug.cpio.gz to build the new vendor_boot-debug.img $(INSTALLED_VENDOR_DEBUG_BOOTIMAGE_TARGET): $(MKBOOTIMG) $(INSTALLED_VENDOR_BOOTIMAGE_TARGET) $(INSTALLED_VENDOR_DEBUG_RAMDISK_TARGET) $(call pretty,"Target vendor_boot debug image: $@") - $(MKBOOTIMG) $(INTERNAL_VENDOR_BOOTIMAGE_ARGS) $(INTERNAL_MKBOOTIMG_VERSION_ARGS) $(BOARD_MKBOOTIMG_ARGS) --vendor_ramdisk $(INSTALLED_VENDOR_DEBUG_RAMDISK_TARGET) --vendor_boot $@ + $(MKBOOTIMG) $(INTERNAL_VENDOR_BOOTIMAGE_ARGS) $(BOARD_MKBOOTIMG_ARGS) --vendor_ramdisk $(INSTALLED_VENDOR_DEBUG_RAMDISK_TARGET) --vendor_boot $@ $(call assert-max-image-size,$@,$(BOARD_VENDOR_BOOTIMAGE_PARTITION_SIZE)) $(if $(BOARD_AVB_VENDOR_BOOT_KEY_PATH),$(call test-key-sign-vendor-bootimage,$@)) @@ -3522,12 +3478,8 @@ $(if $(BOARD_AVB_$(call to-upper,$(1))_KEY_PATH),\ endef ifdef INSTALLED_BOOTIMAGE_TARGET -# multiple hashes for an image are not yet supported, fortunately this -# only arises for GKI where the boot descriptor can be left out -ifeq ($(strip $(BOARD_KERNEL_BINARIES)),) $(eval $(call check-and-set-avb-args,boot)) endif -endif ifdef INSTALLED_VENDOR_BOOTIMAGE_TARGET $(eval $(call check-and-set-avb-args,vendor_boot)) diff --git a/core/clear_vars.mk b/core/clear_vars.mk index 5af057036d..c88a1cd950 100644 --- a/core/clear_vars.mk +++ b/core/clear_vars.mk @@ -21,6 +21,7 @@ LOCAL_ANNOTATION_PROCESSOR_CLASSES:= LOCAL_APIDIFF_NEWAPI:= LOCAL_APIDIFF_OLDAPI:= LOCAL_APK_LIBRARIES:= +LOCAL_APK_SET_MASTER_FILE:= LOCAL_ARM_MODE:= LOCAL_ASFLAGS:= LOCAL_ASSET_DIR:= diff --git a/core/install_jni_libs_internal.mk b/core/install_jni_libs_internal.mk index 93a76662b7..30bcc2c67b 100644 --- a/core/install_jni_libs_internal.mk +++ b/core/install_jni_libs_internal.mk @@ -124,17 +124,15 @@ ifneq ($(strip $(LOCAL_JNI_SHARED_LIBRARIES)),) my_allowed_types := $(my_allowed_ndk_types) native:platform native:product native:vendor native:vndk native:vndk_private native:platform_vndk endif - ifneq (,$(LOCAL_SDK_VERSION)) - ifeq ($(SOONG_ANDROID_MK),$(LOCAL_MODULE_MAKEFILE)) - # SOONG_SDK_VARIANT_MODULES isn't complete yet while parsing Soong modules, and Soong has - # already ensured that apps link against the correct SDK variants, rewrite all JNI libraries - # to the SDK variant. - my_link_deps := $(addprefix SHARED_LIBRARIES:,$(addsuffix .sdk,$(LOCAL_JNI_SHARED_LIBRARIES))) - else + ifeq ($(SOONG_ANDROID_MK),$(LOCAL_MODULE_MAKEFILE)) + # SOONG_SDK_VARIANT_MODULES isn't complete yet while parsing Soong modules, and Soong has + # already ensured that apps link against the correct SDK variants, don't check them. + else + ifneq (,$(LOCAL_SDK_VERSION)) my_link_deps := $(addprefix SHARED_LIBRARIES:,$(call use_soong_sdk_libraries,$(LOCAL_JNI_SHARED_LIBRARIES))) + else + my_link_deps := $(addprefix SHARED_LIBRARIES:,$(LOCAL_JNI_SHARED_LIBRARIES)) endif - else - my_link_deps := $(addprefix SHARED_LIBRARIES:,$(LOCAL_JNI_SHARED_LIBRARIES)) endif my_common := diff --git a/core/instrumentation_test_config_template.xml b/core/instrumentation_test_config_template.xml index 18ea676af0..6ca964e414 100644 --- a/core/instrumentation_test_config_template.xml +++ b/core/instrumentation_test_config_template.xml @@ -17,6 +17,7 @@ <configuration description="Runs {LABEL}."> <option name="test-suite-tag" value="apct" /> <option name="test-suite-tag" value="apct-instrumentation" /> + {EXTRA_CONFIGS} <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> <option name="cleanup-apks" value="true" /> <option name="test-file-name" value="{MODULE}.apk" /> diff --git a/core/soong_android_app_set.mk b/core/soong_android_app_set.mk new file mode 100644 index 0000000000..e84371ca61 --- /dev/null +++ b/core/soong_android_app_set.mk @@ -0,0 +1,32 @@ +# App prebuilt coming from Soong. +# Extra inputs: +# LOCAL_APK_SET_MASTER_FILE + +ifneq ($(LOCAL_MODULE_MAKEFILE),$(SOONG_ANDROID_MK)) + $(call pretty-error,soong_apk_set.mk may only be used from Soong) +endif + +LOCAL_BUILT_MODULE_STEM := $(LOCAL_APK_SET_MASTER_FILE) +LOCAL_INSTALLED_MODULE_STEM := $(LOCAL_APK_SET_MASTER_FILE) + +####################################### +include $(BUILD_SYSTEM)/base_rules.mk +####################################### + +## Extract master APK from APK set into given directory +# $(1) APK set +# $(2) master APK entry (e.g., splits/base-master.apk + +define extract-master-from-apk-set +$(LOCAL_BUILT_MODULE): $(1) + @echo "Extracting $$@" + unzip -pq $$< $(2) >$$@ +endef + +$(eval $(call extract-master-from-apk-set,$(LOCAL_PREBUILT_MODULE_FILE),$(LOCAL_APK_SET_MASTER_FILE))) +LOCAL_POST_INSTALL_CMD := unzip -qo -j -d $(dir $(LOCAL_INSTALLED_MODULE)) \ + $(LOCAL_PREBUILT_MODULE_FILE) -x $(LOCAL_APK_SET_MASTER_FILE) +$(LOCAL_INSTALLED_MODULE): PRIVATE_POST_INSTALL_CMD := $(LOCAL_POST_INSTALL_CMD) +PACKAGES.$(LOCAL_MODULE).OVERRIDES := $(strip $(LOCAL_OVERRIDES_PACKAGES)) + +SOONG_ALREADY_CONV := $(SOONG_ALREADY_CONV) $(LOCAL_MODULE) diff --git a/target/board/BoardConfigGsiCommon.mk b/target/board/BoardConfigGsiCommon.mk index c89e203edc..49f6edcde4 100644 --- a/target/board/BoardConfigGsiCommon.mk +++ b/target/board/BoardConfigGsiCommon.mk @@ -40,6 +40,12 @@ BOARD_AVB_SYSTEM_ALGORITHM := SHA256_RSA2048 BOARD_AVB_SYSTEM_ROLLBACK_INDEX := $(PLATFORM_SECURITY_PATCH_TIMESTAMP) BOARD_AVB_SYSTEM_ROLLBACK_INDEX_LOCATION := 1 +# Enable chain partition for boot, mainly for GKI images. +BOARD_AVB_BOOT_KEY_PATH := external/avb/test/data/testkey_rsa2048.pem +BOARD_AVB_BOOT_ALGORITHM := SHA256_RSA2048 +BOARD_AVB_BOOT_ROLLBACK_INDEX := $(PLATFORM_SECURITY_PATCH_TIMESTAMP) +BOARD_AVB_BOOT_ROLLBACK_INDEX_LOCATION := 2 + # GSI specific System Properties ifneq (,$(filter userdebug eng,$(TARGET_BUILD_VARIANT))) TARGET_SYSTEM_EXT_PROP := build/make/target/board/gsi_system_ext.prop diff --git a/target/product/gsi/current.txt b/target/product/gsi/current.txt index 1bfdf0ace9..cd4d750b6d 100644 --- a/target/product/gsi/current.txt +++ b/target/product/gsi/current.txt @@ -133,7 +133,7 @@ VNDK-core: android.hardware.identity-V2-ndk_platform.so VNDK-core: android.hardware.input.classifier@1.0.so VNDK-core: android.hardware.input.common@1.0.so VNDK-core: android.hardware.ir@1.0.so -VNDK-core: android.hardware.keymaster-V1-ndk_platform.so +VNDK-core: android.hardware.keymaster-V2-ndk_platform.so VNDK-core: android.hardware.keymaster@3.0.so VNDK-core: android.hardware.keymaster@4.0.so VNDK-core: android.hardware.keymaster@4.1.so diff --git a/tools/auto_gen_test_config.py b/tools/auto_gen_test_config.py index c7c5bdc611..943f23815b 100755 --- a/tools/auto_gen_test_config.py +++ b/tools/auto_gen_test_config.py @@ -27,6 +27,7 @@ ATTRIBUTE_RUNNER = 'android:name' ATTRIBUTE_PACKAGE = 'package' PLACEHOLDER_LABEL = '{LABEL}' +PLACEHOLDER_EXTRA_CONFIGS = '{EXTRA_CONFIGS}' PLACEHOLDER_MODULE = '{MODULE}' PLACEHOLDER_PACKAGE = '{PACKAGE}' PLACEHOLDER_RUNNER = '{RUNNER}' @@ -41,16 +42,20 @@ def main(argv): Returns: 0 if no error, otherwise 1. """ - if len(argv) != 4: + if len(argv) != 4 and len(argv) != 6: sys.stderr.write( - 'Invalid arguements. The script requires 4 arguments for file paths: ' + 'Invalid arguments. The script requires 4 arguments for file paths: ' 'target_config android_manifest empty_config ' - 'instrumentation_test_config_template.\n') + 'instrumentation_test_config_template ' + 'and 2 optional arguments for extra configs: ' + '--extra-configs \'EXTRA_CONFIGS\'.\n') return 1 + target_config = argv[0] android_manifest = argv[1] empty_config = argv[2] instrumentation_test_config_template = argv[3] + extra_configs = '\n'.join(argv[5].split('\\n')) if len(argv) == 6 else '' manifest = parse(android_manifest) instrumentation_elements = manifest.getElementsByTagName('instrumentation') @@ -80,6 +85,7 @@ def main(argv): config = config.replace(PLACEHOLDER_MODULE, module) config = config.replace(PLACEHOLDER_PACKAGE, package) config = config.replace(PLACEHOLDER_TEST_TYPE, test_type) + config = config.replace(PLACEHOLDER_EXTRA_CONFIGS, extra_configs) config = config.replace(PLACEHOLDER_RUNNER, runner) with open(target_config, 'w') as config_file: config_file.write(config) diff --git a/tools/releasetools/add_img_to_target_files.py b/tools/releasetools/add_img_to_target_files.py index 7db506c72f..70ea967b3e 100755 --- a/tools/releasetools/add_img_to_target_files.py +++ b/tools/releasetools/add_img_to_target_files.py @@ -548,17 +548,19 @@ def AddCareMapForAbOta(output_zip, ab_partitions, image_paths): care_map_list += care_map # adds fingerprint field to the care_map - build_props = OPTIONS.info_dict.get(partition + ".build.prop", {}) + # TODO(xunchang) revisit the fingerprint calculation for care_map. + partition_props = OPTIONS.info_dict.get(partition + ".build.prop") prop_name_list = ["ro.{}.build.fingerprint".format(partition), "ro.{}.build.thumbprint".format(partition)] - present_props = [x for x in prop_name_list if x in build_props] + present_props = [x for x in prop_name_list if + partition_props and partition_props.GetProp(x)] if not present_props: logger.warning("fingerprint is not present for partition %s", partition) property_id, fingerprint = "unknown", "unknown" else: property_id = present_props[0] - fingerprint = build_props[property_id] + fingerprint = partition_props.GetProp(property_id) care_map_list += [property_id, fingerprint] if not care_map_list: diff --git a/tools/releasetools/build_image.py b/tools/releasetools/build_image.py index f30f7876ab..7567346493 100755 --- a/tools/releasetools/build_image.py +++ b/tools/releasetools/build_image.py @@ -509,9 +509,9 @@ def ImagePropFromGlobalDict(glob_dict, mount_point): d = {} if "build.prop" in glob_dict: - bp = glob_dict["build.prop"] - if "ro.build.date.utc" in bp: - d["timestamp"] = bp["ro.build.date.utc"] + timestamp = glob_dict["build.prop"].GetProp("ro.build.date.utc") + if timestamp: + d["timestamp"] = timestamp def copy_prop(src_p, dest_p): """Copy a property from the global dictionary. diff --git a/tools/releasetools/check_target_files_vintf.py b/tools/releasetools/check_target_files_vintf.py index b3d491fdfd..95d09cc177 100755 --- a/tools/releasetools/check_target_files_vintf.py +++ b/tools/releasetools/check_target_files_vintf.py @@ -80,8 +80,9 @@ def GetArgsForSkus(info_dict): '--property', 'ro.boot.product.vendor.sku=' + vendor_sku] for odm_sku in odm_skus for vendor_sku in vendor_skus] + def GetArgsForShippingApiLevel(info_dict): - shipping_api_level = info_dict['vendor.build.prop'].get( + shipping_api_level = info_dict['vendor.build.prop'].GetProp( 'ro.product.first_api_level') if not shipping_api_level: logger.warning('Cannot determine ro.product.first_api_level') diff --git a/tools/releasetools/common.py b/tools/releasetools/common.py index 1abf5a5cc6..42c29c01c9 100644 --- a/tools/releasetools/common.py +++ b/tools/releasetools/common.py @@ -423,6 +423,14 @@ class BuildInfo(object): def items(self): return self.info_dict.items() + def _GetRawBuildProp(self, prop, partition): + prop_file = '{}.build.prop'.format( + partition) if partition else 'build.prop' + partition_props = self.info_dict.get(prop_file) + if not partition_props: + return None + return partition_props.GetProp(prop) + def GetPartitionBuildProp(self, prop, partition): """Returns the inquired build property for the provided partition.""" # If provided a partition for this property, only look within that @@ -431,31 +439,33 @@ class BuildInfo(object): prop = prop.replace("ro.product", "ro.product.{}".format(partition)) else: prop = prop.replace("ro.", "ro.{}.".format(partition)) - try: - return self.info_dict.get("{}.build.prop".format(partition), {})[prop] - except KeyError: - raise ExternalError("couldn't find %s in %s.build.prop" % - (prop, partition)) + + prop_val = self._GetRawBuildProp(prop, partition) + if prop_val is not None: + return prop_val + raise ExternalError("couldn't find %s in %s.build.prop" % + (prop, partition)) def GetBuildProp(self, prop): """Returns the inquired build property from the standard build.prop file.""" if prop in BuildInfo._RO_PRODUCT_RESOLVE_PROPS: return self._ResolveRoProductBuildProp(prop) - try: - return self.info_dict.get("build.prop", {})[prop] - except KeyError: - raise ExternalError("couldn't find %s in build.prop" % (prop,)) + prop_val = self._GetRawBuildProp(prop, None) + if prop_val is not None: + return prop_val + + raise ExternalError("couldn't find %s in build.prop" % (prop,)) def _ResolveRoProductBuildProp(self, prop): """Resolves the inquired ro.product.* build property""" - prop_val = self.info_dict.get("build.prop", {}).get(prop) + prop_val = self._GetRawBuildProp(prop, None) if prop_val: return prop_val default_source_order = self._GetRoProductPropsDefaultSourceOrder() - source_order_val = self.info_dict.get("build.prop", {}).get( - "ro.product.property_source_order") + source_order_val = self._GetRawBuildProp( + "ro.product.property_source_order", None) if source_order_val: source_order = source_order_val.split(",") else: @@ -466,11 +476,10 @@ class BuildInfo(object): raise ExternalError( "Invalid ro.product.property_source_order '{}'".format(source_order)) - for source in source_order: + for source_partition in source_order: source_prop = prop.replace( - "ro.product", "ro.product.{}".format(source), 1) - prop_val = self.info_dict.get( - "{}.build.prop".format(source), {}).get(source_prop) + "ro.product", "ro.product.{}".format(source_partition), 1) + prop_val = self._GetRawBuildProp(source_prop, source_partition) if prop_val: return prop_val @@ -479,11 +488,9 @@ class BuildInfo(object): def _GetRoProductPropsDefaultSourceOrder(self): # NOTE: refer to CDDs and android.os.Build.VERSION for the definition and # values of these properties for each Android release. - android_codename = self.info_dict.get("build.prop", {}).get( - "ro.build.version.codename") + android_codename = self._GetRawBuildProp("ro.build.version.codename", None) if android_codename == "REL": - android_version = self.info_dict.get("build.prop", {}).get( - "ro.build.version.release") + android_version = self._GetRawBuildProp("ro.build.version.release", None) if android_version == "10": return BuildInfo._RO_PRODUCT_PROPS_DEFAULT_SOURCE_ORDER_ANDROID_10 # NOTE: float() conversion of android_version will have rounding error. @@ -566,6 +573,20 @@ class BuildInfo(object): script.AssertOemProperty(prop, values, oem_no_mount) +def ReadFromInputFile(input_file, fn): + """Reads the contents of fn from input zipfile or directory.""" + if isinstance(input_file, zipfile.ZipFile): + return input_file.read(fn).decode() + else: + path = os.path.join(input_file, *fn.split("/")) + try: + with open(path) as f: + return f.read() + except IOError as e: + if e.errno == errno.ENOENT: + raise KeyError(fn) + + def LoadInfoDict(input_file, repacking=False): """Loads the key/value pairs from the given input target_files. @@ -603,16 +624,7 @@ def LoadInfoDict(input_file, repacking=False): "input_file must be a path str when doing repacking" def read_helper(fn): - if isinstance(input_file, zipfile.ZipFile): - return input_file.read(fn).decode() - else: - path = os.path.join(input_file, *fn.split("/")) - try: - with open(path) as f: - return f.read() - except IOError as e: - if e.errno == errno.ENOENT: - raise KeyError(fn) + return ReadFromInputFile(input_file, fn) try: d = LoadDictionaryFromLines(read_helper("META/misc_info.txt").split("\n")) @@ -675,13 +687,8 @@ def LoadInfoDict(input_file, repacking=False): # system and vendor. for partition in PARTITIONS_WITH_CARE_MAP: partition_prop = "{}.build.prop".format(partition) - d[partition_prop] = LoadBuildProp( - read_helper, "{}/build.prop".format(partition.upper())) - # Some partition might use /<partition>/etc/build.prop as the new path. - # TODO: try new path first when majority of them switch to the new path. - if not d[partition_prop]: - d[partition_prop] = LoadBuildProp( - read_helper, "{}/etc/build.prop".format(partition.upper())) + d[partition_prop] = PartitionBuildProps.FromInputFile( + input_file, partition) d["build.prop"] = d["system.build.prop"] # Set up the salt (based on fingerprint) that will be used when adding AVB @@ -696,15 +703,6 @@ def LoadInfoDict(input_file, repacking=False): return d -def LoadBuildProp(read_helper, prop_file): - try: - data = read_helper(prop_file) - except KeyError: - logger.warning("Failed to read %s", prop_file) - data = "" - return LoadDictionaryFromLines(data.split("\n")) - - def LoadListFromFile(file_path): with open(file_path) as f: return f.read().splitlines() @@ -727,6 +725,121 @@ def LoadDictionaryFromLines(lines): return d +class PartitionBuildProps(object): + """The class holds the build prop of a particular partition. + + This class loads the build.prop and holds the build properties for a given + partition. It also partially recognizes the 'import' statement in the + build.prop; and calculates alternative values of some specific build + properties during runtime. + + Attributes: + input_file: a zipped target-file or an unzipped target-file directory. + partition: name of the partition. + props_allow_override: a list of build properties to search for the + alternative values during runtime. + build_props: a dict of build properties for the given partition. + prop_overrides: a set of props that are overridden by import. + placeholder_values: A dict of runtime variables' values to replace the + placeholders in the build.prop file. We expect exactly one value for + each of the variables. + """ + def __init__(self, input_file, name, placeholder_values=None): + self.input_file = input_file + self.partition = name + self.props_allow_override = [props.format(name) for props in [ + 'ro.product.{}.brand', 'ro.product.{}.name', 'ro.product.{}.device']] + self.build_props = {} + self.prop_overrides = set() + self.placeholder_values = {} + if placeholder_values: + self.placeholder_values = copy.deepcopy(placeholder_values) + + @staticmethod + def FromDictionary(name, build_props): + """Constructs an instance from a build prop dictionary.""" + + props = PartitionBuildProps("unknown", name) + props.build_props = build_props.copy() + return props + + @staticmethod + def FromInputFile(input_file, name, placeholder_values=None): + """Loads the build.prop file and builds the attributes.""" + data = '' + for prop_file in ['{}/etc/build.prop'.format(name.upper()), + '{}/build.prop'.format(name.upper())]: + try: + data = ReadFromInputFile(input_file, prop_file) + break + except KeyError: + logger.warning('Failed to read %s', prop_file) + + props = PartitionBuildProps(input_file, name, placeholder_values) + props._LoadBuildProp(data) + return props + + def _LoadBuildProp(self, data): + for line in data.split('\n'): + line = line.strip() + if not line or line.startswith("#"): + continue + if line.startswith("import"): + overrides = self._ImportParser(line) + duplicates = self.prop_overrides.intersection(overrides.keys()) + if duplicates: + raise ValueError('prop {} is overridden multiple times'.format( + ','.join(duplicates))) + self.prop_overrides = self.prop_overrides.union(overrides.keys()) + self.build_props.update(overrides) + elif "=" in line: + name, value = line.split("=", 1) + if name in self.prop_overrides: + raise ValueError('prop {} is set again after overridden by import ' + 'statement'.format(name)) + self.build_props[name] = value + + def _ImportParser(self, line): + """Parses the build prop in a given import statement.""" + + tokens = line.split() + if tokens[0] != 'import' or (len(tokens) != 2 and len(tokens) != 3) : + raise ValueError('Unrecognized import statement {}'.format(line)) + + if len(tokens) == 3: + logger.info("Import %s from %s, skip", tokens[2], tokens[1]) + return {} + + import_path = tokens[1] + if not re.match(r'^/{}/.*\.prop$'.format(self.partition), import_path): + raise ValueError('Unrecognized import path {}'.format(line)) + + # We only recognize a subset of import statement that the init process + # supports. And we can loose the restriction based on how the dynamic + # fingerprint is used in practice. The placeholder format should be + # ${placeholder}, and its value should be provided by the caller through + # the placeholder_values. + for prop, value in self.placeholder_values.items(): + prop_place_holder = '${{{}}}'.format(prop) + if prop_place_holder in import_path: + import_path = import_path.replace(prop_place_holder, value) + if '$' in import_path: + logger.info('Unresolved place holder in import path %s', import_path) + return {} + + import_path = import_path.replace('/{}'.format(self.partition), + self.partition.upper()) + logger.info('Parsing build props override from %s', import_path) + + lines = ReadFromInputFile(self.input_file, import_path).split('\n') + d = LoadDictionaryFromLines(lines) + return {key: val for key, val in d.items() + if key in self.props_allow_override} + + def GetProp(self, prop): + return self.build_props.get(prop) + + def LoadRecoveryFSTab(read_helper, fstab_version, recovery_fstab_path, system_root_image=False): class Partition(object): diff --git a/tools/releasetools/ota_from_target_files.py b/tools/releasetools/ota_from_target_files.py index 92a46a20cd..ad001d13c7 100755 --- a/tools/releasetools/ota_from_target_files.py +++ b/tools/releasetools/ota_from_target_files.py @@ -193,6 +193,8 @@ A/B OTA specific options from __future__ import print_function import collections +import copy +import itertools import logging import multiprocessing import os.path @@ -229,6 +231,7 @@ OPTIONS.include_secondary = False OPTIONS.no_signing = False OPTIONS.block_based = True OPTIONS.updater_binary = None +OPTIONS.oem_dicts = None OPTIONS.oem_source = None OPTIONS.oem_no_mount = False OPTIONS.full_radio = False @@ -247,6 +250,7 @@ OPTIONS.retrofit_dynamic_partitions = False OPTIONS.skip_compatibility_check = False OPTIONS.output_metadata_path = None OPTIONS.disable_fec_computation = False +OPTIONS.boot_variable_values = None METADATA_NAME = 'META-INF/com/android/metadata' @@ -263,7 +267,8 @@ RETROFIT_DAP_UNZIP_PATTERN = ['OTA/super_*.img', AB_PARTITIONS] # 'system_other' and bootloader partitions. SECONDARY_PAYLOAD_SKIPPED_IMAGES = [ 'boot', 'dtbo', 'modem', 'odm', 'product', 'radio', 'recovery', - 'system_ext', 'vbmeta', 'vbmeta_system', 'vbmeta_vendor', 'vendor'] + 'system_ext', 'vbmeta', 'vbmeta_system', 'vbmeta_vendor', 'vendor', + 'vendor_boot'] class PayloadSigner(object): @@ -1959,6 +1964,36 @@ def GenerateNonAbOtaPackage(target_file, output_file, source_file=None): output_file) +def CalculateRuntimeFingerprints(): + """Returns a set of runtime fingerprints based on the boot variables.""" + + build_info = common.BuildInfo(OPTIONS.info_dict, OPTIONS.oem_dicts) + fingerprints = {build_info.fingerprint} + + if not OPTIONS.boot_variable_values: + return fingerprints + + # Calculate all possible combinations of the values for the boot variables. + keys = OPTIONS.boot_variable_values.keys() + value_list = OPTIONS.boot_variable_values.values() + combinations = [dict(zip(keys, values)) + for values in itertools.product(*value_list)] + for placeholder_values in combinations: + # Reload the info_dict as some build properties may change their values + # based on the value of ro.boot* properties. + info_dict = copy.deepcopy(OPTIONS.info_dict) + for partition in common.PARTITIONS_WITH_CARE_MAP: + partition_prop_key = "{}.build.prop".format(partition) + old_props = info_dict[partition_prop_key] + info_dict[partition_prop_key] = common.PartitionBuildProps.FromInputFile( + old_props.input_file, partition, placeholder_values) + info_dict["build.prop"] = info_dict["system.build.prop"] + + build_info = common.BuildInfo(info_dict, OPTIONS.oem_dicts) + fingerprints.add(build_info.fingerprint) + return fingerprints + + def main(argv): def option_handler(o, a): diff --git a/tools/releasetools/test_add_img_to_target_files.py b/tools/releasetools/test_add_img_to_target_files.py index 3d0766ffda..c82a40bee4 100644 --- a/tools/releasetools/test_add_img_to_target_files.py +++ b/tools/releasetools/test_add_img_to_target_files.py @@ -128,13 +128,16 @@ class AddImagesToTargetFilesTest(test_utils.ReleaseToolsTestCase): 'vendor_image_size' : 40960, 'system_verity_block_device': '/dev/block/system', 'vendor_verity_block_device': '/dev/block/vendor', - 'system.build.prop': { - 'ro.system.build.fingerprint': - 'google/sailfish/12345:user/dev-keys', - }, - 'vendor.build.prop': { - 'ro.vendor.build.fingerprint': 'google/sailfish/678:user/dev-keys', - }, + 'system.build.prop': common.PartitionBuildProps.FromDictionary( + 'system', { + 'ro.system.build.fingerprint': + 'google/sailfish/12345:user/dev-keys'} + ), + 'vendor.build.prop': common.PartitionBuildProps.FromDictionary( + 'vendor', { + 'ro.vendor.build.fingerprint': + 'google/sailfish/678:user/dev-keys'} + ), } # Prepare the META/ folder. @@ -206,18 +209,21 @@ class AddImagesToTargetFilesTest(test_utils.ReleaseToolsTestCase): """Tests the case for device using AVB.""" image_paths = self._test_AddCareMapForAbOta() OPTIONS.info_dict = { - 'extfs_sparse_flag' : '-s', - 'system_image_size' : 65536, - 'vendor_image_size' : 40960, - 'avb_system_hashtree_enable' : 'true', - 'avb_vendor_hashtree_enable' : 'true', - 'system.build.prop': { - 'ro.system.build.fingerprint': - 'google/sailfish/12345:user/dev-keys', - }, - 'vendor.build.prop': { - 'ro.vendor.build.fingerprint': 'google/sailfish/678:user/dev-keys', - } + 'extfs_sparse_flag': '-s', + 'system_image_size': 65536, + 'vendor_image_size': 40960, + 'avb_system_hashtree_enable': 'true', + 'avb_vendor_hashtree_enable': 'true', + 'system.build.prop': common.PartitionBuildProps.FromDictionary( + 'system', { + 'ro.system.build.fingerprint': + 'google/sailfish/12345:user/dev-keys'} + ), + 'vendor.build.prop': common.PartitionBuildProps.FromDictionary( + 'vendor', { + 'ro.vendor.build.fingerprint': + 'google/sailfish/678:user/dev-keys'} + ), } AddCareMapForAbOta(None, ['system', 'vendor'], image_paths) @@ -258,17 +264,21 @@ class AddImagesToTargetFilesTest(test_utils.ReleaseToolsTestCase): """Tests the case for partitions with thumbprint.""" image_paths = self._test_AddCareMapForAbOta() OPTIONS.info_dict = { - 'extfs_sparse_flag' : '-s', - 'system_image_size' : 65536, - 'vendor_image_size' : 40960, + 'extfs_sparse_flag': '-s', + 'system_image_size': 65536, + 'vendor_image_size': 40960, 'system_verity_block_device': '/dev/block/system', 'vendor_verity_block_device': '/dev/block/vendor', - 'system.build.prop': { - 'ro.system.build.thumbprint': 'google/sailfish/123:user/dev-keys', - }, - 'vendor.build.prop' : { - 'ro.vendor.build.thumbprint': 'google/sailfish/456:user/dev-keys', - }, + 'system.build.prop': common.PartitionBuildProps.FromDictionary( + 'system', { + 'ro.system.build.thumbprint': + 'google/sailfish/123:user/dev-keys'} + ), + 'vendor.build.prop': common.PartitionBuildProps.FromDictionary( + 'vendor', { + 'ro.vendor.build.thumbprint': + 'google/sailfish/456:user/dev-keys'} + ), } AddCareMapForAbOta(None, ['system', 'vendor'], image_paths) diff --git a/tools/releasetools/test_common.py b/tools/releasetools/test_common.py index 665eb513d6..787e6757ec 100644 --- a/tools/releasetools/test_common.py +++ b/tools/releasetools/test_common.py @@ -48,109 +48,124 @@ def get_2gb_string(): class BuildInfoTest(test_utils.ReleaseToolsTestCase): TEST_INFO_DICT = { - 'build.prop' : { - 'ro.product.device' : 'product-device', - 'ro.product.name' : 'product-name', - 'ro.build.fingerprint' : 'build-fingerprint', - 'ro.build.foo' : 'build-foo', - }, - 'system.build.prop' : { - 'ro.product.system.brand' : 'product-brand', - 'ro.product.system.name' : 'product-name', - 'ro.product.system.device' : 'product-device', - 'ro.system.build.version.release' : 'version-release', - 'ro.system.build.id' : 'build-id', - 'ro.system.build.version.incremental' : 'version-incremental', - 'ro.system.build.type' : 'build-type', - 'ro.system.build.tags' : 'build-tags', - 'ro.system.build.foo' : 'build-foo', - }, - 'vendor.build.prop' : { - 'ro.product.vendor.brand' : 'vendor-product-brand', - 'ro.product.vendor.name' : 'vendor-product-name', - 'ro.product.vendor.device' : 'vendor-product-device', - 'ro.vendor.build.version.release' : 'vendor-version-release', - 'ro.vendor.build.id' : 'vendor-build-id', - 'ro.vendor.build.version.incremental' : 'vendor-version-incremental', - 'ro.vendor.build.type' : 'vendor-build-type', - 'ro.vendor.build.tags' : 'vendor-build-tags', - }, - 'property1' : 'value1', - 'property2' : 4096, + 'build.prop': common.PartitionBuildProps.FromDictionary( + 'system', { + 'ro.product.device': 'product-device', + 'ro.product.name': 'product-name', + 'ro.build.fingerprint': 'build-fingerprint', + 'ro.build.foo': 'build-foo'} + ), + 'system.build.prop': common.PartitionBuildProps.FromDictionary( + 'system', { + 'ro.product.system.brand': 'product-brand', + 'ro.product.system.name': 'product-name', + 'ro.product.system.device': 'product-device', + 'ro.system.build.version.release': 'version-release', + 'ro.system.build.id': 'build-id', + 'ro.system.build.version.incremental': 'version-incremental', + 'ro.system.build.type': 'build-type', + 'ro.system.build.tags': 'build-tags', + 'ro.system.build.foo': 'build-foo'} + ), + 'vendor.build.prop': common.PartitionBuildProps.FromDictionary( + 'vendor', { + 'ro.product.vendor.brand': 'vendor-product-brand', + 'ro.product.vendor.name': 'vendor-product-name', + 'ro.product.vendor.device': 'vendor-product-device', + 'ro.vendor.build.version.release': 'vendor-version-release', + 'ro.vendor.build.id': 'vendor-build-id', + 'ro.vendor.build.version.incremental': + 'vendor-version-incremental', + 'ro.vendor.build.type': 'vendor-build-type', + 'ro.vendor.build.tags': 'vendor-build-tags'} + ), + 'property1': 'value1', + 'property2': 4096, } TEST_INFO_DICT_USES_OEM_PROPS = { - 'build.prop' : { - 'ro.product.name' : 'product-name', - 'ro.build.thumbprint' : 'build-thumbprint', - 'ro.build.bar' : 'build-bar', - }, - 'vendor.build.prop' : { - 'ro.vendor.build.fingerprint' : 'vendor-build-fingerprint', - }, - 'property1' : 'value1', - 'property2' : 4096, - 'oem_fingerprint_properties' : 'ro.product.device ro.product.brand', + 'build.prop': common.PartitionBuildProps.FromDictionary( + 'system', { + 'ro.product.name': 'product-name', + 'ro.build.thumbprint': 'build-thumbprint', + 'ro.build.bar': 'build-bar'} + ), + 'vendor.build.prop': common.PartitionBuildProps.FromDictionary( + 'vendor', { + 'ro.vendor.build.fingerprint': 'vendor-build-fingerprint'} + ), + 'property1': 'value1', + 'property2': 4096, + 'oem_fingerprint_properties': 'ro.product.device ro.product.brand', } TEST_OEM_DICTS = [ { - 'ro.product.brand' : 'brand1', - 'ro.product.device' : 'device1', + 'ro.product.brand': 'brand1', + 'ro.product.device': 'device1', }, { - 'ro.product.brand' : 'brand2', - 'ro.product.device' : 'device2', + 'ro.product.brand': 'brand2', + 'ro.product.device': 'device2', }, { - 'ro.product.brand' : 'brand3', - 'ro.product.device' : 'device3', + 'ro.product.brand': 'brand3', + 'ro.product.device': 'device3', }, ] TEST_INFO_DICT_PROPERTY_SOURCE_ORDER = { - 'build.prop' : { - 'ro.build.fingerprint' : 'build-fingerprint', - 'ro.product.property_source_order' : - 'product,odm,vendor,system_ext,system', - }, - 'system.build.prop' : { - 'ro.product.system.device' : 'system-product-device', - }, - 'vendor.build.prop' : { - 'ro.product.vendor.device' : 'vendor-product-device', - }, + 'build.prop': common.PartitionBuildProps.FromDictionary( + 'system', { + 'ro.build.fingerprint': 'build-fingerprint', + 'ro.product.property_source_order': + 'product,odm,vendor,system_ext,system'} + ), + 'system.build.prop': common.PartitionBuildProps.FromDictionary( + 'system', { + 'ro.product.system.device': 'system-product-device'} + ), + 'vendor.build.prop': common.PartitionBuildProps.FromDictionary( + 'vendor', { + 'ro.product.vendor.device': 'vendor-product-device'} + ), } TEST_INFO_DICT_PROPERTY_SOURCE_ORDER_ANDROID_10 = { - 'build.prop' : { - 'ro.build.fingerprint' : 'build-fingerprint', - 'ro.product.property_source_order' : - 'product,product_services,odm,vendor,system', - 'ro.build.version.release' : '10', - 'ro.build.version.codename' : 'REL', - }, - 'system.build.prop' : { - 'ro.product.system.device' : 'system-product-device', - }, - 'vendor.build.prop' : { - 'ro.product.vendor.device' : 'vendor-product-device', - }, + 'build.prop': common.PartitionBuildProps.FromDictionary( + 'system', { + 'ro.build.fingerprint': 'build-fingerprint', + 'ro.product.property_source_order': + 'product,product_services,odm,vendor,system', + 'ro.build.version.release': '10', + 'ro.build.version.codename': 'REL'} + ), + 'system.build.prop': common.PartitionBuildProps.FromDictionary( + 'system', { + 'ro.product.system.device': 'system-product-device'} + ), + 'vendor.build.prop': common.PartitionBuildProps.FromDictionary( + 'vendor', { + 'ro.product.vendor.device': 'vendor-product-device'} + ), } TEST_INFO_DICT_PROPERTY_SOURCE_ORDER_ANDROID_9 = { - 'build.prop' : { - 'ro.product.device' : 'product-device', - 'ro.build.fingerprint' : 'build-fingerprint', - 'ro.build.version.release' : '9', - 'ro.build.version.codename' : 'REL', - }, - 'system.build.prop' : { - 'ro.product.system.device' : 'system-product-device', - }, - 'vendor.build.prop' : { - 'ro.product.vendor.device' : 'vendor-product-device', - }, + 'build.prop': common.PartitionBuildProps.FromDictionary( + 'system', { + 'ro.product.device': 'product-device', + 'ro.build.fingerprint': 'build-fingerprint', + 'ro.build.version.release': '9', + 'ro.build.version.codename': 'REL'} + ), + 'system.build.prop': common.PartitionBuildProps.FromDictionary( + 'system', { + 'ro.product.system.device': 'system-product-device'} + ), + 'vendor.build.prop': common.PartitionBuildProps.FromDictionary( + 'vendor', { + 'ro.product.vendor.device': 'vendor-product-device'} + ), } def test_init(self): @@ -178,25 +193,27 @@ class BuildInfoTest(test_utils.ReleaseToolsTestCase): def test_init_badFingerprint(self): info_dict = copy.deepcopy(self.TEST_INFO_DICT) - info_dict['build.prop']['ro.build.fingerprint'] = 'bad fingerprint' + info_dict['build.prop'].build_props[ + 'ro.build.fingerprint'] = 'bad fingerprint' self.assertRaises(ValueError, common.BuildInfo, info_dict, None) - info_dict['build.prop']['ro.build.fingerprint'] = 'bad\x80fingerprint' + info_dict['build.prop'].build_props[ + 'ro.build.fingerprint'] = 'bad\x80fingerprint' self.assertRaises(ValueError, common.BuildInfo, info_dict, None) def test___getitem__(self): target_info = common.BuildInfo(self.TEST_INFO_DICT, None) self.assertEqual('value1', target_info['property1']) self.assertEqual(4096, target_info['property2']) - self.assertEqual('build-foo', target_info['build.prop']['ro.build.foo']) + self.assertEqual('build-foo', + target_info['build.prop'].GetProp('ro.build.foo')) def test___getitem__with_oem_props(self): target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS, self.TEST_OEM_DICTS) self.assertEqual('value1', target_info['property1']) self.assertEqual(4096, target_info['property2']) - self.assertRaises(KeyError, - lambda: target_info['build.prop']['ro.build.foo']) + self.assertIsNone(target_info['build.prop'].GetProp('ro.build.foo')) def test___setitem__(self): target_info = common.BuildInfo(copy.deepcopy(self.TEST_INFO_DICT), None) @@ -204,9 +221,11 @@ class BuildInfoTest(test_utils.ReleaseToolsTestCase): target_info['property1'] = 'value2' self.assertEqual('value2', target_info['property1']) - self.assertEqual('build-foo', target_info['build.prop']['ro.build.foo']) - target_info['build.prop']['ro.build.foo'] = 'build-bar' - self.assertEqual('build-bar', target_info['build.prop']['ro.build.foo']) + self.assertEqual('build-foo', + target_info['build.prop'].GetProp('ro.build.foo')) + target_info['build.prop'].build_props['ro.build.foo'] = 'build-bar' + self.assertEqual('build-bar', + target_info['build.prop'].GetProp('ro.build.foo')) def test_get(self): target_info = common.BuildInfo(self.TEST_INFO_DICT, None) @@ -214,7 +233,8 @@ class BuildInfoTest(test_utils.ReleaseToolsTestCase): self.assertEqual(4096, target_info.get('property2')) self.assertEqual(4096, target_info.get('property2', 1024)) self.assertEqual(1024, target_info.get('property-nonexistent', 1024)) - self.assertEqual('build-foo', target_info.get('build.prop')['ro.build.foo']) + self.assertEqual('build-foo', + target_info.get('build.prop').GetProp('ro.build.foo')) def test_get_with_oem_props(self): target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS, @@ -223,9 +243,7 @@ class BuildInfoTest(test_utils.ReleaseToolsTestCase): self.assertEqual(4096, target_info.get('property2')) self.assertEqual(4096, target_info.get('property2', 1024)) self.assertEqual(1024, target_info.get('property-nonexistent', 1024)) - self.assertIsNone(target_info.get('build.prop').get('ro.build.foo')) - self.assertRaises(KeyError, - lambda: target_info.get('build.prop')['ro.build.foo']) + self.assertIsNone(target_info.get('build.prop').GetProp('ro.build.foo')) def test_items(self): target_info = common.BuildInfo(self.TEST_INFO_DICT, None) @@ -262,7 +280,8 @@ class BuildInfoTest(test_utils.ReleaseToolsTestCase): def test_GetPartitionFingerprint_uses_fingerprint_prop_if_available(self): info_dict = copy.deepcopy(self.TEST_INFO_DICT) - info_dict['vendor.build.prop']['ro.vendor.build.fingerprint'] = 'vendor:fingerprint' + info_dict['vendor.build.prop'].build_props[ + 'ro.vendor.build.fingerprint'] = 'vendor:fingerprint' target_info = common.BuildInfo(info_dict, None) self.assertEqual( target_info.GetPartitionFingerprint('vendor'), @@ -303,14 +322,15 @@ class BuildInfoTest(test_utils.ReleaseToolsTestCase): def test_ResolveRoProductProperty_FromSystem(self): info_dict = copy.deepcopy(self.TEST_INFO_DICT_PROPERTY_SOURCE_ORDER) - del info_dict['vendor.build.prop']['ro.product.vendor.device'] + del info_dict['vendor.build.prop'].build_props['ro.product.vendor.device'] info = common.BuildInfo(info_dict, None) self.assertEqual('system-product-device', info.GetBuildProp('ro.product.device')) def test_ResolveRoProductProperty_InvalidPropertySearchOrder(self): info_dict = copy.deepcopy(self.TEST_INFO_DICT_PROPERTY_SOURCE_ORDER) - info_dict['build.prop']['ro.product.property_source_order'] = 'bad-source' + info_dict['build.prop'].build_props[ + 'ro.product.property_source_order'] = 'bad-source' with self.assertRaisesRegexp(common.ExternalError, 'Invalid ro.product.property_source_order'): info = common.BuildInfo(info_dict, None) @@ -1513,12 +1533,14 @@ class CommonUtilsTest(test_utils.ReleaseToolsTestCase): common.OPTIONS.info_dict = { 'ab_update': 'true', 'avb_avbtool': 'avbtool', - 'build.prop': { - 'ro.build.version.incremental': '6285659', - 'ro.product.device': 'coral', - 'ro.build.fingerprint': 'google/coral/coral:R/RP1A.200311.002/' - '6285659:userdebug/dev-keys' - } + 'build.prop': common.PartitionBuildProps.FromDictionary( + 'system', { + 'ro.build.version.incremental': '6285659', + 'ro.product.device': 'coral', + 'ro.build.fingerprint': + 'google/coral/coral:R/RP1A.200311.002/' + '6285659:userdebug/dev-keys'} + ), } common.OPTIONS.aftl_tool_path = 'aftltool' common.OPTIONS.aftl_server = 'log.endpoints.aftl-dev.cloud.goog:9000' @@ -1551,12 +1573,14 @@ class CommonUtilsTest(test_utils.ReleaseToolsTestCase): common.OPTIONS.info_dict = { 'ab_update': 'true', 'avb_avbtool': 'avbtool', - 'build.prop': { - 'ro.build.version.incremental': '6285659', - 'ro.product.device': 'coral', - 'ro.build.fingerprint': 'google/coral/coral:R/RP1A.200311.002/' - '6285659:userdebug/dev-keys' - } + 'build.prop': common.PartitionBuildProps.FromDictionary( + 'system', { + 'ro.build.version.incremental': '6285659', + 'ro.product.device': 'coral', + 'ro.build.fingerprint': + 'google/coral/coral:R/RP1A.200311.002/' + '6285659:userdebug/dev-keys'} + ) } common.OPTIONS.aftl_tool_path = "aftltool" common.OPTIONS.aftl_server = "log.endpoints.aftl-dev.cloud.goog:9000" @@ -1871,3 +1895,241 @@ super_group_foo_group_size={group_foo_size} lines = self.get_op_list(self.output_path) self.assertEqual(lines, ["remove foo"]) + + +class PartitionBuildPropsTest(test_utils.ReleaseToolsTestCase): + def setUp(self): + self.odm_build_prop = [ + 'ro.odm.build.date.utc=1578430045', + 'ro.odm.build.fingerprint=' + 'google/coral/coral:10/RP1A.200325.001/6337676:user/dev-keys', + 'ro.product.odm.device=coral', + 'import /odm/etc/build_${ro.boot.product.device_name}.prop', + ] + + @staticmethod + def _BuildZipFile(entries): + input_file = common.MakeTempFile(prefix='target_files-', suffix='.zip') + with zipfile.ZipFile(input_file, 'w') as input_zip: + for name, content in entries.items(): + input_zip.writestr(name, content) + + return input_file + + def test_parseBuildProps_noImportStatement(self): + build_prop = [ + 'ro.odm.build.date.utc=1578430045', + 'ro.odm.build.fingerprint=' + 'google/coral/coral:10/RP1A.200325.001/6337676:user/dev-keys', + 'ro.product.odm.device=coral', + ] + input_file = self._BuildZipFile({ + 'ODM/etc/build.prop': '\n'.join(build_prop), + }) + + with zipfile.ZipFile(input_file, 'r') as input_zip: + placeholder_values = { + 'ro.boot.product.device_name': ['std', 'pro'] + } + partition_props = common.PartitionBuildProps.FromInputFile( + input_zip, 'odm', placeholder_values) + + self.assertEqual({ + 'ro.odm.build.date.utc': '1578430045', + 'ro.odm.build.fingerprint': + 'google/coral/coral:10/RP1A.200325.001/6337676:user/dev-keys', + 'ro.product.odm.device': 'coral', + }, partition_props.build_props) + + self.assertEqual(set(), partition_props.prop_overrides) + + def test_parseBuildProps_singleImportStatement(self): + build_std_prop = [ + 'ro.product.odm.device=coral', + 'ro.product.odm.name=product1', + ] + build_pro_prop = [ + 'ro.product.odm.device=coralpro', + 'ro.product.odm.name=product2', + ] + + input_file = self._BuildZipFile({ + 'ODM/etc/build.prop': '\n'.join(self.odm_build_prop), + 'ODM/etc/build_std.prop': '\n'.join(build_std_prop), + 'ODM/etc/build_pro.prop': '\n'.join(build_pro_prop), + }) + + with zipfile.ZipFile(input_file, 'r') as input_zip: + placeholder_values = { + 'ro.boot.product.device_name': 'std' + } + partition_props = common.PartitionBuildProps.FromInputFile( + input_zip, 'odm', placeholder_values) + + self.assertEqual({ + 'ro.odm.build.date.utc': '1578430045', + 'ro.odm.build.fingerprint': + 'google/coral/coral:10/RP1A.200325.001/6337676:user/dev-keys', + 'ro.product.odm.device': 'coral', + 'ro.product.odm.name': 'product1', + }, partition_props.build_props) + + with zipfile.ZipFile(input_file, 'r') as input_zip: + placeholder_values = { + 'ro.boot.product.device_name': 'pro' + } + partition_props = common.PartitionBuildProps.FromInputFile( + input_zip, 'odm', placeholder_values) + + self.assertEqual({ + 'ro.odm.build.date.utc': '1578430045', + 'ro.odm.build.fingerprint': + 'google/coral/coral:10/RP1A.200325.001/6337676:user/dev-keys', + 'ro.product.odm.device': 'coralpro', + 'ro.product.odm.name': 'product2', + }, partition_props.build_props) + + def test_parseBuildProps_noPlaceHolders(self): + build_prop = copy.copy(self.odm_build_prop) + input_file = self._BuildZipFile({ + 'ODM/etc/build.prop': '\n'.join(build_prop), + }) + + with zipfile.ZipFile(input_file, 'r') as input_zip: + partition_props = common.PartitionBuildProps.FromInputFile( + input_zip, 'odm') + + self.assertEqual({ + 'ro.odm.build.date.utc': '1578430045', + 'ro.odm.build.fingerprint': + 'google/coral/coral:10/RP1A.200325.001/6337676:user/dev-keys', + 'ro.product.odm.device': 'coral', + }, partition_props.build_props) + + self.assertEqual(set(), partition_props.prop_overrides) + + def test_parseBuildProps_multipleImportStatements(self): + build_prop = copy.deepcopy(self.odm_build_prop) + build_prop.append( + 'import /odm/etc/build_${ro.boot.product.product_name}.prop') + + build_std_prop = [ + 'ro.product.odm.device=coral', + ] + build_pro_prop = [ + 'ro.product.odm.device=coralpro', + ] + + product1_prop = [ + 'ro.product.odm.name=product1', + 'ro.product.not_care=not_care', + ] + + product2_prop = [ + 'ro.product.odm.name=product2', + 'ro.product.not_care=not_care', + ] + + input_file = self._BuildZipFile({ + 'ODM/etc/build.prop': '\n'.join(build_prop), + 'ODM/etc/build_std.prop': '\n'.join(build_std_prop), + 'ODM/etc/build_pro.prop': '\n'.join(build_pro_prop), + 'ODM/etc/build_product1.prop': '\n'.join(product1_prop), + 'ODM/etc/build_product2.prop': '\n'.join(product2_prop), + }) + + with zipfile.ZipFile(input_file, 'r') as input_zip: + placeholder_values = { + 'ro.boot.product.device_name': 'std', + 'ro.boot.product.product_name': 'product1', + 'ro.boot.product.not_care': 'not_care', + } + partition_props = common.PartitionBuildProps.FromInputFile( + input_zip, 'odm', placeholder_values) + + self.assertEqual({ + 'ro.odm.build.date.utc': '1578430045', + 'ro.odm.build.fingerprint': + 'google/coral/coral:10/RP1A.200325.001/6337676:user/dev-keys', + 'ro.product.odm.device': 'coral', + 'ro.product.odm.name': 'product1' + }, partition_props.build_props) + + with zipfile.ZipFile(input_file, 'r') as input_zip: + placeholder_values = { + 'ro.boot.product.device_name': 'pro', + 'ro.boot.product.product_name': 'product2', + 'ro.boot.product.not_care': 'not_care', + } + partition_props = common.PartitionBuildProps.FromInputFile( + input_zip, 'odm', placeholder_values) + + self.assertEqual({ + 'ro.odm.build.date.utc': '1578430045', + 'ro.odm.build.fingerprint': + 'google/coral/coral:10/RP1A.200325.001/6337676:user/dev-keys', + 'ro.product.odm.device': 'coralpro', + 'ro.product.odm.name': 'product2' + }, partition_props.build_props) + + def test_parseBuildProps_defineAfterOverride(self): + build_prop = copy.deepcopy(self.odm_build_prop) + build_prop.append('ro.product.odm.device=coral') + + build_std_prop = [ + 'ro.product.odm.device=coral', + ] + build_pro_prop = [ + 'ro.product.odm.device=coralpro', + ] + + input_file = self._BuildZipFile({ + 'ODM/etc/build.prop': '\n'.join(build_prop), + 'ODM/etc/build_std.prop': '\n'.join(build_std_prop), + 'ODM/etc/build_pro.prop': '\n'.join(build_pro_prop), + }) + + with zipfile.ZipFile(input_file, 'r') as input_zip: + placeholder_values = { + 'ro.boot.product.device_name': 'std', + } + + self.assertRaises(ValueError, common.PartitionBuildProps.FromInputFile, + input_zip, 'odm', placeholder_values) + + def test_parseBuildProps_duplicateOverride(self): + build_prop = copy.deepcopy(self.odm_build_prop) + build_prop.append( + 'import /odm/etc/build_${ro.boot.product.product_name}.prop') + + build_std_prop = [ + 'ro.product.odm.device=coral', + 'ro.product.odm.name=product1', + ] + build_pro_prop = [ + 'ro.product.odm.device=coralpro', + ] + + product1_prop = [ + 'ro.product.odm.name=product1', + ] + + product2_prop = [ + 'ro.product.odm.name=product2', + ] + + input_file = self._BuildZipFile({ + 'ODM/etc/build.prop': '\n'.join(build_prop), + 'ODM/etc/build_std.prop': '\n'.join(build_std_prop), + 'ODM/etc/build_pro.prop': '\n'.join(build_pro_prop), + 'ODM/etc/build_product1.prop': '\n'.join(product1_prop), + 'ODM/etc/build_product2.prop': '\n'.join(product2_prop), + }) + + with zipfile.ZipFile(input_file, 'r') as input_zip: + placeholder_values = { + 'ro.boot.product.device_name': 'std', + 'ro.boot.product.product_name': 'product1', + } + self.assertRaises(ValueError, common.PartitionBuildProps.FromInputFile, + input_zip, 'odm', placeholder_values) diff --git a/tools/releasetools/test_ota_from_target_files.py b/tools/releasetools/test_ota_from_target_files.py index 38faf64aa6..4077d06e69 100644 --- a/tools/releasetools/test_ota_from_target_files.py +++ b/tools/releasetools/test_ota_from_target_files.py @@ -26,7 +26,8 @@ from ota_from_target_files import ( GetPackageMetadata, GetTargetFilesZipForSecondaryImages, GetTargetFilesZipWithoutPostinstallConfig, NonAbOtaPropertyFiles, Payload, PayloadSigner, POSTINSTALL_CONFIG, PropertyFiles, - StreamingPropertyFiles, WriteFingerprintAssertion) + StreamingPropertyFiles, WriteFingerprintAssertion, + CalculateRuntimeFingerprints) def construct_target_files(secondary=False): @@ -108,55 +109,58 @@ class LoadOemDictsTest(test_utils.ReleaseToolsTestCase): class OtaFromTargetFilesTest(test_utils.ReleaseToolsTestCase): - TEST_TARGET_INFO_DICT = { - 'build.prop' : { - 'ro.product.device' : 'product-device', - 'ro.build.fingerprint' : 'build-fingerprint-target', - 'ro.build.version.incremental' : 'build-version-incremental-target', - 'ro.build.version.sdk' : '27', - 'ro.build.version.security_patch' : '2017-12-01', - 'ro.build.date.utc' : '1500000000', - }, + 'build.prop': common.PartitionBuildProps.FromDictionary( + 'system', { + 'ro.product.device': 'product-device', + 'ro.build.fingerprint': 'build-fingerprint-target', + 'ro.build.version.incremental': 'build-version-incremental-target', + 'ro.build.version.sdk': '27', + 'ro.build.version.security_patch': '2017-12-01', + 'ro.build.date.utc': '1500000000'} + ) } TEST_SOURCE_INFO_DICT = { - 'build.prop' : { - 'ro.product.device' : 'product-device', - 'ro.build.fingerprint' : 'build-fingerprint-source', - 'ro.build.version.incremental' : 'build-version-incremental-source', - 'ro.build.version.sdk' : '25', - 'ro.build.version.security_patch' : '2016-12-01', - 'ro.build.date.utc' : '1400000000', - }, + 'build.prop': common.PartitionBuildProps.FromDictionary( + 'system', { + 'ro.product.device': 'product-device', + 'ro.build.fingerprint': 'build-fingerprint-source', + 'ro.build.version.incremental': 'build-version-incremental-source', + 'ro.build.version.sdk': '25', + 'ro.build.version.security_patch': '2016-12-01', + 'ro.build.date.utc': '1400000000'} + ) } TEST_INFO_DICT_USES_OEM_PROPS = { - 'build.prop' : { - 'ro.product.name' : 'product-name', - 'ro.build.thumbprint' : 'build-thumbprint', - 'ro.build.bar' : 'build-bar', - }, - 'vendor.build.prop' : { - 'ro.vendor.build.fingerprint' : 'vendor-build-fingerprint', - }, - 'property1' : 'value1', - 'property2' : 4096, - 'oem_fingerprint_properties' : 'ro.product.device ro.product.brand', + 'build.prop': common.PartitionBuildProps.FromDictionary( + 'system', { + 'ro.product.name': 'product-name', + 'ro.build.thumbprint': 'build-thumbprint', + 'ro.build.bar': 'build-bar'} + ), + 'vendor.build.prop': common.PartitionBuildProps.FromDictionary( + 'vendor', { + 'ro.vendor.build.fingerprint': 'vendor-build-fingerprint'} + ), + 'property1': 'value1', + 'property2': 4096, + 'oem_fingerprint_properties': 'ro.product.device ro.product.brand', } TEST_OEM_DICTS = [ { - 'ro.product.brand' : 'brand1', - 'ro.product.device' : 'device1', + 'ro.product.brand': 'brand1', + 'ro.product.device': 'device1', }, { - 'ro.product.brand' : 'brand2', - 'ro.product.device' : 'device2', + 'ro.product.brand': 'brand2', + 'ro.product.device': 'device2', }, { - 'ro.product.brand' : 'brand3', - 'ro.product.device' : 'device3', + 'ro.product.brand': 'brand3', + 'ro.product.device': 'device3', }, ] @@ -288,10 +292,10 @@ class OtaFromTargetFilesTest(test_utils.ReleaseToolsTestCase): @staticmethod def _test_GetPackageMetadata_swapBuildTimestamps(target_info, source_info): - (target_info['build.prop']['ro.build.date.utc'], - source_info['build.prop']['ro.build.date.utc']) = ( - source_info['build.prop']['ro.build.date.utc'], - target_info['build.prop']['ro.build.date.utc']) + (target_info['build.prop'].build_props['ro.build.date.utc'], + source_info['build.prop'].build_props['ro.build.date.utc']) = ( + source_info['build.prop'].build_props['ro.build.date.utc'], + target_info['build.prop'].build_props['ro.build.date.utc']) def test_GetPackageMetadata_unintentionalDowngradeDetected(self): target_info_dict = copy.deepcopy(self.TEST_TARGET_INFO_DICT) @@ -528,7 +532,7 @@ class OtaFromTargetFilesTest(test_utils.ReleaseToolsTestCase): def test_WriteFingerprintAssertion_without_oem_props(self): target_info = common.BuildInfo(self.TEST_TARGET_INFO_DICT, None) source_info_dict = copy.deepcopy(self.TEST_TARGET_INFO_DICT) - source_info_dict['build.prop']['ro.build.fingerprint'] = ( + source_info_dict['build.prop'].build_props['ro.build.fingerprint'] = ( 'source-build-fingerprint') source_info = common.BuildInfo(source_info_dict, None) @@ -567,7 +571,7 @@ class OtaFromTargetFilesTest(test_utils.ReleaseToolsTestCase): target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS, self.TEST_OEM_DICTS) source_info_dict = copy.deepcopy(self.TEST_INFO_DICT_USES_OEM_PROPS) - source_info_dict['build.prop']['ro.build.thumbprint'] = ( + source_info_dict['build.prop'].build_props['ro.build.thumbprint'] = ( 'source-build-thumbprint') source_info = common.BuildInfo(source_info_dict, self.TEST_OEM_DICTS) @@ -1315,3 +1319,125 @@ class PayloadTest(test_utils.ReleaseToolsTestCase): Payload.SECONDARY_PAYLOAD_PROPERTIES_TXT): continue self.assertEqual(zipfile.ZIP_STORED, entry_info.compress_type) + + +class RuntimeFingerprintTest(test_utils.ReleaseToolsTestCase): + MISC_INFO = [ + 'recovery_api_version=3', + 'fstab_version=2', + 'recovery_as_boot=true', + ] + + BUILD_PROP = [ + 'ro.build.version.release=version-release', + 'ro.build.id=build-id', + 'ro.build.version.incremental=version-incremental', + 'ro.build.type=build-type', + 'ro.build.tags=build-tags', + ] + + VENDOR_BUILD_PROP = [ + 'ro.product.vendor.brand=vendor-product-brand', + 'ro.product.vendor.name=vendor-product-name', + 'ro.product.vendor.device=vendor-product-device' + ] + + def setUp(self): + common.OPTIONS.oem_dicts = None + self.test_dir = common.MakeTempDir() + self.writeFiles({'META/misc_info.txt': '\n'.join(self.MISC_INFO)}) + + def writeFiles(self, contents_dict): + for path, content in contents_dict.items(): + abs_path = os.path.join(self.test_dir, path) + dir_name = os.path.dirname(abs_path) + if not os.path.exists(dir_name): + os.makedirs(dir_name) + with open(abs_path, 'w') as f: + f.write(content) + + @staticmethod + def constructFingerprint(prefix): + return '{}:version-release/build-id/version-incremental:' \ + 'build-type/build-tags'.format(prefix) + + def test_CalculatePossibleFingerprints_no_dynamic_fingerprint(self): + build_prop = copy.deepcopy(self.BUILD_PROP) + build_prop.extend([ + 'ro.product.brand=product-brand', + 'ro.product.name=product-name', + 'ro.product.device=product-device', + ]) + self.writeFiles({ + 'SYSTEM/build.prop': '\n'.join(build_prop), + 'VENDOR/build.prop': '\n'.join(self.VENDOR_BUILD_PROP), + }) + common.OPTIONS.info_dict = common.LoadInfoDict(self.test_dir) + + self.assertEqual({ + self.constructFingerprint('product-brand/product-name/product-device') + }, CalculateRuntimeFingerprints()) + + def test_CalculatePossibleFingerprints_single_override(self): + vendor_build_prop = copy.deepcopy(self.VENDOR_BUILD_PROP) + vendor_build_prop.extend([ + 'import /vendor/etc/build_${ro.boot.sku_name}.prop', + ]) + self.writeFiles({ + 'SYSTEM/build.prop': '\n'.join(self.BUILD_PROP), + 'VENDOR/build.prop': '\n'.join(vendor_build_prop), + 'VENDOR/etc/build_std.prop': + 'ro.product.vendor.name=vendor-product-std', + 'VENDOR/etc/build_pro.prop': + 'ro.product.vendor.name=vendor-product-pro', + }) + common.OPTIONS.info_dict = common.LoadInfoDict(self.test_dir) + common.OPTIONS.boot_variable_values = { + 'ro.boot.sku_name': ['std', 'pro'] + } + + self.assertEqual({ + self.constructFingerprint( + 'vendor-product-brand/vendor-product-name/vendor-product-device'), + self.constructFingerprint( + 'vendor-product-brand/vendor-product-std/vendor-product-device'), + self.constructFingerprint( + 'vendor-product-brand/vendor-product-pro/vendor-product-device'), + }, CalculateRuntimeFingerprints()) + + def test_CalculatePossibleFingerprints_multiple_overrides(self): + vendor_build_prop = copy.deepcopy(self.VENDOR_BUILD_PROP) + vendor_build_prop.extend([ + 'import /vendor/etc/build_${ro.boot.sku_name}.prop', + 'import /vendor/etc/build_${ro.boot.device_name}.prop', + ]) + self.writeFiles({ + 'SYSTEM/build.prop': '\n'.join(self.BUILD_PROP), + 'VENDOR/build.prop': '\n'.join(vendor_build_prop), + 'VENDOR/etc/build_std.prop': + 'ro.product.vendor.name=vendor-product-std', + 'VENDOR/etc/build_product1.prop': + 'ro.product.vendor.device=vendor-device-product1', + 'VENDOR/etc/build_pro.prop': + 'ro.product.vendor.name=vendor-product-pro', + 'VENDOR/etc/build_product2.prop': + 'ro.product.vendor.device=vendor-device-product2', + }) + common.OPTIONS.info_dict = common.LoadInfoDict(self.test_dir) + common.OPTIONS.boot_variable_values = { + 'ro.boot.sku_name': ['std', 'pro'], + 'ro.boot.device_name': ['product1', 'product2'], + } + + self.assertEqual({ + self.constructFingerprint( + 'vendor-product-brand/vendor-product-name/vendor-product-device'), + self.constructFingerprint( + 'vendor-product-brand/vendor-product-std/vendor-device-product1'), + self.constructFingerprint( + 'vendor-product-brand/vendor-product-pro/vendor-device-product1'), + self.constructFingerprint( + 'vendor-product-brand/vendor-product-std/vendor-device-product2'), + self.constructFingerprint( + 'vendor-product-brand/vendor-product-pro/vendor-device-product2'), + }, CalculateRuntimeFingerprints()) |