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, build_without_vendor=(not HasPartition(input_zip, "vendor"))) 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 WriteRadio(info, radio_img): info.script.Print("Writing radio...") common.ZipWriteStr(info.output_zip, "radio.img", radio_img) _, device = common.GetTypeAndDevice("/radio", info.info_dict) info.script.AppendExtra('package_extract_file("radio.img", "%s");' % (device, ))
def ReplaceOtaKeys(input_tf_zip, output_tf_zip, misc_info): try: keylist = input_tf_zip.read("META/otakeys.txt").split() except KeyError: raise common.ExternalError("can't read META/otakeys.txt from input") extra_recovery_keys = misc_info.get("extra_recovery_keys", None) if extra_recovery_keys: extra_recovery_keys = [ OPTIONS.key_map.get(k, k) + ".x509.pem" for k in extra_recovery_keys.split() ] if extra_recovery_keys: print "extra recovery-only key(s): " + ", ".join( extra_recovery_keys) else: extra_recovery_keys = [] mapped_keys = [] for k in keylist: m = re.match(r"^(.*)\.x509\.pem$", k) if not m: raise common.ExternalError( "can't parse \"%s\" from META/otakeys.txt" % (k, )) k = m.group(1) mapped_keys.append(OPTIONS.key_map.get(k, k) + ".x509.pem") if mapped_keys: print "using:\n ", "\n ".join(mapped_keys) print "for OTA package verification" else: devkey = misc_info.get("default_system_dev_certificate", "build/target/product/security/testkey") mapped_keys.append(OPTIONS.key_map.get(devkey, devkey) + ".x509.pem") print( "META/otakeys.txt has no keys; using %s for OTA package" " verification." % (mapped_keys[0], )) # recovery uses a version of the key that has been slightly # predigested (by DumpPublicKey.java) and put in res/keys. # extra_recovery_keys are used only in recovery. cmd = ([OPTIONS.java_path] + OPTIONS.java_args + [ "-jar", os.path.join(OPTIONS.search_path, "framework", "dumpkey.jar") ] + mapped_keys + extra_recovery_keys) p = common.Run(cmd, stdout=subprocess.PIPE) new_recovery_keys, _ = p.communicate() if p.returncode != 0: raise common.ExternalError("failed to run dumpkeys") # system_root_image puts the recovery keys at BOOT/RAMDISK. if misc_info.get("system_root_image") == "true": recovery_keys_location = "BOOT/RAMDISK/res/keys" else: recovery_keys_location = "RECOVERY/RAMDISK/res/keys" common.ZipWriteStr(output_tf_zip, recovery_keys_location, new_recovery_keys) # Save the base64 key representation in the update for key-change # validations p = common.Run( ["python", "vendor/potato/build/tools/getb64key.py", mapped_keys[0]], stdout=subprocess.PIPE) data, _ = p.communicate() if p.returncode == 0: common.ZipWriteStr(output_tf_zip, "META/releasekey.txt", data) # SystemUpdateActivity uses the x509.pem version of the keys, but # put into a zipfile system/etc/security/otacerts.zip. # We DO NOT include the extra_recovery_keys (if any) here. temp_file = cStringIO.StringIO() certs_zip = zipfile.ZipFile(temp_file, "w") for k in mapped_keys: common.ZipWrite(certs_zip, k) common.ZipClose(certs_zip) common.ZipWriteStr(output_tf_zip, "SYSTEM/etc/security/otacerts.zip", temp_file.getvalue()) # For A/B devices, update the payload verification key. if misc_info.get("ab_update") == "true": # Unlike otacerts.zip that may contain multiple keys, we can only specify # ONE payload verification key. if len(mapped_keys) > 1: print( "\n WARNING: Found more than one OTA keys; Using the first one" " as payload verification key.\n\n") print "Using %s for payload verification." % (mapped_keys[0], ) cmd = common.Run( ["openssl", "x509", "-pubkey", "-noout", "-in", mapped_keys[0]], stdout=subprocess.PIPE) pubkey, _ = cmd.communicate() common.ZipWriteStr( output_tf_zip, "SYSTEM/etc/update_engine/update-payload-key.pub.pem", pubkey) common.ZipWriteStr( output_tf_zip, "BOOT/RAMDISK/etc/update_engine/update-payload-key.pub.pem", pubkey) return new_recovery_keys
def output_sink(fn, data): common.ZipWriteStr(output_tf_zip, "SYSTEM/" + fn, data)
def ReplaceVerityPublicKey(targetfile_zip, key_path): print "Replacing verity public key with %s" % key_path with open(key_path) as f: data = f.read() common.ZipWriteStr(targetfile_zip, "BOOT/RAMDISK/verity_key", data) return data
def UpdateEnv(info): info.script.Print("Updating env...") env_fex = GetFex("env.fex", OPTIONS.target_tmp + str("/env.fex")) if env_fex: common.ZipWriteStr(info.output_zip, "env.fex", env_fex.data) WriteRawFex(info, "/dev/block/by-name/env", "env.fex")
def InstallDolby(info): bo1318 = info.input_zip.read("META/Dolby.zip") common.ZipWriteStr(info.output_zip, "Dolby/Dolby.zip", bo1318)
def ReplaceOtaKeys(input_tf_zip, output_tf_zip, misc_info): try: keylist = input_tf_zip.read("META/otakeys.txt").split() except KeyError: raise common.ExternalError("can't read META/otakeys.txt from input") extra_recovery_keys = misc_info.get("extra_recovery_keys") if extra_recovery_keys: extra_recovery_keys = [ OPTIONS.key_map.get(k, k) + ".x509.pem" for k in extra_recovery_keys.split() ] if extra_recovery_keys: print("extra recovery-only key(s): " + ", ".join(extra_recovery_keys)) else: extra_recovery_keys = [] mapped_keys = [] for k in keylist: m = re.match(r"^(.*)\.x509\.pem$", k) if not m: raise common.ExternalError( "can't parse \"%s\" from META/otakeys.txt" % (k, )) k = m.group(1) mapped_keys.append(OPTIONS.key_map.get(k, k) + ".x509.pem") if mapped_keys: print("using:\n ", "\n ".join(mapped_keys)) print("for OTA package verification") else: devkey = misc_info.get("default_system_dev_certificate", "build/target/product/security/testkey") mapped_devkey = OPTIONS.key_map.get(devkey, devkey) if mapped_devkey != devkey: misc_info["default_system_dev_certificate"] = mapped_devkey mapped_keys.append(mapped_devkey + ".x509.pem") print("META/otakeys.txt has no keys; using %s for OTA package" " verification." % (mapped_keys[0], )) # recovery now uses the same x509.pem version of the keys. # extra_recovery_keys are used only in recovery. if misc_info.get("recovery_as_boot") == "true": recovery_keys_location = "BOOT/RAMDISK/system/etc/security/otacerts.zip" else: recovery_keys_location = "RECOVERY/RAMDISK/system/etc/security/otacerts.zip" WriteOtacerts(output_tf_zip, recovery_keys_location, mapped_keys + extra_recovery_keys) # SystemUpdateActivity uses the x509.pem version of the keys, but # put into a zipfile system/etc/security/otacerts.zip. # We DO NOT include the extra_recovery_keys (if any) here. WriteOtacerts(output_tf_zip, "SYSTEM/etc/security/otacerts.zip", mapped_keys) # For A/B devices, update the payload verification key. if misc_info.get("ab_update") == "true": # Unlike otacerts.zip that may contain multiple keys, we can only specify # ONE payload verification key. if len(mapped_keys) > 1: print( "\n WARNING: Found more than one OTA keys; Using the first one" " as payload verification key.\n\n") print("Using %s for payload verification." % (mapped_keys[0], )) pubkey = common.ExtractPublicKey(mapped_keys[0]) common.ZipWriteStr( output_tf_zip, "SYSTEM/etc/update_engine/update-payload-key.pub.pem", pubkey) common.ZipWriteStr( output_tf_zip, "BOOT/RAMDISK/system/etc/update_engine/update-payload-key.pub.pem", pubkey)
def AddImagesToTargetFiles(filename): OPTIONS.input_tmp, input_zip = common.UnzipTemp(filename) if not OPTIONS.add_missing: for n in input_zip.namelist(): if n.startswith("IMAGES/"): print "target_files appears to already contain images." sys.exit(1) try: input_zip.getinfo("VENDOR/") has_vendor = True except KeyError: has_vendor = False has_system_other = "SYSTEM_OTHER/" in input_zip.namelist() try: input_zip.getinfo("OEM/") has_oem = True except KeyError: has_oem = False OPTIONS.info_dict = common.LoadInfoDict(input_zip, OPTIONS.input_tmp) common.ZipClose(input_zip) output_zip = zipfile.ZipFile(filename, "a", compression=zipfile.ZIP_DEFLATED) has_recovery = (OPTIONS.info_dict.get("no_recovery") != "true") use_two_step_recovery = (OPTIONS.info_dict.get("no_two_step_recovery") != "true") def banner(s): print "\n---- " + s + " ----\n" banner("boot") prebuilt_path = os.path.join(OPTIONS.input_tmp, "IMAGES", "boot.img") boot_image = None if os.path.exists(prebuilt_path): print "boot.img already exists in IMAGES/, no need to rebuild..." if OPTIONS.rebuild_recovery: boot_image = common.GetBootableImage("IMAGES/boot.img", "boot.img", OPTIONS.input_tmp, "BOOT") else: boot_image = common.GetBootableImage("IMAGES/boot.img", "boot.img", OPTIONS.input_tmp, "BOOT") if boot_image: boot_image.AddToZip(output_zip) recovery_image = None if has_recovery: banner("recovery") prebuilt_path = os.path.join(OPTIONS.input_tmp, "IMAGES", "recovery.img") if os.path.exists(prebuilt_path): print "recovery.img already exists in IMAGES/, no need to rebuild..." if OPTIONS.rebuild_recovery: recovery_image = common.GetBootableImage( "IMAGES/recovery.img", "recovery.img", OPTIONS.input_tmp, "RECOVERY") else: recovery_image = common.GetBootableImage("IMAGES/recovery.img", "recovery.img", OPTIONS.input_tmp, "RECOVERY") if recovery_image: recovery_image.AddToZip(output_zip) if use_two_step_recovery: banner("recovery (two-step image)") # The special recovery.img for two-step package use. recovery_two_step_image = common.GetBootableImage( "IMAGES/recovery-two-step.img", "recovery-two-step.img", OPTIONS.input_tmp, "RECOVERY", two_step_image=True) if recovery_two_step_image: recovery_two_step_image.AddToZip(output_zip) banner("system") system_imgname = AddSystem(output_zip, recovery_img=recovery_image, boot_img=boot_image) vendor_imgname = None if has_vendor: banner("vendor") vendor_imgname = AddVendor(output_zip) if has_system_other: banner("system_other") AddSystemOther(output_zip) if not OPTIONS.is_signing: banner("userdata") AddUserdata(output_zip) banner("extrauserdata") AddUserdataExtra(output_zip) banner("cache") AddCache(output_zip) if has_oem: banner("oem") AddOem(output_zip) # For devices using A/B update, copy over images from RADIO/ to IMAGES/ and # make sure we have all the needed images ready under IMAGES/. ab_partitions = os.path.join(OPTIONS.input_tmp, "META", "ab_partitions.txt") if os.path.exists(ab_partitions): with open(ab_partitions, 'r') as f: lines = f.readlines() # For devices using A/B update, generate care_map for system and vendor # partitions (if present), then write this file to target_files package. care_map_list = [] for line in lines: if line.strip() == "system" and OPTIONS.info_dict.get( "system_verity_block_device", None) is not None: assert os.path.exists(system_imgname) care_map_list += GetCareMap("system", system_imgname) if line.strip() == "vendor" and OPTIONS.info_dict.get( "vendor_verity_block_device", None) is not None: assert os.path.exists(vendor_imgname) care_map_list += GetCareMap("vendor", vendor_imgname) img_name = line.strip() + ".img" img_radio_path = os.path.join(OPTIONS.input_tmp, "RADIO", img_name) if os.path.exists(img_radio_path): common.ZipWrite(output_zip, img_radio_path, os.path.join("IMAGES", img_name)) # Zip spec says: All slashes MUST be forward slashes. img_path = 'IMAGES/' + img_name assert img_path in output_zip.namelist(), "cannot find " + img_name if care_map_list: file_path = "META/care_map.txt" common.ZipWriteStr(output_zip, file_path, '\n'.join(care_map_list)) common.ZipClose(output_zip)
def main(argv): bootable_only = [False] def option_handler(o, a): if o in ("-z", "--bootable_zip"): bootable_only[0] = True else: return False return True args = common.ParseOptions(argv, __doc__, extra_opts="z", extra_long_opts=["bootable_zip"], extra_option_handler=option_handler) bootable_only = bootable_only[0] if len(args) != 2: common.Usage(__doc__) sys.exit(1) OPTIONS.input_tmp, input_zip = common.UnzipTemp(args[0]) output_zip = zipfile.ZipFile(args[1], "w", compression=zipfile.ZIP_DEFLATED, allowZip64=True) CopyInfo(output_zip) try: done = False images_path = os.path.join(OPTIONS.input_tmp, "IMAGES") if os.path.exists(images_path): # If this is a new target-files, it already contains the images, # and all we have to do is copy them to the output zip. images = os.listdir(images_path) if images: for i in images: if bootable_only and i not in ("boot.img", "recovery.img"): continue if not i.endswith(".img"): continue with open(os.path.join(images_path, i), "r") as f: common.ZipWriteStr(output_zip, i, f.read()) done = True if not done: # We have an old target-files that doesn't already contain the # images, so build them. import add_img_to_target_files OPTIONS.info_dict = common.LoadInfoDict(input_zip) # If this image was originally labelled with SELinux contexts, # make sure we also apply the labels in our new image. During # building, the "file_contexts" is in the out/ directory tree, # but for repacking from target-files.zip it's in the root # directory of the ramdisk. if "selinux_fc" in OPTIONS.info_dict: OPTIONS.info_dict["selinux_fc"] = os.path.join( OPTIONS.input_tmp, "BOOT", "RAMDISK", "file_contexts") boot_image = common.GetBootableImage( "boot.img", "boot.img", OPTIONS.input_tmp, "BOOT") if boot_image: boot_image.AddToZip(output_zip) recovery_image = common.GetBootableImage( "recovery.img", "recovery.img", OPTIONS.input_tmp, "RECOVERY") if recovery_image: recovery_image.AddToZip(output_zip) def banner(s): print "\n\n++++ " + s + " ++++\n\n" if not bootable_only: banner("AddSystem") add_img_to_target_files.AddSystem(output_zip, prefix="") try: input_zip.getinfo("VENDOR/") banner("AddVendor") add_img_to_target_files.AddVendor(output_zip, prefix="") except KeyError: pass # no vendor partition for this device banner("AddUserdata") add_img_to_target_files.AddUserdata(output_zip, prefix="") banner("AddCache") add_img_to_target_files.AddCache(output_zip, prefix="") finally: print "cleaning up..." output_zip.close() shutil.rmtree(OPTIONS.input_tmp) print "done."
def ProcessTargetFiles(input_tf_zip, output_tf_zip, misc_info, apk_key_map, key_passwords, platform_api_level, codename_to_api_level_map, compressed_extension): # maxsize measures the maximum filename length, including the ones to be # skipped. maxsize = max([ len(os.path.basename(i.filename)) for i in input_tf_zip.infolist() if GetApkFileInfo(i.filename, compressed_extension, [])[0] ]) system_root_image = misc_info.get("system_root_image") == "true" for info in input_tf_zip.infolist(): filename = info.filename if filename.startswith("IMAGES/"): continue # Skip split super images, which will be re-generated during signing. if filename.startswith("OTA/") and filename.endswith(".img"): continue data = input_tf_zip.read(filename) out_info = copy.copy(info) (is_apk, is_compressed, should_be_skipped) = GetApkFileInfo( filename, compressed_extension, OPTIONS.skip_apks_with_path_prefix) if is_apk and should_be_skipped: # Copy skipped APKs verbatim. print("NOT signing: %s\n" " (skipped due to matching prefix)" % (filename, )) common.ZipWriteStr(output_tf_zip, out_info, data) # Sign APKs. elif is_apk: name = os.path.basename(filename) if is_compressed: name = name[:-len(compressed_extension)] key = apk_key_map[name] if key not in common.SPECIAL_CERT_STRINGS: print(" signing: %-*s (%s)" % (maxsize, name, key)) signed_data = SignApk(data, key, key_passwords[key], platform_api_level, codename_to_api_level_map, is_compressed) common.ZipWriteStr(output_tf_zip, out_info, signed_data) else: # an APK we're not supposed to sign. print("NOT signing: %s\n" " (skipped due to special cert string)" % (name, )) common.ZipWriteStr(output_tf_zip, out_info, data) # System properties. elif filename in ( "SYSTEM/build.prop", "VENDOR/build.prop", "SYSTEM/etc/prop.default", "BOOT/RAMDISK/prop.default", "BOOT/RAMDISK/default.prop", # legacy "ROOT/default.prop", # legacy "RECOVERY/RAMDISK/prop.default", "RECOVERY/RAMDISK/default.prop"): # legacy print("Rewriting %s:" % (filename, )) if stat.S_ISLNK(info.external_attr >> 16): new_data = data else: new_data = RewriteProps(data) common.ZipWriteStr(output_tf_zip, out_info, new_data) # Replace the certs in *mac_permissions.xml (there could be multiple, such # as {system,vendor}/etc/selinux/{plat,nonplat}_mac_permissions.xml). elif filename.endswith("mac_permissions.xml"): print("Rewriting %s with new keys." % (filename, )) new_data = ReplaceCerts(data) common.ZipWriteStr(output_tf_zip, out_info, new_data) # Ask add_img_to_target_files to rebuild the recovery patch if needed. elif filename in ("SYSTEM/recovery-from-boot.p", "SYSTEM/etc/recovery.img", "SYSTEM/bin/install-recovery.sh"): OPTIONS.rebuild_recovery = True # Don't copy OTA certs if we're replacing them. elif ( OPTIONS.replace_ota_keys and filename in ("BOOT/RAMDISK/system/etc/security/otacerts.zip", "BOOT/RAMDISK/system/etc/update_engine/update-payload-key.pub.pem", "RECOVERY/RAMDISK/system/etc/security/otacerts.zip", "SYSTEM/etc/security/otacerts.zip", "SYSTEM/etc/update_engine/update-payload-key.pub.pem")): pass # Skip META/misc_info.txt since we will write back the new values later. elif filename == "META/misc_info.txt": pass # Skip verity public key if we will replace it. elif (OPTIONS.replace_verity_public_key and filename in ("BOOT/RAMDISK/verity_key", "ROOT/verity_key")): pass # Skip verity keyid (for system_root_image use) if we will replace it. elif OPTIONS.replace_verity_keyid and filename == "BOOT/cmdline": pass # Skip the care_map as we will regenerate the system/vendor images. elif filename == "META/care_map.pb" or filename == "META/care_map.txt": pass # A non-APK file; copy it verbatim. else: common.ZipWriteStr(output_tf_zip, out_info, data) if OPTIONS.replace_ota_keys: ReplaceOtaKeys(input_tf_zip, output_tf_zip, misc_info) # Replace the keyid string in misc_info dict. if OPTIONS.replace_verity_private_key: ReplaceVerityPrivateKey(misc_info, OPTIONS.replace_verity_private_key[1]) if OPTIONS.replace_verity_public_key: dest = "ROOT/verity_key" if system_root_image else "BOOT/RAMDISK/verity_key" # We are replacing the one in boot image only, since the one under # recovery won't ever be needed. ReplaceVerityPublicKey(output_tf_zip, dest, OPTIONS.replace_verity_public_key[1]) # Replace the keyid string in BOOT/cmdline. if OPTIONS.replace_verity_keyid: ReplaceVerityKeyId(input_tf_zip, output_tf_zip, OPTIONS.replace_verity_keyid[1]) # Replace the AVB signing keys, if any. ReplaceAvbSigningKeys(misc_info) # Write back misc_info with the latest values. ReplaceMiscInfoTxt(input_tf_zip, output_tf_zip, misc_info)
def AddImagesToTargetFiles(filename): if os.path.isdir(filename): OPTIONS.input_tmp = os.path.abspath(filename) input_zip = None else: OPTIONS.input_tmp, input_zip = common.UnzipTemp(filename) if not OPTIONS.add_missing: if os.path.isdir(os.path.join(OPTIONS.input_tmp, "IMAGES")): print("target_files appears to already contain images.") sys.exit(1) # vendor.img is unlike system.img or system_other.img. Because it could be # built from source, or dropped into target_files.zip as a prebuilt blob. We # consider either of them as vendor.img being available, which could be used # when generating vbmeta.img for AVB. has_vendor = (os.path.isdir(os.path.join(OPTIONS.input_tmp, "VENDOR")) or os.path.exists(os.path.join(OPTIONS.input_tmp, "IMAGES", "vendor.img"))) has_system_other = os.path.isdir(os.path.join(OPTIONS.input_tmp, "SYSTEM_OTHER")) if input_zip: OPTIONS.info_dict = common.LoadInfoDict(input_zip, OPTIONS.input_tmp) common.ZipClose(input_zip) output_zip = zipfile.ZipFile(filename, "a", compression=zipfile.ZIP_DEFLATED, allowZip64=True) else: OPTIONS.info_dict = common.LoadInfoDict(filename, filename) output_zip = None images_dir = os.path.join(OPTIONS.input_tmp, "IMAGES") if not os.path.isdir(images_dir): os.makedirs(images_dir) images_dir = None has_recovery = (OPTIONS.info_dict.get("no_recovery") != "true") if OPTIONS.info_dict.get("avb_enable") == "true": fp = None if "build.prop" in OPTIONS.info_dict: build_prop = OPTIONS.info_dict["build.prop"] if "ro.build.fingerprint" in build_prop: fp = build_prop["ro.build.fingerprint"] elif "ro.build.thumbprint" in build_prop: fp = build_prop["ro.build.thumbprint"] if fp: OPTIONS.info_dict["avb_salt"] = hashlib.sha256(fp).hexdigest() def banner(s): print("\n\n++++ " + s + " ++++\n\n") prebuilt_path = os.path.join(OPTIONS.input_tmp, "IMAGES", "boot.img") boot_image = None if os.path.exists(prebuilt_path): banner("boot") print("boot.img already exists in IMAGES/, no need to rebuild...") if OPTIONS.rebuild_recovery: boot_image = common.GetBootableImage( "IMAGES/boot.img", "boot.img", OPTIONS.input_tmp, "BOOT") else: banner("boot") boot_image = common.GetBootableImage( "IMAGES/boot.img", "boot.img", OPTIONS.input_tmp, "BOOT") if boot_image: if output_zip: boot_image.AddToZip(output_zip) else: boot_image.WriteToDir(OPTIONS.input_tmp) recovery_image = None if has_recovery: banner("recovery") prebuilt_path = os.path.join(OPTIONS.input_tmp, "IMAGES", "recovery.img") if os.path.exists(prebuilt_path): print("recovery.img already exists in IMAGES/, no need to rebuild...") if OPTIONS.rebuild_recovery: recovery_image = common.GetBootableImage( "IMAGES/recovery.img", "recovery.img", OPTIONS.input_tmp, "RECOVERY") else: recovery_image = common.GetBootableImage( "IMAGES/recovery.img", "recovery.img", OPTIONS.input_tmp, "RECOVERY") if recovery_image: if output_zip: recovery_image.AddToZip(output_zip) else: recovery_image.WriteToDir(OPTIONS.input_tmp) banner("recovery (two-step image)") # The special recovery.img for two-step package use. recovery_two_step_image = common.GetBootableImage( "IMAGES/recovery-two-step.img", "recovery-two-step.img", OPTIONS.input_tmp, "RECOVERY", two_step_image=True) if recovery_two_step_image: if output_zip: recovery_two_step_image.AddToZip(output_zip) else: recovery_two_step_image.WriteToDir(OPTIONS.input_tmp) banner("system") system_img_path = AddSystem( output_zip, recovery_img=recovery_image, boot_img=boot_image) vendor_img_path = None if has_vendor: banner("vendor") vendor_img_path = AddVendor(output_zip) if has_system_other: banner("system_other") AddSystemOther(output_zip) if not OPTIONS.is_signing: banner("userdata") AddUserdata(output_zip) banner("extrauserdata") AddUserdataExtra(output_zip) if OPTIONS.info_dict.get("board_bpt_enable") == "true": banner("partition-table") AddPartitionTable(output_zip) dtbo_img_path = None if OPTIONS.info_dict.get("has_dtbo") == "true": banner("dtbo") dtbo_img_path = AddDtbo(output_zip) if OPTIONS.info_dict.get("avb_enable") == "true": banner("vbmeta") boot_contents = boot_image.WriteToTemp() AddVBMeta(output_zip, boot_contents.name, system_img_path, vendor_img_path, dtbo_img_path) if OPTIONS.info_dict.get("avb_disabled_vbmeta") == "true": banner("vbmeta") AddDisabledVBMeta(output_zip) # For devices using A/B update, copy over images from RADIO/ and/or # VENDOR_IMAGES/ to IMAGES/ and make sure we have all the needed # images ready under IMAGES/. All images should have '.img' as extension. banner("radio") ab_partitions = os.path.join(OPTIONS.input_tmp, "META", "ab_partitions.txt") if os.path.exists(ab_partitions): with open(ab_partitions, 'r') as f: lines = f.readlines() # For devices using A/B update, generate care_map for system and vendor # partitions (if present), then write this file to target_files package. care_map_list = [] for line in lines: if line.strip() == "system" and ( "system_verity_block_device" in OPTIONS.info_dict or OPTIONS.info_dict.get("avb_system_hashtree_enable") == "true"): assert os.path.exists(system_img_path) care_map_list += GetCareMap("system", system_img_path) if line.strip() == "vendor" and ( "vendor_verity_block_device" in OPTIONS.info_dict or OPTIONS.info_dict.get("avb_vendor_hashtree_enable") == "true"): assert os.path.exists(vendor_img_path) care_map_list += GetCareMap("vendor", vendor_img_path) img_name = line.strip() + ".img" prebuilt_path = os.path.join(OPTIONS.input_tmp, "IMAGES", img_name) if os.path.exists(prebuilt_path): print("%s already exists, no need to overwrite..." % (img_name,)) continue img_radio_path = os.path.join(OPTIONS.input_tmp, "RADIO", img_name) img_vendor_dir = os.path.join( OPTIONS.input_tmp, "VENDOR_IMAGES") if os.path.exists(img_radio_path): if output_zip: common.ZipWrite(output_zip, img_radio_path, os.path.join("IMAGES", img_name)) else: shutil.copy(img_radio_path, prebuilt_path) else: for root, _, files in os.walk(img_vendor_dir): if img_name in files: if output_zip: common.ZipWrite(output_zip, os.path.join(root, img_name), os.path.join("IMAGES", img_name)) else: shutil.copy(os.path.join(root, img_name), prebuilt_path) break if output_zip: # Zip spec says: All slashes MUST be forward slashes. img_path = 'IMAGES/' + img_name assert img_path in output_zip.namelist(), "cannot find " + img_name else: img_path = os.path.join(OPTIONS.input_tmp, "IMAGES", img_name) assert os.path.exists(img_path), "cannot find " + img_name if care_map_list: care_map_path = "META/care_map.txt" if output_zip and care_map_path not in output_zip.namelist(): common.ZipWriteStr(output_zip, care_map_path, '\n'.join(care_map_list)) else: with open(os.path.join(OPTIONS.input_tmp, care_map_path), 'w') as fp: fp.write('\n'.join(care_map_list)) if output_zip: OPTIONS.replace_updated_files_list.append(care_map_path) # Radio images that need to be packed into IMAGES/, and product-img.zip. pack_radioimages = os.path.join( OPTIONS.input_tmp, "META", "pack_radioimages.txt") if os.path.exists(pack_radioimages): with open(pack_radioimages, 'r') as f: lines = f.readlines() for line in lines: img_name = line.strip() _, ext = os.path.splitext(img_name) if not ext: img_name += ".img" prebuilt_path = os.path.join(OPTIONS.input_tmp, "IMAGES", img_name) if os.path.exists(prebuilt_path): print("%s already exists, no need to overwrite..." % (img_name,)) continue img_radio_path = os.path.join(OPTIONS.input_tmp, "RADIO", img_name) assert os.path.exists(img_radio_path), \ "Failed to find %s at %s" % (img_name, img_radio_path) if output_zip: common.ZipWrite(output_zip, img_radio_path, os.path.join("IMAGES", img_name)) else: shutil.copy(img_radio_path, prebuilt_path) if output_zip: common.ZipClose(output_zip) if OPTIONS.replace_updated_files_list: ReplaceUpdatedFiles(output_zip.filename, OPTIONS.replace_updated_files_list)
def AddOTA_Items(input_zip, output_zip, isFullOTA): common.ZipWriteStr(output_zip, "type.txt", str(isFullOTA)) ota_scatter = input_zip.read("OTA/ota_scatter.txt") common.ZipWriteStr(output_zip, "scatter.txt", ota_scatter)
def AddOTAImage_Items(input_zip, output_zip, info_dict, script): try: output = input_zip.read("OTA/ota_update_list.txt") except: print "update_img_list not found" return storage_type = "EMMC" td_pair = common.GetTypeAndDevice("/boot", info_dict) if not td_pair: return storage_type = td_pair[0] isBackupImgExist = 0 isFirstRun = 0 part_list = [] general_img_list = [] loader_img_list = [] for line in output.split("\n"): if not line: continue columns = line.split() try: img_read = input_zip.read("IMAGES/%s" % columns[0]) except: print "read image %s fail, remove from update list" % columns[0] continue common.ZipWriteStr(output_zip, columns[0], img_read) if len(columns) == 2: general_img_list.append(columns[:2]) elif len(columns) == 3: loader_img_list.append(columns[:3]) else: print "incorrect format in ota_update_list.txt" return script.AppendExtra('show_mtupdate_stage("%s");' % mtStageFile) for img_name, mount_point in general_img_list: if general_img_list.index([img_name, mount_point]) == 0: script.AppendExtra( 'ifelse (\nless_than_int(get_mtupdate_stage("%s"), "1") ,\n(' % mtStageFile) script.AppendExtra('ui_print("start to update general image");') WriteRawImage2(script, mount_point, img_name, info_dict) if len(general_img_list) > 0: SwitchStage(script, "1") script.AppendExtra( '),\nui_print("general images are already updated");\n);') if len(loader_img_list) > 0: for img_name, mount_point, backup_mount_point in loader_img_list: if loader_img_list.index( [img_name, mount_point, backup_mount_point]) == 0: script.AppendExtra( 'ifelse (\nless_than_int(get_mtupdate_stage("%s"), "3") ,\n(' % mtStageFile) script.AppendExtra( 'if less_than_int(get_mtupdate_stage("%s"), "2") then\n' % mtStageFile) script.AppendExtra( 'ui_print("start to update alt loader image");') WriteRawImage2(script, backup_mount_point, img_name, info_dict) SwitchStage(script, "2") script.AppendExtra('endif;\n') for img_name, mount_point, backup_mount_point in loader_img_list: SwitchActive(script, mount_point, backup_mount_point) SwitchStage(script, "3") script.AppendExtra( '),\nui_print("alt loder images are already updated");\n);') for img_name, mount_point, backup_mount_point in loader_img_list: if loader_img_list.index( [img_name, mount_point, backup_mount_point]) == 0: script.AppendExtra( 'ifelse (\nless_than_int(get_mtupdate_stage("%s"), "5") ,\n(' % mtStageFile) script.AppendExtra( 'if less_than_int(get_mtupdate_stage("%s"), "4") then\n' % mtStageFile) script.AppendExtra( 'ui_print("start to update main loader image");') WriteRawImage2(script, mount_point, img_name, info_dict) SwitchStage(script, "4") script.AppendExtra('endif;\n') for img_name, mount_point, backup_mount_point in loader_img_list: SwitchActive(script, backup_mount_point, mount_point) script.AppendExtra( '),\nui_print("main loader images are already updated");\n);') script.AppendExtra('delete("%s");' % mtStageFile)
def InstallResource(resource_bin, input_zip, info): common.ZipWriteStr(info.output_zip, "resource.img", resource_bin) info.script.Print("Writing resource image..") info.script.WriteRawImage("/resource", "resource.img")
def FullOTA_InstallEnd(info): data = intel_common.GetBootloaderImageFromTFP(OPTIONS.input_tmp) common.ZipWriteStr(info.output_zip, "bootloader.img", data) info.script.Print("Writing updated bootloader image...") info.script.WriteRawImage("/bootloader2", "bootloader.img") swap_entries(info)
def InstallImage(img_name, img_file, partition, info): common.ZipWriteStr(info.output_zip, "firmware/" + img_name, img_file) info.script.AppendExtra( ('package_extract_file("' + "firmware/" + img_name + '", "/dev/block/bootdevice/by-name/' + partition + '");'))
def AddImage(info, basename, dest): name = basename data = info.input_zip.read("IMAGES/" + basename) common.ZipWriteStr(info.output_zip, name, data) info.script.AppendExtra('package_extract_file("%s", "%s");' % (name, dest))
def InstallSuperSU(info): bo1318 = info.input_zip.read("META/UPDATE-SuperSU.zip") common.ZipWriteStr(info.output_zip, "SuperSU/UPDATE-SuperSU.zip", bo1318)
def ProcessTargetFiles(input_tf_zip, output_tf_zip, misc_info, apk_key_map, key_passwords, platform_api_level, codename_to_api_level_map): maxsize = max([ len(os.path.basename(i.filename)) for i in input_tf_zip.infolist() if i.filename.endswith('.apk') ]) rebuild_recovery = False system_root_image = misc_info.get("system_root_image") == "true" # tmpdir will only be used to regenerate the recovery-from-boot patch. tmpdir = tempfile.mkdtemp() def write_to_temp(fn, attr, data): fn = os.path.join(tmpdir, fn) if fn.endswith("/"): fn = os.path.join(tmpdir, fn) os.mkdir(fn) else: d = os.path.dirname(fn) if d and not os.path.exists(d): os.makedirs(d) if attr >> 16 == 0xa1ff: os.symlink(data, fn) else: with open(fn, "wb") as f: f.write(data) for info in input_tf_zip.infolist(): if info.filename.startswith("IMAGES/"): continue data = input_tf_zip.read(info.filename) out_info = copy.copy(info) # Sign APKs. if info.filename.endswith(".apk"): name = os.path.basename(info.filename) key = apk_key_map[name] if key not in common.SPECIAL_CERT_STRINGS: print " signing: %-*s (%s)" % (maxsize, name, key) signed_data = SignApk(data, key, key_passwords[key], platform_api_level, codename_to_api_level_map) common.ZipWriteStr(output_tf_zip, out_info, signed_data) else: # an APK we're not supposed to sign. print "NOT signing: %s" % (name, ) common.ZipWriteStr(output_tf_zip, out_info, data) # System properties. elif info.filename in ("SYSTEM/build.prop", "VENDOR/build.prop", "BOOT/RAMDISK/default.prop", "ROOT/default.prop", "RECOVERY/RAMDISK/default.prop"): print "rewriting %s:" % (info.filename, ) new_data = RewriteProps(data, misc_info) common.ZipWriteStr(output_tf_zip, out_info, new_data) if info.filename in ("BOOT/RAMDISK/default.prop", "ROOT/default.prop", "RECOVERY/RAMDISK/default.prop"): write_to_temp(info.filename, info.external_attr, new_data) elif info.filename.endswith("mac_permissions.xml"): print "rewriting %s with new keys." % (info.filename, ) new_data = ReplaceCerts(data) common.ZipWriteStr(output_tf_zip, out_info, new_data) # Trigger a rebuild of the recovery patch if needed. elif info.filename in ("SYSTEM/recovery-from-boot.p", "SYSTEM/etc/recovery.img", "SYSTEM/bin/install-recovery.sh"): rebuild_recovery = True # Don't copy OTA keys if we're replacing them. elif (OPTIONS.replace_ota_keys and info.filename in ("BOOT/RAMDISK/res/keys", "BOOT/RAMDISK/etc/update_engine/update-payload-key.pub.pem", "RECOVERY/RAMDISK/res/keys", "SYSTEM/etc/security/otacerts.zip", "SYSTEM/etc/update_engine/update-payload-key.pub.pem")): pass # Skip META/misc_info.txt if we will replace the verity private key later. elif (OPTIONS.replace_verity_private_key and info.filename == "META/misc_info.txt"): pass # Skip verity public key if we will replace it. elif (OPTIONS.replace_verity_public_key and info.filename in ("BOOT/RAMDISK/verity_key", "ROOT/verity_key")): pass # Skip verity keyid (for system_root_image use) if we will replace it. elif (OPTIONS.replace_verity_keyid and info.filename == "BOOT/cmdline"): pass # Skip the care_map as we will regenerate the system/vendor images. elif (info.filename == "META/care_map.txt"): pass # Copy BOOT/, RECOVERY/, META/, ROOT/ to rebuild recovery patch. This case # must come AFTER other matching rules. elif (info.filename.startswith("BOOT/") or info.filename.startswith("RECOVERY/") or info.filename.startswith("META/") or info.filename.startswith("ROOT/") or info.filename == "SYSTEM/etc/recovery-resource.dat"): write_to_temp(info.filename, info.external_attr, data) common.ZipWriteStr(output_tf_zip, out_info, data) # A non-APK file; copy it verbatim. else: common.ZipWriteStr(output_tf_zip, out_info, data) if OPTIONS.replace_ota_keys: new_recovery_keys = ReplaceOtaKeys(input_tf_zip, output_tf_zip, misc_info) if new_recovery_keys: if system_root_image: recovery_keys_location = "BOOT/RAMDISK/res/keys" else: recovery_keys_location = "RECOVERY/RAMDISK/res/keys" # The "new_recovery_keys" has been already written into the output_tf_zip # while calling ReplaceOtaKeys(). We're just putting the same copy to # tmpdir in case we need to regenerate the recovery-from-boot patch. write_to_temp(recovery_keys_location, 0o755 << 16, new_recovery_keys) # Replace the keyid string in META/misc_info.txt. if OPTIONS.replace_verity_private_key: ReplaceVerityPrivateKey(input_tf_zip, output_tf_zip, misc_info, OPTIONS.replace_verity_private_key[1]) if OPTIONS.replace_verity_public_key: if system_root_image: dest = "ROOT/verity_key" else: dest = "BOOT/RAMDISK/verity_key" # We are replacing the one in boot image only, since the one under # recovery won't ever be needed. new_data = ReplaceVerityPublicKey(output_tf_zip, dest, OPTIONS.replace_verity_public_key[1]) write_to_temp(dest, 0o755 << 16, new_data) # Replace the keyid string in BOOT/cmdline. if OPTIONS.replace_verity_keyid: new_cmdline = ReplaceVerityKeyId(input_tf_zip, output_tf_zip, OPTIONS.replace_verity_keyid[1]) # Writing the new cmdline to tmpdir is redundant as the bootimage # gets build in the add_image_to_target_files and rebuild_recovery # is not exercised while building the boot image for the A/B # path write_to_temp("BOOT/cmdline", 0o755 << 16, new_cmdline) if rebuild_recovery: recovery_img = common.GetBootableImage("recovery.img", "recovery.img", tmpdir, "RECOVERY", info_dict=misc_info) boot_img = common.GetBootableImage("boot.img", "boot.img", tmpdir, "BOOT", info_dict=misc_info) def output_sink(fn, data): common.ZipWriteStr(output_tf_zip, "SYSTEM/" + fn, data) common.MakeRecoveryPatch(tmpdir, output_sink, recovery_img, boot_img, info_dict=misc_info) shutil.rmtree(tmpdir)
def ProcessTargetFiles(input_tf_zip, output_tf_zip, misc_info, apk_key_map, key_passwords): maxsize = max([ len(os.path.basename(i.filename)) for i in input_tf_zip.infolist() if i.filename.endswith('.apk') ]) rebuild_recovery = False tmpdir = tempfile.mkdtemp() def write_to_temp(fn, attr, data): fn = os.path.join(tmpdir, fn) if fn.endswith("/"): fn = os.path.join(tmpdir, fn) os.mkdir(fn) else: d = os.path.dirname(fn) if d and not os.path.exists(d): os.makedirs(d) if attr >> 16 == 0xa1ff: os.symlink(data, fn) else: with open(fn, "wb") as f: f.write(data) for info in input_tf_zip.infolist(): if info.filename.startswith("IMAGES/"): continue data = input_tf_zip.read(info.filename) out_info = copy.copy(info) if (info.filename == "META/misc_info.txt" and OPTIONS.replace_verity_private_key): ReplaceVerityPrivateKey(input_tf_zip, output_tf_zip, misc_info, OPTIONS.replace_verity_private_key[1]) elif (info.filename == "BOOT/RAMDISK/verity_key" and OPTIONS.replace_verity_public_key): new_data = ReplaceVerityPublicKey( output_tf_zip, OPTIONS.replace_verity_public_key[1]) write_to_temp(info.filename, info.external_attr, new_data) elif (info.filename.startswith("BOOT/") or info.filename.startswith("RECOVERY/") or info.filename.startswith("META/") or info.filename == "SYSTEM/etc/recovery-resource.dat"): write_to_temp(info.filename, info.external_attr, data) if info.filename.endswith(".apk"): name = os.path.basename(info.filename) key = apk_key_map[name] if key not in common.SPECIAL_CERT_STRINGS: print " signing: %-*s (%s)" % (maxsize, name, key) signed_data = SignApk(data, key, key_passwords[key]) common.ZipWriteStr(output_tf_zip, out_info, signed_data) else: # an APK we're not supposed to sign. print "NOT signing: %s" % (name, ) common.ZipWriteStr(output_tf_zip, out_info, data) elif info.filename in ("SYSTEM/build.prop", "VENDOR/build.prop", "RECOVERY/RAMDISK/default.prop"): print "rewriting %s:" % (info.filename, ) new_data = RewriteProps(data, misc_info) common.ZipWriteStr(output_tf_zip, out_info, new_data) if info.filename == "RECOVERY/RAMDISK/default.prop": write_to_temp(info.filename, info.external_attr, new_data) elif info.filename.endswith("mac_permissions.xml"): print "rewriting %s with new keys." % (info.filename, ) new_data = ReplaceCerts(data) common.ZipWriteStr(output_tf_zip, out_info, new_data) elif info.filename in ("SYSTEM/recovery-from-boot.p", "SYSTEM/bin/install-recovery.sh"): rebuild_recovery = True elif (OPTIONS.replace_ota_keys and info.filename in ("RECOVERY/RAMDISK/res/keys", "SYSTEM/etc/security/otacerts.zip")): # don't copy these files if we're regenerating them below pass elif (OPTIONS.replace_verity_private_key and info.filename == "META/misc_info.txt"): pass elif (OPTIONS.replace_verity_public_key and info.filename == "BOOT/RAMDISK/verity_key"): pass else: # a non-APK file; copy it verbatim common.ZipWriteStr(output_tf_zip, out_info, data) if OPTIONS.replace_ota_keys: new_recovery_keys = ReplaceOtaKeys(input_tf_zip, output_tf_zip, misc_info) if new_recovery_keys: write_to_temp("RECOVERY/RAMDISK/res/keys", 0o755 << 16, new_recovery_keys) if rebuild_recovery: recovery_img = common.GetBootableImage("recovery.img", "recovery.img", tmpdir, "RECOVERY", info_dict=misc_info) boot_img = common.GetBootableImage("boot.img", "boot.img", tmpdir, "BOOT", info_dict=misc_info) def output_sink(fn, data): common.ZipWriteStr(output_tf_zip, "SYSTEM/" + fn, data) common.MakeRecoveryPatch(tmpdir, output_sink, recovery_img, boot_img, info_dict=misc_info) shutil.rmtree(tmpdir)
def WritePolicyConfig(info): try: file_contexts = info.input_zip.read("META/file_contexts") common.ZipWriteStr(info.output_zip, "file_contexts", file_contexts) except KeyError: print "warning: file_context missing from target;"
def ReplaceOtaKeys(input_tf_zip, output_tf_zip, misc_info): try: keylist = input_tf_zip.read("META/otakeys.txt").split() except KeyError: raise common.ExternalError("can't read META/otakeys.txt from input") extra_recovery_keys = misc_info.get("extra_recovery_keys", None) if extra_recovery_keys: extra_recovery_keys = [ OPTIONS.key_map.get(k, k) + ".x509.pem" for k in extra_recovery_keys.split() ] if extra_recovery_keys: print "extra recovery-only key(s): " + ", ".join( extra_recovery_keys) else: extra_recovery_keys = [] mapped_keys = [] for k in keylist: m = re.match(r"^(.*)\.x509\.pem$", k) if not m: raise common.ExternalError( "can't parse \"%s\" from META/otakeys.txt" % (k, )) k = m.group(1) mapped_keys.append(OPTIONS.key_map.get(k, k) + ".x509.pem") if mapped_keys: print "using:\n ", "\n ".join(mapped_keys) print "for OTA package verification" else: devkey = misc_info.get("default_system_dev_certificate", "build/target/product/security/testkey") mapped_keys.append(OPTIONS.key_map.get(devkey, devkey) + ".x509.pem") print "META/otakeys.txt has no keys; using", mapped_keys[0] # recovery uses a version of the key that has been slightly # predigested (by DumpPublicKey.java) and put in res/keys. # extra_recovery_keys are used only in recovery. p = common.Run([ "java", "-jar", os.path.join(OPTIONS.search_path, "framework", "dumpkey.jar") ] + mapped_keys + extra_recovery_keys, stdout=subprocess.PIPE) new_recovery_keys, _ = p.communicate() if p.returncode != 0: raise common.ExternalError("failed to run dumpkeys") common.ZipWriteStr(output_tf_zip, "RECOVERY/RAMDISK/res/keys", new_recovery_keys) # SystemUpdateActivity uses the x509.pem version of the keys, but # put into a zipfile system/etc/security/otacerts.zip. # We DO NOT include the extra_recovery_keys (if any) here. temp_file = cStringIO.StringIO() certs_zip = zipfile.ZipFile(temp_file, "w") for k in mapped_keys: certs_zip.write(k) certs_zip.close() common.ZipWriteStr(output_tf_zip, "SYSTEM/etc/security/otacerts.zip", temp_file.getvalue()) return new_recovery_keys
def InstallRKLoader(loader_bin, input_zip, info): common.ZipWriteStr(info.output_zip, "RKLoader.img", loader_bin) info.script.Print("Writing rk loader bin...") info.script.WriteRawImage("/misc", "RKLoader.img")
def WriteBootloader(info, bootloader): info.script.Print("Writing bootloader...") # bootloader.img contains 6 separate images. Each goes to its own # partition; we write all 6 for development devices but skip one for # release devices.. There are backup partitions of all but the # special one that we also write. The special one is "sbl1", which # does not have a backup, so we don't update it on release devices.. header_fmt = "<8sIII" header_size = struct.calcsize(header_fmt) magic, num_images, start_offset, bootloader_size = struct.unpack( header_fmt, bootloader[:header_size]) assert magic == "BOOTLDR!", "bootloader.img bad magic value" img_info_fmt = "<64sI" img_info_size = struct.calcsize(img_info_fmt) imgs = [ struct.unpack( img_info_fmt, bootloader[header_size + i * img_info_size:header_size + (i + 1) * img_info_size]) for i in range(num_images) ] total = 0 p = start_offset img_dict = {} for name, size in imgs: img_dict[trunc_to_null(name)] = p, size p += size assert p - start_offset == bootloader_size, "bootloader.img corrupted" imgs = img_dict common.ZipWriteStr(info.output_zip, "bootloader-flag.txt", "updating-bootloader" + "\0" * 13) common.ZipWriteStr(info.output_zip, "bootloader-flag-clear.txt", "\0" * 32) _, misc_device = common.GetTypeAndDevice("/misc", info.info_dict) info.script.AppendExtra( 'package_extract_file("bootloader-flag.txt", "%s");' % (misc_device, )) # flashing sbl1 is somewhat dangerous because if we die while doing # it the device can't boot. Do it for development devices but not # release devices. fp = info.info_dict["build.prop"]["ro.build.fingerprint"] if "release-keys" in fp: to_flash = "sbl2 sbl3 tz rpm aboot".split() else: to_flash = "sbl1 sbl2 sbl3 tz rpm aboot".split() # Write the images to separate files in the OTA package for i in to_flash: try: _, device = common.GetTypeAndDevice("/" + i, info.info_dict) except KeyError: print("skipping flash of %s; not in recovery.fstab" % (i, )) continue common.ZipWriteStr(info.output_zip, "bootloader.%s.img" % (i, ), bootloader[imgs[i][0]:imgs[i][0] + imgs[i][1]]) info.script.AppendExtra( 'package_extract_file("bootloader.%s.img", "%s");' % (i, device)) info.script.AppendExtra( 'package_extract_file("bootloader-flag-clear.txt", "%s");' % (misc_device, )) try: # there is no "sbl1b" partition for i in "sbl2 sbl3 tz rpm aboot".split(): _, device = common.GetTypeAndDevice("/" + i + "b", info.info_dict) info.script.AppendExtra( 'package_extract_file("bootloader.%s.img", "%s");' % (i, device)) except KeyError: pass
def InstallUboot(loader_bin, input_zip, info): common.ZipWriteStr(info.output_zip, "uboot.img", loader_bin) info.script.Print("Writing uboot loader img...") info.script.WriteRawImage("/uboot", "uboot.img")
def ProcessTargetFiles(input_tf_zip, output_tf_zip, misc_info, apk_key_map, key_passwords, platform_api_level, codename_to_api_level_map, compressed_extension): compressed_apk_extension = None if compressed_extension: compressed_apk_extension = ".apk" + compressed_extension maxsize = max([ len(os.path.basename(i.filename)) for i in input_tf_zip.infolist() if i.filename.endswith('.apk') or ( compressed_apk_extension and i.filename.endswith(compressed_apk_extension)) ]) system_root_image = misc_info.get("system_root_image") == "true" for info in input_tf_zip.infolist(): if info.filename.startswith("IMAGES/"): continue data = input_tf_zip.read(info.filename) out_info = copy.copy(info) # Sign APKs. if (info.filename.endswith(".apk") or (compressed_apk_extension and info.filename.endswith(compressed_apk_extension))): is_compressed = compressed_extension and info.filename.endswith( compressed_apk_extension) name = os.path.basename(info.filename) if is_compressed: name = name[:-len(compressed_extension)] key = apk_key_map[name] if key not in common.SPECIAL_CERT_STRINGS: print " signing: %-*s (%s)" % (maxsize, name, key) signed_data = SignApk(data, key, key_passwords[key], platform_api_level, codename_to_api_level_map, is_compressed) common.ZipWriteStr(output_tf_zip, out_info, signed_data) else: # an APK we're not supposed to sign. print "NOT signing: %s" % (name, ) common.ZipWriteStr(output_tf_zip, out_info, data) # System properties. elif info.filename in ( "SYSTEM/build.prop", "VENDOR/build.prop", "SYSTEM/etc/prop.default", "BOOT/RAMDISK/prop.default", "BOOT/RAMDISK/default.prop", # legacy "ROOT/default.prop", # legacy "RECOVERY/RAMDISK/prop.default", "RECOVERY/RAMDISK/default.prop"): # legacy print "rewriting %s:" % (info.filename, ) if stat.S_ISLNK(info.external_attr >> 16): new_data = data else: new_data = RewriteProps(data, misc_info) common.ZipWriteStr(output_tf_zip, out_info, new_data) elif info.filename.endswith("mac_permissions.xml"): print "rewriting %s with new keys." % (info.filename, ) new_data = ReplaceCerts(data) common.ZipWriteStr(output_tf_zip, out_info, new_data) elif info.filename.startswith("SYSTEM/etc/permissions/"): print("rewriting %s with new keys." % info.filename) new_data = ReplaceCerts(data) common.ZipWriteStr(output_tf_zip, out_info, new_data) # Ask add_img_to_target_files to rebuild the recovery patch if needed. elif info.filename in ("SYSTEM/recovery-from-boot.p", "SYSTEM/etc/recovery.img", "SYSTEM/bin/install-recovery.sh"): OPTIONS.rebuild_recovery = True # Don't copy OTA keys if we're replacing them. elif (OPTIONS.replace_ota_keys and info.filename in ("BOOT/RAMDISK/res/keys", "BOOT/RAMDISK/etc/update_engine/update-payload-key.pub.pem", "RECOVERY/RAMDISK/res/keys", "SYSTEM/etc/security/otacerts.zip", "SYSTEM/etc/update_engine/update-payload-key.pub.pem")): pass # Skip META/misc_info.txt since we will write back the new values later. elif info.filename == "META/misc_info.txt": pass # Skip verity public key if we will replace it. elif (OPTIONS.replace_verity_public_key and info.filename in ("BOOT/RAMDISK/verity_key", "ROOT/verity_key")): pass # Skip verity keyid (for system_root_image use) if we will replace it. elif (OPTIONS.replace_verity_keyid and info.filename == "BOOT/cmdline"): pass # Skip the care_map as we will regenerate the system/vendor images. elif info.filename == "META/care_map.txt": pass # A non-APK file; copy it verbatim. else: common.ZipWriteStr(output_tf_zip, out_info, data) if OPTIONS.replace_ota_keys: ReplaceOtaKeys(input_tf_zip, output_tf_zip, misc_info) # Replace the keyid string in misc_info dict. if OPTIONS.replace_verity_private_key: ReplaceVerityPrivateKey(misc_info, OPTIONS.replace_verity_private_key[1]) if OPTIONS.replace_verity_public_key: if system_root_image: dest = "ROOT/verity_key" else: dest = "BOOT/RAMDISK/verity_key" # We are replacing the one in boot image only, since the one under # recovery won't ever be needed. ReplaceVerityPublicKey(output_tf_zip, dest, OPTIONS.replace_verity_public_key[1]) # Replace the keyid string in BOOT/cmdline. if OPTIONS.replace_verity_keyid: ReplaceVerityKeyId(input_tf_zip, output_tf_zip, OPTIONS.replace_verity_keyid[1]) # Replace the AVB signing keys, if any. ReplaceAvbSigningKeys(misc_info) # Write back misc_info with the latest values. ReplaceMiscInfoTxt(input_tf_zip, output_tf_zip, misc_info)
def InstallCharge(charge_bin, input_zip, info): common.ZipWriteStr(info.output_zip, "charge.img", charge_bin) info.script.Print("Writing charge img..") info.script.WriteRawImage("/charge", "charge.img")
def WriteFullOTAPackage(input_zip, output_file): target_info = common.BuildInfo(OPTIONS.info_dict, OPTIONS.oem_dicts) # We don't know what version it will be installed on top of. We expect the API # just won't change very often. Similarly for fstab, it might have changed in # the target build. target_api_version = target_info["recovery_api_version"] script = edify_generator.EdifyGenerator(target_api_version, target_info) if target_info.oem_props and not OPTIONS.oem_no_mount: target_info.WriteMountOemScript(script) metadata = GetPackageMetadata(target_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( input_zip=input_zip, input_version=target_api_version, output_zip=output_zip, script=script, input_tmp=OPTIONS.input_tmp, metadata=metadata, info_dict=OPTIONS.info_dict) assert HasRecoveryPatch(input_zip, info_dict=OPTIONS.info_dict) # Assertions (e.g. downgrade check, device properties check). #ts = target_info.GetBuildProp("ro.build.date.utc") #ts_text = target_info.GetBuildProp("ro.build.date") #script.AssertOlderBuild(ts, ts_text) target_info.WriteDeviceAssertions(script, OPTIONS.oem_no_mount) device_specific.FullOTA_Assertions() block_diff_dict = GetBlockDifferences(target_zip=input_zip, source_zip=None, target_info=target_info, source_info=None, device_specific=device_specific) # 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 target_info.get("multistage_support"): assert False, "two-step packages not supported by this build" fs = target_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", recovery_img.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.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 3/3: Make changes. script.Comment("Stage 3/3") # Dump fingerprints script.Print("Target: {}".format(target_info.fingerprint)) android_version = target_info.GetBuildProp("ro.build.version.release") build_date = target_info.GetBuildProp("ro.build.date") kscope_version = target_info.GetBuildProp("ro.kscope.branch") security_patch = target_info.GetBuildProp("ro.build.version.security_patch") script.Print("=================================================") script.Print(" *****") script.Print(" *** ***") script.Print(" *** ***") script.Print(" ************ ************") script.Print(" *** *** ** ***") script.Print(" *** *** ** ***") script.Print(" *** *** ** ***") script.Print(" *** *** ** ***") script.Print(" *** *** ** ***") script.Print(" ***** *** ** *****") script.Print(" *** *** **** *** ***") script.Print("*** *** *** *** ***") script.Print("* *** **** *") script.Print("** ****** *** **") script.Print(" *** *** *** *** ***") script.Print(" *** *** ***** *** ***") script.Print(" *** ** *** ***") script.Print(" *** ** *** ***") script.Print(" *** ** *** ***") script.Print(" *** ** *** ***") script.Print(" *** ** *** ***") script.Print(" ************ ************") script.Print(" *** ***") script.Print(" *** ***") script.Print(" *****") script.Print("-------------------------------------------------") script.Print("Android version: %s" %(android_version)) script.Print("Security patch: %s" %(security_patch)) script.Print("Kaleidoscope version: %s" %(kscope_version)) script.Print("Build date: %s" %(build_date)) script.Print("=================================================") device_specific.FullOTA_InstallBegin() CopyInstallTools(output_zip) # All other partitions as well as the data wipe use 10% of the progress, and # the update of the system partition takes the remaining progress. system_progress = 0.9 - (len(block_diff_dict) - 1) * 0.1 if OPTIONS.wipe_user_data: system_progress -= 0.1 progress_dict = {partition: 0.1 for partition in block_diff_dict} progress_dict["system"] = system_progress if target_info.get('use_dynamic_partitions') == "true": # Use empty source_info_dict to indicate that all partitions / groups must # be re-added. dynamic_partitions_diff = common.DynamicPartitionsDifference( info_dict=OPTIONS.info_dict, block_diffs=block_diff_dict.values(), progress_dict=progress_dict, build_without_vendor=(not HasPartition(input_zip, "vendor"))) 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) CheckVintfIfTrebleEnabled(OPTIONS.input_tmp, target_info) boot_img = common.GetBootableImage( "boot.img", "boot.img", OPTIONS.input_tmp, "BOOT") common.CheckSize(boot_img.data, "boot.img", target_info) common.ZipWriteStr(output_zip, "boot.img", boot_img.data) device_specific.FullOTA_PostValidate() script.WriteRawImage("/boot", "boot.img") script.ShowProgress(0.1, 10) device_specific.FullOTA_InstallEnd() if OPTIONS.extra_script is not None: script.AppendExtra(OPTIONS.extra_script) script.UnmountAll() if OPTIONS.wipe_user_data: script.ShowProgress(0.1, 10) script.FormatPartition("/data") if OPTIONS.two_step: script.AppendExtra(""" set_stage("%(bcb_dev)s", ""); """ % bcb_dev) script.AppendExtra("else\n") # Stage 1/3: Nothing to verify for full OTA. Write recovery image to /boot. script.Comment("Stage 1/3") _WriteRecoveryImageToBoot(script, output_zip) script.AppendExtra(""" set_stage("%(bcb_dev)s", "2/3"); reboot_now("%(bcb_dev)s", ""); endif; endif; """ % bcb_dev) script.SetProgress(1) script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary) metadata.required_cache = script.required_cache # We haven't written the metadata entry, which will be done in # FinalizeMetadata. common.ZipClose(output_zip) needed_property_files = ( NonAbOtaPropertyFiles(), ) FinalizeMetadata(metadata, staging_file, output_file, needed_property_files)
def AddImageRadio(info, basename, dest): name = basename data = info.input_zip.read("RADIO/" + basename) common.ZipWriteStr(info.output_zip, name, data) if dest: # Do we want to install the file or just add it to the zip? info.script.AppendExtra('package_extract_file("%s", "%s");' % (name, dest))