diff --git a/tools/releasetools/ota_from_target_files.py b/tools/releasetools/ota_from_target_files.py index 4a5facddf3..b65764b589 100755 --- a/tools/releasetools/ota_from_target_files.py +++ b/tools/releasetools/ota_from_target_files.py @@ -1038,7 +1038,11 @@ def GenerateAbOtaPackage(target_file, output_file, source_file=None): partition_timestamps_flags = [] # Enforce a max timestamp this payload can be applied on top of. if OPTIONS.downgrade: - max_timestamp = source_info.GetBuildProp("ro.build.date.utc") + # When generating ota between merged target-files, partition build date can + # decrease in target, at the same time as ro.build.date.utc increases, + # so always pick largest value. + max_timestamp = max(source_info.GetBuildProp("ro.build.date.utc"), + str(metadata.postcondition.timestamp)) partition_timestamps_flags = GeneratePartitionTimestampFlagsDowngrade( metadata.precondition.partition_state, metadata.postcondition.partition_state diff --git a/tools/releasetools/ota_utils.py b/tools/releasetools/ota_utils.py index ddd2d36cb2..048a497585 100644 --- a/tools/releasetools/ota_utils.py +++ b/tools/releasetools/ota_utils.py @@ -364,26 +364,66 @@ def HandleDowngradeMetadata(metadata_proto, target_info, source_info): # Only incremental OTAs are allowed to reach here. assert OPTIONS.incremental_source is not None + # used for logging upon errors + log_downgrades = [] + log_upgrades = [] + post_timestamp = target_info.GetBuildProp("ro.build.date.utc") pre_timestamp = source_info.GetBuildProp("ro.build.date.utc") - is_downgrade = int(post_timestamp) < int(pre_timestamp) + if int(post_timestamp) < int(pre_timestamp): + logger.info(f"ro.build.date.utc pre timestamp: {pre_timestamp}, " + f"post timestamp: {post_timestamp}. Downgrade detected.") + log_downgrades.append(f"ro.build.date.utc pre: {pre_timestamp} post: {post_timestamp}") + else: + logger.info(f"ro.build.date.utc pre timestamp: {pre_timestamp}, " + f"post timestamp: {post_timestamp}.") + log_upgrades.append(f"ro.build.date.utc pre: {pre_timestamp} post: {post_timestamp}") + + # When merging system and vendor target files, it is not enough + # to check ro.build.date.utc, the timestamp for each partition must + # be checked. + if source_info.is_ab: + ab_partitions = set(source_info.get("ab_partitions")) + for partition in sorted(set(PARTITIONS_WITH_BUILD_PROP) & ab_partitions): + + partition_prop = source_info.get('{}.build.prop'.format(partition)) + # Skip if the partition is missing, or it doesn't have a build.prop + if not partition_prop or not partition_prop.build_props: + continue + partition_prop = target_info.get('{}.build.prop'.format(partition)) + # Skip if the partition is missing, or it doesn't have a build.prop + if not partition_prop or not partition_prop.build_props: + continue + + post_timestamp = target_info.GetPartitionBuildProp( + 'ro.build.date.utc', partition) + pre_timestamp = source_info.GetPartitionBuildProp( + 'ro.build.date.utc', partition) + if int(post_timestamp) < int(pre_timestamp): + logger.info(f"Partition {partition} pre timestamp: {pre_timestamp}, " + f"post time: {post_timestamp}. Downgrade detected.") + log_downgrades.append(f"{partition} pre: {pre_timestamp} post: {post_timestamp}") + else: + logger.info(f"Partition {partition} pre timestamp: {pre_timestamp}, " + f"post timestamp: {post_timestamp}.") + log_upgrades.append(f"{partition} pre: {pre_timestamp} post: {post_timestamp}") if OPTIONS.spl_downgrade: metadata_proto.spl_downgrade = True if OPTIONS.downgrade: - if not is_downgrade: + if len(log_downgrades) == 0: raise RuntimeError( "--downgrade or --override_timestamp specified but no downgrade " - "detected: pre: %s, post: %s" % (pre_timestamp, post_timestamp)) + "detected. Current values for ro.build.date.utc: " + ', '.join(log_upgrades)) metadata_proto.downgrade = True else: - if is_downgrade: + if len(log_downgrades) != 0: raise RuntimeError( - "Downgrade detected based on timestamp check: pre: %s, post: %s. " + "Downgrade detected based on timestamp check in ro.build.date.utc. " "Need to specify --override_timestamp OR --downgrade to allow " - "building the incremental." % (pre_timestamp, post_timestamp)) - + "building the incremental. Downgrades detected for: " + + ', '.join(log_downgrades)) def ComputeRuntimeBuildInfos(default_build_info, boot_variable_values): """Returns a set of build info objects that may exist during runtime.""" diff --git a/tools/releasetools/test_ota_from_target_files.py b/tools/releasetools/test_ota_from_target_files.py index ad0f7a8eba..d1e76b9307 100644 --- a/tools/releasetools/test_ota_from_target_files.py +++ b/tools/releasetools/test_ota_from_target_files.py @@ -163,6 +163,20 @@ class OtaFromTargetFilesTest(test_utils.ReleaseToolsTestCase): 'oem_fingerprint_properties': 'ro.product.device ro.product.brand', } + TEST_TARGET_VENDOR_INFO_DICT = common.PartitionBuildProps.FromDictionary( + 'vendor', { + 'ro.vendor.build.date.utc' : '87654321', + 'ro.product.vendor.device':'vendor-device', + 'ro.vendor.build.fingerprint': 'build-fingerprint-vendor'} + ) + + TEST_SOURCE_VENDOR_INFO_DICT = common.PartitionBuildProps.FromDictionary( + 'vendor', { + 'ro.vendor.build.date.utc' : '12345678', + 'ro.product.vendor.device':'vendor-device', + 'ro.vendor.build.fingerprint': 'build-fingerprint-vendor'} + ) + def setUp(self): self.testdata_dir = test_utils.get_testdata_dir() self.assertTrue(os.path.exists(self.testdata_dir)) @@ -351,6 +365,13 @@ class OtaFromTargetFilesTest(test_utils.ReleaseToolsTestCase): source_info['build.prop'].build_props['ro.build.date.utc'], target_info['build.prop'].build_props['ro.build.date.utc']) + @staticmethod + def _test_GetPackageMetadata_swapVendorBuildTimestamps(target_info, source_info): + (target_info['vendor.build.prop'].build_props['ro.vendor.build.date.utc'], + source_info['vendor.build.prop'].build_props['ro.vendor.build.date.utc']) = ( + source_info['vendor.build.prop'].build_props['ro.vendor.build.date.utc'], + target_info['vendor.build.prop'].build_props['ro.vendor.build.date.utc']) + def test_GetPackageMetadata_unintentionalDowngradeDetected(self): target_info_dict = copy.deepcopy(self.TEST_TARGET_INFO_DICT) source_info_dict = copy.deepcopy(self.TEST_SOURCE_INFO_DICT) @@ -363,6 +384,24 @@ class OtaFromTargetFilesTest(test_utils.ReleaseToolsTestCase): self.assertRaises(RuntimeError, self.GetLegacyOtaMetadata, target_info, source_info) + def test_GetPackageMetadata_unintentionalVendorDowngradeDetected(self): + target_info_dict = copy.deepcopy(self.TEST_TARGET_INFO_DICT) + target_info_dict['ab_update'] = 'true' + target_info_dict['ab_partitions'] = ['vendor'] + target_info_dict["vendor.build.prop"] = copy.deepcopy(self.TEST_TARGET_VENDOR_INFO_DICT) + source_info_dict = copy.deepcopy(self.TEST_SOURCE_INFO_DICT) + source_info_dict['ab_update'] = 'true' + source_info_dict['ab_partitions'] = ['vendor'] + source_info_dict["vendor.build.prop"] = copy.deepcopy(self.TEST_SOURCE_VENDOR_INFO_DICT) + self._test_GetPackageMetadata_swapVendorBuildTimestamps( + target_info_dict, source_info_dict) + + target_info = common.BuildInfo(target_info_dict, None) + source_info = common.BuildInfo(source_info_dict, None) + common.OPTIONS.incremental_source = '' + self.assertRaises(RuntimeError, self.GetLegacyOtaMetadata, target_info, + source_info) + def test_GetPackageMetadata_downgrade(self): target_info_dict = copy.deepcopy(self.TEST_TARGET_INFO_DICT) source_info_dict = copy.deepcopy(self.TEST_SOURCE_INFO_DICT) @@ -397,6 +436,55 @@ class OtaFromTargetFilesTest(test_utils.ReleaseToolsTestCase): }, metadata) + def test_GetPackageMetadata_vendorDowngrade(self): + target_info_dict = copy.deepcopy(self.TEST_TARGET_INFO_DICT) + target_info_dict['ab_update'] = 'true' + target_info_dict['ab_partitions'] = ['vendor'] + target_info_dict["vendor.build.prop"] = copy.deepcopy(self.TEST_TARGET_VENDOR_INFO_DICT) + source_info_dict = copy.deepcopy(self.TEST_SOURCE_INFO_DICT) + source_info_dict['ab_update'] = 'true' + source_info_dict['ab_partitions'] = ['vendor'] + source_info_dict["vendor.build.prop"] = copy.deepcopy(self.TEST_SOURCE_VENDOR_INFO_DICT) + self._test_GetPackageMetadata_swapVendorBuildTimestamps( + target_info_dict, source_info_dict) + + target_info = common.BuildInfo(target_info_dict, None) + source_info = common.BuildInfo(source_info_dict, None) + common.OPTIONS.incremental_source = '' + common.OPTIONS.downgrade = True + common.OPTIONS.wipe_user_data = True + common.OPTIONS.spl_downgrade = True + metadata = self.GetLegacyOtaMetadata(target_info, source_info) + # Reset spl_downgrade so other tests are unaffected + common.OPTIONS.spl_downgrade = False + + self.assertDictEqual( + { + 'ota-downgrade': 'yes', + 'ota-type': 'AB', + 'ota-required-cache': '0', + 'ota-wipe': 'yes', + 'post-build': 'build-fingerprint-target', + 'post-build-incremental': 'build-version-incremental-target', + 'post-sdk-level': '27', + 'post-security-patch-level': '2017-12-01', + 'post-timestamp': '1500000000', + 'pre-device': 'product-device', + 'pre-build': 'build-fingerprint-source', + 'pre-build-incremental': 'build-version-incremental-source', + 'spl-downgrade': 'yes', + }, + metadata) + + post_build = GetPackageMetadata(target_info, source_info).postcondition + self.assertEqual('vendor', post_build.partition_state[0].partition_name) + self.assertEqual('12345678', post_build.partition_state[0].version) + + pre_build = GetPackageMetadata(target_info, source_info).precondition + self.assertEqual('vendor', pre_build.partition_state[0].partition_name) + self.assertEqual('87654321', pre_build.partition_state[0].version) + + @test_utils.SkipIfExternalToolsUnavailable() def test_GetTargetFilesZipForSecondaryImages(self): input_file = construct_target_files(secondary=True)