Merge changes Iac093a40,I2fe7e93a am: 8aba9ec7c8

Change-Id: I7d79d696d261945c7287336e080064d24fc2e5da
This commit is contained in:
Tianjie Xu 2020-05-08 21:09:41 +00:00 committed by Automerger Merge Worker
commit 93eec51aa4
8 changed files with 774 additions and 231 deletions

View File

@ -548,17 +548,19 @@ def AddCareMapForAbOta(output_zip, ab_partitions, image_paths):
care_map_list += care_map care_map_list += care_map
# adds fingerprint field to the 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), prop_name_list = ["ro.{}.build.fingerprint".format(partition),
"ro.{}.build.thumbprint".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: if not present_props:
logger.warning("fingerprint is not present for partition %s", partition) logger.warning("fingerprint is not present for partition %s", partition)
property_id, fingerprint = "unknown", "unknown" property_id, fingerprint = "unknown", "unknown"
else: else:
property_id = present_props[0] property_id = present_props[0]
fingerprint = build_props[property_id] fingerprint = partition_props.GetProp(property_id)
care_map_list += [property_id, fingerprint] care_map_list += [property_id, fingerprint]
if not care_map_list: if not care_map_list:

View File

@ -509,9 +509,9 @@ def ImagePropFromGlobalDict(glob_dict, mount_point):
d = {} d = {}
if "build.prop" in glob_dict: if "build.prop" in glob_dict:
bp = glob_dict["build.prop"] timestamp = glob_dict["build.prop"].GetProp("ro.build.date.utc")
if "ro.build.date.utc" in bp: if timestamp:
d["timestamp"] = bp["ro.build.date.utc"] d["timestamp"] = timestamp
def copy_prop(src_p, dest_p): def copy_prop(src_p, dest_p):
"""Copy a property from the global dictionary. """Copy a property from the global dictionary.

View File

@ -80,8 +80,9 @@ def GetArgsForSkus(info_dict):
'--property', 'ro.boot.product.vendor.sku=' + vendor_sku] '--property', 'ro.boot.product.vendor.sku=' + vendor_sku]
for odm_sku in odm_skus for vendor_sku in vendor_skus] for odm_sku in odm_skus for vendor_sku in vendor_skus]
def GetArgsForShippingApiLevel(info_dict): 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') 'ro.product.first_api_level')
if not shipping_api_level: if not shipping_api_level:
logger.warning('Cannot determine ro.product.first_api_level') logger.warning('Cannot determine ro.product.first_api_level')

View File

