Merge "releasetools: Add a BuildInfo class." am: b54f6157a4
am: 177f57df64
Change-Id: Id579d8dfffcc2ee6cb3009dce4b27568e2ace77d
This commit is contained in:
commit
82749884e4
3 changed files with 593 additions and 203 deletions
|
@ -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
|
||||||
|
|
|
@ -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...")
|
||||||
|
|
302
tools/releasetools/test_ota_from_target_files.py
Normal file
302
tools/releasetools/test_ota_from_target_files.py
Normal 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'])
|
Loading…
Reference in a new issue