diff --git a/tools/releasetools/ota_from_target_files.py b/tools/releasetools/ota_from_target_files.py index 16b278a51..b75341410 100755 --- a/tools/releasetools/ota_from_target_files.py +++ b/tools/releasetools/ota_from_target_files.py @@ -189,6 +189,13 @@ A/B OTA specific options --payload_signer_key_size Deprecated. Use the '--payload_signer_maximum_signature_size' instead. + --boot_variable_file + A file that contains the possible values of ro.boot.* properties. It's + used to calculate the possible runtime fingerprints when some + ro.product.* properties are overridden by the 'import' statement. + The file expects one property per line, and each line has the following + format: 'prop_name=value1,value2'. e.g. 'ro.boot.product.sku=std,pro' + --skip_postinstall Skip the postinstall hooks when generating an A/B OTA package (default: False). Note that this discards ALL the hooks, including non-optional @@ -257,8 +264,8 @@ 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 OPTIONS.force_non_ab = False +OPTIONS.boot_variable_file = None METADATA_NAME = 'META-INF/com/android/metadata' @@ -931,13 +938,23 @@ def GetPackageMetadata(target_info, source_info=None): assert isinstance(target_info, common.BuildInfo) assert source_info is None or isinstance(source_info, common.BuildInfo) + separator = '|' + + boot_variable_values = {} + if OPTIONS.boot_variable_file: + d = common.LoadDictionaryFromFile(OPTIONS.boot_variable_file) + for key, values in d.items(): + boot_variable_values[key] = [val.strip() for val in values.split(',')] + + post_build_devices, post_build_fingerprints = \ + CalculateRuntimeDevicesAndFingerprints(target_info, boot_variable_values) metadata = { - 'post-build' : target_info.fingerprint, - 'post-build-incremental' : target_info.GetBuildProp( + 'post-build': separator.join(sorted(post_build_fingerprints)), + 'post-build-incremental': target_info.GetBuildProp( 'ro.build.version.incremental'), - 'post-sdk-level' : target_info.GetBuildProp( + 'post-sdk-level': target_info.GetBuildProp( 'ro.build.version.sdk'), - 'post-security-patch-level' : target_info.GetBuildProp( + 'post-security-patch-level': target_info.GetBuildProp( 'ro.build.version.security_patch'), } @@ -955,12 +972,15 @@ def GetPackageMetadata(target_info, source_info=None): is_incremental = source_info is not None if is_incremental: - metadata['pre-build'] = source_info.fingerprint + pre_build_devices, pre_build_fingerprints = \ + CalculateRuntimeDevicesAndFingerprints(source_info, + boot_variable_values) + metadata['pre-build'] = separator.join(sorted(pre_build_fingerprints)) metadata['pre-build-incremental'] = source_info.GetBuildProp( 'ro.build.version.incremental') - metadata['pre-device'] = source_info.device + metadata['pre-device'] = separator.join(sorted(pre_build_devices)) else: - metadata['pre-device'] = target_info.device + metadata['pre-device'] = separator.join(sorted(post_build_devices)) # Use the actual post-timestamp, even for a downgrade case. metadata['post-timestamp'] = target_info.GetBuildProp('ro.build.date.utc') @@ -1972,24 +1992,24 @@ def GenerateNonAbOtaPackage(target_file, output_file, source_file=None): output_file) -def CalculateRuntimeFingerprints(): - """Returns a set of runtime fingerprints based on the boot variables.""" +def CalculateRuntimeDevicesAndFingerprints(build_info, boot_variable_values): + """Returns a tuple of sets for runtime devices and fingerprints""" - build_info = common.BuildInfo(OPTIONS.info_dict, OPTIONS.oem_dicts) + device_names = {build_info.device} fingerprints = {build_info.fingerprint} - if not OPTIONS.boot_variable_values: - return fingerprints + if not boot_variable_values: + return device_names, 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() + keys = boot_variable_values.keys() + value_list = 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) + info_dict = copy.deepcopy(build_info.info_dict) for partition in common.PARTITIONS_WITH_CARE_MAP: partition_prop_key = "{}.build.prop".format(partition) old_props = info_dict[partition_prop_key] @@ -1997,9 +2017,10 @@ def CalculateRuntimeFingerprints(): 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 + new_build_info = common.BuildInfo(info_dict, build_info.oem_dicts) + device_names.add(new_build_info.device) + fingerprints.add(new_build_info.fingerprint) + return device_names, fingerprints def main(argv): @@ -2077,6 +2098,8 @@ def main(argv): OPTIONS.disable_fec_computation = True elif o == "--force_non_ab": OPTIONS.force_non_ab = True + elif o == "--boot_variable_file": + OPTIONS.boot_variable_file = a else: return False return True @@ -2114,6 +2137,7 @@ def main(argv): "output_metadata_path=", "disable_fec_computation", "force_non_ab", + "boot_variable_file=", ], extra_option_handler=option_handler) if len(args) != 2: diff --git a/tools/releasetools/test_ota_from_target_files.py b/tools/releasetools/test_ota_from_target_files.py index 4077d06e6..7783f963b 100644 --- a/tools/releasetools/test_ota_from_target_files.py +++ b/tools/releasetools/test_ota_from_target_files.py @@ -27,7 +27,7 @@ from ota_from_target_files import ( GetTargetFilesZipWithoutPostinstallConfig, NonAbOtaPropertyFiles, Payload, PayloadSigner, POSTINSTALL_CONFIG, PropertyFiles, StreamingPropertyFiles, WriteFingerprintAssertion, - CalculateRuntimeFingerprints) + CalculateRuntimeDevicesAndFingerprints) def construct_target_files(secondary=False): @@ -1334,6 +1334,9 @@ class RuntimeFingerprintTest(test_utils.ReleaseToolsTestCase): 'ro.build.version.incremental=version-incremental', 'ro.build.type=build-type', 'ro.build.tags=build-tags', + 'ro.build.version.sdk=30', + 'ro.build.version.security_patch=2020', + 'ro.build.date.utc=12345678' ] VENDOR_BUILD_PROP = [ @@ -1345,11 +1348,12 @@ class RuntimeFingerprintTest(test_utils.ReleaseToolsTestCase): def setUp(self): common.OPTIONS.oem_dicts = None self.test_dir = common.MakeTempDir() - self.writeFiles({'META/misc_info.txt': '\n'.join(self.MISC_INFO)}) + self.writeFiles({'META/misc_info.txt': '\n'.join(self.MISC_INFO)}, + self.test_dir) - def writeFiles(self, contents_dict): + def writeFiles(self, contents_dict, out_dir): for path, content in contents_dict.items(): - abs_path = os.path.join(self.test_dir, path) + abs_path = os.path.join(out_dir, path) dir_name = os.path.dirname(abs_path) if not os.path.exists(dir_name): os.makedirs(dir_name) @@ -1371,12 +1375,14 @@ class RuntimeFingerprintTest(test_utils.ReleaseToolsTestCase): 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.test_dir) - self.assertEqual({ - self.constructFingerprint('product-brand/product-name/product-device') - }, CalculateRuntimeFingerprints()) + build_info = common.BuildInfo(common.LoadInfoDict(self.test_dir)) + expected = ({'product-device'}, + {self.constructFingerprint( + 'product-brand/product-name/product-device')}) + self.assertEqual(expected, + CalculateRuntimeDevicesAndFingerprints(build_info, {})) def test_CalculatePossibleFingerprints_single_override(self): vendor_build_prop = copy.deepcopy(self.VENDOR_BUILD_PROP) @@ -1390,20 +1396,22 @@ class RuntimeFingerprintTest(test_utils.ReleaseToolsTestCase): '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.test_dir) - self.assertEqual({ + build_info = common.BuildInfo(common.LoadInfoDict(self.test_dir)) + boot_variable_values = {'ro.boot.sku_name': ['std', 'pro']} + + expected = ({'vendor-product-device'}, { 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()) + }) + self.assertEqual( + expected, CalculateRuntimeDevicesAndFingerprints( + build_info, boot_variable_values)) def test_CalculatePossibleFingerprints_multiple_overrides(self): vendor_build_prop = copy.deepcopy(self.VENDOR_BUILD_PROP) @@ -1422,14 +1430,17 @@ class RuntimeFingerprintTest(test_utils.ReleaseToolsTestCase): '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 = { + }, self.test_dir) + + build_info = common.BuildInfo(common.LoadInfoDict(self.test_dir)) + boot_variable_values = { 'ro.boot.sku_name': ['std', 'pro'], 'ro.boot.device_name': ['product1', 'product2'], } - self.assertEqual({ + expected_devices = {'vendor-product-device', 'vendor-device-product1', + 'vendor-device-product2'} + expected_fingerprints = { self.constructFingerprint( 'vendor-product-brand/vendor-product-name/vendor-product-device'), self.constructFingerprint( @@ -1439,5 +1450,108 @@ class RuntimeFingerprintTest(test_utils.ReleaseToolsTestCase): self.constructFingerprint( 'vendor-product-brand/vendor-product-std/vendor-device-product2'), self.constructFingerprint( - 'vendor-product-brand/vendor-product-pro/vendor-device-product2'), - }, CalculateRuntimeFingerprints()) + 'vendor-product-brand/vendor-product-pro/vendor-device-product2') + } + self.assertEqual((expected_devices, expected_fingerprints), + CalculateRuntimeDevicesAndFingerprints( + build_info, boot_variable_values)) + + def test_GetPackageMetadata_full_package(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', + }, self.test_dir) + + common.OPTIONS.boot_variable_file = common.MakeTempFile() + with open(common.OPTIONS.boot_variable_file, 'w') as f: + f.write('ro.boot.sku_name=std,pro') + + build_info = common.BuildInfo(common.LoadInfoDict(self.test_dir)) + metadata = GetPackageMetadata(build_info) + self.assertEqual('vendor-product-device', metadata['pre-device']) + fingerprints = [ + self.constructFingerprint( + 'vendor-product-brand/vendor-product-name/vendor-product-device'), + self.constructFingerprint( + 'vendor-product-brand/vendor-product-pro/vendor-product-device'), + self.constructFingerprint( + 'vendor-product-brand/vendor-product-std/vendor-product-device'), + ] + self.assertEqual('|'.join(fingerprints), metadata['post-build']) + + def test_GetPackageMetadata_incremental_package(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.device=vendor-device-std', + 'VENDOR/etc/build_pro.prop': + 'ro.product.vendor.device=vendor-device-pro', + }, self.test_dir) + + common.OPTIONS.boot_variable_file = common.MakeTempFile() + with open(common.OPTIONS.boot_variable_file, 'w') as f: + f.write('ro.boot.sku_name=std,pro') + + source_dir = common.MakeTempDir() + source_build_prop = [ + 'ro.build.version.release=source-version-release', + 'ro.build.id=source-build-id', + 'ro.build.version.incremental=source-version-incremental', + 'ro.build.type=build-type', + 'ro.build.tags=build-tags', + 'ro.build.version.sdk=29', + 'ro.build.version.security_patch=2020', + 'ro.build.date.utc=12340000' + ] + self.writeFiles({ + 'META/misc_info.txt': '\n'.join(self.MISC_INFO), + 'SYSTEM/build.prop': '\n'.join(source_build_prop), + 'VENDOR/build.prop': '\n'.join(vendor_build_prop), + 'VENDOR/etc/build_std.prop': + 'ro.product.vendor.device=vendor-device-std', + 'VENDOR/etc/build_pro.prop': + 'ro.product.vendor.device=vendor-device-pro', + }, source_dir) + common.OPTIONS.incremental_source = source_dir + + target_info = common.BuildInfo(common.LoadInfoDict(self.test_dir)) + source_info = common.BuildInfo(common.LoadInfoDict(source_dir)) + + metadata = GetPackageMetadata(target_info, source_info) + self.assertEqual( + 'vendor-device-pro|vendor-device-std|vendor-product-device', + metadata['pre-device']) + suffix = ':source-version-release/source-build-id/' \ + 'source-version-incremental:build-type/build-tags' + pre_fingerprints = [ + 'vendor-product-brand/vendor-product-name/vendor-device-pro' + '{}'.format(suffix), + 'vendor-product-brand/vendor-product-name/vendor-device-std' + '{}'.format(suffix), + 'vendor-product-brand/vendor-product-name/vendor-product-device' + '{}'.format(suffix), + ] + self.assertEqual('|'.join(pre_fingerprints), metadata['pre-build']) + + post_fingerprints = [ + self.constructFingerprint( + 'vendor-product-brand/vendor-product-name/vendor-device-pro'), + self.constructFingerprint( + 'vendor-product-brand/vendor-product-name/vendor-device-std'), + self.constructFingerprint( + 'vendor-product-brand/vendor-product-name/vendor-product-device'), + ] + self.assertEqual('|'.join(post_fingerprints), metadata['post-build'])