Merge "releasetools: Add a BuildInfo class." am: b54f6157a4

am: 177f57df64

Change-Id: Id579d8dfffcc2ee6cb3009dce4b27568e2ace77d
This commit is contained in:
Tao Bao 2018-01-10 21:25:58 +00:00 committed by android-build-merger
commit 82749884e4
3 changed files with 593 additions and 203 deletions

View file

@ -77,14 +77,14 @@ class EdifyGenerator(object):
with temporary=True) to this one.""" with temporary=True) to this one."""
self.script.extend(other.script) self.script.extend(other.script)
def AssertOemProperty(self, name, values): def AssertOemProperty(self, name, values, oem_no_mount):
"""Assert that a property on the OEM paritition matches allowed values.""" """Assert that a property on the OEM paritition matches allowed values."""
if not name: if not name:
raise ValueError("must specify an OEM property") raise ValueError("must specify an OEM property")
if not values: if not values:
raise ValueError("must specify the OEM value") raise ValueError("must specify the OEM value")
get_prop_command = None
if common.OPTIONS.oem_no_mount: if oem_no_mount:
get_prop_command = 'getprop("%s")' % name get_prop_command = 'getprop("%s")' % name
else: else:
get_prop_command = 'file_getprop("/oem/oem.prop", "%s")' % name get_prop_command = 'file_getprop("/oem/oem.prop", "%s")' % name

View file

