Merge "releasetools: Move BuildInfo into common."

This commit is contained in:
Tao Bao 2019-10-08 17:44:15 +00:00 committed by Gerrit Code Review
commit 163df7d40b
4 changed files with 526 additions and 496 deletions

View File

@ -284,6 +284,225 @@ def CloseInheritedPipes():
pass
class BuildInfo(object):
"""A class that holds the information for a given build.
This class wraps up the property querying for a given source or target build.
It abstracts away the logic of handling OEM-specific properties, and caches
the commonly used properties such as fingerprint.
There are two types of info dicts: a) build-time info dict, which is generated
at build time (i.e. included in a target_files zip); b) OEM info dict that is
specified at package generation time (via command line argument
'--oem_settings'). If a build doesn't use OEM-specific properties (i.e. not
having "oem_fingerprint_properties" in build-time info dict), all the queries
would be answered based on build-time info dict only. Otherwise if using
OEM-specific properties, some of them will be calculated from two info dicts.
Users can query properties similarly as using a dict() (e.g. info['fstab']),
or to query build properties via GetBuildProp() or GetVendorBuildProp().
Attributes:
info_dict: The build-time info dict.
is_ab: Whether it's a build that uses A/B OTA.
oem_dicts: A list of OEM dicts.
oem_props: A list of OEM properties that should be read from OEM dicts; None
if the build doesn't use any OEM-specific property.
fingerprint: The fingerprint of the build, which would be calculated based
on OEM properties if applicable.
device: The device name, which could come from OEM dicts if applicable.
"""
_RO_PRODUCT_RESOLVE_PROPS = ["ro.product.brand", "ro.product.device",
"ro.product.manufacturer", "ro.product.model",
"ro.product.name"]
_RO_PRODUCT_PROPS_DEFAULT_SOURCE_ORDER = ["product", "odm", "vendor",
"system_ext", "system"]
def __init__(self, info_dict, oem_dicts):
"""Initializes a BuildInfo instance with the given dicts.
Note that it only wraps up the given dicts, without making copies.
Arguments:
info_dict: The build-time info dict.
oem_dicts: A list of OEM dicts (which is parsed from --oem_settings). Note
that it always uses the first dict to calculate the fingerprint or the
device name. The rest would be used for asserting OEM properties only
(e.g. one package can be installed on one of these devices).
Raises:
ValueError: On invalid inputs.
"""
self.info_dict = info_dict
self.oem_dicts = oem_dicts
self._is_ab = info_dict.get("ab_update") == "true"
self._oem_props = info_dict.get("oem_fingerprint_properties")
if self._oem_props:
assert oem_dicts, "OEM source required for this build"
# These two should be computed only after setting self._oem_props.
self._device = self.GetOemProperty("ro.product.device")
self._fingerprint = self.CalculateFingerprint()
# Sanity check the build fingerprint.
if (' ' in self._fingerprint or
any(ord(ch) > 127 for ch in self._fingerprint)):
raise ValueError(
'Invalid build fingerprint: "{}". See the requirement in Android CDD '
'3.2.2. Build Parameters.'.format(self._fingerprint))
@property
def is_ab(self):
return self._is_ab
@property
def device(self):
return self._device
@property
def fingerprint(self):
return self._fingerprint
@property
def vendor_fingerprint(self):
return self._fingerprint_of("vendor")
@property
def product_fingerprint(self):
return self._fingerprint_of("product")
@property
def odm_fingerprint(self):
return self._fingerprint_of("odm")
def _fingerprint_of(self, partition):
if partition + ".build.prop" not in self.info_dict:
return None
build_prop = self.info_dict[partition + ".build.prop"]
if "ro." + partition + ".build.fingerprint" in build_prop:
return build_prop["ro." + partition + ".build.fingerprint"]
if "ro." + partition + ".build.thumbprint" in build_prop:
return build_prop["ro." + partition + ".build.thumbprint"]
return None
@property
def oem_props(self):
return self._oem_props
def __getitem__(self, key):
return self.info_dict[key]
def __setitem__(self, key, value):
self.info_dict[key] = value
def get(self, key, default=None):
return self.info_dict.get(key, default)
def items(self):
return self.info_dict.items()
def GetBuildProp(self, prop):
"""Returns the inquired build property."""
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,))
def _ResolveRoProductBuildProp(self, prop):
"""Resolves the inquired ro.product.* build property"""
prop_val = self.info_dict.get("build.prop", {}).get(prop)
if prop_val:
return prop_val
source_order_val = self.info_dict.get("build.prop", {}).get(
"ro.product.property_source_order")
if source_order_val:
source_order = source_order_val.split(",")
else:
source_order = BuildInfo._RO_PRODUCT_PROPS_DEFAULT_SOURCE_ORDER
# Check that all sources in ro.product.property_source_order are valid
if any([x not in BuildInfo._RO_PRODUCT_PROPS_DEFAULT_SOURCE_ORDER
for x in source_order]):
raise ExternalError(
"Invalid ro.product.property_source_order '{}'".format(source_order))
for source 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)
if prop_val:
return prop_val
raise ExternalError("couldn't resolve {}".format(prop))
def GetVendorBuildProp(self, prop):
"""Returns the inquired vendor build property."""
try:
return self.info_dict.get("vendor.build.prop", {})[prop]
except KeyError:
raise ExternalError(
"couldn't find %s in vendor.build.prop" % (prop,))
def GetOemProperty(self, key):
if self.oem_props is not None and key in self.oem_props:
return self.oem_dicts[0][key]
return self.GetBuildProp(key)
def CalculateFingerprint(self):
if self.oem_props is None:
try:
return self.GetBuildProp("ro.build.fingerprint")
except ExternalError:
return "{}/{}/{}:{}/{}/{}:{}/{}".format(
self.GetBuildProp("ro.product.brand"),
self.GetBuildProp("ro.product.name"),
self.GetBuildProp("ro.product.device"),
self.GetBuildProp("ro.build.version.release"),
self.GetBuildProp("ro.build.id"),
self.GetBuildProp("ro.build.version.incremental"),
self.GetBuildProp("ro.build.type"),
self.GetBuildProp("ro.build.tags"))
return "%s/%s/%s:%s" % (
self.GetOemProperty("ro.product.brand"),
self.GetOemProperty("ro.product.name"),
self.GetOemProperty("ro.product.device"),
self.GetBuildProp("ro.build.thumbprint"))
def WriteMountOemScript(self, script):
assert self.oem_props is not None
recovery_mount_options = self.info_dict.get("recovery_mount_options")
script.Mount("/oem", recovery_mount_options)
def WriteDeviceAssertions(self, script, oem_no_mount):
# Read the property directly if not using OEM properties.
if not self.oem_props:
script.AssertDevice(self.device)
return
# Otherwise assert OEM properties.
if not self.oem_dicts:
raise ExternalError(
"No OEM file provided to answer expected assertions")
for prop in self.oem_props.split():
values = []
for oem_dict in self.oem_dicts:
if prop in oem_dict:
values.append(oem_dict[prop])
if not values:
raise ExternalError(
"The OEM file is missing the property %s" % (prop,))
script.AssertOemProperty(prop, values, oem_no_mount)
def LoadInfoDict(input_file, repacking=False):
"""Loads the key/value pairs from the given input target_files.

View File

@ -258,225 +258,6 @@ SECONDARY_PAYLOAD_SKIPPED_IMAGES = [
'system_ext', 'vbmeta', 'vbmeta_system', 'vbmeta_vendor', 'vendor']
class BuildInfo(object):
"""A class that holds the information for a given build.
This class wraps up the property querying for a given source or target build.
It abstracts away the logic of handling OEM-specific properties, and caches
the commonly used properties such as fingerprint.
There are two types of info dicts: a) build-time info dict, which is generated
at build time (i.e. included in a target_files zip); b) OEM info dict that is
specified at package generation time (via command line argument
'--oem_settings'). If a build doesn't use OEM-specific properties (i.e. not
having "oem_fingerprint_properties" in build-time info dict), all the queries
would be answered based on build-time info dict only. Otherwise if using
OEM-specific properties, some of them will be calculated from two info dicts.
Users can query properties similarly as using a dict() (e.g. info['fstab']),
or to query build properties via GetBuildProp() or GetVendorBuildProp().
Attributes:
info_dict: The build-time info dict.
is_ab: Whether it's a build that uses A/B OTA.
oem_dicts: A list of OEM dicts.
oem_props: A list of OEM properties that should be read from OEM dicts; None
if the build doesn't use any OEM-specific property.
fingerprint: The fingerprint of the build, which would be calculated based
on OEM properties if applicable.
device: The device name, which could come from OEM dicts if applicable.
"""
_RO_PRODUCT_RESOLVE_PROPS = ["ro.product.brand", "ro.product.device",
"ro.product.manufacturer", "ro.product.model",
"ro.product.name"]
_RO_PRODUCT_PROPS_DEFAULT_SOURCE_ORDER = ["product", "odm", "vendor",
"system_ext", "system"]
def __init__(self, info_dict, oem_dicts):
"""Initializes a BuildInfo instance with the given dicts.
Note that it only wraps up the given dicts, without making copies.
Arguments:
info_dict: The build-time info dict.
oem_dicts: A list of OEM dicts (which is parsed from --oem_settings). Note
that it always uses the first dict to calculate the fingerprint or the
device name. The rest would be used for asserting OEM properties only
(e.g. one package can be installed on one of these devices).
Raises:
ValueError: On invalid inputs.
"""
self.info_dict = info_dict
self.oem_dicts = oem_dicts
self._is_ab = info_dict.get("ab_update") == "true"
self._oem_props = info_dict.get("oem_fingerprint_properties")
if self._oem_props:
assert oem_dicts, "OEM source required for this build"
# These two should be computed only after setting self._oem_props.
self._device = self.GetOemProperty("ro.product.device")
self._fingerprint = self.CalculateFingerprint()
# Sanity check the build fingerprint.
if (' ' in self._fingerprint or
any(ord(ch) > 127 for ch in self._fingerprint)):
raise ValueError(
'Invalid build fingerprint: "{}". See the requirement in Android CDD '
'3.2.2. Build Parameters.'.format(self._fingerprint))
@property
def is_ab(self):
return self._is_ab
@property
def device(self):
return self._device
@property
def fingerprint(self):
return self._fingerprint
@property
def vendor_fingerprint(self):
return self._fingerprint_of("vendor")
@property
def product_fingerprint(self):
return self._fingerprint_of("product")
@property
def odm_fingerprint(self):
return self._fingerprint_of("odm")
def _fingerprint_of(self, partition):
if partition + ".build.prop" not in self.info_dict:
return None
build_prop = self.info_dict[partition + ".build.prop"]
if "ro." + partition + ".build.fingerprint" in build_prop:
return build_prop["ro." + partition + ".build.fingerprint"]
if "ro." + partition + ".build.thumbprint" in build_prop:
return build_prop["ro." + partition + ".build.thumbprint"]
return None
@property
def oem_props(self):
return self._oem_props
def __getitem__(self, key):
return self.info_dict[key]
def __setitem__(self, key, value):
self.info_dict[key] = value
def get(self, key, default=None):
return self.info_dict.get(key, default)
def items(self):
return self.info_dict.items()
def GetBuildProp(self, prop):
"""Returns the inquired build property."""
if prop in BuildInfo._RO_PRODUCT_RESOLVE_PROPS:
return self._ResolveRoProductBuildProp(prop)
try:
return self.info_dict.get("build.prop", {})[prop]
except KeyError:
raise common.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)
if prop_val:
return prop_val
source_order_val = self.info_dict.get("build.prop", {}).get(
"ro.product.property_source_order")
if source_order_val:
source_order = source_order_val.split(",")
else:
source_order = BuildInfo._RO_PRODUCT_PROPS_DEFAULT_SOURCE_ORDER
# Check that all sources in ro.product.property_source_order are valid
if any([x not in BuildInfo._RO_PRODUCT_PROPS_DEFAULT_SOURCE_ORDER
for x in source_order]):
raise common.ExternalError(
"Invalid ro.product.property_source_order '{}'".format(source_order))
for source 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)
if prop_val:
return prop_val
raise common.ExternalError("couldn't resolve {}".format(prop))
def GetVendorBuildProp(self, prop):
"""Returns the inquired vendor build property."""
try:
return self.info_dict.get("vendor.build.prop", {})[prop]
except KeyError:
raise common.ExternalError(
"couldn't find %s in vendor.build.prop" % (prop,))
def GetOemProperty(self, key):
if self.oem_props is not None and key in self.oem_props:
return self.oem_dicts[0][key]
return self.GetBuildProp(key)
def CalculateFingerprint(self):
if self.oem_props is None:
try:
return self.GetBuildProp("ro.build.fingerprint")
except common.ExternalError:
return "{}/{}/{}:{}/{}/{}:{}/{}".format(
self.GetBuildProp("ro.product.brand"),
self.GetBuildProp("ro.product.name"),
self.GetBuildProp("ro.product.device"),
self.GetBuildProp("ro.build.version.release"),
self.GetBuildProp("ro.build.id"),
self.GetBuildProp("ro.build.version.incremental"),
self.GetBuildProp("ro.build.type"),
self.GetBuildProp("ro.build.tags"))
return "%s/%s/%s:%s" % (
self.GetOemProperty("ro.product.brand"),
self.GetOemProperty("ro.product.name"),
self.GetOemProperty("ro.product.device"),
self.GetBuildProp("ro.build.thumbprint"))
def WriteMountOemScript(self, script):
assert self.oem_props is not None
recovery_mount_options = self.info_dict.get("recovery_mount_options")
script.Mount("/oem", recovery_mount_options)
def WriteDeviceAssertions(self, script, oem_no_mount):
# Read the property directly if not using OEM properties.
if not self.oem_props:
script.AssertDevice(self.device)
return
# Otherwise assert OEM properties.
if not self.oem_dicts:
raise common.ExternalError(
"No OEM file provided to answer expected assertions")
for prop in self.oem_props.split():
values = []
for oem_dict in self.oem_dicts:
if prop in oem_dict:
values.append(oem_dict[prop])
if not values:
raise common.ExternalError(
"The OEM file is missing the property %s" % (prop,))
script.AssertOemProperty(prop, values, oem_no_mount)
class PayloadSigner(object):
"""A class that wraps the payload signing works.
@ -904,7 +685,7 @@ def GetBlockDifferences(target_zip, source_zip, target_info, source_info,
def WriteFullOTAPackage(input_zip, output_file):
target_info = BuildInfo(OPTIONS.info_dict, OPTIONS.oem_dicts)
target_info = common.BuildInfo(OPTIONS.info_dict, OPTIONS.oem_dicts)
# We don't know what version it will be installed on top of. We expect the API
# just won't change very often. Similarly for fstab, it might have changed in
@ -1130,8 +911,8 @@ def GetPackageMetadata(target_info, source_info=None):
Returns:
A dict to be written into package metadata entry.
"""
assert isinstance(target_info, BuildInfo)
assert source_info is None or isinstance(source_info, BuildInfo)
assert isinstance(target_info, common.BuildInfo)
assert source_info is None or isinstance(source_info, common.BuildInfo)
metadata = {
'post-build' : target_info.fingerprint,
@ -1544,8 +1325,8 @@ def FinalizeMetadata(metadata, input_file, output_file, needed_property_files):
def WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_file):
target_info = BuildInfo(OPTIONS.target_info_dict, OPTIONS.oem_dicts)
source_info = BuildInfo(OPTIONS.source_info_dict, OPTIONS.oem_dicts)
target_info = common.BuildInfo(OPTIONS.target_info_dict, OPTIONS.oem_dicts)
source_info = common.BuildInfo(OPTIONS.source_info_dict, OPTIONS.oem_dicts)
target_api_version = target_info["recovery_api_version"]
source_api_version = source_info["recovery_api_version"]
@ -2024,10 +1805,10 @@ def GenerateAbOtaPackage(target_file, output_file, source_file=None):
compression=zipfile.ZIP_DEFLATED)
if source_file is not None:
target_info = BuildInfo(OPTIONS.target_info_dict, OPTIONS.oem_dicts)
source_info = BuildInfo(OPTIONS.source_info_dict, OPTIONS.oem_dicts)
target_info = common.BuildInfo(OPTIONS.target_info_dict, OPTIONS.oem_dicts)
source_info = common.BuildInfo(OPTIONS.source_info_dict, OPTIONS.oem_dicts)
else:
target_info = BuildInfo(OPTIONS.info_dict, OPTIONS.oem_dicts)
target_info = common.BuildInfo(OPTIONS.info_dict, OPTIONS.oem_dicts)
source_info = None
# Metadata to comply with Android OTA package format.

View File

@ -44,6 +44,210 @@ def get_2gb_string():
yield b'\0' * (step_size - block_size)
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',
},
'vendor.build.prop' : {
'ro.vendor.build.fingerprint' : 'vendor-build-fingerprint',
},
'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',
}
TEST_OEM_DICTS = [
{
'ro.product.brand' : 'brand1',
'ro.product.device' : 'device1',
},
{
'ro.product.brand' : 'brand2',
'ro.product.device' : 'device2',
},
{
'ro.product.brand' : 'brand3',
'ro.product.device' : 'device3',
},
]
def test_init(self):
target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
self.assertEqual('product-device', target_info.device)
self.assertEqual('build-fingerprint', target_info.fingerprint)
self.assertFalse(target_info.is_ab)
self.assertIsNone(target_info.oem_props)
def test_init_with_oem_props(self):
target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
self.TEST_OEM_DICTS)
self.assertEqual('device1', target_info.device)
self.assertEqual('brand1/product-name/device1:build-thumbprint',
target_info.fingerprint)
# Swap the order in oem_dicts, which would lead to different BuildInfo.
oem_dicts = copy.copy(self.TEST_OEM_DICTS)
oem_dicts[0], oem_dicts[2] = oem_dicts[2], oem_dicts[0]
target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
oem_dicts)
self.assertEqual('device3', target_info.device)
self.assertEqual('brand3/product-name/device3:build-thumbprint',
target_info.fingerprint)
# Missing oem_dict should be rejected.
self.assertRaises(AssertionError, common.BuildInfo,
self.TEST_INFO_DICT_USES_OEM_PROPS, None)
def test_init_badFingerprint(self):
info_dict = copy.deepcopy(self.TEST_INFO_DICT)
info_dict['build.prop']['ro.build.fingerprint'] = 'bad fingerprint'
self.assertRaises(ValueError, common.BuildInfo, info_dict, None)
info_dict['build.prop']['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'])
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'])
def test___setitem__(self):
target_info = common.BuildInfo(copy.deepcopy(self.TEST_INFO_DICT), None)
self.assertEqual('value1', target_info['property1'])
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'])
def test_get(self):
target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
self.assertEqual('value1', target_info.get('property1'))
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'])
def test_get_with_oem_props(self):
target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
self.TEST_OEM_DICTS)
self.assertEqual('value1', target_info.get('property1'))
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'])
def test_items(self):
target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
items = target_info.items()
self.assertIn(('property1', 'value1'), items)
self.assertIn(('property2', 4096), items)
def test_GetBuildProp(self):
target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
self.assertEqual('build-foo', target_info.GetBuildProp('ro.build.foo'))
self.assertRaises(common.ExternalError, target_info.GetBuildProp,
'ro.build.nonexistent')
def test_GetBuildProp_with_oem_props(self):
target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
self.TEST_OEM_DICTS)
self.assertEqual('build-bar', target_info.GetBuildProp('ro.build.bar'))
self.assertRaises(common.ExternalError, target_info.GetBuildProp,
'ro.build.nonexistent')
def test_GetVendorBuildProp(self):
target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
self.assertEqual('vendor-build-fingerprint',
target_info.GetVendorBuildProp(
'ro.vendor.build.fingerprint'))
self.assertRaises(common.ExternalError, target_info.GetVendorBuildProp,
'ro.build.nonexistent')
def test_GetVendorBuildProp_with_oem_props(self):
target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
self.TEST_OEM_DICTS)
self.assertEqual('vendor-build-fingerprint',
target_info.GetVendorBuildProp(
'ro.vendor.build.fingerprint'))
self.assertRaises(common.ExternalError, target_info.GetVendorBuildProp,
'ro.build.nonexistent')
def test_vendor_fingerprint(self):
target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
self.assertEqual('vendor-build-fingerprint',
target_info.vendor_fingerprint)
def test_vendor_fingerprint_blacklisted(self):
target_info_dict = copy.deepcopy(self.TEST_INFO_DICT_USES_OEM_PROPS)
del target_info_dict['vendor.build.prop']['ro.vendor.build.fingerprint']
target_info = common.BuildInfo(target_info_dict, self.TEST_OEM_DICTS)
self.assertIsNone(target_info.vendor_fingerprint)
def test_vendor_fingerprint_without_vendor_build_prop(self):
target_info_dict = copy.deepcopy(self.TEST_INFO_DICT_USES_OEM_PROPS)
del target_info_dict['vendor.build.prop']
target_info = common.BuildInfo(target_info_dict, self.TEST_OEM_DICTS)
self.assertIsNone(target_info.vendor_fingerprint)
def test_WriteMountOemScript(self):
target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
self.TEST_OEM_DICTS)
script_writer = test_utils.MockScriptWriter()
target_info.WriteMountOemScript(script_writer)
self.assertEqual([('Mount', '/oem', None)], script_writer.lines)
def test_WriteDeviceAssertions(self):
target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
script_writer = test_utils.MockScriptWriter()
target_info.WriteDeviceAssertions(script_writer, False)
self.assertEqual([('AssertDevice', 'product-device')], script_writer.lines)
def test_WriteDeviceAssertions_with_oem_props(self):
target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
self.TEST_OEM_DICTS)
script_writer = test_utils.MockScriptWriter()
target_info.WriteDeviceAssertions(script_writer, False)
self.assertEqual(
[
('AssertOemProperty', 'ro.product.device',
['device1', 'device2', 'device3'], False),
('AssertOemProperty', 'ro.product.brand',
['brand1', 'brand2', 'brand3'], False),
],
script_writer.lines)
class CommonZipTest(test_utils.ReleaseToolsTestCase):
def _verify(self, zip_file, zip_file_name, arcname, expected_hash,

View File

@ -22,7 +22,7 @@ import zipfile
import common
import test_utils
from ota_from_target_files import (
_LoadOemDicts, AbOtaPropertyFiles, BuildInfo, FinalizeMetadata,
_LoadOemDicts, AbOtaPropertyFiles, FinalizeMetadata,
GetPackageMetadata, GetTargetFilesZipForSecondaryImages,
GetTargetFilesZipWithoutPostinstallConfig, NonAbOtaPropertyFiles,
Payload, PayloadSigner, POSTINSTALL_CONFIG, PropertyFiles,
@ -74,262 +74,6 @@ def construct_target_files(secondary=False):
return target_files
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',
},
'vendor.build.prop' : {
'ro.vendor.build.fingerprint' : 'vendor-build-fingerprint',
},
'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',
}
TEST_OEM_DICTS = [
{
'ro.product.brand' : 'brand1',
'ro.product.device' : 'device1',
},
{
'ro.product.brand' : 'brand2',
'ro.product.device' : 'device2',
},
{
'ro.product.brand' : 'brand3',
'ro.product.device' : 'device3',
},
]
def test_init(self):
target_info = BuildInfo(self.TEST_INFO_DICT, None)
self.assertEqual('product-device', target_info.device)
self.assertEqual('build-fingerprint', target_info.fingerprint)
self.assertFalse(target_info.is_ab)
self.assertIsNone(target_info.oem_props)
def test_init_with_oem_props(self):
target_info = BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
self.TEST_OEM_DICTS)
self.assertEqual('device1', target_info.device)
self.assertEqual('brand1/product-name/device1:build-thumbprint',
target_info.fingerprint)
# Swap the order in oem_dicts, which would lead to different BuildInfo.
oem_dicts = copy.copy(self.TEST_OEM_DICTS)
oem_dicts[0], oem_dicts[2] = oem_dicts[2], oem_dicts[0]
target_info = BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS, oem_dicts)
self.assertEqual('device3', target_info.device)
self.assertEqual('brand3/product-name/device3:build-thumbprint',
target_info.fingerprint)
# Missing oem_dict should be rejected.
self.assertRaises(AssertionError, BuildInfo,
self.TEST_INFO_DICT_USES_OEM_PROPS, None)
def test_init_badFingerprint(self):
info_dict = copy.deepcopy(self.TEST_INFO_DICT)
info_dict['build.prop']['ro.build.fingerprint'] = 'bad fingerprint'
self.assertRaises(ValueError, BuildInfo, info_dict, None)
info_dict['build.prop']['ro.build.fingerprint'] = 'bad\x80fingerprint'
self.assertRaises(ValueError, BuildInfo, info_dict, None)
def test___getitem__(self):
target_info = 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'])
def test___getitem__with_oem_props(self):
target_info = 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'])
def test___setitem__(self):
target_info = BuildInfo(copy.deepcopy(self.TEST_INFO_DICT), None)
self.assertEqual('value1', target_info['property1'])
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'])
def test_get(self):
target_info = BuildInfo(self.TEST_INFO_DICT, None)
self.assertEqual('value1', target_info.get('property1'))
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'])
def test_get_with_oem_props(self):
target_info = BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
self.TEST_OEM_DICTS)
self.assertEqual('value1', target_info.get('property1'))
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'])
def test_items(self):
target_info = BuildInfo(self.TEST_INFO_DICT, None)
items = target_info.items()
self.assertIn(('property1', 'value1'), items)
self.assertIn(('property2', 4096), items)
def test_GetBuildProp(self):
target_info = BuildInfo(self.TEST_INFO_DICT, None)
self.assertEqual('build-foo', target_info.GetBuildProp('ro.build.foo'))
self.assertRaises(common.ExternalError, target_info.GetBuildProp,
'ro.build.nonexistent')
def test_GetBuildProp_with_oem_props(self):
target_info = BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
self.TEST_OEM_DICTS)
self.assertEqual('build-bar', target_info.GetBuildProp('ro.build.bar'))
self.assertRaises(common.ExternalError, target_info.GetBuildProp,
'ro.build.nonexistent')
def test_GetVendorBuildProp(self):
target_info = BuildInfo(self.TEST_INFO_DICT, None)
self.assertEqual('vendor-build-fingerprint',
target_info.GetVendorBuildProp(
'ro.vendor.build.fingerprint'))
self.assertRaises(common.ExternalError, target_info.GetVendorBuildProp,
'ro.build.nonexistent')
def test_GetVendorBuildProp_with_oem_props(self):
target_info = BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
self.TEST_OEM_DICTS)
self.assertEqual('vendor-build-fingerprint',
target_info.GetVendorBuildProp(
'ro.vendor.build.fingerprint'))
self.assertRaises(common.ExternalError, target_info.GetVendorBuildProp,
'ro.build.nonexistent')
def test_vendor_fingerprint(self):
target_info = BuildInfo(self.TEST_INFO_DICT, None)
self.assertEqual('vendor-build-fingerprint',
target_info.vendor_fingerprint)
def test_vendor_fingerprint_blacklisted(self):
target_info_dict = copy.deepcopy(self.TEST_INFO_DICT_USES_OEM_PROPS)
del target_info_dict['vendor.build.prop']['ro.vendor.build.fingerprint']
target_info = BuildInfo(target_info_dict, self.TEST_OEM_DICTS)
self.assertIsNone(target_info.vendor_fingerprint)
def test_vendor_fingerprint_without_vendor_build_prop(self):
target_info_dict = copy.deepcopy(self.TEST_INFO_DICT_USES_OEM_PROPS)
del target_info_dict['vendor.build.prop']
target_info = BuildInfo(target_info_dict, self.TEST_OEM_DICTS)
self.assertIsNone(target_info.vendor_fingerprint)
def test_WriteMountOemScript(self):
target_info = BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
self.TEST_OEM_DICTS)
script_writer = test_utils.MockScriptWriter()
target_info.WriteMountOemScript(script_writer)
self.assertEqual([('Mount', '/oem', None)], script_writer.lines)
def test_WriteDeviceAssertions(self):
target_info = BuildInfo(self.TEST_INFO_DICT, None)
script_writer = test_utils.MockScriptWriter()
target_info.WriteDeviceAssertions(script_writer, False)
self.assertEqual([('AssertDevice', 'product-device')], script_writer.lines)
def test_WriteDeviceAssertions_with_oem_props(self):
target_info = BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
self.TEST_OEM_DICTS)
script_writer = test_utils.MockScriptWriter()
target_info.WriteDeviceAssertions(script_writer, False)
self.assertEqual(
[
('AssertOemProperty', 'ro.product.device',
['device1', 'device2', 'device3'], False),
('AssertOemProperty', 'ro.product.brand',
['brand1', 'brand2', 'brand3'], False),
],
script_writer.lines)
def test_WriteFingerprintAssertion_without_oem_props(self):
target_info = BuildInfo(self.TEST_INFO_DICT, None)
source_info_dict = copy.deepcopy(self.TEST_INFO_DICT)
source_info_dict['build.prop']['ro.build.fingerprint'] = (
'source-build-fingerprint')
source_info = BuildInfo(source_info_dict, None)
script_writer = test_utils.MockScriptWriter()
WriteFingerprintAssertion(script_writer, target_info, source_info)
self.assertEqual(
[('AssertSomeFingerprint', 'source-build-fingerprint',
'build-fingerprint')],
script_writer.lines)
def test_WriteFingerprintAssertion_with_source_oem_props(self):
target_info = BuildInfo(self.TEST_INFO_DICT, None)
source_info = BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
self.TEST_OEM_DICTS)
script_writer = test_utils.MockScriptWriter()
WriteFingerprintAssertion(script_writer, target_info, source_info)
self.assertEqual(
[('AssertFingerprintOrThumbprint', 'build-fingerprint',
'build-thumbprint')],
script_writer.lines)
def test_WriteFingerprintAssertion_with_target_oem_props(self):
target_info = BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
self.TEST_OEM_DICTS)
source_info = BuildInfo(self.TEST_INFO_DICT, None)
script_writer = test_utils.MockScriptWriter()
WriteFingerprintAssertion(script_writer, target_info, source_info)
self.assertEqual(
[('AssertFingerprintOrThumbprint', 'build-fingerprint',
'build-thumbprint')],
script_writer.lines)
def test_WriteFingerprintAssertion_with_both_oem_props(self):
target_info = 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-build-thumbprint')
source_info = BuildInfo(source_info_dict, self.TEST_OEM_DICTS)
script_writer = test_utils.MockScriptWriter()
WriteFingerprintAssertion(script_writer, target_info, source_info)
self.assertEqual(
[('AssertSomeThumbprint', 'build-thumbprint',
'source-build-thumbprint')],
script_writer.lines)
class LoadOemDictsTest(test_utils.ReleaseToolsTestCase):
def test_NoneDict(self):
@ -387,6 +131,35 @@ class OtaFromTargetFilesTest(test_utils.ReleaseToolsTestCase):
},
}
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',
}
TEST_OEM_DICTS = [
{
'ro.product.brand' : 'brand1',
'ro.product.device' : 'device1',
},
{
'ro.product.brand' : 'brand2',
'ro.product.device' : 'device2',
},
{
'ro.product.brand' : 'brand3',
'ro.product.device' : 'device3',
},
]
def setUp(self):
self.testdata_dir = test_utils.get_testdata_dir()
self.assertTrue(os.path.exists(self.testdata_dir))
@ -408,7 +181,7 @@ class OtaFromTargetFilesTest(test_utils.ReleaseToolsTestCase):
def test_GetPackageMetadata_abOta_full(self):
target_info_dict = copy.deepcopy(self.TEST_TARGET_INFO_DICT)
target_info_dict['ab_update'] = 'true'
target_info = BuildInfo(target_info_dict, None)
target_info = common.BuildInfo(target_info_dict, None)
metadata = GetPackageMetadata(target_info)
self.assertDictEqual(
{
@ -426,8 +199,8 @@ class OtaFromTargetFilesTest(test_utils.ReleaseToolsTestCase):
def test_GetPackageMetadata_abOta_incremental(self):
target_info_dict = copy.deepcopy(self.TEST_TARGET_INFO_DICT)
target_info_dict['ab_update'] = 'true'
target_info = BuildInfo(target_info_dict, None)
source_info = BuildInfo(self.TEST_SOURCE_INFO_DICT, None)
target_info = common.BuildInfo(target_info_dict, None)
source_info = common.BuildInfo(self.TEST_SOURCE_INFO_DICT, None)
common.OPTIONS.incremental_source = ''
metadata = GetPackageMetadata(target_info, source_info)
self.assertDictEqual(
@ -446,7 +219,7 @@ class OtaFromTargetFilesTest(test_utils.ReleaseToolsTestCase):
metadata)
def test_GetPackageMetadata_nonAbOta_full(self):
target_info = BuildInfo(self.TEST_TARGET_INFO_DICT, None)
target_info = common.BuildInfo(self.TEST_TARGET_INFO_DICT, None)
metadata = GetPackageMetadata(target_info)
self.assertDictEqual(
{
@ -461,8 +234,8 @@ class OtaFromTargetFilesTest(test_utils.ReleaseToolsTestCase):
metadata)
def test_GetPackageMetadata_nonAbOta_incremental(self):
target_info = BuildInfo(self.TEST_TARGET_INFO_DICT, None)
source_info = BuildInfo(self.TEST_SOURCE_INFO_DICT, None)
target_info = common.BuildInfo(self.TEST_TARGET_INFO_DICT, None)
source_info = common.BuildInfo(self.TEST_SOURCE_INFO_DICT, None)
common.OPTIONS.incremental_source = ''
metadata = GetPackageMetadata(target_info, source_info)
self.assertDictEqual(
@ -480,7 +253,7 @@ class OtaFromTargetFilesTest(test_utils.ReleaseToolsTestCase):
metadata)
def test_GetPackageMetadata_wipe(self):
target_info = BuildInfo(self.TEST_TARGET_INFO_DICT, None)
target_info = common.BuildInfo(self.TEST_TARGET_INFO_DICT, None)
common.OPTIONS.wipe_user_data = True
metadata = GetPackageMetadata(target_info)
self.assertDictEqual(
@ -497,7 +270,7 @@ class OtaFromTargetFilesTest(test_utils.ReleaseToolsTestCase):
metadata)
def test_GetPackageMetadata_retrofitDynamicPartitions(self):
target_info = BuildInfo(self.TEST_TARGET_INFO_DICT, None)
target_info = common.BuildInfo(self.TEST_TARGET_INFO_DICT, None)
common.OPTIONS.retrofit_dynamic_partitions = True
metadata = GetPackageMetadata(target_info)
self.assertDictEqual(
@ -526,8 +299,8 @@ class OtaFromTargetFilesTest(test_utils.ReleaseToolsTestCase):
self._test_GetPackageMetadata_swapBuildTimestamps(
target_info_dict, source_info_dict)
target_info = BuildInfo(target_info_dict, None)
source_info = BuildInfo(source_info_dict, None)
target_info = common.BuildInfo(target_info_dict, None)
source_info = common.BuildInfo(source_info_dict, None)
common.OPTIONS.incremental_source = ''
self.assertRaises(RuntimeError, GetPackageMetadata, target_info,
source_info)
@ -538,8 +311,8 @@ class OtaFromTargetFilesTest(test_utils.ReleaseToolsTestCase):
self._test_GetPackageMetadata_swapBuildTimestamps(
target_info_dict, source_info_dict)
target_info = BuildInfo(target_info_dict, None)
source_info = BuildInfo(source_info_dict, None)
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
@ -752,6 +525,59 @@ class OtaFromTargetFilesTest(test_utils.ReleaseToolsTestCase):
FinalizeMetadata(metadata, zip_file, output_file, needed_property_files)
self.assertIn('ota-test-property-files', metadata)
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-build-fingerprint')
source_info = common.BuildInfo(source_info_dict, None)
script_writer = test_utils.MockScriptWriter()
WriteFingerprintAssertion(script_writer, target_info, source_info)
self.assertEqual(
[('AssertSomeFingerprint', 'source-build-fingerprint',
'build-fingerprint-target')],
script_writer.lines)
def test_WriteFingerprintAssertion_with_source_oem_props(self):
target_info = common.BuildInfo(self.TEST_TARGET_INFO_DICT, None)
source_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
self.TEST_OEM_DICTS)
script_writer = test_utils.MockScriptWriter()
WriteFingerprintAssertion(script_writer, target_info, source_info)
self.assertEqual(
[('AssertFingerprintOrThumbprint', 'build-fingerprint-target',
'build-thumbprint')],
script_writer.lines)
def test_WriteFingerprintAssertion_with_target_oem_props(self):
target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
self.TEST_OEM_DICTS)
source_info = common.BuildInfo(self.TEST_TARGET_INFO_DICT, None)
script_writer = test_utils.MockScriptWriter()
WriteFingerprintAssertion(script_writer, target_info, source_info)
self.assertEqual(
[('AssertFingerprintOrThumbprint', 'build-fingerprint-target',
'build-thumbprint')],
script_writer.lines)
def test_WriteFingerprintAssertion_with_both_oem_props(self):
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-build-thumbprint')
source_info = common.BuildInfo(source_info_dict, self.TEST_OEM_DICTS)
script_writer = test_utils.MockScriptWriter()
WriteFingerprintAssertion(script_writer, target_info, source_info)
self.assertEqual(
[('AssertSomeThumbprint', 'build-thumbprint',
'source-build-thumbprint')],
script_writer.lines)
class TestPropertyFiles(PropertyFiles):
"""A class that extends PropertyFiles for testing purpose."""