From 726d20a68ff73e5a75fc772eddfb1a3fb5b32872 Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Sat, 9 May 2020 05:24:18 +0000 Subject: [PATCH] Reland "Add a wrapper class PartitionBuildProp"" This reverts commit 6022545272a7360f7faa5e00ad0e7a8f5ed9d5c3. The build prop for a partition used to be a simple key:value dictionary. But we need more fields to hold the alternative build props overriden by the 'import' statement. Therefore, add a new class as a wrapper for these props first. Bug: 152167826 Change-Id: I5c952cd2a976ba1a09ddc66d56c2b8b55a61986b Merged-In: I5c952cd2a976ba1a09ddc66d56c2b8b55a61986b Test: unittests pass --- tools/releasetools/add_img_to_target_files.py | 8 +- tools/releasetools/build_image.py | 6 +- .../releasetools/check_target_files_vintf.py | 3 +- tools/releasetools/common.py | 145 ++++++---- .../test_add_img_to_target_files.py | 66 +++-- tools/releasetools/test_common.py | 258 +++++++++++------- .../test_ota_from_target_files.py | 83 +++--- 7 files changed, 351 insertions(+), 218 deletions(-) diff --git a/tools/releasetools/add_img_to_target_files.py b/tools/releasetools/add_img_to_target_files.py index 3ca139a5d..490b44a6f 100755 --- a/tools/releasetools/add_img_to_target_files.py +++ b/tools/releasetools/add_img_to_target_files.py @@ -600,17 +600,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 54bb857ac..8cf074147 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 b3d491fdf..95d09cc17 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 7d702c651..f7c99a65a 100644 --- a/tools/releasetools/common.py +++ b/tools/releasetools/common.py @@ -422,6 +422,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 @@ -430,31 +438,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: @@ -465,11 +475,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 @@ -478,11 +487,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. @@ -565,6 +572,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. @@ -602,16 +623,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")) @@ -679,13 +691,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 //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 @@ -700,15 +707,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() @@ -731,6 +729,61 @@ 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 dictionary of build properties for the given partition. + prop_overrides: a dict of list. And each list holds the overridden values + for props_allow_override. + """ + + def __init__(self, input_file, name): + self.input_file = input_file + self.partition = name + self.props_allow_override = [props.format(name) for props in [ + 'ro.product.{}.name', 'ro.product.{}.device']] + self.build_props = {} + self.prop_overrides = {} + + @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): + """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) + props.build_props = LoadDictionaryFromLines(data.split('\n')) + return props + + 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/test_add_img_to_target_files.py b/tools/releasetools/test_add_img_to_target_files.py index 3d0766ffd..c82a40bee 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 173ef53cc..4fb135cd9 100644 --- a/tools/releasetools/test_common.py +++ b/tools/releasetools/test_common.py @@ -47,109 +47,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): @@ -177,25 +192,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) @@ -203,9 +220,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) @@ -213,7 +232,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, @@ -222,9 +242,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) @@ -261,7 +279,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'), @@ -302,14 +321,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) @@ -1792,3 +1812,47 @@ 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.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: + 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({}, partition_props.prop_overrides) diff --git a/tools/releasetools/test_ota_from_target_files.py b/tools/releasetools/test_ota_from_target_files.py index 38faf64aa..e0078632c 100644 --- a/tools/releasetools/test_ota_from_target_files.py +++ b/tools/releasetools/test_ota_from_target_files.py @@ -108,55 +108,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 +291,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 +531,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 +570,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)