DO NOT MERGE releasetools: Support generating downgrade incremental OTAs.

Add --downgrade flag to ota_from_target_files.py script. It allows
generating an incremental OTA that updates from a newer build to an
older one (based on timestamp comparison). "post-timestamp" line in the
metadata file will be replaced by "ota-downgrade=yes". A data wipe will
always be enforced, so "ota-wipe=yes" will also be included in the
metadata file.

Bug: 26883782
Change-Id: Iaa05f662d948b7ab632a9fbb7051cc3f8bf68c21
(cherry picked from commit 5d1825664a)
This commit is contained in:
Tao Bao 2016-02-23 11:38:39 -08:00
parent 773b017ee9
commit 177c610e7f
1 changed files with 89 additions and 25 deletions

View File

@ -64,6 +64,13 @@ Usage: ota_from_target_files [flags] input_target_files output_ota_package
the build scripts (used for developer OTA packages which the build scripts (used for developer OTA packages which
legitimately need to go back and forth). legitimately need to go back and forth).
--downgrade
Intentionally generate an incremental OTA that updates from a newer
build to an older one (based on timestamp comparison). "post-timestamp"
will be replaced by "ota-downgrade=yes" in the metadata file. A data
wipe will always be enforced, so "ota-wipe=yes" will also be included in
the metadata file.
-e (--extra_script) <file> -e (--extra_script) <file>
Insert the contents of file at the end of the update script. Insert the contents of file at the end of the update script.
@ -118,6 +125,7 @@ OPTIONS.prohibit_verbatim = set(("system/build.prop",))
OPTIONS.patch_threshold = 0.95 OPTIONS.patch_threshold = 0.95
OPTIONS.wipe_user_data = False OPTIONS.wipe_user_data = False
OPTIONS.omit_prereq = False OPTIONS.omit_prereq = False
OPTIONS.downgrade = False
OPTIONS.extra_script = None OPTIONS.extra_script = None
OPTIONS.aslr_mode = True OPTIONS.aslr_mode = True
OPTIONS.worker_threads = multiprocessing.cpu_count() // 2 OPTIONS.worker_threads = multiprocessing.cpu_count() // 2
@ -685,6 +693,8 @@ reboot_now("%(bcb_dev)s", "");
endif; endif;
endif; endif;
""" % bcb_dev) """ % bcb_dev)
script.SetProgress(1)
script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary) script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary)
WriteMetadata(metadata, output_zip) WriteMetadata(metadata, output_zip)
@ -744,13 +754,44 @@ def WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip):
source_version, OPTIONS.target_info_dict, source_version, OPTIONS.target_info_dict,
fstab=OPTIONS.source_info_dict["fstab"]) fstab=OPTIONS.source_info_dict["fstab"])
oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties")
recovery_mount_options = OPTIONS.source_info_dict.get(
"recovery_mount_options")
oem_dict = None
if oem_props is not None and len(oem_props) > 0:
if OPTIONS.oem_source is None:
raise common.ExternalError("OEM source required for this build")
script.Mount("/oem", recovery_mount_options)
oem_dict = common.LoadDictionaryFromLines(
open(OPTIONS.oem_source).readlines())
metadata = { metadata = {
"pre-device": GetBuildProp("ro.product.device", "pre-device": GetOemProperty("ro.product.device", oem_props, oem_dict,
OPTIONS.source_info_dict), OPTIONS.source_info_dict),
"post-timestamp": GetBuildProp("ro.build.date.utc", "ota-type": "BLOCK",
OPTIONS.target_info_dict),
} }
post_timestamp = GetBuildProp("ro.build.date.utc", OPTIONS.target_info_dict)
pre_timestamp = GetBuildProp("ro.build.date.utc", OPTIONS.source_info_dict)
is_downgrade = long(post_timestamp) < long(pre_timestamp)
if OPTIONS.downgrade:
metadata["ota-downgrade"] = "yes"
if not is_downgrade:
raise RuntimeError("--downgrade specified but no downgrade detected: "
"pre: %s, post: %s" % (pre_timestamp, post_timestamp))
else:
if is_downgrade:
# Non-fatal here to allow generating such a package which may require
# manual work to adjust the post-timestamp. A legit use case is that we
# cut a new build C (after having A and B), but want to enfore the
# update path of A -> C -> B. Specifying --downgrade may not help since
# that would enforce a data wipe for C -> B update.
print("\nWARNING: downgrade detected: pre: %s, post: %s.\n"
"The package may not be deployed properly. "
"Try --downgrade?\n" % (pre_timestamp, post_timestamp))
metadata["post-timestamp"] = post_timestamp
device_specific = common.DeviceSpecificParams( device_specific = common.DeviceSpecificParams(
source_zip=source_zip, source_zip=source_zip,
source_version=source_version, source_version=source_version,
@ -761,14 +802,10 @@ def WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip):
metadata=metadata, metadata=metadata,
info_dict=OPTIONS.source_info_dict) info_dict=OPTIONS.source_info_dict)
# TODO: Currently this works differently from WriteIncrementalOTAPackage(). target_fp = CalculateFingerprint(oem_props, oem_dict,
# This function doesn't consider thumbprints when writing OPTIONS.target_info_dict)
# metadata["pre/post-build"]. One possible reason is that the current source_fp = CalculateFingerprint(oem_props, oem_dict,
# devices with thumbprints are all using file-based OTAs. Long term we OPTIONS.source_info_dict)
# should factor out the common parts into a shared one to avoid further
# divergence.
source_fp = GetBuildProp("ro.build.fingerprint", OPTIONS.source_info_dict)
target_fp = GetBuildProp("ro.build.fingerprint", OPTIONS.target_info_dict)
metadata["pre-build"] = source_fp metadata["pre-build"] = source_fp
metadata["post-build"] = target_fp metadata["post-build"] = target_fp
@ -807,17 +844,6 @@ def WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip):
else: else:
vendor_diff = None vendor_diff = None
oem_props = OPTIONS.target_info_dict.get("oem_fingerprint_properties")
recovery_mount_options = OPTIONS.source_info_dict.get(
"recovery_mount_options")
oem_dict = None
if oem_props is not None and len(oem_props) > 0:
if OPTIONS.oem_source is None:
raise common.ExternalError("OEM source required for this build")
script.Mount("/oem", recovery_mount_options)
oem_dict = common.LoadDictionaryFromLines(
open(OPTIONS.oem_source).readlines())
AppendAssertions(script, OPTIONS.target_info_dict, oem_dict) AppendAssertions(script, OPTIONS.target_info_dict, oem_dict)
device_specific.IncrementalOTA_Assertions() device_specific.IncrementalOTA_Assertions()
@ -972,6 +998,7 @@ else
if OPTIONS.wipe_user_data: if OPTIONS.wipe_user_data:
script.Print("Erasing user data...") script.Print("Erasing user data...")
script.FormatPartition("/data") script.FormatPartition("/data")
metadata["ota-wipe"] = "yes"
if OPTIONS.two_step: if OPTIONS.two_step:
script.AppendExtra(""" script.AppendExtra("""
@ -1147,10 +1174,30 @@ def WriteIncrementalOTAPackage(target_zip, source_zip, output_zip):
metadata = { metadata = {
"pre-device": GetOemProperty("ro.product.device", oem_props, oem_dict, "pre-device": GetOemProperty("ro.product.device", oem_props, oem_dict,
OPTIONS.source_info_dict), OPTIONS.source_info_dict),
"post-timestamp": GetBuildProp("ro.build.date.utc", "ota-type": "FILE",
OPTIONS.target_info_dict),
} }
post_timestamp = GetBuildProp("ro.build.date.utc", OPTIONS.target_info_dict)
pre_timestamp = GetBuildProp("ro.build.date.utc", OPTIONS.source_info_dict)
is_downgrade = long(post_timestamp) < long(pre_timestamp)
if OPTIONS.downgrade:
metadata["ota-downgrade"] = "yes"
if not is_downgrade:
raise RuntimeError("--downgrade specified but no downgrade detected: "
"pre: %s, post: %s" % (pre_timestamp, post_timestamp))
else:
if is_downgrade:
# Non-fatal here to allow generating such a package which may require
# manual work to adjust the post-timestamp. A legit use case is that we
# cut a new build C (after having A and B), but want to enfore the
# update path of A -> C -> B. Specifying --downgrade may not help since
# that would enforce a data wipe for C -> B update.
print("\nWARNING: downgrade detected: pre: %s, post: %s.\n"
"The package may not be deployed properly. "
"Try --downgrade?\n" % (pre_timestamp, post_timestamp))
metadata["post-timestamp"] = post_timestamp
device_specific = common.DeviceSpecificParams( device_specific = common.DeviceSpecificParams(
source_zip=source_zip, source_zip=source_zip,
source_version=source_version, source_version=source_version,
@ -1476,6 +1523,7 @@ else
if OPTIONS.wipe_user_data: if OPTIONS.wipe_user_data:
script.Print("Erasing user data...") script.Print("Erasing user data...")
script.FormatPartition("/data") script.FormatPartition("/data")
metadata["ota-wipe"] = "yes"
if OPTIONS.two_step: if OPTIONS.two_step:
script.AppendExtra(""" script.AppendExtra("""
@ -1517,6 +1565,9 @@ def main(argv):
OPTIONS.wipe_user_data = True OPTIONS.wipe_user_data = True
elif o in ("-n", "--no_prereq"): elif o in ("-n", "--no_prereq"):
OPTIONS.omit_prereq = True OPTIONS.omit_prereq = True
elif o == "--downgrade":
OPTIONS.downgrade = True
OPTIONS.wipe_user_data = True
elif o in ("-o", "--oem_settings"): elif o in ("-o", "--oem_settings"):
OPTIONS.oem_source = a OPTIONS.oem_source = a
elif o in ("-e", "--extra_script"): elif o in ("-e", "--extra_script"):
@ -1564,6 +1615,7 @@ def main(argv):
"full_bootloader", "full_bootloader",
"wipe_user_data", "wipe_user_data",
"no_prereq", "no_prereq",
"downgrade",
"extra_script=", "extra_script=",
"worker_threads=", "worker_threads=",
"aslr_mode=", "aslr_mode=",
@ -1581,6 +1633,18 @@ def main(argv):
common.Usage(__doc__) common.Usage(__doc__)
sys.exit(1) sys.exit(1)
if OPTIONS.downgrade:
# Sanity check to enforce a data wipe.
if not OPTIONS.wipe_user_data:
raise ValueError("Cannot downgrade without a data wipe")
# We should only allow downgrading incrementals (as opposed to full).
# Otherwise the device may go back from arbitrary build with this full
# OTA package.
if OPTIONS.incremental_source is None:
raise ValueError("Cannot generate downgradable full OTAs - consider"
"using --omit_prereq?")
if OPTIONS.extra_script is not None: if OPTIONS.extra_script is not None:
OPTIONS.extra_script = open(OPTIONS.extra_script).read() OPTIONS.extra_script = open(OPTIONS.extra_script).read()