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
This commit is contained in:
Tao Bao
2016-02-23 11:38:39 -08:00
parent de4078449f
commit 5d1825664a

View File

@@ -63,6 +63,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.
@@ -126,6 +133,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
@@ -698,8 +706,9 @@ reboot_now("%(bcb_dev)s", "");
endif; endif;
endif; endif;
""" % bcb_dev) """ % bcb_dev)
script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary)
script.SetProgress(1)
script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary)
metadata["ota-required-cache"] = str(script.required_cache) metadata["ota-required-cache"] = str(script.required_cache)
WriteMetadata(metadata, output_zip) WriteMetadata(metadata, output_zip)
@@ -775,11 +784,30 @@ def WriteBlockIncrementalOTAPackage(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",
OPTIONS.target_info_dict),
"ota-type": "BLOCK", "ota-type": "BLOCK",
} }
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,
@@ -1009,6 +1037,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("""
@@ -1392,11 +1421,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",
OPTIONS.target_info_dict),
"ota-type": "FILE", "ota-type": "FILE",
} }
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,
@@ -1725,6 +1773,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("""
@@ -1767,6 +1816,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"):
@@ -1818,6 +1870,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=",
@@ -1837,6 +1890,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?")
# 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.
input_zip = zipfile.ZipFile(args[0], "r") input_zip = zipfile.ZipFile(args[0], "r")