@ -49,8 +49,10 @@ Usage: ota_from_target_files [flags] input_target_files output_ota_package
-o (--oem_settings) <main_file[,additional_files...]> -o (--oem_settings) <main_file[,additional_files...]>
Comma seperated list of files used to specify the expected OEM-specific Comma seperated list of files used to specify the expected OEM-specific
properties on the OEM partition of the intended device. properties on the OEM partition of the intended device. Multiple expected
Multiple expected values can be used by providing multiple files. values can be used by providing multiple files. Only the first dict will
be used to compute fingerprint, while the rest will be used to assert
OEM-specific properties.
--oem_no_mount --oem_no_mount
For devices with OEM-specific properties but without an OEM partition, For devices with OEM-specific properties but without an OEM partition,
@ -128,16 +130,11 @@ Usage: ota_from_target_files [flags] input_target_files output_ota_package
from __future__ import print_function from __future__ import print_function
import sys
if sys.hexversion < 0x02070000:
print("Python 2.7 or newer is required.", file=sys.stderr)
sys.exit(1)
import multiprocessing import multiprocessing
import os.path import os.path
import subprocess
import shlex import shlex
import subprocess
import sys
import tempfile import tempfile
import zipfile import zipfile
@ -145,6 +142,11 @@ import common
import edify_generator import edify_generator
import sparse_img import sparse_img
if sys.hexversion < 0x02070000:
print("Python 2.7 or newer is required.", file=sys.stderr)
sys.exit(1)
OPTIONS = common.OPTIONS OPTIONS = common.OPTIONS
OPTIONS.package_key = None OPTIONS.package_key = None
OPTIONS.incremental_source = None OPTIONS.incremental_source = None
@ -179,6 +181,136 @@ METADATA_NAME = 'META-INF/com/android/metadata'
UNZIP_PATTERN = ['IMAGES/*', 'META/*'] UNZIP_PATTERN = ['IMAGES/*', 'META/*']
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.
"""
def __init__(self, info_dict, oem_dicts):
"""Initializes a BuildInfo instance with the given dicts.
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).
"""
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()
@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 oem_props(self):
return self._oem_props
def __getitem__(self, key):
return self.info_dict[key]
def get(self, key, default=None):
return self.info_dict.get(key, default)
def GetBuildProp(self, prop):
"""Returns the inquired build property."""
try:
return self.info_dict.get("build.prop", {})[prop]
except KeyError:
raise common.ExternalError("couldn't find %s in build.prop" % (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:
return self.GetBuildProp("ro.build.fingerprint")
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)
def SignOutput(temp_zip_name, output_zip_name): def SignOutput(temp_zip_name, output_zip_name):
pw = OPTIONS.key_passwords[OPTIONS.package_key] pw = OPTIONS.key_passwords[OPTIONS.package_key]
@ -186,37 +318,15 @@ def SignOutput(temp_zip_name, output_zip_name):
whole_file=True) whole_file=True)
def AppendAssertions(script, info_dict, oem_dicts=None): def _LoadOemDicts(oem_source):
oem_props = info_dict.get("oem_fingerprint_properties")
if not oem_props:
device = GetBuildProp("ro.product.device", info_dict)
script.AssertDevice(device)
else:
if not oem_dicts:
raise common.ExternalError(
"No OEM file provided to answer expected assertions")
for prop in oem_props.split():
values = []
for oem_dict in oem_dicts:
if oem_dict.get(prop):
values.append(oem_dict[prop])
if not values:
raise common.ExternalError(
"The OEM file is missing the property %s" % prop)
script.AssertOemProperty(prop, values)
def _LoadOemDicts(script, recovery_mount_options=None):
"""Returns the list of loaded OEM properties dict.""" """Returns the list of loaded OEM properties dict."""
oem_dicts = None if not oem_source:
if OPTIONS.oem_source is None: return None
raise common.ExternalError("OEM source required for this build")
if not OPTIONS.oem_no_mount and script:
script.Mount("/oem", recovery_mount_options)
oem_dicts = [] oem_dicts = []
for oem_file in OPTIONS.oem_source: for oem_file in oem_source:
oem_dicts.append(common.LoadDictionaryFromLines( with open(oem_file) as fp:
open(oem_file).readlines())) oem_dicts.append(common.LoadDictionaryFromLines(fp.readlines()))
return oem_dicts return oem_dicts
@ -267,25 +377,30 @@ def HasVendorPartition(target_files_zip):
return False return False
def HasTrebleEnabled(target_files_zip, info_dict): def HasTrebleEnabled(target_files_zip, target_info):
return (HasVendorPartition(target_files_zip) and return (HasVendorPartition(target_files_zip) and
GetBuildProp("ro.treble.enabled", info_dict) == "true") target_info.GetBuildProp("ro.treble.enabled") == "true")
def GetOemProperty(name, oem_props, oem_dict, info_dict): def WriteFingerprintAssertion(script, target_info, source_info):
if oem_props is not None and name in oem_props: source_oem_props = source_info.oem_props
return oem_dict[name] target_oem_props = target_info.oem_props
return GetBuildProp(name, info_dict)
if source_oem_props is None and target_oem_props is None:
def CalculateFingerprint(oem_props, oem_dict, info_dict): script.AssertSomeFingerprint(
if oem_props is None: source_info.fingerprint, target_info.fingerprint)
return GetBuildProp("ro.build.fingerprint", info_dict) elif source_oem_props is not None and target_oem_props is not None:
return "%s/%s/%s:%s" % ( script.AssertSomeThumbprint(
GetOemProperty("ro.product.brand", oem_props, oem_dict, info_dict), target_info.GetBuildProp("ro.build.thumbprint"),
GetOemProperty("ro.product.name", oem_props, oem_dict, info_dict), source_info.GetBuildProp("ro.build.thumbprint"))
GetOemProperty("ro.product.device", oem_props, oem_dict, info_dict), elif source_oem_props is None and target_oem_props is not None:
GetBuildProp("ro.build.thumbprint", info_dict)) script.AssertFingerprintOrThumbprint(
source_info.fingerprint,
target_info.GetBuildProp("ro.build.thumbprint"))
else:
script.AssertFingerprintOrThumbprint(
target_info.fingerprint,
source_info.GetBuildProp("ro.build.thumbprint"))
def GetImage(which, tmpdir): def GetImage(which, tmpdir):
@ -313,9 +428,8 @@ def GetImage(which, tmpdir):
return sparse_img.SparseImage(path, mappath, clobbered_blocks) return sparse_img.SparseImage(path, mappath, clobbered_blocks)
def AddCompatibilityArchiveIfTrebleEnabled(target_zip, output_zip, def AddCompatibilityArchiveIfTrebleEnabled(target_zip, output_zip, target_info,
target_info_dict, source_info=None):
source_info_dict=None):
"""Adds compatibility info into the output zip if it's Treble-enabled target. """Adds compatibility info into the output zip if it's Treble-enabled target.
Metadata used for on-device compatibility verification is retrieved from Metadata used for on-device compatibility verification is retrieved from
@ -328,9 +442,9 @@ def AddCompatibilityArchiveIfTrebleEnabled(target_zip, output_zip,
Args: Args:
target_zip: Zip file containing the source files to be included for OTA. target_zip: Zip file containing the source files to be included for OTA.
output_zip: Zip file that will be sent for OTA. output_zip: Zip file that will be sent for OTA.
target_info_dict: The dict that holds the target build info. target_info: The BuildInfo instance that holds the target build info.
source_info_dict: The dict that holds the source build info, if generating source_info: The BuildInfo instance that holds the source build info, if
an incremental OTA; None otherwise. generating an incremental OTA; None otherwise.
""" """
def AddCompatibilityArchive(system_updated, vendor_updated): def AddCompatibilityArchive(system_updated, vendor_updated):
@ -353,8 +467,8 @@ def AddCompatibilityArchiveIfTrebleEnabled(target_zip, output_zip,
# Create new archive. # Create new archive.
compatibility_archive = tempfile.NamedTemporaryFile() compatibility_archive = tempfile.NamedTemporaryFile()
compatibility_archive_zip = zipfile.ZipFile(compatibility_archive, "w", compatibility_archive_zip = zipfile.ZipFile(
compression=zipfile.ZIP_DEFLATED) compatibility_archive, "w", compression=zipfile.ZIP_DEFLATED)
# Add metadata. # Add metadata.
for file_name in compatibility_files: for file_name in compatibility_files:
@ -375,59 +489,55 @@ def AddCompatibilityArchiveIfTrebleEnabled(target_zip, output_zip,
# Will only proceed if the target has enabled the Treble support (as well as # Will only proceed if the target has enabled the Treble support (as well as
# having a /vendor partition). # having a /vendor partition).
if not HasTrebleEnabled(target_zip, target_info_dict): if not HasTrebleEnabled(target_zip, target_info):
return return
# We don't support OEM thumbprint in Treble world (which calculates # We don't support OEM thumbprint in Treble world (which calculates
# fingerprints in a different way as shown in CalculateFingerprint()). # fingerprints in a different way as shown in CalculateFingerprint()).
assert not target_info_dict.get("oem_fingerprint_properties") assert not target_info.oem_props
# Full OTA carries the info for system/vendor both. # Full OTA carries the info for system/vendor both.
if source_info_dict is None: if source_info is None:
AddCompatibilityArchive(True, True) AddCompatibilityArchive(True, True)
return return
assert not source_info_dict.get("oem_fingerprint_properties") assert not source_info.oem_props
source_fp = GetBuildProp("ro.build.fingerprint", source_info_dict) source_fp = source_info.fingerprint
target_fp = GetBuildProp("ro.build.fingerprint", target_info_dict) target_fp = target_info.fingerprint
system_updated = source_fp != target_fp system_updated = source_fp != target_fp
source_fp_vendor = GetVendorBuildProp("ro.vendor.build.fingerprint", source_fp_vendor = source_info.GetVendorBuildProp(
source_info_dict) "ro.vendor.build.fingerprint")
target_fp_vendor = GetVendorBuildProp("ro.vendor.build.fingerprint", target_fp_vendor = target_info.GetVendorBuildProp(
target_info_dict) "ro.vendor.build.fingerprint")
vendor_updated = source_fp_vendor != target_fp_vendor vendor_updated = source_fp_vendor != target_fp_vendor
AddCompatibilityArchive(system_updated, vendor_updated) AddCompatibilityArchive(system_updated, vendor_updated)
def WriteFullOTAPackage(input_zip, output_zip): def WriteFullOTAPackage(input_zip, output_zip):
# TODO: how to determine this? We don't know what version it will target_info = BuildInfo(OPTIONS.info_dict, OPTIONS.oem_dicts)
# be installed on top of. For now, we expect the API just won't
# change very often. Similarly for fstab, it might have changed
# in the target build.
script = edify_generator.EdifyGenerator(3, OPTIONS.info_dict)
oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties") # We don't know what version it will be installed on top of. We expect the API
oem_dicts = None # just won't change very often. Similarly for fstab, it might have changed in
if oem_props: # the target build.
recovery_mount_options = OPTIONS.info_dict.get("recovery_mount_options") target_api_version = target_info["recovery_api_version"]
oem_dicts = _LoadOemDicts(script, recovery_mount_options) script = edify_generator.EdifyGenerator(target_api_version, target_info)
if target_info.oem_props and not OPTIONS.oem_no_mount:
target_info.WriteMountOemScript(script)
target_fp = CalculateFingerprint(oem_props, oem_dicts and oem_dicts[0],
OPTIONS.info_dict)
metadata = { metadata = {
"post-build": target_fp, "post-build": target_info.fingerprint,
"pre-device": GetOemProperty("ro.product.device", oem_props, "pre-device": target_info.device,
oem_dicts and oem_dicts[0], "post-timestamp": target_info.GetBuildProp("ro.build.date.utc"),
OPTIONS.info_dict), "ota-type" : "BLOCK",
"post-timestamp": GetBuildProp("ro.build.date.utc", OPTIONS.info_dict),
} }
device_specific = common.DeviceSpecificParams( device_specific = common.DeviceSpecificParams(
input_zip=input_zip, input_zip=input_zip,
input_version=OPTIONS.info_dict["recovery_api_version"], input_version=target_api_version,
output_zip=output_zip, output_zip=output_zip,
script=script, script=script,
input_tmp=OPTIONS.input_tmp, input_tmp=OPTIONS.input_tmp,
@ -436,13 +546,12 @@ def WriteFullOTAPackage(input_zip, output_zip):
assert HasRecoveryPatch(input_zip) assert HasRecoveryPatch(input_zip)
metadata["ota-type"] = "BLOCK" # Assertions (e.g. downgrade check, device properties check).
ts = target_info.GetBuildProp("ro.build.date.utc")
ts = GetBuildProp("ro.build.date.utc", OPTIONS.info_dict) ts_text = target_info.GetBuildProp("ro.build.date")
ts_text = GetBuildProp("ro.build.date", OPTIONS.info_dict)
script.AssertOlderBuild(ts, ts_text) script.AssertOlderBuild(ts, ts_text)
AppendAssertions(script, OPTIONS.info_dict, oem_dicts) target_info.WriteDeviceAssertions(script, OPTIONS.oem_no_mount)
device_specific.FullOTA_Assertions() device_specific.FullOTA_Assertions()
# Two-step package strategy (in chronological order, which is *not* # Two-step package strategy (in chronological order, which is *not*
@ -468,9 +577,9 @@ def WriteFullOTAPackage(input_zip, output_zip):
recovery_img = common.GetBootableImage("recovery.img", "recovery.img", recovery_img = common.GetBootableImage("recovery.img", "recovery.img",
OPTIONS.input_tmp, "RECOVERY") OPTIONS.input_tmp, "RECOVERY")
if OPTIONS.two_step: if OPTIONS.two_step:
if not OPTIONS.info_dict.get("multistage_support", None): if not target_info.get("multistage_support"):
assert False, "two-step packages not supported by this build" assert False, "two-step packages not supported by this build"
fs = OPTIONS.info_dict["fstab"]["/misc"] fs = target_info["fstab"]["/misc"]
assert fs.fs_type.upper() == "EMMC", \ assert fs.fs_type.upper() == "EMMC", \
"two-step packages only supported on devices with EMMC /misc partitions" "two-step packages only supported on devices with EMMC /misc partitions"
bcb_dev = {"bcb_dev": fs.device} bcb_dev = {"bcb_dev": fs.device}
@ -492,7 +601,7 @@ else if get_stage("%(bcb_dev)s") == "3/3" then
script.Comment("Stage 3/3") script.Comment("Stage 3/3")
# Dump fingerprints # Dump fingerprints
script.Print("Target: %s" % target_fp) script.Print("Target: {}".format(target_info.fingerprint))
device_specific.FullOTA_InstallBegin() device_specific.FullOTA_InstallBegin()
@ -525,10 +634,9 @@ else if get_stage("%(bcb_dev)s") == "3/3" then
vendor_diff = common.BlockDifference("vendor", vendor_tgt) vendor_diff = common.BlockDifference("vendor", vendor_tgt)
vendor_diff.WriteScript(script, output_zip) vendor_diff.WriteScript(script, output_zip)
AddCompatibilityArchiveIfTrebleEnabled(input_zip, output_zip, AddCompatibilityArchiveIfTrebleEnabled(input_zip, output_zip, target_info)
OPTIONS.info_dict)
common.CheckSize(boot_img.data, "boot.img", OPTIONS.info_dict) common.CheckSize(boot_img.data, "boot.img", target_info)
common.ZipWriteStr(output_zip, "boot.img", boot_img.data) common.ZipWriteStr(output_zip, "boot.img", boot_img.data)
script.ShowProgress(0.05, 5) script.ShowProgress(0.05, 5)
@ -592,12 +700,12 @@ def GetVendorBuildProp(prop, info_dict):
"couldn't find %s in vendor.build.prop" % (prop,)) "couldn't find %s in vendor.build.prop" % (prop,))
def HandleDowngradeMetadata(metadata): def HandleDowngradeMetadata(metadata, target_info, source_info):
# Only incremental OTAs are allowed to reach here. # Only incremental OTAs are allowed to reach here.
assert OPTIONS.incremental_source is not None assert OPTIONS.incremental_source is not None
post_timestamp = GetBuildProp("ro.build.date.utc", OPTIONS.target_info_dict) post_timestamp = target_info.GetBuildProp("ro.build.date.utc")
pre_timestamp = GetBuildProp("ro.build.date.utc", OPTIONS.source_info_dict) pre_timestamp = source_info.GetBuildProp("ro.build.date.utc")
is_downgrade = long(post_timestamp) < long(pre_timestamp) is_downgrade = long(post_timestamp) < long(pre_timestamp)
if OPTIONS.downgrade: if OPTIONS.downgrade:
@ -607,72 +715,65 @@ def HandleDowngradeMetadata(metadata):
metadata["ota-downgrade"] = "yes" metadata["ota-downgrade"] = "yes"
elif OPTIONS.timestamp: elif OPTIONS.timestamp:
if not is_downgrade: if not is_downgrade:
raise RuntimeError("--timestamp specified but no timestamp hack needed: " raise RuntimeError("--override_timestamp specified but no timestamp hack "
"pre: %s, post: %s" % (pre_timestamp, post_timestamp)) "needed: pre: %s, post: %s" % (pre_timestamp,
post_timestamp))
metadata["post-timestamp"] = str(long(pre_timestamp) + 1) metadata["post-timestamp"] = str(long(pre_timestamp) + 1)
else: else:
if is_downgrade: if is_downgrade:
raise RuntimeError("Downgrade detected based on timestamp check: " raise RuntimeError("Downgrade detected based on timestamp check: "
"pre: %s, post: %s. Need to specify --timestamp OR " "pre: %s, post: %s. Need to specify "
"--downgrade to allow building the incremental." % ( "--override_timestamp OR --downgrade to allow "
pre_timestamp, post_timestamp)) "building the incremental." % (pre_timestamp,
post_timestamp))
metadata["post-timestamp"] = post_timestamp metadata["post-timestamp"] = post_timestamp
def WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip): def WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip):
source_version = OPTIONS.source_info_dict["recovery_api_version"] target_info = BuildInfo(OPTIONS.target_info_dict, OPTIONS.oem_dicts)
target_version = OPTIONS.target_info_dict["recovery_api_version"] source_info = BuildInfo(OPTIONS.source_info_dict, OPTIONS.oem_dicts)
if source_version == 0: target_api_version = target_info["recovery_api_version"]
source_api_version = source_info["recovery_api_version"]
if source_api_version == 0:
print("WARNING: generating edify script for a source that " print("WARNING: generating edify script for a source that "
"can't install it.") "can't install it.")
script = edify_generator.EdifyGenerator(
source_version, OPTIONS.target_info_dict,
fstab=OPTIONS.source_info_dict["fstab"])
source_oem_props = OPTIONS.source_info_dict.get("oem_fingerprint_properties") script = edify_generator.EdifyGenerator(
target_oem_props = OPTIONS.target_info_dict.get("oem_fingerprint_properties") source_api_version, target_info, fstab=source_info["fstab"])
oem_dicts = None
if source_oem_props or target_oem_props: if target_info.oem_props or source_info.oem_props:
recovery_mount_options = OPTIONS.source_info_dict.get( if not OPTIONS.oem_no_mount:
"recovery_mount_options") source_info.WriteMountOemScript(script)
oem_dicts = _LoadOemDicts(script, recovery_mount_options)
metadata = { metadata = {
"pre-device": GetOemProperty("ro.product.device", source_oem_props, "pre-device": source_info.device,
oem_dicts and oem_dicts[0],
OPTIONS.source_info_dict),
"ota-type": "BLOCK", "ota-type": "BLOCK",
} }
HandleDowngradeMetadata(metadata) HandleDowngradeMetadata(metadata, target_info, source_info)
device_specific = common.DeviceSpecificParams( device_specific = common.DeviceSpecificParams(
source_zip=source_zip, source_zip=source_zip,
source_version=source_version, source_version=source_api_version,
target_zip=target_zip, target_zip=target_zip,
target_version=target_version, target_version=target_api_version,
output_zip=output_zip, output_zip=output_zip,
script=script, script=script,
metadata=metadata, metadata=metadata,
info_dict=OPTIONS.source_info_dict) info_dict=source_info)
source_fp = CalculateFingerprint(source_oem_props, oem_dicts and oem_dicts[0], metadata["pre-build"] = source_info.fingerprint
OPTIONS.source_info_dict) metadata["post-build"] = target_info.fingerprint
target_fp = CalculateFingerprint(target_oem_props, oem_dicts and oem_dicts[0], metadata["pre-build-incremental"] = source_info.GetBuildProp(
OPTIONS.target_info_dict) "ro.build.version.incremental")
metadata["pre-build"] = source_fp metadata["post-build-incremental"] = target_info.GetBuildProp(
metadata["post-build"] = target_fp "ro.build.version.incremental")
metadata["pre-build-incremental"] = GetBuildProp(
"ro.build.version.incremental", OPTIONS.source_info_dict)
metadata["post-build-incremental"] = GetBuildProp(
"ro.build.version.incremental", OPTIONS.target_info_dict)
source_boot = common.GetBootableImage( source_boot = common.GetBootableImage(
"/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT", "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT", source_info)
OPTIONS.source_info_dict)
target_boot = common.GetBootableImage( target_boot = common.GetBootableImage(
"/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT") "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT", target_info)
updating_boot = (not OPTIONS.two_step and updating_boot = (not OPTIONS.two_step and
(source_boot.data != target_boot.data)) (source_boot.data != target_boot.data))
@ -683,19 +784,18 @@ def WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip):
system_tgt = GetImage("system", OPTIONS.target_tmp) system_tgt = GetImage("system", OPTIONS.target_tmp)
blockimgdiff_version = max( blockimgdiff_version = max(
int(i) for i in int(i) for i in target_info.get("blockimgdiff_versions", "1").split(","))
OPTIONS.info_dict.get("blockimgdiff_versions", "1").split(","))
assert blockimgdiff_version >= 3 assert blockimgdiff_version >= 3
# Check the first block of the source system partition for remount R/W only # Check the first block of the source system partition for remount R/W only
# if the filesystem is ext4. # if the filesystem is ext4.
system_src_partition = OPTIONS.source_info_dict["fstab"]["/system"] system_src_partition = source_info["fstab"]["/system"]
check_first_block = system_src_partition.fs_type == "ext4" check_first_block = system_src_partition.fs_type == "ext4"
# Disable using imgdiff for squashfs. 'imgdiff -z' expects input files to be # Disable using imgdiff for squashfs. 'imgdiff -z' expects input files to be
# in zip formats. However with squashfs, a) all files are compressed in LZ4; # in zip formats. However with squashfs, a) all files are compressed in LZ4;
# b) the blocks listed in block map may not contain all the bytes for a given # b) the blocks listed in block map may not contain all the bytes for a given
# file (because they're rounded to be 4K-aligned). # file (because they're rounded to be 4K-aligned).
system_tgt_partition = OPTIONS.target_info_dict["fstab"]["/system"] system_tgt_partition = target_info["fstab"]["/system"]
disable_imgdiff = (system_src_partition.fs_type == "squashfs" or disable_imgdiff = (system_src_partition.fs_type == "squashfs" or
system_tgt_partition.fs_type == "squashfs") system_tgt_partition.fs_type == "squashfs")
system_diff = common.BlockDifference("system", system_tgt, system_src, system_diff = common.BlockDifference("system", system_tgt, system_src,
@ -711,7 +811,7 @@ def WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip):
# Check first block of vendor partition for remount R/W only if # Check first block of vendor partition for remount R/W only if
# disk type is ext4 # disk type is ext4
vendor_partition = OPTIONS.source_info_dict["fstab"]["/vendor"] vendor_partition = source_info["fstab"]["/vendor"]
check_first_block = vendor_partition.fs_type == "ext4" check_first_block = vendor_partition.fs_type == "ext4"
disable_imgdiff = vendor_partition.fs_type == "squashfs" disable_imgdiff = vendor_partition.fs_type == "squashfs"
vendor_diff = common.BlockDifference("vendor", vendor_tgt, vendor_src, vendor_diff = common.BlockDifference("vendor", vendor_tgt, vendor_src,
@ -722,10 +822,10 @@ def WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip):
vendor_diff = None vendor_diff = None
AddCompatibilityArchiveIfTrebleEnabled( AddCompatibilityArchiveIfTrebleEnabled(
target_zip, output_zip, OPTIONS.target_info_dict, target_zip, output_zip, target_info, source_info)
OPTIONS.source_info_dict)
AppendAssertions(script, OPTIONS.target_info_dict, oem_dicts) # Assertions (e.g. device properties check).
target_info.WriteDeviceAssertions(script, OPTIONS.oem_no_mount)
device_specific.IncrementalOTA_Assertions() device_specific.IncrementalOTA_Assertions()
# Two-step incremental package strategy (in chronological order, # Two-step incremental package strategy (in chronological order,
@ -751,12 +851,12 @@ def WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip):
# (allow recovery to mark itself finished and reboot) # (allow recovery to mark itself finished and reboot)
if OPTIONS.two_step: if OPTIONS.two_step:
if not OPTIONS.source_info_dict.get("multistage_support", None): if not source_info.get("multistage_support"):
assert False, "two-step packages not supported by this build" assert False, "two-step packages not supported by this build"
fs = OPTIONS.source_info_dict["fstab"]["/misc"] fs = OPTIONS.source_info_dict["fstab"]["/misc"]
assert fs.fs_type.upper() == "EMMC", \ assert fs.fs_type.upper() == "EMMC", \
"two-step packages only supported on devices with EMMC /misc partitions" "two-step packages only supported on devices with EMMC /misc partitions"
bcb_dev = {"bcb_dev": fs.device} bcb_dev = {"bcb_dev" : fs.device}
common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data) common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
script.AppendExtra(""" script.AppendExtra("""
if get_stage("%(bcb_dev)s") == "2/3" then if get_stage("%(bcb_dev)s") == "2/3" then
@ -776,27 +876,14 @@ else if get_stage("%(bcb_dev)s") != "3/3" then
script.Comment("Stage 1/3") script.Comment("Stage 1/3")
# Dump fingerprints # Dump fingerprints
script.Print("Source: %s" % (source_fp,)) script.Print("Source: {}".format(source_info.fingerprint))
script.Print("Target: %s" % (target_fp,)) script.Print("Target: {}".format(target_info.fingerprint))
script.Print("Verifying current system...") script.Print("Verifying current system...")
device_specific.IncrementalOTA_VerifyBegin() device_specific.IncrementalOTA_VerifyBegin()
if source_oem_props is None and target_oem_props is None: WriteFingerprintAssertion(script, target_info, source_info)
script.AssertSomeFingerprint(source_fp, target_fp)
elif source_oem_props is not None and target_oem_props is not None:
script.AssertSomeThumbprint(
GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict),
GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
elif source_oem_props is None and target_oem_props is not None:
script.AssertFingerprintOrThumbprint(
source_fp,
GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict))
else:
script.AssertFingerprintOrThumbprint(
target_fp,
GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
# Check the required cache size (i.e. stashed blocks). # Check the required cache size (i.e. stashed blocks).
size = [] size = []
@ -806,8 +893,7 @@ else if get_stage("%(bcb_dev)s") != "3/3" then
size.append(vendor_diff.required_cache) size.append(vendor_diff.required_cache)
if updating_boot: if updating_boot:
boot_type, boot_device = common.GetTypeAndDevice( boot_type, boot_device = common.GetTypeAndDevice("/boot", source_info)
"/boot", OPTIONS.source_info_dict)
d = common.Difference(target_boot, source_boot) d = common.Difference(target_boot, source_boot)
_, _, d = d.ComputePatch() _, _, d = d.ComputePatch()
if d is None: if d is None:
@ -984,7 +1070,8 @@ def WriteABOTAPackageWithBrilloScript(target_file, output_file,
cmd.extend(["-passin", "pass:" + pw] if pw else ["-nocrypt"]) cmd.extend(["-passin", "pass:" + pw] if pw else ["-nocrypt"])
rsa_key = common.MakeTempFile(prefix="key-", suffix=".key") rsa_key = common.MakeTempFile(prefix="key-", suffix=".key")
cmd.extend(["-out", rsa_key]) cmd.extend(["-out", rsa_key])
p1 = common.Run(cmd, verbose=False, stdout=log_file, stderr=subprocess.STDOUT) p1 = common.Run(cmd, verbose=False, stdout=log_file,
stderr=subprocess.STDOUT)
p1.communicate() p1.communicate()
assert p1.returncode == 0, "openssl pkcs8 failed" assert p1.returncode == 0, "openssl pkcs8 failed"
@ -993,35 +1080,32 @@ def WriteABOTAPackageWithBrilloScript(target_file, output_file,
output_zip = zipfile.ZipFile(temp_zip_file, "w", output_zip = zipfile.ZipFile(temp_zip_file, "w",
compression=zipfile.ZIP_DEFLATED) compression=zipfile.ZIP_DEFLATED)
# Metadata to comply with Android OTA package format. if source_file is not None:
oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties", None) target_info = BuildInfo(OPTIONS.target_info_dict, OPTIONS.oem_dicts)
oem_dicts = None source_info = BuildInfo(OPTIONS.source_info_dict, OPTIONS.oem_dicts)
if oem_props: else:
oem_dicts = _LoadOemDicts(None) target_info = BuildInfo(OPTIONS.info_dict, OPTIONS.oem_dicts)
source_info = None
# Metadata to comply with Android OTA package format.
metadata = { metadata = {
"post-build": CalculateFingerprint(oem_props, oem_dicts and oem_dicts[0], "post-build" : target_info.fingerprint,
OPTIONS.info_dict), "post-build-incremental" : target_info.GetBuildProp(
"post-build-incremental" : GetBuildProp("ro.build.version.incremental", "ro.build.version.incremental"),
OPTIONS.info_dict), "ota-required-cache" : "0",
"pre-device": GetOemProperty("ro.product.device", oem_props, "ota-type" : "AB",
oem_dicts and oem_dicts[0],
OPTIONS.info_dict),
"ota-required-cache": "0",
"ota-type": "AB",
} }
if source_file is not None: if source_file is not None:
metadata["pre-build"] = CalculateFingerprint(oem_props, metadata["pre-device"] = source_info.device
oem_dicts and oem_dicts[0], metadata["pre-build"] = source_info.fingerprint
OPTIONS.source_info_dict) metadata["pre-build-incremental"] = source_info.GetBuildProp(
metadata["pre-build-incremental"] = GetBuildProp( "ro.build.version.incremental")
"ro.build.version.incremental", OPTIONS.source_info_dict)
HandleDowngradeMetadata(metadata) HandleDowngradeMetadata(metadata, target_info, source_info)
else: else:
metadata["post-timestamp"] = GetBuildProp( metadata["pre-device"] = target_info.device
"ro.build.date.utc", OPTIONS.info_dict) metadata["post-timestamp"] = target_info.GetBuildProp("ro.build.date.utc")
# 1. Generate payload. # 1. Generate payload.
payload_file = common.MakeTempFile(prefix="payload-", suffix=".bin") payload_file = common.MakeTempFile(prefix="payload-", suffix=".bin")
@ -1125,23 +1209,23 @@ def WriteABOTAPackageWithBrilloScript(target_file, output_file,
# If dm-verity is supported for the device, copy contents of care_map # If dm-verity is supported for the device, copy contents of care_map
# into A/B OTA package. # into A/B OTA package.
target_zip = zipfile.ZipFile(target_file, "r") target_zip = zipfile.ZipFile(target_file, "r")
if (OPTIONS.info_dict.get("verity") == "true" or if (target_info.get("verity") == "true" or
OPTIONS.info_dict.get("avb_enable") == "true"): target_info.get("avb_enable") == "true"):
care_map_path = "META/care_map.txt" care_map_path = "META/care_map.txt"
namelist = target_zip.namelist() namelist = target_zip.namelist()
if care_map_path in namelist: if care_map_path in namelist:
care_map_data = target_zip.read(care_map_path) care_map_data = target_zip.read(care_map_path)
common.ZipWriteStr(output_zip, "care_map.txt", care_map_data, common.ZipWriteStr(output_zip, "care_map.txt", care_map_data,
compress_type=zipfile.ZIP_STORED) compress_type=zipfile.ZIP_STORED)
else: else:
print("Warning: cannot find care map file in target_file package") print("Warning: cannot find care map file in target_file package")
# OPTIONS.source_info_dict must be None for incrementals. # source_info must be None for full OTAs.
if source_file is None: if source_file is None:
assert OPTIONS.source_info_dict is None assert source_info is None
AddCompatibilityArchiveIfTrebleEnabled( AddCompatibilityArchiveIfTrebleEnabled(
target_zip, output_zip, OPTIONS.info_dict, OPTIONS.source_info_dict) target_zip, output_zip, target_info, source_info)
common.ZipClose(target_zip) common.ZipClose(target_zip)
@ -1294,12 +1378,15 @@ def main(argv):
# Load the dict file from the zip directly to have a peek at the OTA type. # Load the dict file from the zip directly to have a peek at the OTA type.
# For packages using A/B update, unzipping is not needed. # For packages using A/B update, unzipping is not needed.
if OPTIONS.extracted_input is not None: if OPTIONS.extracted_input is not None:
OPTIONS.info_dict = common.LoadInfoDict(OPTIONS.extracted_input, OPTIONS.extracted_input) OPTIONS.info_dict = common.LoadInfoDict(OPTIONS.extracted_input,
OPTIONS.extracted_input)
else: else:
input_zip = zipfile.ZipFile(args[0], "r") input_zip = zipfile.ZipFile(args[0], "r")
OPTIONS.info_dict = common.LoadInfoDict(input_zip) OPTIONS.info_dict = common.LoadInfoDict(input_zip)
common.ZipClose(input_zip) common.ZipClose(input_zip)
OPTIONS.oem_dicts = _LoadOemDicts(OPTIONS.oem_source)
ab_update = OPTIONS.info_dict.get("ab_update") == "true" ab_update = OPTIONS.info_dict.get("ab_update") == "true"
# Use the default key to sign the package if not specified with package_key. # Use the default key to sign the package if not specified with package_key.
@ -1342,7 +1429,8 @@ def main(argv):
if OPTIONS.extracted_input is not None: if OPTIONS.extracted_input is not None:
OPTIONS.input_tmp = OPTIONS.extracted_input OPTIONS.input_tmp = OPTIONS.extracted_input
OPTIONS.target_tmp = OPTIONS.input_tmp OPTIONS.target_tmp = OPTIONS.input_tmp
OPTIONS.info_dict = common.LoadInfoDict(OPTIONS.input_tmp, OPTIONS.input_tmp) OPTIONS.info_dict = common.LoadInfoDict(OPTIONS.input_tmp,
OPTIONS.input_tmp)
input_zip = zipfile.ZipFile(args[0], "r") input_zip = zipfile.ZipFile(args[0], "r")
else: else:
print("unzipping target target-files...") print("unzipping target target-files...")

View file

@ -0,0 +1,302 @@
#
# Copyright (C) 2018 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
import copy
import unittest
import common
from ota_from_target_files import (
_LoadOemDicts, BuildInfo, WriteFingerprintAssertion)
class MockScriptWriter(object):
"""A class that mocks edify_generator.EdifyGenerator.
It simply pushes the incoming arguments onto script stack, which is to assert
the calls to EdifyGenerator functions.
"""
def __init__(self):
self.script = []
def Mount(self, *args):
self.script.append(('Mount',) + args)
def AssertDevice(self, *args):
self.script.append(('AssertDevice',) + args)
def AssertOemProperty(self, *args):
self.script.append(('AssertOemProperty',) + args)
def AssertFingerprintOrThumbprint(self, *args):
self.script.append(('AssertFingerprintOrThumbprint',) + args)
def AssertSomeFingerprint(self, *args):
self.script.append(('AssertSomeFingerprint',) + args)
def AssertSomeThumbprint(self, *args):
self.script.append(('AssertSomeThumbprint',) + args)
class BuildInfoTest(unittest.TestCase):
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___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_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_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_WriteMountOemScript(self):
target_info = BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
self.TEST_OEM_DICTS)
script_writer = MockScriptWriter()
target_info.WriteMountOemScript(script_writer)
self.assertEqual([('Mount', '/oem', None)], script_writer.script)
def test_WriteDeviceAssertions(self):
target_info = BuildInfo(self.TEST_INFO_DICT, None)
script_writer = MockScriptWriter()
target_info.WriteDeviceAssertions(script_writer, False)
self.assertEqual([('AssertDevice', 'product-device')], script_writer.script)
def test_WriteDeviceAssertions_with_oem_props(self):
target_info = BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
self.TEST_OEM_DICTS)
script_writer = 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.script)
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 = MockScriptWriter()
WriteFingerprintAssertion(script_writer, target_info, source_info)
self.assertEqual(
[('AssertSomeFingerprint', 'source-build-fingerprint',
'build-fingerprint')],
script_writer.script)
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 = MockScriptWriter()
WriteFingerprintAssertion(script_writer, target_info, source_info)
self.assertEqual(
[('AssertFingerprintOrThumbprint', 'build-fingerprint',
'build-thumbprint')],
script_writer.script)
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 = MockScriptWriter()
WriteFingerprintAssertion(script_writer, target_info, source_info)
self.assertEqual(
[('AssertFingerprintOrThumbprint', 'build-fingerprint',
'build-thumbprint')],
script_writer.script)
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 = MockScriptWriter()
WriteFingerprintAssertion(script_writer, target_info, source_info)
self.assertEqual(
[('AssertSomeThumbprint', 'build-thumbprint',
'source-build-thumbprint')],
script_writer.script)
class LoadOemDictsTest(unittest.TestCase):
def tearDown(self):
common.Cleanup()
def test_NoneDict(self):
self.assertIsNone(_LoadOemDicts(None))
def test_SingleDict(self):
dict_file = common.MakeTempFile()
with open(dict_file, 'w') as dict_fp:
dict_fp.write('abc=1\ndef=2\nxyz=foo\na.b.c=bar\n')
oem_dicts = _LoadOemDicts([dict_file])
self.assertEqual(1, len(oem_dicts))
self.assertEqual('foo', oem_dicts[0]['xyz'])
self.assertEqual('bar', oem_dicts[0]['a.b.c'])
def test_MultipleDicts(self):
oem_source = []
for i in range(3):
dict_file = common.MakeTempFile()
with open(dict_file, 'w') as dict_fp:
dict_fp.write(
'ro.build.index={}\ndef=2\nxyz=foo\na.b.c=bar\n'.format(i))
oem_source.append(dict_file)
oem_dicts = _LoadOemDicts(oem_source)
self.assertEqual(3, len(oem_dicts))
for i, oem_dict in enumerate(oem_dicts):
self.assertEqual('2', oem_dict['def'])
self.assertEqual('foo', oem_dict['xyz'])
self.assertEqual('bar', oem_dict['a.b.c'])
self.assertEqual('{}'.format(i), oem_dict['ro.build.index'])