add option to generate two-step recovery files

When run with the -2 option, ota_from_target_files will generate a
package (full or incremental) that does some extra reboots in order to
install the new recovery first, so that the rest of the installation
is done with the new recovery.  This can be useful if (say) the
package installation needs some features from the newer kernel.

For incremental packages, the verification phase is still done with
the old recovery.

This is only supported on devices where the misc partition is EMMC
(not MTD).

Two-step packages are slower to install and possibly confusing to
users (they will see their device reboot four times instead of twice),
so only use this option if necessary.

Change-Id: I3267d905e5e8eb1a1eb61bf48255b8b24ffc4ad1
This commit is contained in:
Doug Zongker
2013-11-25 14:44:12 -08:00
parent 816d73f9eb
commit 9b23f2cd78
2 changed files with 144 additions and 21 deletions

View File

@@ -1263,6 +1263,7 @@ ifdef PRODUCT_EXTRA_RECOVERY_KEYS
endif
$(hide) echo "mkbootimg_args=$(BOARD_MKBOOTIMG_ARGS)" >> $(zip_root)/META/misc_info.txt
$(hide) echo "use_set_metadata=1" >> $(zip_root)/META/misc_info.txt
$(hide) echo "multistage_support=1" >> $(zip_root)/META/misc_info.txt
$(call generate-userimage-prop-dictionary, $(zip_root)/META/misc_info.txt)
@# Zip everything up, preserving symlinks
$(hide) (cd $(zip_root) && zip -qry ../$(notdir $@) .)

View File

