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
diff --git a/tools/releasetools/ota_from_target_files.py b/tools/releasetools/ota_from_target_files.py
index bea33a3..73f7e33 100755
--- a/tools/releasetools/ota_from_target_files.py
+++ b/tools/releasetools/ota_from_target_files.py
@@ -63,6 +63,13 @@
the build scripts (used for developer OTA packages which
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>
Insert the contents of file at the end of the update script.
@@ -126,6 +133,7 @@
OPTIONS.patch_threshold = 0.95
OPTIONS.wipe_user_data = False
OPTIONS.omit_prereq = False
+OPTIONS.downgrade = False
OPTIONS.extra_script = None
OPTIONS.aslr_mode = True
OPTIONS.worker_threads = multiprocessing.cpu_count() // 2
@@ -698,8 +706,9 @@
endif;
endif;
""" % 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)
WriteMetadata(metadata, output_zip)
@@ -775,11 +784,30 @@
metadata = {
"pre-device": GetOemProperty("ro.product.device", oem_props, oem_dict,
OPTIONS.source_info_dict),
- "post-timestamp": GetBuildProp("ro.build.date.utc",
- OPTIONS.target_info_dict),
"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(
source_zip=source_zip,
source_version=source_version,
@@ -1009,6 +1037,7 @@
if OPTIONS.wipe_user_data:
script.Print("Erasing user data...")
script.FormatPartition("/data")
+ metadata["ota-wipe"] = "yes"
if OPTIONS.two_step:
script.AppendExtra("""
@@ -1392,11 +1421,30 @@
metadata = {
"pre-device": GetOemProperty("ro.product.device", oem_props, oem_dict,
OPTIONS.source_info_dict),
- "post-timestamp": GetBuildProp("ro.build.date.utc",
- OPTIONS.target_info_dict),
"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(
source_zip=source_zip,
source_version=source_version,
@@ -1725,6 +1773,7 @@
if OPTIONS.wipe_user_data:
script.Print("Erasing user data...")
script.FormatPartition("/data")
+ metadata["ota-wipe"] = "yes"
if OPTIONS.two_step:
script.AppendExtra("""
@@ -1767,6 +1816,9 @@
OPTIONS.wipe_user_data = True
elif o in ("-n", "--no_prereq"):
OPTIONS.omit_prereq = True
+ elif o == "--downgrade":
+ OPTIONS.downgrade = True
+ OPTIONS.wipe_user_data = True
elif o in ("-o", "--oem_settings"):
OPTIONS.oem_source = a
elif o in ("-e", "--extra_script"):
@@ -1818,6 +1870,7 @@
"full_bootloader",
"wipe_user_data",
"no_prereq",
+ "downgrade",
"extra_script=",
"worker_threads=",
"aslr_mode=",
@@ -1837,6 +1890,18 @@
common.Usage(__doc__)
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.
# For packages using A/B update, unzipping is not needed.
input_zip = zipfile.ZipFile(args[0], "r")