Add an option to input the boot variables for OTA package generation
The values of the ro.boot* variables are not part of the image files and are provided (e.g. by bootloaders) at runtime. Meanwhile, their values may affect some of the device build properties, as a different build.prop file can be imported by init during runtime. This cl adds an option to accepts a list of possible values for some boot variables. The OTA generation script later use these values to calculate the alternative runtime fingerprints of the device; and list the device names and fingerprints in the OTA package's metadata. The OTA metadata is verified by the OTA server or recovery to ensure the correct OTA package is used for update. We haven't made any restrictions on what ro.boot* variables can be used for fingerprint override. One possible candidate can be the skus listed in ODM_MANIFEST_SKUS. Bug: 152167826 Test: unittests pass, generate an OTA file with the new option Change-Id: I637dea3472354236d2fd1ef0a3306712b3283c29
This commit is contained in:
parent
ed67178e4a
commit
d6867167d1
2 changed files with 179 additions and 41 deletions
|
@ -189,6 +189,13 @@ A/B OTA specific options
|
|||
--payload_signer_key_size <key_size>
|
||||
Deprecated. Use the '--payload_signer_maximum_signature_size' instead.
|
||||
|
||||
--boot_variable_file <path>
|
||||
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:
|
||||
|
|
|
@ -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'])
|
||||
|
|
Loading…
Reference in a new issue