@@ -52,6 +52,11 @@ Usage: ota_from_target_files [flags] input_target_files output_ota_package
-a (--aslr_mode) <on|off>
Specify whether to turn on ASLR for the package (on by default).
-2 (--two_step)
Generate a 'two-step' OTA package, where recovery is updated
first, so that any changes made to the system partition are done
using the new recovery (new kernel, etc.).
"""
import sys
@@ -88,6 +93,7 @@ OPTIONS.omit_prereq = False
OPTIONS.extra_script = None
OPTIONS.aslr_mode = True
OPTIONS.worker_threads = 3
OPTIONS.two_step = False
def MostPopularKey(d, default):
"""Given a dict, return the key corresponding to the largest
@@ -404,6 +410,46 @@ def WriteFullOTAPackage(input_zip, output_zip):
AppendAssertions(script, OPTIONS.info_dict)
device_specific.FullOTA_Assertions()
# Two-step package strategy (in chronological order, which is *not*
# the order in which the generated script has things):
#
# if stage is not "2/3" or "3/3":
# write recovery image to boot partition
# set stage to "2/3"
# reboot to boot partition and restart recovery
# else if stage is "2/3":
# write recovery image to recovery partition
# set stage to "3/3"
# reboot to recovery partition and restart recovery
# else:
# (stage must be "3/3")
# set stage to ""
# do normal full package installation:
# wipe and install system, boot image, etc.
# set up system to update recovery partition on first boot
# complete script normally (allow recovery to mark itself finished and reboot)
recovery_img = common.GetBootableImage("recovery.img", "recovery.img",
OPTIONS.input_tmp, "RECOVERY")
if OPTIONS.two_step:
if not OPTIONS.info_dict.get("multistage_support", None):
assert False, "two-step packages not supported by this build"
fs = OPTIONS.info_dict["fstab"]["/misc"]
assert fs.fs_type.upper() == "EMMC", \
"two-step packages only supported on devices with EMMC /misc partitions"
bcb_dev = {"bcb_dev": fs.device}
common.ZipWriteStr(output_zip, "recovery.img", recovery_img.data)
script.AppendExtra("""
if get_stage("%(bcb_dev)s", "stage") == "2/3" then
""" % bcb_dev)
script.WriteRawImage("/recovery", "recovery.img")
script.AppendExtra("""
set_stage("%(bcb_dev)s", "3/3");
reboot_now("%(bcb_dev)s", "recovery");
else if get_stage("%(bcb_dev)s", "stage") == "3/3" then
""" % bcb_dev)
device_specific.FullOTA_InstallBegin()
script.ShowProgress(0.5, 0)
@@ -424,8 +470,6 @@ def WriteFullOTAPackage(input_zip, output_zip):
boot_img = common.GetBootableImage("boot.img", "boot.img",
OPTIONS.input_tmp, "BOOT")
recovery_img = common.GetBootableImage("recovery.img", "recovery.img",
OPTIONS.input_tmp, "RECOVERY")
MakeRecoveryPatch(OPTIONS.input_tmp, output_zip, recovery_img, boot_img)
Item.GetMetadata(input_zip)
@@ -445,6 +489,19 @@ def WriteFullOTAPackage(input_zip, output_zip):
script.AppendExtra(OPTIONS.extra_script)
script.UnmountAll()
if OPTIONS.two_step:
script.AppendExtra("""
set_stage("%(bcb_dev)s", "");
""" % bcb_dev)
script.AppendExtra("else\n")
script.WriteRawImage("/boot", "recovery.img")
script.AppendExtra("""
set_stage("%(bcb_dev)s", "2/3");
reboot_now("%(bcb_dev)s", "");
endif;
endif;
""" % bcb_dev)
script.AddToZip(input_zip, output_zip)
WriteMetadata(metadata, output_zip)
@@ -560,7 +617,8 @@ def WriteIncrementalOTAPackage(target_zip, source_zip, output_zip):
OPTIONS.source_info_dict)
target_boot = common.GetBootableImage(
"/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT")
updating_boot = (source_boot.data != target_boot.data)
updating_boot = (not OPTIONS.two_step and
(source_boot.data != target_boot.data))
source_recovery = common.GetBootableImage(
"/tmp/recovery.img", "recovery.img", OPTIONS.source_tmp, "RECOVERY",
@@ -578,6 +636,46 @@ def WriteIncrementalOTAPackage(target_zip, source_zip, output_zip):
AppendAssertions(script, OPTIONS.target_info_dict)
device_specific.IncrementalOTA_Assertions()
# Two-step incremental package strategy (in chronological order,
# which is *not* the order in which the generated script has
# things):
#
# if stage is not "2/3" or "3/3":
# do verification on current system
# write recovery image to boot partition
# set stage to "2/3"
# reboot to boot partition and restart recovery
# else if stage is "2/3":
# write recovery image to recovery partition
# set stage to "3/3"
# reboot to recovery partition and restart recovery
# else:
# (stage must be "3/3")
# perform update:
# patch system files, etc.
# force full install of new boot image
# set up system to update recovery partition on first boot
# complete script normally (allow recovery to mark itself finished and reboot)
if OPTIONS.two_step:
if not OPTIONS.info_dict.get("multistage_support", None):
assert False, "two-step packages not supported by this build"
fs = OPTIONS.info_dict["fstab"]["/misc"]
assert fs.fs_type.upper() == "EMMC", \
"two-step packages only supported on devices with EMMC /misc partitions"
bcb_dev = {"bcb_dev": fs.device}
common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
script.AppendExtra("""
if get_stage("%(bcb_dev)s", "stage") == "2/3" then
""" % bcb_dev)
script.AppendExtra("sleep(20);\n");
script.WriteRawImage("/recovery", "recovery.img")
script.AppendExtra("""
set_stage("%(bcb_dev)s", "3/3");
reboot_now("%(bcb_dev)s", "recovery");
else if get_stage("%(bcb_dev)s", "stage") != "3/3" then
""" % bcb_dev)
script.Print("Verifying current system...")
device_specific.IncrementalOTA_VerifyBegin()
@@ -615,10 +713,23 @@ def WriteIncrementalOTAPackage(target_zip, source_zip, output_zip):
device_specific.IncrementalOTA_VerifyEnd()
if OPTIONS.two_step:
script.WriteRawImage("/boot", "recovery.img")
script.AppendExtra("""
set_stage("%(bcb_dev)s", "2/3");
reboot_now("%(bcb_dev)s", "");
else
""" % bcb_dev)
script.Comment("---- start making changes here ----")
device_specific.IncrementalOTA_InstallBegin()
if OPTIONS.two_step:
common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
script.WriteRawImage("/boot", "boot.img")
print "writing full boot image (forced by two-step mode)"
if OPTIONS.wipe_user_data:
script.Print("Erasing user data...")
script.FormatPartition("/data")
@@ -646,23 +757,24 @@ def WriteIncrementalOTAPackage(target_zip, source_zip, output_zip):
so_far += tf.size
script.SetProgress(so_far / total_patch_size)
if updating_boot:
# Produce the boot image by applying a patch to the current
# contents of the boot partition, and write it back to the
# partition.
script.Print("Patching boot image...")
script.ApplyPatch("%s:%s:%d:%s:%d:%s"
% (boot_type, boot_device,
source_boot.size, source_boot.sha1,
target_boot.size, target_boot.sha1),
"-",
target_boot.size, target_boot.sha1,
source_boot.sha1, "patch/boot.img.p")
so_far += target_boot.size
script.SetProgress(so_far / total_patch_size)
print "boot image changed; including."
else:
print "boot image unchanged; skipping."
if not OPTIONS.two_step:
if updating_boot:
# Produce the boot image by applying a patch to the current
# contents of the boot partition, and write it back to the
# partition.
script.Print("Patching boot image...")
script.ApplyPatch("%s:%s:%d:%s:%d:%s"
% (boot_type, boot_device,
source_boot.size, source_boot.sha1,
target_boot.size, target_boot.sha1),
"-",
target_boot.size, target_boot.sha1,
source_boot.sha1, "patch/boot.img.p")
so_far += target_boot.size
script.SetProgress(so_far / total_patch_size)
print "boot image changed; including."
else:
print "boot image unchanged; skipping."
if updating_recovery:
# Recovery is generated as a patch using both the boot image
@@ -747,6 +859,13 @@ def WriteIncrementalOTAPackage(target_zip, source_zip, output_zip):
script.ApplyPatch("/"+fn, "-", tf.size, tf.sha1, sf.sha1, "patch/"+fn+".p")
script.SetPermissions("/system/build.prop", 0, 0, 0644, None, None)
if OPTIONS.two_step:
script.AppendExtra("""
set_stage("%(bcb_dev)s", "");
endif;
endif;
""" % bcb_dev)
script.AddToZip(target_zip, output_zip)
WriteMetadata(metadata, output_zip)
@@ -773,12 +892,14 @@ def main(argv):
OPTIONS.aslr_mode = False
elif o in ("--worker_threads"):
OPTIONS.worker_threads = int(a)
elif o in ("-2", "--two_step"):
OPTIONS.two_step = True
else:
return False
return True
args = common.ParseOptions(argv, __doc__,
extra_opts="b:k:i:d:wne:a:",
extra_opts="b:k:i:d:wne:a:2",
extra_long_opts=["board_config=",
"package_key=",
"incremental_from=",
@@ -787,6 +908,7 @@ def main(argv):
"extra_script=",
"worker_threads=",
"aslr_mode=",
"two_step",
],
extra_option_handler=option_handler)