Skip to content

Commit

Permalink
add option to generate two-step recovery files
Browse files Browse the repository at this point in the history
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.

cherry-picked from commit 9b23f2c

Conflicts:
	core/Makefile
	tools/releasetools/ota_from_target_files

Change-Id: I4d7b7f3fe45483d9b76bef8ee973eb4e2db4dcdd
  • Loading branch information
Doug Zongker authored and Brint E. Kriebel committed Jun 17, 2014
1 parent 17ddde0 commit 734e973
Show file tree
Hide file tree
Showing 2 changed files with 146 additions and 22 deletions.
1 change: 1 addition & 0 deletions core/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -1388,6 +1388,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
$(hide) echo "update_rename_support=1" >> $(zip_root)/META/misc_info.txt
$(call generate-userimage-prop-dictionary, $(zip_root)/META/misc_info.txt)
ifdef PRODUCT_DEFAULT_DEV_CERTIFICATE
Expand Down
167 changes: 145 additions & 22 deletions tools/releasetools/ota_from_target_files
Original file line number Diff line number Diff line change
Expand Up @@ -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.).
--backup <boolean>
Enable or disable the execution of backuptool.sh.
Disabled by default.
Expand Down Expand Up @@ -103,6 +108,7 @@ OPTIONS.omit_prereq = False
OPTIONS.extra_script = None
OPTIONS.aslr_mode = True
OPTIONS.worker_threads = 3
OPTIONS.two_step = False
OPTIONS.backuptool = False
OPTIONS.override_device = 'auto'
OPTIONS.override_prop = False
Expand Down Expand Up @@ -459,6 +465,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()

if OPTIONS.backuptool:
Expand Down Expand Up @@ -486,8 +532,6 @@ def WriteFullOTAPackage(input_zip, output_zip):
boot_img = common.GetBootableImage("boot.img", "boot.img",
OPTIONS.input_tmp, "BOOT")
if not OPTIONS.no_separate_recovery:
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)
Expand All @@ -511,6 +555,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)

Expand Down Expand Up @@ -651,7 +708,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))

if not OPTIONS.no_separate_recovery:
source_recovery = common.GetBootableImage(
Expand All @@ -670,6 +728,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()
Expand Down Expand Up @@ -707,10 +805,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")
Expand Down Expand Up @@ -739,23 +850,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
Expand Down Expand Up @@ -847,6 +959,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)

Expand All @@ -872,6 +991,8 @@ 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
elif o in ("--backup"):
OPTIONS.backuptool = bool(a.lower() == 'true')
elif o in ("--override_device"):
Expand All @@ -885,7 +1006,7 @@ def main(argv):
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=",
Expand All @@ -894,10 +1015,12 @@ def main(argv):
"extra_script=",
"worker_threads=",
"aslr_mode=",
"two_step",
"backup=",
"override_device=",
"override_prop=",
"no_separate_recovery="],
"no_separate_recovery=",
],
extra_option_handler=option_handler)

if len(args) != 2:
Expand Down

0 comments on commit 734e973

Please sign in to comment.