def IncrementalOTA_InstallEnd(info): try: target_bootloader_img = info.target_zip.read("RADIO/bootloader.img") try: source_bootloader_img = info.source_zip.read( "RADIO/bootloader.img") except KeyError: source_bootloader_img = None if source_bootloader_img == target_bootloader_img: print "bootloader unchanged; skipping" else: common.ZipWriteStr(info.output_zip, "bootloader.img", target_bootloader_img) info.script.Print("Writing bootloader...") info.script.WriteRawImage("/bootloader", "bootloader.img") except KeyError: print "no bootloader.img in target target_files; skipping install" tf = FindRadio(info.target_zip) if not tf: # failed to read TARGET radio image: don't include any radio in update. print "no radio.img in target target_files; skipping install" else: tf = common.File("radio.img", tf) sf = FindRadio(info.source_zip) if not sf: # failed to read SOURCE radio image: include the whole target # radio image. tf.AddToZip(info.output_zip) info.script.Print("Writing radio...") info.script.WriteRawImage("/radio", tf.name) else: sf = common.File("radio.img", sf) if tf.sha1 == sf.sha1: print "radio image unchanged; skipping" else: diff = common.Difference(tf, sf) common.ComputeDifferences([diff]) _, _, d = diff.GetPatch() if d is None or len( d) > tf.size * common.OPTIONS.patch_threshold: # computing difference failed, or difference is nearly as # big as the target: simply send the target. tf.AddToZip(info.output_zip) info.script.Print("Writing radio...") info.script.WriteRawImage("radio", tf.name) else: common.ZipWriteStr(info.output_zip, "radio.img.p", d) info.script.Print("Patching radio...") radio_type, radio_device = common.GetTypeAndDevice( "/radio", info.info_dict) info.script.ApplyPatch( "%s:%s:%d:%s:%d:%s" % (radio_type, radio_device, sf.size, sf.sha1, tf.size, tf.sha1), "-", tf.size, tf.sha1, sf.sha1, "radio.img.p")
def AddToOutputZip(self, output_zip, **kwargs): self.verbatim = kwargs.get("verbatim", self.verbatim) if self.update_flag: PartitionUpdater.AddToOutputZip(self, output_zip) if self.verbatim: # if verbatim, do not make patch, use whole file common.ZipWriteStr(output_zip, self.target.file_name, self.target.bin.data) print self.file_name + " changed; verbatim." else: d = common.Difference(self.target.bin, self.source.bin) _,_, d = d.ComputePatch() print "%-20s target: %d source: %d diff: %d" % ( self.file_name, self.target.bin.size, self.source.bin.size, len(d)) common.ZipWriteStr(output_zip, "patch/" + self.file_name + ".p", d) PartitionUpdater.need_cache_space = max(PartitionUpdater.need_cache_space, self.source.size) PartitionUpdater.total_verify_size += self.source.size PartitionUpdater.total_update_size += self.target.size print ("%-20s changed; including." % self.file_name) elif (self.target.bin): print ("%-20s unchanged; skipping." % self.file_name) else: print ("no target %s; skipping." % self.file_name)
def ComputeFWUpdatePatches(source_tfp_dir, target_tfp_dir, variant=None, existing_ota_zip=None): patch_list = None verbatim = None output_files = None # In case an already "fixed up" ota package is passed - Do nothing if existing_ota_zip and patch_or_verbatim_exists("fwu_image.bin", existing_ota_zip): return verbatim, patch_list, output_files src_fwupdate_data = readfile_from_provdata(source_tfp_dir, "fwu_image.bin", variant) if not src_fwupdate_data: return verbatim, patch_list, output_files tgt_fwupdate_data = readfile_from_provdata(target_tfp_dir, "fwu_image.bin", variant) if not tgt_fwupdate_data: return verbatim, patch_list, output_files src_fwupdate = common.File("fwu_image.bin", src_fwupdate_data) tgt_fwupdate = common.File("fwu_image.bin", tgt_fwupdate_data) diffs = [common.Difference(tgt_fwupdate, src_fwupdate)] common.ComputeDifferences(diffs) tf, sf, d = diffs[0].GetPatch() verbatim = False # If the patch size is almost as big as the actual file # the fwu_image will be included in the OTA verbatim. if d is None or len(d) > tf.size * 0.95: print("Firmware update image will be included verbatim") verbatim = True else: patch_list = (tf,sf) output_files = d return verbatim, patch_list, output_files
def WriteRadio(info, target_radio_img, source_radio_img=None): tf = common.File("radio.img", target_radio_img) if source_radio_img is None: tf.AddToZip(info.output_zip) info.script.Print("Writing radio...") info.script.WriteRawImage("/radio", tf.name) else: sf = common.File("radio.img", source_radio_img) if tf.sha1 == sf.sha1: print "radio image unchanged; skipping" else: diff = common.Difference(tf, sf, diff_program="bsdiff") common.ComputeDifferences([diff]) _, _, d = diff.GetPatch() if d is None or len(d) > tf.size * common.OPTIONS.patch_threshold: # computing difference failed, or difference is nearly as # big as the target: simply send the target. tf.AddToZip(info.output_zip) info.script.Print("Writing radio...") info.script.WriteRawImage("/radio", tf.name) else: common.ZipWriteStr(info.output_zip, "radio.img.p", d) info.script.Print("Patching radio...") radio_type, radio_device = common.GetTypeAndDevice( "/radio", info.info_dict) info.script.ApplyPatch( "%s:%s:%d:%s:%d:%s" % (radio_type, radio_device, sf.size, sf.sha1, tf.size, tf.sha1), "-", tf.size, tf.sha1, sf.sha1, "radio.img.p")
def IncrementalOTA_InstallEnd(info): try: target_bootloader_img = info.target_zip.read("RADIO/bootloader.img") try: source_bootloader_img = info.source_zip.read( "RADIO/bootloader.img") except KeyError: source_bootloader_img = None if source_bootloader_img == target_bootloader_img: print "bootloader unchanged; skipping" else: WriteBootloader(info, target_bootloader_img) except KeyError: print "no bootloader.img in target target_files; skipping install" tf = FindRadio(info.target_zip) if not tf: # failed to read TARGET radio image: don't include any radio in update. print "no radio.img in target target_files; skipping install" # we have checked the existence of the radio image in # IncrementalOTA_VerifyEnd(), so it won't reach here. assert common.OPTIONS.full_radio == False else: tf = common.File("radio.img", tf) sf = FindRadio(info.source_zip) if not sf or common.OPTIONS.full_radio: # failed to read SOURCE radio image or one has specified the option to # include the whole target radio image. print( "no radio image in source target_files or full_radio specified; " "installing complete image") WriteRadio(info, tf.data) else: sf = common.File("radio.img", sf) if tf.sha1 == sf.sha1: print "radio image unchanged; skipping" else: diff = common.Difference(tf, sf, diff_program="bsdiff") common.ComputeDifferences([diff]) _, _, d = diff.GetPatch() if d is None or len( d) > tf.size * common.OPTIONS.patch_threshold: # computing difference failed, or difference is nearly as # big as the target: simply send the target. WriteRadio(info, tf.data) else: common.ZipWriteStr(info.output_zip, "radio.img.p", d) info.script.Print("Patching radio...") radio_type, radio_device = common.GetTypeAndDevice( "/radio", info.info_dict) info.script.ApplyPatch( "%s:%s:%d:%s:%d:%s" % (radio_type, radio_device, sf.size, sf.sha1, tf.size, tf.sha1), "-", tf.size, tf.sha1, sf.sha1, "radio.img.p")
def MakeRecoveryPatch(input_tmp, output_zip, recovery_img, boot_img): """Generate a binary patch that creates the recovery image starting with the boot image. (Most of the space in these images is just the kernel, which is identical for the two, so the resulting patch should be efficient.) Add it to the output zip, along with a shell script that is run from init.rc on first boot to actually do the patching and install the new recovery image. recovery_img and boot_img should be File objects for the corresponding images. info should be the dictionary returned by common.LoadInfoDict() on the input target_files. Returns an Item for the shell script, which must be made executable. """ diff_program = ["imgdiff"] path = os.path.join(input_tmp, "SYSTEM", "etc", "recovery-resource.dat") if os.path.exists(path): diff_program.append("-b") diff_program.append(path) bonus_args = "-b /system/etc/recovery-resource.dat" else: bonus_args = "" d = common.Difference(recovery_img, boot_img, diff_program=diff_program) _, _, patch = d.ComputePatch() common.ZipWriteStr(output_zip, "recovery/recovery-from-boot.p", patch) Item.Get("system/recovery-from-boot.p", dir=False) boot_type, boot_device = common.GetTypeAndDevice("/boot", OPTIONS.info_dict) recovery_type, recovery_device = common.GetTypeAndDevice( "/recovery", OPTIONS.info_dict) sh = """#!/system/bin/sh if ! applypatch -c %(recovery_type)s:%(recovery_device)s:%(recovery_size)d:%(recovery_sha1)s; then log -t recovery "Installing new recovery image" applypatch %(bonus_args)s %(boot_type)s:%(boot_device)s:%(boot_size)d:%(boot_sha1)s %(recovery_type)s:%(recovery_device)s %(recovery_sha1)s %(recovery_size)d %(boot_sha1)s:/system/recovery-from-boot.p else log -t recovery "Recovery image already installed" fi """ % { 'boot_size': boot_img.size, 'boot_sha1': boot_img.sha1, 'recovery_size': recovery_img.size, 'recovery_sha1': recovery_img.sha1, 'boot_type': boot_type, 'boot_device': boot_device, 'recovery_type': recovery_type, 'recovery_device': recovery_device, 'bonus_args': bonus_args, } common.ZipWriteStr(output_zip, "recovery/etc/install-recovery.sh", sh) return Item.Get("system/etc/install-recovery.sh", dir=False)
def ComputeBootloaderPatch(source_tfp_dir, target_tfp_dir, variant=None, base_variant=None, existing_ota_zip=None): target_data = LoadBootloaderFiles(target_tfp_dir, variant=variant, base_variant=base_variant) source_data = LoadBootloaderFiles(source_tfp_dir, variant=variant, base_variant=base_variant) diffs = [] # List of files that will be included in the OTA verbatim because # they are either new or the patch is > 95% in size of the original # file. If this isn't empty you just need to call edify generator # UnpackPackageDir("bootloader", "/bootloader") verbatim_targets = [] # Returned list of common.File objects that need to be added to # the OTA archive, for each one call AddToZip() output_files = [] # Returned list of patches to be created. # Each element is a tuple of the form (path, target File object, # source File object, target file size) patch_list = [] for fn in sorted(target_data.keys()): filepath = os.path.join('bootloader', fn) if existing_ota_zip and patch_or_verbatim_exists(filepath, existing_ota_zip): continue tf = target_data[fn] sf = source_data.get(fn, None) if sf is None: verbatim_targets.append(fn) output_files.append(tf) elif tf.sha1 != sf.sha1: diffs.append(common.Difference(tf, sf)) common.ComputeDifferences(diffs) for diff in diffs: tf, sf, d = diff.GetPatch() if d is None or len(d) > tf.size * 0.95: output_files.append(tf) verbatim_targets.append(tf.name) else: output_files.append(common.File("patch/" + tf.name + ".p", d)) patch_list.append((tf, sf)) # output list of files that need to be deleted, pass this to # edify generator DeleteFiles in InstallEnd delete_files = ["/bootloader/"+i for i in sorted(source_data) if i not in target_data] return (output_files, delete_files, patch_list, verbatim_targets)
def IncrementalOTA_InstallEnd(info): tf = FindRadio(info.target_zip) if not tf: # failed to read TARGET radio image: don't include any radio in update. print "no radio.img in target target_files; skipping install" else: tf = common.File("uboot.bin", tf) sf = FindRadio(info.source_zip) if not sf: # failed to read SOURCE radio image: include the whole target # radio image. WriteRadio(info, tf.data) else: sf = common.File("uboot.bin", sf) if tf.sha1 == sf.sha1: print "u-boot-with-spl-mbr-gpt.bin unchanged; skipping" else: diff = common.Difference(tf, sf, diff_program="bsdiff") common.ComputeDifferences([diff]) _, _, d = diff.GetPatch() if d is None or len( d) > tf.size * common.OPTIONS.patch_threshold: # computing difference failed, or difference is nearly as # big as the target: simply send the target. WriteRadio(info, tf.data) else: common.ZipWriteStr(info.output_zip, "u-boot-with-spl-mbr-gpt.bin.p", d) info.script.Print("Patching uboot...") uboot_type, uboot_device = common.GetTypeAndDevice( "/uboot", info.info_dict) info.script.PatchCheck("%s:%s:%d:%s:%d:%s" % (uboot_type, uboot_device, sf.size, sf.sha1, tf.size, tf.sha1)) info.script.ApplyPatch( "%s:%s:%d:%s:%d:%s" % (uboot_type, uboot_device, sf.size, sf.sha1, tf.size, tf.sha1), "-", tf.size, tf.sha1, sf.sha1, "u-boot-with-spl-mbr-gpt.bin.p")
def ComputeBinOrImgPatches(source_tfp_dir, target_tfp_dir, filename=None, variant=None, existing_ota_zip=None): patch_list = [] verbatim = None output_files = None if filename is None: print("Error input, no filename ") return [None, None, None] source_loader_filepath = os.path.join(source_tfp_dir, "RADIO", filename) if not os.path.exists(source_loader_filepath): print("Source:Can't find ", source_loader_filepath) return [None, None, None] source_loader_file = open(source_loader_filepath) source_loader_data = source_loader_file.read() source_loader_file.close() target_loader_filepath = os.path.join(target_tfp_dir, "RADIO", filename) if not os.path.exists(target_loader_filepath): print("Target Can't find ", target_loader_filepath) return [None, None, None] target_loader_file = open(target_loader_filepath) target_loader_data = target_loader_file.read() target_loader_file.close() src_bin = common.File(filename, source_loader_data) tgt_bin = common.File(filename, target_loader_data) diffs = [common.Difference(tgt_bin, src_bin)] common.ComputeDifferences(diffs) tf, sf, d = diffs[0].GetPatch() verbatim = False # If the patch size is almost as big as the actual file # the image will be included in the OTA verbatim. if d is None or len(d) > tf.size * 0.95: print(filename, "update will be included verbatim") verbatim = True else: patch_list = (tf,sf) output_files = d return verbatim, patch_list, output_files
def IncrementalEspUpdateInit(info): global target_data global source_data global delete_files global bootloader_update target_data = LoadBootloaderFiles(OPTIONS.target_tmp) source_data = LoadBootloaderFiles(OPTIONS.source_tmp) diffs = [] for fn in sorted(target_data.keys()): tf = target_data[fn] sf = source_data.get(fn, None) if sf is None: tf.AddToZip(info.output_zip) verbatim_targets.append(fn) elif tf.sha1 != sf.sha1: diffs.append(common.Difference(tf, sf)) common.ComputeDifferences(diffs) for diff in diffs: tf, sf, d = diff.GetPatch() if d is None or len(d) > tf.size * 0.95: tf.AddToZip(info.output_zip) verbatim_targets.append(tf.name) else: common.ZipWriteStr(info.output_zip, "patch/" + tf.name + ".p", d) patch_list.append( (tf.name, tf, sf, tf.size, common.sha1(d).hexdigest())) delete_files = ( ["/" + i[0] for i in verbatim_targets] + ["/" + i for i in sorted(source_data) if i not in target_data]) if (delete_files or patch_list or verbatim_targets or os.path.exists(os.path.join(OPTIONS.target_tmp, sfu_path))): print "EFI System Partition will be updated" bootloader_update = True
def update_raw_image_verify(info, in_img, out_img, node, inc): if inc: src = get_image(info.source_zip, in_img) tgt = get_image(info.target_zip, in_img) else: src = None tgt = get_image(info.input_zip, in_img) if not tgt: return imgtype, imgdev = common.GetTypeAndDevice(node, info.info_dict) if src: if src.data == tgt.data: print "%s images identical, not patching" % (in_img, ) return else: print "%s images differ, will patch" % (in_img, ) d = common.Difference(tgt, src) _, _, d = d.ComputePatch() print "%s target: %d source: %d diff: %d" % (out_img, tgt.size, src.size, len(d)) out_img = "patch/%s.p" % (out_img, ) common.ZipWriteStr(info.output_zip, out_img, d) info.script.PatchCheck( "%s:%s:%d:%s:%d:%s" % (imgtype, imgdev, src.size, src.sha1, tgt.size, tgt.sha1)) info.script.CacheFreeSpaceCheck(src.size) img[node] = {} img[node]["out_img"] = out_img img[node]["src"] = src img[node]["tgt"] = tgt img[node]["type"] = imgtype img[node]["dev"] = imgdev
def IncrementalOTA_VerifyEnd(info): global patchinfo print "Calculating fwupdate patch information" src_fwupdate = get_file_data(OPTIONS.source_tmp, "fwu_image.bin") tgt_fwupdate = get_file_data(OPTIONS.target_tmp, "fwu_image.bin") diffs = [common.Difference(tgt_fwupdate, src_fwupdate)] common.ComputeDifferences(diffs) tf, sf, d = diffs[0].GetPatch() # If the patch size is almost as big as the actual file don't bother if d is None or len(d) > tf.size * 0.95: print "Firmware update image will be included verbatim" return common.ZipWriteStr(info.output_zip, "patch/fwu_image.bin.p", d) fwu_type, fwu_device = common.GetTypeAndDevice("/fwupdate", OPTIONS.info_dict) # This check ensure fwupdate partition is in an expected state before # the OTA system makes any changes info.script.PatchCheck("%s:%s:%d:%s:%d:%s" % (fwu_type, fwu_device, sf.size, sf.sha1, tf.size, tf.sha1)) patchinfo = (fwu_type, fwu_device, sf, tf)
def WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_file): target_info = common.BuildInfo(OPTIONS.target_info_dict, OPTIONS.oem_dicts) source_info = common.BuildInfo(OPTIONS.source_info_dict, OPTIONS.oem_dicts) target_api_version = target_info["recovery_api_version"] source_api_version = source_info["recovery_api_version"] if source_api_version == 0: logger.warning( "Generating edify script for a source that can't install it.") script = edify_generator.EdifyGenerator( source_api_version, target_info, fstab=source_info["fstab"]) if target_info.oem_props or source_info.oem_props: if not OPTIONS.oem_no_mount: source_info.WriteMountOemScript(script) metadata = GetPackageMetadata(target_info, source_info) if not OPTIONS.no_signing: staging_file = common.MakeTempFile(suffix='.zip') else: staging_file = output_file output_zip = zipfile.ZipFile( staging_file, "w", compression=zipfile.ZIP_DEFLATED) device_specific = common.DeviceSpecificParams( source_zip=source_zip, source_version=source_api_version, source_tmp=OPTIONS.source_tmp, target_zip=target_zip, target_version=target_api_version, target_tmp=OPTIONS.target_tmp, output_zip=output_zip, script=script, metadata=metadata, info_dict=source_info) source_boot = common.GetBootableImage( "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT", source_info) target_boot = common.GetBootableImage( "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT", target_info) updating_boot = (not OPTIONS.two_step and (source_boot.data != target_boot.data)) target_recovery = common.GetBootableImage( "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY") block_diff_dict = GetBlockDifferences(target_zip=target_zip, source_zip=source_zip, target_info=target_info, source_info=source_info, device_specific=device_specific) CheckVintfIfTrebleEnabled(OPTIONS.target_tmp, target_info) # Assertions (e.g. device properties check). target_info.WriteDeviceAssertions(script, OPTIONS.oem_no_mount) 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 source_info.get("multistage_support"): assert False, "two-step packages not supported by this build" fs = source_info["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") == "2/3" then """ % bcb_dev) # Stage 2/3: Write recovery image to /recovery (currently running /boot). script.Comment("Stage 2/3") 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") != "3/3" then """ % bcb_dev) # Stage 1/3: (a) Verify the current system. script.Comment("Stage 1/3") # Dump fingerprints script.Print("Source: {}".format(source_info.fingerprint)) script.Print("Target: {}".format(target_info.fingerprint)) script.Print("Verifying current system...") device_specific.IncrementalOTA_VerifyBegin() WriteFingerprintAssertion(script, target_info, source_info) # Check the required cache size (i.e. stashed blocks). required_cache_sizes = [diff.required_cache for diff in block_diff_dict.values()] if updating_boot: boot_type, boot_device_expr = common.GetTypeAndDeviceExpr("/boot", source_info) d = common.Difference(target_boot, source_boot) _, _, d = d.ComputePatch() if d is None: include_full_boot = True common.ZipWriteStr(output_zip, "boot.img", target_boot.data) else: include_full_boot = False logger.info( "boot target: %d source: %d diff: %d", target_boot.size, source_boot.size, len(d)) common.ZipWriteStr(output_zip, "boot.img.p", d) target_expr = 'concat("{}:",{},":{}:{}")'.format( boot_type, boot_device_expr, target_boot.size, target_boot.sha1) source_expr = 'concat("{}:",{},":{}:{}")'.format( boot_type, boot_device_expr, source_boot.size, source_boot.sha1) script.PatchPartitionExprCheck(target_expr, source_expr) required_cache_sizes.append(target_boot.size) if required_cache_sizes: script.CacheFreeSpaceCheck(max(required_cache_sizes)) # Verify the existing partitions. for diff in block_diff_dict.values(): diff.WriteVerifyScript(script, touched_blocks_only=True) device_specific.IncrementalOTA_VerifyEnd() if OPTIONS.two_step: # Stage 1/3: (b) Write recovery image to /boot. _WriteRecoveryImageToBoot(script, output_zip) script.AppendExtra(""" set_stage("%(bcb_dev)s", "2/3"); reboot_now("%(bcb_dev)s", ""); else """ % bcb_dev) # Stage 3/3: Make changes. script.Comment("Stage 3/3") script.Comment("---- start making changes here ----") device_specific.IncrementalOTA_InstallBegin() progress_dict = {partition: 0.1 for partition in block_diff_dict} progress_dict["system"] = 1 - len(block_diff_dict) * 0.1 if OPTIONS.source_info_dict.get("use_dynamic_partitions") == "true": if OPTIONS.target_info_dict.get("use_dynamic_partitions") != "true": raise RuntimeError( "can't generate incremental that disables dynamic partitions") dynamic_partitions_diff = common.DynamicPartitionsDifference( info_dict=OPTIONS.target_info_dict, source_info_dict=OPTIONS.source_info_dict, block_diffs=block_diff_dict.values(), progress_dict=progress_dict) dynamic_partitions_diff.WriteScript( script, output_zip, write_verify_script=OPTIONS.verify) else: for block_diff in block_diff_dict.values(): block_diff.WriteScript(script, output_zip, progress=progress_dict.get(block_diff.partition), write_verify_script=OPTIONS.verify) if OPTIONS.two_step: common.ZipWriteStr(output_zip, "boot.img", target_boot.data) script.WriteRawImage("/boot", "boot.img") logger.info("writing full boot image (forced by two-step mode)") if not OPTIONS.two_step: if updating_boot: if include_full_boot: logger.info("boot image changed; including full.") script.Print("Installing boot image...") script.WriteRawImage("/boot", "boot.img") else: # Produce the boot image by applying a patch to the current # contents of the boot partition, and write it back to the # partition. logger.info("boot image changed; including patch.") script.Print("Patching boot image...") script.ShowProgress(0.1, 10) target_expr = 'concat("{}:",{},":{}:{}")'.format( boot_type, boot_device_expr, target_boot.size, target_boot.sha1) source_expr = 'concat("{}:",{},":{}:{}")'.format( boot_type, boot_device_expr, source_boot.size, source_boot.sha1) script.PatchPartitionExpr(target_expr, source_expr, '"boot.img.p"') else: logger.info("boot image unchanged; skipping.") # Do device-specific installation (eg, write radio image). device_specific.IncrementalOTA_InstallEnd() if OPTIONS.extra_script is not None: script.AppendExtra(OPTIONS.extra_script) if OPTIONS.wipe_user_data: script.Print("Erasing user data...") script.FormatPartition("/data") if OPTIONS.two_step: script.AppendExtra(""" set_stage("%(bcb_dev)s", ""); endif; endif; """ % bcb_dev) script.SetProgress(1) # For downgrade OTAs, we prefer to use the update-binary in the source # build that is actually newer than the one in the target build. if OPTIONS.downgrade: script.AddToZip(source_zip, output_zip, input_path=OPTIONS.updater_binary) else: script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary) metadata.required_cache = script.required_cache # We haven't written the metadata entry yet, which will be handled in # FinalizeMetadata(). common.ZipClose(output_zip) # Sign the generated zip package unless no_signing is specified. needed_property_files = ( NonAbOtaPropertyFiles(), ) FinalizeMetadata(metadata, staging_file, output_file, needed_property_files)
def OTA_VerifyEnd(info, api_version, target_zip, source_zip=None): if api_version < 3: print "warning radio-update: no support for api_version less than 3" return False print "Loading radio filesmap..." filesmap = LoadFilesMap(target_zip) if filesmap == {}: print "warning radio-update: no or invalid filesmap file found" return False print "Loading radio target..." tgt_files = GetRadioFiles(target_zip) if tgt_files == {}: print "warning radio-update: no radio images in input target_files" return False src_files = None if source_zip is not None: print "Loading radio source..." src_files = GetRadioFiles(source_zip) update_list = {} largest_source_size = 0 if info.type == 'MMC': part = "EMMC" else: part = "MTD" print "Preparing radio-update files..." for fn in tgt_files: dest, destBak = GetFileDestination(fn, filesmap) if dest is None: continue tf = tgt_files[fn] sf = None if src_files is not None: sf = src_files.get(fn, None) full = sf is None or fn.endswith('.enc') if not full: # no difference - skip this file if tf.sha1 == sf.sha1: continue d = common.Difference(tf, sf) _, _, d = d.ComputePatch() # no difference - skip this file if d is None: continue # if patch is almost as big as the file - don't bother patching full = len(d) > tf.size * common.OPTIONS.patch_threshold if not full: f = "patch/firmware-update/" + fn + ".p" common.ZipWriteStr(info.output_zip, f, d) update_list[f] = (dest, destBak, tf, sf) largest_source_size = max(largest_source_size, sf.size) if full: f = "firmware-update/" + fn common.ZipWriteStr(info.output_zip, f, tf.data) update_list[f] = (dest, destBak, None, None) global bootImages global binImages global fwImages bootImages, binImages, fwImages = SplitFwTypes(update_list) # If there are incremental patches verify them if largest_source_size != 0: info.script.Comment("---- radio update verification ----") info.script.Print("Verifying radio-update...") for f in bootImages: dest, destBak, tf, sf = bootImages[f] # Not incremental if sf is None: continue info.script.PatchCheck( "%s:%s:%d:%s:%d:%s" % (part, dest, sf.size, sf.sha1, tf.size, tf.sha1)) if destBak is not None: info.script.PatchCheck( "%s:%s:%d:%s:%d:%s" % (part, destBak, sf.size, sf.sha1, tf.size, tf.sha1)) for f in binImages: dest, destBak, tf, sf = binImages[f] # Not incremental if sf is None: continue info.script.PatchCheck( "%s:%s:%d:%s:%d:%s" % (part, dest, sf.size, sf.sha1, tf.size, tf.sha1)) last_mounted = "" for f in fwImages: dest, destBak, tf, sf = fwImages[f] # Not incremental if sf is None: continue # Get the filename without the path and the patch (.p) extention f = f.split("/")[-1][:-2] # Parse filesmap destination paths for "/dev/" pattern in the beginng. # This would mean that the file must be written to block device - # fs mount needed if dest.startswith("/dev/"): if last_mounted != dest: info.script.AppendExtra('unmount("/firmware");') info.script.AppendExtra( 'mount("vfat", "%s", "%s", "/firmware");' % (part, dest)) last_mounted = dest dest = "/firmware/image/" + f else: dest = dest + "/" + f info.script.PatchCheck(dest, tf.sha1, sf.sha1) info.script.CacheFreeSpaceCheck(largest_source_size) return True
def WriteIncrementalOTAPackage(target_zip, source_zip, output_zip): source_version = OPTIONS.source_info_dict["recovery_api_version"] target_version = OPTIONS.target_info_dict["recovery_api_version"] if source_version == 0: print( "WARNING: generating edify script for a source that " "can't install it.") script = edify_generator.EdifyGenerator(source_version, OPTIONS.target_info_dict) if OPTIONS.override_prop: metadata = { "post-timestamp": GetBuildProp("ro.build.date.utc", OPTIONS.target_info_dict), } else: metadata = { "pre-device": GetBuildProp("ro.product.device", OPTIONS.source_info_dict), "post-timestamp": GetBuildProp("ro.build.date.utc", OPTIONS.target_info_dict), } device_specific = common.DeviceSpecificParams( source_zip=source_zip, source_version=source_version, target_zip=target_zip, target_version=target_version, output_zip=output_zip, script=script, metadata=metadata, info_dict=OPTIONS.info_dict) print "Loading target..." target_data = LoadSystemFiles(target_zip) print "Loading source..." source_data = LoadSystemFiles(source_zip) verbatim_targets = [] patch_list = [] diffs = [] renames = {} largest_source_size = 0 matching_file_cache = {} for fn in source_data.keys(): sf = source_data[fn] assert fn == sf.name matching_file_cache["path:" + fn] = sf # Only allow eligability for filename/sha matching # if there isn't a perfect path match. if target_data.get(sf.name) is None: matching_file_cache["file:" + fn.split("/")[-1]] = sf matching_file_cache["sha:" + sf.sha1] = sf for fn in sorted(target_data.keys()): tf = target_data[fn] assert fn == tf.name sf = ClosestFileMatch(tf, matching_file_cache, renames) if sf is not None and sf.name != tf.name: print "File has moved from " + sf.name + " to " + tf.name renames[sf.name] = tf if sf is None or fn in OPTIONS.require_verbatim: # This file should be included verbatim if fn in OPTIONS.prohibit_verbatim: raise common.ExternalError("\"%s\" must be sent verbatim" % (fn, )) print "send", fn, "verbatim" tf.AddToZip(output_zip) verbatim_targets.append((fn, tf.size)) elif tf.sha1 != sf.sha1: # File is different; consider sending as a patch diffs.append(common.Difference(tf, sf)) else: # Target file data identical to source (may still be renamed) pass # common.ComputeDifferences(diffs) # # for diff in diffs: # tf, sf, d = diff.GetPatch() # if d is None or len(d) > tf.size * OPTIONS.patch_threshold: # # patch is almost as big as the file; don't bother patching # tf.AddToZip(output_zip) # verbatim_targets.append((tf.name, tf.size)) # else: # common.ZipWriteStr(output_zip, "patch/" + sf.name + ".p", d) # patch_list.append((sf.name, tf, sf, tf.size, common.sha1(d).hexdigest())) # largest_source_size = max(largest_source_size, sf.size) if not OPTIONS.override_prop: 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["post-build"] = target_fp script.Mount("/system") script.AssertSomeFingerprint(source_fp, target_fp) source_boot = common.GetBootableImage("/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT", 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) #source_recovery = common.GetBootableImage( # "/tmp/recovery.img", "recovery.img", OPTIONS.source_tmp, "RECOVERY", # OPTIONS.source_info_dict) #target_recovery = common.GetBootableImage( # "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY") #updating_recovery = (source_recovery.data != target_recovery.data) updating_recovery = False # Here's how we divide up the progress bar: # 0.1 for verifying the start state (PatchCheck calls) # 0.8 for applying patches (ApplyPatch calls) # 0.1 for unpacking verbatim files, symlinking, and doing the # device-specific commands. AppendAssertions(script, OPTIONS.target_info_dict) device_specific.IncrementalOTA_Assertions() script.Print("Verifying current system...") device_specific.IncrementalOTA_VerifyBegin() script.ShowProgress(0.1, 0) total_verify_size = float(sum([i[2].size for i in patch_list]) + 1) if updating_boot: total_verify_size += source_boot.size so_far = 0 for fn, tf, sf, size, patch_sha in patch_list: script.PatchCheck("/" + fn, tf.sha1, sf.sha1) so_far += sf.size script.SetProgress(so_far / total_verify_size) if updating_boot: d = common.Difference(target_boot, source_boot) _, _, d = d.ComputePatch() print "boot target: %d source: %d diff: %d" % ( target_boot.size, source_boot.size, len(d)) common.ZipWriteStr(output_zip, "patch/boot.img.p", d) boot_type, boot_device = common.GetTypeAndDevice( "/boot", OPTIONS.info_dict) script.PatchCheck( "%s:%s:%d:%s:%d:%s" % (boot_type, boot_device, source_boot.size, source_boot.sha1, target_boot.size, target_boot.sha1)) so_far += source_boot.size script.SetProgress(so_far / total_verify_size) if patch_list or updating_recovery or updating_boot: script.CacheFreeSpaceCheck(largest_source_size) device_specific.IncrementalOTA_VerifyEnd() script.Comment("---- start making changes here ----") device_specific.IncrementalOTA_InstallBegin() if OPTIONS.wipe_user_data: script.Print("Erasing user data...") script.FormatPartition("/data") script.Print("Removing unneeded files...") script.DeleteFiles(["/" + i[0] for i in verbatim_targets] + [ "/" + i for i in sorted(source_data) if i not in target_data and i not in renames ] + ["/system/recovery.img"]) script.ShowProgress(0.8, 0) total_patch_size = float(sum([i[1].size for i in patch_list]) + 1) if updating_boot: total_patch_size += target_boot.size so_far = 0 script.Print("Patching system files...") deferred_patch_list = [] for item in patch_list: fn, tf, sf, size, _ = item if tf.name == "system/build.prop": deferred_patch_list.append(item) continue script.ApplyPatch("/" + fn, "-", tf.size, tf.sha1, sf.sha1, "patch/" + fn + ".p") 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 updating_recovery: # Recovery is generated as a patch using both the boot image # (which contains the same linux kernel as recovery) and the file # /system/etc/recovery-resource.dat (which contains all the images # used in the recovery UI) as sources. This lets us minimize the # size of the patch, which must be included in every OTA package. # # For older builds where recovery-resource.dat is not present, we # use only the boot image as the source. MakeRecoveryPatch(OPTIONS.target_tmp, output_zip, target_recovery, target_boot) script.DeleteFiles([ "/system/recovery-from-boot.p", "/system/etc/install-recovery.sh" ]) print "recovery image changed; including as patch from boot." else: print "recovery image unchanged; skipping." script.ShowProgress(0.1, 10) target_symlinks = CopySystemFiles(target_zip, None) target_symlinks_d = dict([(i[1], i[0]) for i in target_symlinks]) temp_script = script.MakeTemporary() Item.GetMetadata(target_zip) Item.Get("system").SetPermissions(temp_script) # Note that this call will mess up the tree of Items, so make sure # we're done with it. source_symlinks = CopySystemFiles(source_zip, None) source_symlinks_d = dict([(i[1], i[0]) for i in source_symlinks]) # Delete all the symlinks in source that aren't in target. This # needs to happen before verbatim files are unpacked, in case a # symlink in the source is replaced by a real file in the target. to_delete = [] for dest, link in source_symlinks: if link not in target_symlinks_d: to_delete.append(link) script.DeleteFiles(to_delete) if verbatim_targets: script.Print("Unpacking new files...") script.UnpackPackageDir("system", "/system") #if updating_recovery: # script.Print("Unpacking new recovery...") # script.UnpackPackageDir("recovery", "/system") if len(renames) > 0: script.Print("Renaming files...") for src in renames: print "Renaming " + src + " to " + renames[src].name script.RenameFile(src, renames[src].name) script.Print("Symlinks and permissions...") # Create all the symlinks that don't already exist, or point to # somewhere different than what we want. Delete each symlink before # creating it, since the 'symlink' command won't overwrite. to_create = [] for dest, link in target_symlinks: if link in source_symlinks_d: if dest != source_symlinks_d[link]: to_create.append((dest, link)) else: to_create.append((dest, link)) script.DeleteFiles([i[1] for i in to_create]) script.MakeSymlinks(to_create) # Now that the symlinks are created, we can set all the # permissions. script.AppendScript(temp_script) # Do device-specific installation (eg, write radio image). device_specific.IncrementalOTA_InstallEnd() if OPTIONS.extra_script is not None: script.AppendExtra(OPTIONS.extra_script) # Patch the build.prop file last, so if something fails but the # device can still come up, it appears to be the old build and will # get set the OTA package again to retry. script.Print("Patching remaining system files...") for item in deferred_patch_list: fn, tf, sf, size, _ = item script.ApplyPatch("/" + fn, "-", tf.size, tf.sha1, sf.sha1, "patch/" + fn + ".p") script.SetPermissions("/system/build.prop", 0, 0, 0644, None, None) script.AddToZip(target_zip, output_zip) WriteMetadata(metadata, output_zip)