@ -423,6 +423,14 @@ class BuildInfo(object):
def items(self): def items(self):
return self.info_dict.items() 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): def GetPartitionBuildProp(self, prop, partition):
"""Returns the inquired build property for the provided partition.""" """Returns the inquired build property for the provided partition."""
# If provided a partition for this property, only look within that # If provided a partition for this property, only look within that
@ -431,9 +439,10 @@ class BuildInfo(object):
prop = prop.replace("ro.product", "ro.product.{}".format(partition)) prop = prop.replace("ro.product", "ro.product.{}".format(partition))
else: else:
prop = prop.replace("ro.", "ro.{}.".format(partition)) prop = prop.replace("ro.", "ro.{}.".format(partition))
try:
return self.info_dict.get("{}.build.prop".format(partition), {})[prop] prop_val = self._GetRawBuildProp(prop, partition)
except KeyError: if prop_val is not None:
return prop_val
raise ExternalError("couldn't find %s in %s.build.prop" % raise ExternalError("couldn't find %s in %s.build.prop" %
(prop, partition)) (prop, partition))
@ -442,20 +451,21 @@ class BuildInfo(object):
if prop in BuildInfo._RO_PRODUCT_RESOLVE_PROPS: if prop in BuildInfo._RO_PRODUCT_RESOLVE_PROPS:
return self._ResolveRoProductBuildProp(prop) return self._ResolveRoProductBuildProp(prop)
try: prop_val = self._GetRawBuildProp(prop, None)
return self.info_dict.get("build.prop", {})[prop] if prop_val is not None:
except KeyError: return prop_val
raise ExternalError("couldn't find %s in build.prop" % (prop,)) raise ExternalError("couldn't find %s in build.prop" % (prop,))
def _ResolveRoProductBuildProp(self, prop): def _ResolveRoProductBuildProp(self, prop):
"""Resolves the inquired ro.product.* build property""" """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: if prop_val:
return prop_val return prop_val
default_source_order = self._GetRoProductPropsDefaultSourceOrder() default_source_order = self._GetRoProductPropsDefaultSourceOrder()
source_order_val = self.info_dict.get("build.prop", {}).get( source_order_val = self._GetRawBuildProp(
"ro.product.property_source_order") "ro.product.property_source_order", None)
if source_order_val: if source_order_val:
source_order = source_order_val.split(",") source_order = source_order_val.split(",")
else: else:
@ -466,11 +476,10 @@ class BuildInfo(object):
raise ExternalError( raise ExternalError(
"Invalid ro.product.property_source_order '{}'".format(source_order)) "Invalid ro.product.property_source_order '{}'".format(source_order))
for source in source_order: for source_partition in source_order:
source_prop = prop.replace( source_prop = prop.replace(
"ro.product", "ro.product.{}".format(source), 1) "ro.product", "ro.product.{}".format(source_partition), 1)
prop_val = self.info_dict.get( prop_val = self._GetRawBuildProp(source_prop, source_partition)
"{}.build.prop".format(source), {}).get(source_prop)
if prop_val: if prop_val:
return prop_val return prop_val
@ -479,11 +488,9 @@ class BuildInfo(object):
def _GetRoProductPropsDefaultSourceOrder(self): def _GetRoProductPropsDefaultSourceOrder(self):
# NOTE: refer to CDDs and android.os.Build.VERSION for the definition and # NOTE: refer to CDDs and android.os.Build.VERSION for the definition and
# values of these properties for each Android release. # values of these properties for each Android release.
android_codename = self.info_dict.get("build.prop", {}).get( android_codename = self._GetRawBuildProp("ro.build.version.codename", None)
"ro.build.version.codename")
if android_codename == "REL": if android_codename == "REL":
android_version = self.info_dict.get("build.prop", {}).get( android_version = self._GetRawBuildProp("ro.build.version.release", None)
"ro.build.version.release")
if android_version == "10": if android_version == "10":
return BuildInfo._RO_PRODUCT_PROPS_DEFAULT_SOURCE_ORDER_ANDROID_10 return BuildInfo._RO_PRODUCT_PROPS_DEFAULT_SOURCE_ORDER_ANDROID_10
# NOTE: float() conversion of android_version will have rounding error. # NOTE: float() conversion of android_version will have rounding error.
@ -566,6 +573,20 @@ class BuildInfo(object):
script.AssertOemProperty(prop, values, oem_no_mount) 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): def LoadInfoDict(input_file, repacking=False):
"""Loads the key/value pairs from the given input target_files. """Loads the key/value pairs from the given input target_files.
@ -603,16 +624,7 @@ def LoadInfoDict(input_file, repacking=False):
"input_file must be a path str when doing repacking" "input_file must be a path str when doing repacking"
def read_helper(fn): def read_helper(fn):
if isinstance(input_file, zipfile.ZipFile): return ReadFromInputFile(input_file, fn)
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)
try: try:
d = LoadDictionaryFromLines(read_helper("META/misc_info.txt").split("\n")) d = LoadDictionaryFromLines(read_helper("META/misc_info.txt").split("\n"))
@ -675,13 +687,8 @@ def LoadInfoDict(input_file, repacking=False):
# system and vendor. # system and vendor.
for partition in PARTITIONS_WITH_CARE_MAP: for partition in PARTITIONS_WITH_CARE_MAP:
partition_prop = "{}.build.prop".format(partition) partition_prop = "{}.build.prop".format(partition)
d[partition_prop] = LoadBuildProp( d[partition_prop] = PartitionBuildProps.FromInputFile(
read_helper, "{}/build.prop".format(partition.upper())) input_file, partition)
# Some partition might use /<partition>/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["build.prop"] = d["system.build.prop"] d["build.prop"] = d["system.build.prop"]
# Set up the salt (based on fingerprint) that will be used when adding AVB # Set up the salt (based on fingerprint) that will be used when adding AVB
@ -696,15 +703,6 @@ def LoadInfoDict(input_file, repacking=False):
return d 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): def LoadListFromFile(file_path):
with open(file_path) as f: with open(file_path) as f:
return f.read().splitlines() return f.read().splitlines()
@ -727,6 +725,116 @@ def LoadDictionaryFromLines(lines):
return d 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 dict of build properties for the given partition.
prop_overrides: a set of props that are overridden by import.
placeholder_values: A dict of runtime variables' values to replace the
placeholders in the build.prop file. We expect exactly one value for
each of the variables.
"""
def __init__(self, input_file, name, placeholder_values=None):
self.input_file = input_file
self.partition = name
self.props_allow_override = [props.format(name) for props in [
'ro.product.{}.brand', 'ro.product.{}.name', 'ro.product.{}.device']]
self.build_props = {}
self.prop_overrides = set()
self.placeholder_values = {}
if placeholder_values:
self.placeholder_values = copy.deepcopy(placeholder_values)
@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, placeholder_values=None):
"""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, placeholder_values)
props._LoadBuildProp(data)
return props
def _LoadBuildProp(self, data):
for line in data.split('\n'):
line = line.strip()
if not line or line.startswith("#"):
continue
if line.startswith("import"):
overrides = self._ImportParser(line)
duplicates = self.prop_overrides.intersection(overrides.keys())
if duplicates:
raise ValueError('prop {} is overridden multiple times'.format(
','.join(duplicates)))
self.prop_overrides = self.prop_overrides.union(overrides.keys())
self.build_props.update(overrides)
elif "=" in line:
name, value = line.split("=", 1)
if name in self.prop_overrides:
raise ValueError('prop {} is set again after overridden by import '
'statement'.format(name))
self.build_props[name] = value
def _ImportParser(self, line):
"""Parses the build prop in a given import statement."""
tokens = line.split()
if len(tokens) != 2 or tokens[0] != 'import':
raise ValueError('Unrecognized import statement {}'.format(line))
import_path = tokens[1]
if not re.match(r'^/{}/.*\.prop$'.format(self.partition), import_path):
raise ValueError('Unrecognized import path {}'.format(line))
# We only recognize a subset of import statement that the init process
# supports. And we can loose the restriction based on how the dynamic
# fingerprint is used in practice. The placeholder format should be
# ${placeholder}, and its value should be provided by the caller through
# the placeholder_values.
for prop, value in self.placeholder_values.items():
prop_place_holder = '${{{}}}'.format(prop)
if prop_place_holder in import_path:
import_path = import_path.replace(prop_place_holder, value)
if '$' in import_path:
logger.info('Unresolved place holder in import path %s', import_path)
return {}
import_path = import_path.replace('/{}'.format(self.partition),
self.partition.upper())
logger.info('Parsing build props override from %s', import_path)
lines = ReadFromInputFile(self.input_file, import_path).split('\n')
d = LoadDictionaryFromLines(lines)
return {key: val for key, val in d.items()
if key in self.props_allow_override}
def GetProp(self, prop):
return self.build_props.get(prop)
def LoadRecoveryFSTab(read_helper, fstab_version, recovery_fstab_path, def LoadRecoveryFSTab(read_helper, fstab_version, recovery_fstab_path,
system_root_image=False): system_root_image=False):
class Partition(object): class Partition(object):

View File

@ -193,6 +193,8 @@ A/B OTA specific options
from __future__ import print_function from __future__ import print_function
import collections import collections
import copy
import itertools
import logging import logging
import multiprocessing import multiprocessing
import os.path import os.path
@ -229,6 +231,7 @@ OPTIONS.include_secondary = False
OPTIONS.no_signing = False OPTIONS.no_signing = False
OPTIONS.block_based = True OPTIONS.block_based = True
OPTIONS.updater_binary = None OPTIONS.updater_binary = None
OPTIONS.oem_dicts = None
OPTIONS.oem_source = None OPTIONS.oem_source = None
OPTIONS.oem_no_mount = False OPTIONS.oem_no_mount = False
OPTIONS.full_radio = False OPTIONS.full_radio = False
@ -247,6 +250,7 @@ OPTIONS.retrofit_dynamic_partitions = False
OPTIONS.skip_compatibility_check = False OPTIONS.skip_compatibility_check = False
OPTIONS.output_metadata_path = None OPTIONS.output_metadata_path = None
OPTIONS.disable_fec_computation = False OPTIONS.disable_fec_computation = False
OPTIONS.boot_variable_values = None
METADATA_NAME = 'META-INF/com/android/metadata' METADATA_NAME = 'META-INF/com/android/metadata'
@ -1959,6 +1963,36 @@ def GenerateNonAbOtaPackage(target_file, output_file, source_file=None):
output_file) output_file)
def CalculateRuntimeFingerprints():
"""Returns a set of runtime fingerprints based on the boot variables."""
build_info = common.BuildInfo(OPTIONS.info_dict, OPTIONS.oem_dicts)
fingerprints = {build_info.fingerprint}
if not OPTIONS.boot_variable_values:
return 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()
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)
for partition in common.PARTITIONS_WITH_CARE_MAP:
partition_prop_key = "{}.build.prop".format(partition)
old_props = info_dict[partition_prop_key]
info_dict[partition_prop_key] = common.PartitionBuildProps.FromInputFile(
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
def main(argv): def main(argv):
def option_handler(o, a): def option_handler(o, a):

View File

@ -128,13 +128,16 @@ class AddImagesToTargetFilesTest(test_utils.ReleaseToolsTestCase):
'vendor_image_size' : 40960, 'vendor_image_size' : 40960,
'system_verity_block_device': '/dev/block/system', 'system_verity_block_device': '/dev/block/system',
'vendor_verity_block_device': '/dev/block/vendor', 'vendor_verity_block_device': '/dev/block/vendor',
'system.build.prop': { 'system.build.prop': common.PartitionBuildProps.FromDictionary(
'system', {
'ro.system.build.fingerprint': 'ro.system.build.fingerprint':
'google/sailfish/12345:user/dev-keys', 'google/sailfish/12345:user/dev-keys'}
}, ),
'vendor.build.prop': { 'vendor.build.prop': common.PartitionBuildProps.FromDictionary(
'ro.vendor.build.fingerprint': 'google/sailfish/678:user/dev-keys', 'vendor', {
}, 'ro.vendor.build.fingerprint':
'google/sailfish/678:user/dev-keys'}
),
} }
# Prepare the META/ folder. # Prepare the META/ folder.
@ -206,18 +209,21 @@ class AddImagesToTargetFilesTest(test_utils.ReleaseToolsTestCase):
"""Tests the case for device using AVB.""" """Tests the case for device using AVB."""
image_paths = self._test_AddCareMapForAbOta() image_paths = self._test_AddCareMapForAbOta()
OPTIONS.info_dict = { OPTIONS.info_dict = {
'extfs_sparse_flag' : '-s', 'extfs_sparse_flag': '-s',
'system_image_size' : 65536, 'system_image_size': 65536,
'vendor_image_size' : 40960, 'vendor_image_size': 40960,
'avb_system_hashtree_enable' : 'true', 'avb_system_hashtree_enable': 'true',
'avb_vendor_hashtree_enable' : 'true', 'avb_vendor_hashtree_enable': 'true',
'system.build.prop': { 'system.build.prop': common.PartitionBuildProps.FromDictionary(
'system', {
'ro.system.build.fingerprint': 'ro.system.build.fingerprint':
'google/sailfish/12345:user/dev-keys', 'google/sailfish/12345:user/dev-keys'}
}, ),
'vendor.build.prop': { 'vendor.build.prop': common.PartitionBuildProps.FromDictionary(
'ro.vendor.build.fingerprint': 'google/sailfish/678:user/dev-keys', 'vendor', {
} 'ro.vendor.build.fingerprint':
'google/sailfish/678:user/dev-keys'}
),
} }
AddCareMapForAbOta(None, ['system', 'vendor'], image_paths) AddCareMapForAbOta(None, ['system', 'vendor'], image_paths)
@ -258,17 +264,21 @@ class AddImagesToTargetFilesTest(test_utils.ReleaseToolsTestCase):
"""Tests the case for partitions with thumbprint.""" """Tests the case for partitions with thumbprint."""
image_paths = self._test_AddCareMapForAbOta() image_paths = self._test_AddCareMapForAbOta()
OPTIONS.info_dict = { OPTIONS.info_dict = {
'extfs_sparse_flag' : '-s', 'extfs_sparse_flag': '-s',
'system_image_size' : 65536, 'system_image_size': 65536,
'vendor_image_size' : 40960, 'vendor_image_size': 40960,
'system_verity_block_device': '/dev/block/system', 'system_verity_block_device': '/dev/block/system',
'vendor_verity_block_device': '/dev/block/vendor', 'vendor_verity_block_device': '/dev/block/vendor',
'system.build.prop': { 'system.build.prop': common.PartitionBuildProps.FromDictionary(
'ro.system.build.thumbprint': 'google/sailfish/123:user/dev-keys', 'system', {
}, 'ro.system.build.thumbprint':
'vendor.build.prop' : { 'google/sailfish/123:user/dev-keys'}
'ro.vendor.build.thumbprint': 'google/sailfish/456: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) AddCareMapForAbOta(None, ['system', 'vendor'], image_paths)

View File

@ -48,109 +48,124 @@ def get_2gb_string():
class BuildInfoTest(test_utils.ReleaseToolsTestCase): class BuildInfoTest(test_utils.ReleaseToolsTestCase):
TEST_INFO_DICT = { TEST_INFO_DICT = {
'build.prop' : { 'build.prop': common.PartitionBuildProps.FromDictionary(
'ro.product.device' : 'product-device', 'system', {
'ro.product.name' : 'product-name', 'ro.product.device': 'product-device',
'ro.build.fingerprint' : 'build-fingerprint', 'ro.product.name': 'product-name',
'ro.build.foo' : 'build-foo', 'ro.build.fingerprint': 'build-fingerprint',
}, 'ro.build.foo': 'build-foo'}
'system.build.prop' : { ),
'ro.product.system.brand' : 'product-brand', 'system.build.prop': common.PartitionBuildProps.FromDictionary(
'ro.product.system.name' : 'product-name', 'system', {
'ro.product.system.device' : 'product-device', 'ro.product.system.brand': 'product-brand',
'ro.system.build.version.release' : 'version-release', 'ro.product.system.name': 'product-name',
'ro.system.build.id' : 'build-id', 'ro.product.system.device': 'product-device',
'ro.system.build.version.incremental' : 'version-incremental', 'ro.system.build.version.release': 'version-release',
'ro.system.build.type' : 'build-type', 'ro.system.build.id': 'build-id',
'ro.system.build.tags' : 'build-tags', 'ro.system.build.version.incremental': 'version-incremental',
'ro.system.build.foo' : 'build-foo', 'ro.system.build.type': 'build-type',
}, 'ro.system.build.tags': 'build-tags',
'vendor.build.prop' : { 'ro.system.build.foo': 'build-foo'}
'ro.product.vendor.brand' : 'vendor-product-brand', ),
'ro.product.vendor.name' : 'vendor-product-name', 'vendor.build.prop': common.PartitionBuildProps.FromDictionary(
'ro.product.vendor.device' : 'vendor-product-device', 'vendor', {
'ro.vendor.build.version.release' : 'vendor-version-release', 'ro.product.vendor.brand': 'vendor-product-brand',
'ro.vendor.build.id' : 'vendor-build-id', 'ro.product.vendor.name': 'vendor-product-name',
'ro.vendor.build.version.incremental' : 'vendor-version-incremental', 'ro.product.vendor.device': 'vendor-product-device',
'ro.vendor.build.type' : 'vendor-build-type', 'ro.vendor.build.version.release': 'vendor-version-release',
'ro.vendor.build.tags' : 'vendor-build-tags', 'ro.vendor.build.id': 'vendor-build-id',
}, 'ro.vendor.build.version.incremental':
'property1' : 'value1', 'vendor-version-incremental',
'property2' : 4096, 'ro.vendor.build.type': 'vendor-build-type',
'ro.vendor.build.tags': 'vendor-build-tags'}
),
'property1': 'value1',
'property2': 4096,
} }
TEST_INFO_DICT_USES_OEM_PROPS = { TEST_INFO_DICT_USES_OEM_PROPS = {
'build.prop' : { 'build.prop': common.PartitionBuildProps.FromDictionary(
'ro.product.name' : 'product-name', 'system', {
'ro.build.thumbprint' : 'build-thumbprint', 'ro.product.name': 'product-name',
'ro.build.bar' : 'build-bar', 'ro.build.thumbprint': 'build-thumbprint',
}, 'ro.build.bar': 'build-bar'}
'vendor.build.prop' : { ),
'ro.vendor.build.fingerprint' : 'vendor-build-fingerprint', 'vendor.build.prop': common.PartitionBuildProps.FromDictionary(
}, 'vendor', {
'property1' : 'value1', 'ro.vendor.build.fingerprint': 'vendor-build-fingerprint'}
'property2' : 4096, ),
'oem_fingerprint_properties' : 'ro.product.device ro.product.brand', 'property1': 'value1',
'property2': 4096,
'oem_fingerprint_properties': 'ro.product.device ro.product.brand',
} }
TEST_OEM_DICTS = [ TEST_OEM_DICTS = [
{ {
'ro.product.brand' : 'brand1', 'ro.product.brand': 'brand1',
'ro.product.device' : 'device1', 'ro.product.device': 'device1',
}, },
{ {
'ro.product.brand' : 'brand2', 'ro.product.brand': 'brand2',
'ro.product.device' : 'device2', 'ro.product.device': 'device2',
}, },
{ {
'ro.product.brand' : 'brand3', 'ro.product.brand': 'brand3',
'ro.product.device' : 'device3', 'ro.product.device': 'device3',
}, },
] ]
TEST_INFO_DICT_PROPERTY_SOURCE_ORDER = { TEST_INFO_DICT_PROPERTY_SOURCE_ORDER = {
'build.prop' : { 'build.prop': common.PartitionBuildProps.FromDictionary(
'ro.build.fingerprint' : 'build-fingerprint', 'system', {
'ro.product.property_source_order' : 'ro.build.fingerprint': 'build-fingerprint',
'product,odm,vendor,system_ext,system', 'ro.product.property_source_order':
}, 'product,odm,vendor,system_ext,system'}
'system.build.prop' : { ),
'ro.product.system.device' : 'system-product-device', 'system.build.prop': common.PartitionBuildProps.FromDictionary(
}, 'system', {
'vendor.build.prop' : { 'ro.product.system.device': 'system-product-device'}
'ro.product.vendor.device' : 'vendor-product-device', ),
}, 'vendor.build.prop': common.PartitionBuildProps.FromDictionary(
'vendor', {
'ro.product.vendor.device': 'vendor-product-device'}
),
} }
TEST_INFO_DICT_PROPERTY_SOURCE_ORDER_ANDROID_10 = { TEST_INFO_DICT_PROPERTY_SOURCE_ORDER_ANDROID_10 = {
'build.prop' : { 'build.prop': common.PartitionBuildProps.FromDictionary(
'ro.build.fingerprint' : 'build-fingerprint', 'system', {
'ro.product.property_source_order' : 'ro.build.fingerprint': 'build-fingerprint',
'ro.product.property_source_order':
'product,product_services,odm,vendor,system', 'product,product_services,odm,vendor,system',
'ro.build.version.release' : '10', 'ro.build.version.release': '10',
'ro.build.version.codename' : 'REL', 'ro.build.version.codename': 'REL'}
}, ),
'system.build.prop' : { 'system.build.prop': common.PartitionBuildProps.FromDictionary(
'ro.product.system.device' : 'system-product-device', 'system', {
}, 'ro.product.system.device': 'system-product-device'}
'vendor.build.prop' : { ),
'ro.product.vendor.device' : 'vendor-product-device', 'vendor.build.prop': common.PartitionBuildProps.FromDictionary(
}, 'vendor', {
'ro.product.vendor.device': 'vendor-product-device'}
),
} }
TEST_INFO_DICT_PROPERTY_SOURCE_ORDER_ANDROID_9 = { TEST_INFO_DICT_PROPERTY_SOURCE_ORDER_ANDROID_9 = {
'build.prop' : { 'build.prop': common.PartitionBuildProps.FromDictionary(
'ro.product.device' : 'product-device', 'system', {
'ro.build.fingerprint' : 'build-fingerprint', 'ro.product.device': 'product-device',
'ro.build.version.release' : '9', 'ro.build.fingerprint': 'build-fingerprint',
'ro.build.version.codename' : 'REL', 'ro.build.version.release': '9',
}, 'ro.build.version.codename': 'REL'}
'system.build.prop' : { ),
'ro.product.system.device' : 'system-product-device', 'system.build.prop': common.PartitionBuildProps.FromDictionary(
}, 'system', {
'vendor.build.prop' : { 'ro.product.system.device': 'system-product-device'}
'ro.product.vendor.device' : 'vendor-product-device', ),
}, 'vendor.build.prop': common.PartitionBuildProps.FromDictionary(
'vendor', {
'ro.product.vendor.device': 'vendor-product-device'}
),
} }
def test_init(self): def test_init(self):
@ -178,25 +193,27 @@ class BuildInfoTest(test_utils.ReleaseToolsTestCase):
def test_init_badFingerprint(self): def test_init_badFingerprint(self):
info_dict = copy.deepcopy(self.TEST_INFO_DICT) 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) 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) self.assertRaises(ValueError, common.BuildInfo, info_dict, None)
def test___getitem__(self): def test___getitem__(self):
target_info = common.BuildInfo(self.TEST_INFO_DICT, None) target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
self.assertEqual('value1', target_info['property1']) self.assertEqual('value1', target_info['property1'])
self.assertEqual(4096, target_info['property2']) 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): def test___getitem__with_oem_props(self):
target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS, target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
self.TEST_OEM_DICTS) self.TEST_OEM_DICTS)
self.assertEqual('value1', target_info['property1']) self.assertEqual('value1', target_info['property1'])
self.assertEqual(4096, target_info['property2']) self.assertEqual(4096, target_info['property2'])
self.assertRaises(KeyError, self.assertIsNone(target_info['build.prop'].GetProp('ro.build.foo'))
lambda: target_info['build.prop']['ro.build.foo'])
def test___setitem__(self): def test___setitem__(self):
target_info = common.BuildInfo(copy.deepcopy(self.TEST_INFO_DICT), None) target_info = common.BuildInfo(copy.deepcopy(self.TEST_INFO_DICT), None)
@ -204,9 +221,11 @@ class BuildInfoTest(test_utils.ReleaseToolsTestCase):
target_info['property1'] = 'value2' target_info['property1'] = 'value2'
self.assertEqual('value2', target_info['property1']) self.assertEqual('value2', target_info['property1'])
self.assertEqual('build-foo', target_info['build.prop']['ro.build.foo']) self.assertEqual('build-foo',
target_info['build.prop']['ro.build.foo'] = 'build-bar' target_info['build.prop'].GetProp('ro.build.foo'))
self.assertEqual('build-bar', target_info['build.prop']['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): def test_get(self):
target_info = common.BuildInfo(self.TEST_INFO_DICT, None) target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
@ -214,7 +233,8 @@ class BuildInfoTest(test_utils.ReleaseToolsTestCase):
self.assertEqual(4096, target_info.get('property2')) self.assertEqual(4096, target_info.get('property2'))
self.assertEqual(4096, target_info.get('property2', 1024)) self.assertEqual(4096, target_info.get('property2', 1024))
self.assertEqual(1024, target_info.get('property-nonexistent', 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): def test_get_with_oem_props(self):
target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS, target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
@ -223,9 +243,7 @@ class BuildInfoTest(test_utils.ReleaseToolsTestCase):
self.assertEqual(4096, target_info.get('property2')) self.assertEqual(4096, target_info.get('property2'))
self.assertEqual(4096, target_info.get('property2', 1024)) self.assertEqual(4096, target_info.get('property2', 1024))
self.assertEqual(1024, target_info.get('property-nonexistent', 1024)) self.assertEqual(1024, target_info.get('property-nonexistent', 1024))
self.assertIsNone(target_info.get('build.prop').get('ro.build.foo')) self.assertIsNone(target_info.get('build.prop').GetProp('ro.build.foo'))
self.assertRaises(KeyError,
lambda: target_info.get('build.prop')['ro.build.foo'])
def test_items(self): def test_items(self):
target_info = common.BuildInfo(self.TEST_INFO_DICT, None) target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
@ -262,7 +280,8 @@ class BuildInfoTest(test_utils.ReleaseToolsTestCase):
def test_GetPartitionFingerprint_uses_fingerprint_prop_if_available(self): def test_GetPartitionFingerprint_uses_fingerprint_prop_if_available(self):
info_dict = copy.deepcopy(self.TEST_INFO_DICT) 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) target_info = common.BuildInfo(info_dict, None)
self.assertEqual( self.assertEqual(
target_info.GetPartitionFingerprint('vendor'), target_info.GetPartitionFingerprint('vendor'),
@ -303,14 +322,15 @@ class BuildInfoTest(test_utils.ReleaseToolsTestCase):
def test_ResolveRoProductProperty_FromSystem(self): def test_ResolveRoProductProperty_FromSystem(self):
info_dict = copy.deepcopy(self.TEST_INFO_DICT_PROPERTY_SOURCE_ORDER) 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) info = common.BuildInfo(info_dict, None)
self.assertEqual('system-product-device', self.assertEqual('system-product-device',
info.GetBuildProp('ro.product.device')) info.GetBuildProp('ro.product.device'))
def test_ResolveRoProductProperty_InvalidPropertySearchOrder(self): def test_ResolveRoProductProperty_InvalidPropertySearchOrder(self):
info_dict = copy.deepcopy(self.TEST_INFO_DICT_PROPERTY_SOURCE_ORDER) 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, with self.assertRaisesRegexp(common.ExternalError,
'Invalid ro.product.property_source_order'): 'Invalid ro.product.property_source_order'):
info = common.BuildInfo(info_dict, None) info = common.BuildInfo(info_dict, None)
@ -1513,12 +1533,14 @@ class CommonUtilsTest(test_utils.ReleaseToolsTestCase):
common.OPTIONS.info_dict = { common.OPTIONS.info_dict = {
'ab_update': 'true', 'ab_update': 'true',
'avb_avbtool': 'avbtool', 'avb_avbtool': 'avbtool',
'build.prop': { 'build.prop': common.PartitionBuildProps.FromDictionary(
'system', {
'ro.build.version.incremental': '6285659', 'ro.build.version.incremental': '6285659',
'ro.product.device': 'coral', 'ro.product.device': 'coral',
'ro.build.fingerprint': 'google/coral/coral:R/RP1A.200311.002/' 'ro.build.fingerprint':
'6285659:userdebug/dev-keys' 'google/coral/coral:R/RP1A.200311.002/'
} '6285659:userdebug/dev-keys'}
),
} }
common.OPTIONS.aftl_tool_path = 'aftltool' common.OPTIONS.aftl_tool_path = 'aftltool'
common.OPTIONS.aftl_server = 'log.endpoints.aftl-dev.cloud.goog:9000' common.OPTIONS.aftl_server = 'log.endpoints.aftl-dev.cloud.goog:9000'
@ -1551,12 +1573,14 @@ class CommonUtilsTest(test_utils.ReleaseToolsTestCase):
common.OPTIONS.info_dict = { common.OPTIONS.info_dict = {
'ab_update': 'true', 'ab_update': 'true',
'avb_avbtool': 'avbtool', 'avb_avbtool': 'avbtool',
'build.prop': { 'build.prop': common.PartitionBuildProps.FromDictionary(
'system', {
'ro.build.version.incremental': '6285659', 'ro.build.version.incremental': '6285659',
'ro.product.device': 'coral', 'ro.product.device': 'coral',
'ro.build.fingerprint': 'google/coral/coral:R/RP1A.200311.002/' 'ro.build.fingerprint':
'6285659:userdebug/dev-keys' 'google/coral/coral:R/RP1A.200311.002/'
} '6285659:userdebug/dev-keys'}
)
} }
common.OPTIONS.aftl_tool_path = "aftltool" common.OPTIONS.aftl_tool_path = "aftltool"
common.OPTIONS.aftl_server = "log.endpoints.aftl-dev.cloud.goog:9000" common.OPTIONS.aftl_server = "log.endpoints.aftl-dev.cloud.goog:9000"
@ -1871,3 +1895,241 @@ super_group_foo_group_size={group_foo_size}
lines = self.get_op_list(self.output_path) lines = self.get_op_list(self.output_path)
self.assertEqual(lines, ["remove foo"]) self.assertEqual(lines, ["remove foo"])
class PartitionBuildPropsTest(test_utils.ReleaseToolsTestCase):
def setUp(self):
self.odm_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:
placeholder_values = {
'ro.boot.product.device_name': ['std', 'pro']
}
partition_props = common.PartitionBuildProps.FromInputFile(
input_zip, 'odm', placeholder_values)
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(set(), partition_props.prop_overrides)
def test_parseBuildProps_singleImportStatement(self):
build_std_prop = [
'ro.product.odm.device=coral',
'ro.product.odm.name=product1',
]
build_pro_prop = [
'ro.product.odm.device=coralpro',
'ro.product.odm.name=product2',
]
input_file = self._BuildZipFile({
'ODM/etc/build.prop': '\n'.join(self.odm_build_prop),
'ODM/etc/build_std.prop': '\n'.join(build_std_prop),
'ODM/etc/build_pro.prop': '\n'.join(build_pro_prop),
})
with zipfile.ZipFile(input_file, 'r') as input_zip:
placeholder_values = {
'ro.boot.product.device_name': 'std'
}
partition_props = common.PartitionBuildProps.FromInputFile(
input_zip, 'odm', placeholder_values)
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',
'ro.product.odm.name': 'product1',
}, partition_props.build_props)
with zipfile.ZipFile(input_file, 'r') as input_zip:
placeholder_values = {
'ro.boot.product.device_name': 'pro'
}
partition_props = common.PartitionBuildProps.FromInputFile(
input_zip, 'odm', placeholder_values)
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': 'coralpro',
'ro.product.odm.name': 'product2',
}, partition_props.build_props)
def test_parseBuildProps_noPlaceHolders(self):
build_prop = copy.copy(self.odm_build_prop)
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(set(), partition_props.prop_overrides)
def test_parseBuildProps_multipleImportStatements(self):
build_prop = copy.deepcopy(self.odm_build_prop)
build_prop.append(
'import /odm/etc/build_${ro.boot.product.product_name}.prop')
build_std_prop = [
'ro.product.odm.device=coral',
]
build_pro_prop = [
'ro.product.odm.device=coralpro',
]
product1_prop = [
'ro.product.odm.name=product1',
'ro.product.not_care=not_care',
]
product2_prop = [
'ro.product.odm.name=product2',
'ro.product.not_care=not_care',
]
input_file = self._BuildZipFile({
'ODM/etc/build.prop': '\n'.join(build_prop),
'ODM/etc/build_std.prop': '\n'.join(build_std_prop),
'ODM/etc/build_pro.prop': '\n'.join(build_pro_prop),
'ODM/etc/build_product1.prop': '\n'.join(product1_prop),
'ODM/etc/build_product2.prop': '\n'.join(product2_prop),
})
with zipfile.ZipFile(input_file, 'r') as input_zip:
placeholder_values = {
'ro.boot.product.device_name': 'std',
'ro.boot.product.product_name': 'product1',
'ro.boot.product.not_care': 'not_care',
}
partition_props = common.PartitionBuildProps.FromInputFile(
input_zip, 'odm', placeholder_values)
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',
'ro.product.odm.name': 'product1'
}, partition_props.build_props)
with zipfile.ZipFile(input_file, 'r') as input_zip:
placeholder_values = {
'ro.boot.product.device_name': 'pro',
'ro.boot.product.product_name': 'product2',
'ro.boot.product.not_care': 'not_care',
}
partition_props = common.PartitionBuildProps.FromInputFile(
input_zip, 'odm', placeholder_values)
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': 'coralpro',
'ro.product.odm.name': 'product2'
}, partition_props.build_props)
def test_parseBuildProps_defineAfterOverride(self):
build_prop = copy.deepcopy(self.odm_build_prop)
build_prop.append('ro.product.odm.device=coral')
build_std_prop = [
'ro.product.odm.device=coral',
]
build_pro_prop = [
'ro.product.odm.device=coralpro',
]
input_file = self._BuildZipFile({
'ODM/etc/build.prop': '\n'.join(build_prop),
'ODM/etc/build_std.prop': '\n'.join(build_std_prop),
'ODM/etc/build_pro.prop': '\n'.join(build_pro_prop),
})
with zipfile.ZipFile(input_file, 'r') as input_zip:
placeholder_values = {
'ro.boot.product.device_name': 'std',
}
self.assertRaises(ValueError, common.PartitionBuildProps.FromInputFile,
input_zip, 'odm', placeholder_values)
def test_parseBuildProps_duplicateOverride(self):
build_prop = copy.deepcopy(self.odm_build_prop)
build_prop.append(
'import /odm/etc/build_${ro.boot.product.product_name}.prop')
build_std_prop = [
'ro.product.odm.device=coral',
'ro.product.odm.name=product1',
]
build_pro_prop = [
'ro.product.odm.device=coralpro',
]
product1_prop = [
'ro.product.odm.name=product1',
]
product2_prop = [
'ro.product.odm.name=product2',
]
input_file = self._BuildZipFile({
'ODM/etc/build.prop': '\n'.join(build_prop),
'ODM/etc/build_std.prop': '\n'.join(build_std_prop),
'ODM/etc/build_pro.prop': '\n'.join(build_pro_prop),
'ODM/etc/build_product1.prop': '\n'.join(product1_prop),
'ODM/etc/build_product2.prop': '\n'.join(product2_prop),
})
with zipfile.ZipFile(input_file, 'r') as input_zip:
placeholder_values = {
'ro.boot.product.device_name': 'std',
'ro.boot.product.product_name': 'product1',
}
self.assertRaises(ValueError, common.PartitionBuildProps.FromInputFile,
input_zip, 'odm', placeholder_values)

View File

@ -26,7 +26,8 @@ from ota_from_target_files import (
GetPackageMetadata, GetTargetFilesZipForSecondaryImages, GetPackageMetadata, GetTargetFilesZipForSecondaryImages,
GetTargetFilesZipWithoutPostinstallConfig, NonAbOtaPropertyFiles, GetTargetFilesZipWithoutPostinstallConfig, NonAbOtaPropertyFiles,
Payload, PayloadSigner, POSTINSTALL_CONFIG, PropertyFiles, Payload, PayloadSigner, POSTINSTALL_CONFIG, PropertyFiles,
StreamingPropertyFiles, WriteFingerprintAssertion) StreamingPropertyFiles, WriteFingerprintAssertion,
CalculateRuntimeFingerprints)
def construct_target_files(secondary=False): def construct_target_files(secondary=False):
@ -108,55 +109,58 @@ class LoadOemDictsTest(test_utils.ReleaseToolsTestCase):
class OtaFromTargetFilesTest(test_utils.ReleaseToolsTestCase): class OtaFromTargetFilesTest(test_utils.ReleaseToolsTestCase):
TEST_TARGET_INFO_DICT = { TEST_TARGET_INFO_DICT = {
'build.prop' : { 'build.prop': common.PartitionBuildProps.FromDictionary(
'ro.product.device' : 'product-device', 'system', {
'ro.build.fingerprint' : 'build-fingerprint-target', 'ro.product.device': 'product-device',
'ro.build.version.incremental' : 'build-version-incremental-target', 'ro.build.fingerprint': 'build-fingerprint-target',
'ro.build.version.sdk' : '27', 'ro.build.version.incremental': 'build-version-incremental-target',
'ro.build.version.security_patch' : '2017-12-01', 'ro.build.version.sdk': '27',
'ro.build.date.utc' : '1500000000', 'ro.build.version.security_patch': '2017-12-01',
}, 'ro.build.date.utc': '1500000000'}
)
} }
TEST_SOURCE_INFO_DICT = { TEST_SOURCE_INFO_DICT = {
'build.prop' : { 'build.prop': common.PartitionBuildProps.FromDictionary(
'ro.product.device' : 'product-device', 'system', {
'ro.build.fingerprint' : 'build-fingerprint-source', 'ro.product.device': 'product-device',
'ro.build.version.incremental' : 'build-version-incremental-source', 'ro.build.fingerprint': 'build-fingerprint-source',
'ro.build.version.sdk' : '25', 'ro.build.version.incremental': 'build-version-incremental-source',
'ro.build.version.security_patch' : '2016-12-01', 'ro.build.version.sdk': '25',
'ro.build.date.utc' : '1400000000', 'ro.build.version.security_patch': '2016-12-01',
}, 'ro.build.date.utc': '1400000000'}
)
} }
TEST_INFO_DICT_USES_OEM_PROPS = { TEST_INFO_DICT_USES_OEM_PROPS = {
'build.prop' : { 'build.prop': common.PartitionBuildProps.FromDictionary(
'ro.product.name' : 'product-name', 'system', {
'ro.build.thumbprint' : 'build-thumbprint', 'ro.product.name': 'product-name',
'ro.build.bar' : 'build-bar', 'ro.build.thumbprint': 'build-thumbprint',
}, 'ro.build.bar': 'build-bar'}
'vendor.build.prop' : { ),
'ro.vendor.build.fingerprint' : 'vendor-build-fingerprint', 'vendor.build.prop': common.PartitionBuildProps.FromDictionary(
}, 'vendor', {
'property1' : 'value1', 'ro.vendor.build.fingerprint': 'vendor-build-fingerprint'}
'property2' : 4096, ),
'oem_fingerprint_properties' : 'ro.product.device ro.product.brand', 'property1': 'value1',
'property2': 4096,
'oem_fingerprint_properties': 'ro.product.device ro.product.brand',
} }
TEST_OEM_DICTS = [ TEST_OEM_DICTS = [
{ {
'ro.product.brand' : 'brand1', 'ro.product.brand': 'brand1',
'ro.product.device' : 'device1', 'ro.product.device': 'device1',
}, },
{ {
'ro.product.brand' : 'brand2', 'ro.product.brand': 'brand2',
'ro.product.device' : 'device2', 'ro.product.device': 'device2',
}, },
{ {
'ro.product.brand' : 'brand3', 'ro.product.brand': 'brand3',
'ro.product.device' : 'device3', 'ro.product.device': 'device3',
}, },
] ]
@ -288,10 +292,10 @@ class OtaFromTargetFilesTest(test_utils.ReleaseToolsTestCase):
@staticmethod @staticmethod
def _test_GetPackageMetadata_swapBuildTimestamps(target_info, source_info): def _test_GetPackageMetadata_swapBuildTimestamps(target_info, source_info):
(target_info['build.prop']['ro.build.date.utc'], (target_info['build.prop'].build_props['ro.build.date.utc'],
source_info['build.prop']['ro.build.date.utc']) = ( source_info['build.prop'].build_props['ro.build.date.utc']) = (
source_info['build.prop']['ro.build.date.utc'], source_info['build.prop'].build_props['ro.build.date.utc'],
target_info['build.prop']['ro.build.date.utc']) target_info['build.prop'].build_props['ro.build.date.utc'])
def test_GetPackageMetadata_unintentionalDowngradeDetected(self): def test_GetPackageMetadata_unintentionalDowngradeDetected(self):
target_info_dict = copy.deepcopy(self.TEST_TARGET_INFO_DICT) target_info_dict = copy.deepcopy(self.TEST_TARGET_INFO_DICT)
@ -528,7 +532,7 @@ class OtaFromTargetFilesTest(test_utils.ReleaseToolsTestCase):
def test_WriteFingerprintAssertion_without_oem_props(self): def test_WriteFingerprintAssertion_without_oem_props(self):
target_info = common.BuildInfo(self.TEST_TARGET_INFO_DICT, None) target_info = common.BuildInfo(self.TEST_TARGET_INFO_DICT, None)
source_info_dict = copy.deepcopy(self.TEST_TARGET_INFO_DICT) 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-build-fingerprint')
source_info = common.BuildInfo(source_info_dict, None) source_info = common.BuildInfo(source_info_dict, None)
@ -567,7 +571,7 @@ class OtaFromTargetFilesTest(test_utils.ReleaseToolsTestCase):
target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS, target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
self.TEST_OEM_DICTS) self.TEST_OEM_DICTS)
source_info_dict = copy.deepcopy(self.TEST_INFO_DICT_USES_OEM_PROPS) 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-build-thumbprint')
source_info = common.BuildInfo(source_info_dict, self.TEST_OEM_DICTS) source_info = common.BuildInfo(source_info_dict, self.TEST_OEM_DICTS)
@ -1315,3 +1319,125 @@ class PayloadTest(test_utils.ReleaseToolsTestCase):
Payload.SECONDARY_PAYLOAD_PROPERTIES_TXT): Payload.SECONDARY_PAYLOAD_PROPERTIES_TXT):
continue continue
self.assertEqual(zipfile.ZIP_STORED, entry_info.compress_type) self.assertEqual(zipfile.ZIP_STORED, entry_info.compress_type)
class RuntimeFingerprintTest(test_utils.ReleaseToolsTestCase):
MISC_INFO = [
'recovery_api_version=3',
'fstab_version=2',
'recovery_as_boot=true',
]
BUILD_PROP = [
'ro.build.version.release=version-release',
'ro.build.id=build-id',
'ro.build.version.incremental=version-incremental',
'ro.build.type=build-type',
'ro.build.tags=build-tags',
]
VENDOR_BUILD_PROP = [
'ro.product.vendor.brand=vendor-product-brand',
'ro.product.vendor.name=vendor-product-name',
'ro.product.vendor.device=vendor-product-device'
]
def setUp(self):
common.OPTIONS.oem_dicts = None
self.test_dir = common.MakeTempDir()
self.writeFiles({'META/misc_info.txt': '\n'.join(self.MISC_INFO)})
def writeFiles(self, contents_dict):
for path, content in contents_dict.items():
abs_path = os.path.join(self.test_dir, path)
dir_name = os.path.dirname(abs_path)
if not os.path.exists(dir_name):
os.makedirs(dir_name)
with open(abs_path, 'w') as f:
f.write(content)
@staticmethod
def constructFingerprint(prefix):
return '{}:version-release/build-id/version-incremental:' \
'build-type/build-tags'.format(prefix)
def test_CalculatePossibleFingerprints_no_dynamic_fingerprint(self):
build_prop = copy.deepcopy(self.BUILD_PROP)
build_prop.extend([
'ro.product.brand=product-brand',
'ro.product.name=product-name',
'ro.product.device=product-device',
])
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.assertEqual({
self.constructFingerprint('product-brand/product-name/product-device')
}, CalculateRuntimeFingerprints())
def test_CalculatePossibleFingerprints_single_override(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',
})
common.OPTIONS.info_dict = common.LoadInfoDict(self.test_dir)
common.OPTIONS.boot_variable_values = {
'ro.boot.sku_name': ['std', 'pro']
}
self.assertEqual({
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())
def test_CalculatePossibleFingerprints_multiple_overrides(self):
vendor_build_prop = copy.deepcopy(self.VENDOR_BUILD_PROP)
vendor_build_prop.extend([
'import /vendor/etc/build_${ro.boot.sku_name}.prop',
'import /vendor/etc/build_${ro.boot.device_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_product1.prop':
'ro.product.vendor.device=vendor-device-product1',
'VENDOR/etc/build_pro.prop':
'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 = {
'ro.boot.sku_name': ['std', 'pro'],
'ro.boot.device_name': ['product1', 'product2'],
}
self.assertEqual({
self.constructFingerprint(
'vendor-product-brand/vendor-product-name/vendor-product-device'),
self.constructFingerprint(
'vendor-product-brand/vendor-product-std/vendor-device-product1'),
self.constructFingerprint(
'vendor-product-brand/vendor-product-pro/vendor-device-product1'),
self.constructFingerprint(
'vendor-product-brand/vendor-product-std/vendor-device-product2'),
self.constructFingerprint(
'vendor-product-brand/vendor-product-pro/vendor-device-product2'),
}, CalculateRuntimeFingerprints())