Reland "Add a wrapper class PartitionBuildProp""
This reverts commit 6022545272a7360f7faa5e00ad0e7a8f5ed9d5c3.
The build prop for a partition used to be a simple key:value
dictionary. But we need more fields to hold the alternative build
props overriden by the 'import' statement. Therefore, add a new
class as a wrapper for these props first.
Bug: 152167826
Change-Id: I5c952cd2a976ba1a09ddc66d56c2b8b55a61986b
Test: unittests pass
diff --git a/tools/releasetools/common.py b/tools/releasetools/common.py
index 1abf5a5..71ae21f 100644
--- a/tools/releasetools/common.py
+++ b/tools/releasetools/common.py
@@ -423,6 +423,14 @@
def items(self):
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):
"""Returns the inquired build property for the provided partition."""
# If provided a partition for this property, only look within that
@@ -431,31 +439,33 @@
prop = prop.replace("ro.product", "ro.product.{}".format(partition))
else:
prop = prop.replace("ro.", "ro.{}.".format(partition))
- try:
- return self.info_dict.get("{}.build.prop".format(partition), {})[prop]
- except KeyError:
- raise ExternalError("couldn't find %s in %s.build.prop" %
- (prop, partition))
+
+ prop_val = self._GetRawBuildProp(prop, partition)
+ if prop_val is not None:
+ return prop_val
+ raise ExternalError("couldn't find %s in %s.build.prop" %
+ (prop, partition))
def GetBuildProp(self, prop):
"""Returns the inquired build property from the standard build.prop file."""
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,))
+ prop_val = self._GetRawBuildProp(prop, None)
+ if prop_val is not None:
+ return prop_val
+
+ 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)
+ prop_val = self._GetRawBuildProp(prop, None)
if prop_val:
return prop_val
default_source_order = self._GetRoProductPropsDefaultSourceOrder()
- source_order_val = self.info_dict.get("build.prop", {}).get(
- "ro.product.property_source_order")
+ source_order_val = self._GetRawBuildProp(
+ "ro.product.property_source_order", None)
if source_order_val:
source_order = source_order_val.split(",")
else:
@@ -466,11 +476,10 @@
raise ExternalError(
"Invalid ro.product.property_source_order '{}'".format(source_order))
- for source in source_order:
+ for source_partition 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)
+ "ro.product", "ro.product.{}".format(source_partition), 1)
+ prop_val = self._GetRawBuildProp(source_prop, source_partition)
if prop_val:
return prop_val
@@ -479,11 +488,9 @@
def _GetRoProductPropsDefaultSourceOrder(self):
# NOTE: refer to CDDs and android.os.Build.VERSION for the definition and
# values of these properties for each Android release.
- android_codename = self.info_dict.get("build.prop", {}).get(
- "ro.build.version.codename")
+ android_codename = self._GetRawBuildProp("ro.build.version.codename", None)
if android_codename == "REL":
- android_version = self.info_dict.get("build.prop", {}).get(
- "ro.build.version.release")
+ android_version = self._GetRawBuildProp("ro.build.version.release", None)
if android_version == "10":
return BuildInfo._RO_PRODUCT_PROPS_DEFAULT_SOURCE_ORDER_ANDROID_10
# NOTE: float() conversion of android_version will have rounding error.
@@ -566,6 +573,20 @@
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):
"""Loads the key/value pairs from the given input target_files.
@@ -603,16 +624,7 @@
"input_file must be a path str when doing repacking"
def read_helper(fn):
- 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)
+ return ReadFromInputFile(input_file, fn)
try:
d = LoadDictionaryFromLines(read_helper("META/misc_info.txt").split("\n"))
@@ -675,13 +687,8 @@
# system and vendor.
for partition in PARTITIONS_WITH_CARE_MAP:
partition_prop = "{}.build.prop".format(partition)
- d[partition_prop] = LoadBuildProp(
- read_helper, "{}/build.prop".format(partition.upper()))
- # 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[partition_prop] = PartitionBuildProps.FromInputFile(
+ input_file, partition)
d["build.prop"] = d["system.build.prop"]
# Set up the salt (based on fingerprint) that will be used when adding AVB
@@ -696,15 +703,6 @@
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):
with open(file_path) as f:
return f.read().splitlines()
@@ -727,6 +725,61 @@
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 dictionary of build properties for the given partition.
+ prop_overrides: a dict of list. And each list holds the overridden values
+ for props_allow_override.
+ """
+
+ def __init__(self, input_file, name):
+ self.input_file = input_file
+ self.partition = name
+ self.props_allow_override = [props.format(name) for props in [
+ 'ro.product.{}.name', 'ro.product.{}.device']]
+ self.build_props = {}
+ self.prop_overrides = {}
+
+ @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):
+ """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)
+ props.build_props = LoadDictionaryFromLines(data.split('\n'))
+ return props
+
+ def GetProp(self, prop):
+ return self.build_props.get(prop)
+
+
def LoadRecoveryFSTab(read_helper, fstab_version, recovery_fstab_path,
system_root_image=False):
class Partition(object):