Android OTA升级(二)之ota_from_target_files文件分析
  HvTJUzsxOBtS 2023年11月24日 22 0


概要:

此部分为全包升级主要实现过程,涉及到ota_from_target_files 文件,这个也是制作全包和差分包的主要工具,接下来我们就着重分析怎么利用这个工具制作full_ota_package的。

主要流程:

Android OTA升级(二)之ota_from_target_files文件分析_差分


源码分析:

上节中Makefile中otapackage目标最后的cmd为:

$(hide) MTK_SECURITY_SW_SUPPORT=$(MTK_SECURITY_SW_SUPPORT)MKBOOTIMG=$(MKBOOTIMG) \

         ./build/tools/releasetools/ota_from_target_files-v \

              --block \

              -p $(HOST_OUT) \

              -k $(KEY_CERT_PAIR) \

              $(if $(OEM_OTA_CONFIG), -o$(OEM_OTA_CONFIG)) \

              $(BUILT_TARGET_FILES_PACKAGE) $@

所以跳到ota_from_target_files python文件中,接下来我们先看一下这个文件的作用,usage以及cmd所带的参数的意义。

"""
Given a target-files zipfile, produces an OTA package that installs
that build.  An incremental OTA is produced if -i is given, otherwise
a full OTA is produced.
Usage:  ota_from_target_files [flags] input_target_files output_ota_package
  --board_config  <file>
      Deprecated.
  -k (--package_key) <key> Key to use to sign the package (default is
the value of default_system_dev_certificate from the input
target-files's META/misc_info.txt, or
      "build/target/product/security/testkey" if that value is not
specified).
      For incremental OTAs, the default value is based on the source
target-file, not the target build.
  -i  (--incremental_from)  <file>
      Generate an incremental OTA using the given target-files zip as
the starting build.
  -v  (--verify)
      Remount and verify the checksums of the files written to the
system and vendor (if used) partitions.  Incremental builds only.
  -o  (--oem_settings)  <file>
      Use the file to specify the expected OEM-specific properties
on the OEM partition of the intended device.
  -w  (--wipe_user_data)
      Generate an OTA package that will wipe the user data partition
when installed.
  -n  (--no_prereq)
      Omit the timestamp prereq check normally included at the top of
the build scripts (used for developer OTA packages which
legitimately need to go back and forth).
  -e  (--extra_script)  <file>
      Insert the contents of file at the end of the update script.
  -r  (--preloader)  <file>
      Specify 'preloader.img' to upgrade.
  -l  (--logo)  <file>
      Specify 'logo.img' to upgrade.
  -u  (--uboot)  <file>
      Specify 'uboot.img/lk.img' to upgrade.
  -a  (--aslr_mode)  <on|off>
      Specify whether to turn on ASLR for the package (on by default).
  -2  (--two_step)
      Generate a 'two-step' OTA package, where recovery is updated
first, so that any changes made to the system partition are done
using the new recovery (new kernel, etc.).
  --block
      Generate a block-based OTA if possible.  Will fall back to a
file-based OTA if the target_files is older and doesn't support
block-based OTAs.
  -b  (--binary)  <file>
      Use the given binary as the update-binary in the output package,
instead of the binary in the build's target_files.  Use for
development only.
  -t  (--worker_threads) <int>
      Specifies the number of worker-threads that will be used when
generating patches for incremental updates (defaults to 3).
  -f  (--special_factory_reset)
      After upgrade, it will execute special factory reset.
  -z  (--trustonic)  <file>
      Specify 'mobicore.bin' to upgrade.
"""

文件的作用如英文描述:给出一个target_file的zip包生成一个ota包,如果带–i 参数则生成差分包,否则生成full_ota_package

Usage: ota_from_target_files [flags] input_target_filesoutput_ota_package

–board_config

Deprecated.

-k :指定sign package使用的key

-i : 制作差分包,后面跟差分包基础包

-v:verify功能

-o:指定oem厂商信息

-w:清楚data区

-n:是否检查时间戳信息,以便确定向前或者向后升级

-e:指定额外的升级脚本

-r:是否升级preloader分区

-l:是否升级logo分区

-u:是否升级uboot分区

-a:是否打开ASLR

-2:生成一个two-step ota包

–block:生成一个block_base的ota包

-b:指定一个update-binary存放到ota包中

-t:指定线程数

-f:指定是否恢复出厂设置

-z:指定mobicore.bin

从此处开始执行,然后跳到main函数中

if __name__ == '__main__':
try:
common.CloseInheritedPipes()
main(sys.argv[1:])
except common.ExternalError, e:
print
print "   ERROR: %s" % (e,)
print
sys.exit(1)
finally:
common.Cleanup()

下面这一段代码主要作用是解析命令行参数,除了上面已经分析的参数外,另外加上了

Common.ParseOptions()接口中的参数列表,如下:

for o, a in opts:

if o in ("-h", "--help"):

Usage(docstring)

sys.exit()

elif o in ("-v", "--verbose"):

     OPTIONS.verbose = True

elif o in ("-p", "--path"):

      OPTIONS.search_path = a

elif o in ("--signapk_path",):

      OPTIONS.signapk_path = a

elif o in ("--extra_signapk_args",):

     OPTIONS.extra_signapk_args = shlex.split(a)

elif o in ("--java_path",):

      OPTIONS.java_path = a

elif o in ("--java_args",):

      OPTIONS.java_args = a

elif o in ("--public_key_suffix",):

      OPTIONS.public_key_suffix = a

elif o in ("--private_key_suffix",):

      OPTIONS.private_key_suffix = a

elif o in ("-s", "--device_specific"):

      OPTIONS.device_specific = a

elif o in ("-x", "--extra"):

key, value = a.split("=", 1)

OPTIONS.extras[key] = value

else:

if extra_option_handler is None or notextra_option_handler(o, a):

assert False, "unknown option \"%s\""% (o,)

由之前Makefile的cmd来看:

./build/tools/releasetools/ota_from_target_files-v \

              --block \

              -p $(HOST_OUT) \

              -k $(KEY_CERT_PAIR) \

              $(if $(OEM_OTA_CONFIG), -o$(OEM_OTA_CONFIG)) \

             $(BUILT_TARGET_FILES_PACKAGE) $@

此段代码执行结束后:

OPTIONS.block_based = True

OPTIONS.verbose = True(此处注意:-v代表verbose;–verify代表verify)

OPTIONS.search_path= $(HOST_OUT) /* xxx/out/host/linux-x86 */

OPTIONS.package_key = $(KEY_CERT_PAIR)

args = [‘xxx-target_files-eng.xx.zip’,’xxx-ota-eng.xx.zip’]//前者是obj下的压缩包,后者是最终的ota包。

如有疑惑,请自行参考getopt解析命令行参数模块代码

def main(argv):
def option_handler(o, a):
if o == "--board_config":
pass   # deprecated
elif o in ("-k", "--package_key"):
      OPTIONS.package_key = a
elif o in ("-i", "--incremental_from"):
      OPTIONS.incremental_source = a
elif o in ("-w", "--wipe_user_data"):
      OPTIONS.wipe_user_data = True
elif o in ("-n", "--no_prereq"):
      OPTIONS.omit_prereq = True
elif o in ("-o", "--oem_settings"):
      OPTIONS.oem_source = a
elif o in ("-e", "--extra_script"):
      OPTIONS.extra_script = a
elif o in ("-a", "--aslr_mode"):
      if a in ("on", "On", "true", "True", "yes", "Yes"):
        OPTIONS.aslr_mode = True
else:
        OPTIONS.aslr_mode = False
elif o in ("-t", "--worker_threads"):
if a.isdigit():
        OPTIONS.worker_threads = int(a)
else:
raise ValueError("Cannot parse value %r for option %r - only "
                         "integers are allowed." % (a, o))
elif o in ("-f", "--special_factory_reset"):
      OPTIONS.special_factory_reset = True
elif o in ("-x", "--tee"):
      OPTIONS.tee = a 
elif o in ("-z", "--trustonic"):
      OPTIONS.trustonic = a
elif o in ("-2", "--two_step"):
      OPTIONS.two_step = True
elif o in ("-r", "--preloader"):
      OPTIONS.preloader = a
elif o in ("-l", "--logo"):
      OPTIONS.logo = a
elif o in ("-u", "--uboot"):
      OPTIONS.uboot = a
elif o in ("-f", "--special_factory_reset"):
      OPTIONS.special_factory_reset = True
elif o in ("-g", "--ubifs"):
      OPTIONS.ubifs = True
elif o == "--no_signing":
      OPTIONS.no_signing = True
elif o in ("--verify"):
      OPTIONS.verify = True
elif o == "--block":
      OPTIONS.block_based = True
elif o in ("-b", "--binary"):
      OPTIONS.updater_binary = a
elif o in ("--no_fallback_to_full",):
      OPTIONS.fallback_to_full = False
else:
return False
return True
 
args = common.ParseOptions(argv, __doc__,
                             extra_opts="b:k:i:d:wfgne:r:l:u:x:z:t:a:2o:",
                             extra_long_opts=["board_config=",
                                              "package_key=",
                                              "incremental_from=",
                                              "wipe_user_data",
                                              "special_factory_reset",
                                              "ubifs",
                                              "tee=",
                                              "trustonic=",
                                              "no_prereq",
                                              "extra_script=",
                                              "preloader=",
                                              "logo=",
                                              "uboot=",
                                              "worker_threads=",
                                              "aslr_mode=",
                                              "two_step",
                                              "no_signing",
                                              "block",
                                              "binary=",
                                              "oem_settings=",
                                              "verify",
                                              "no_fallback_to_full",
                                              ],
                             extra_option_handler=option_handler)
if os.getenv("MTK_SECURITY_SW_SUPPORT", "no")=="no":
    OPTIONS.mtk_sec_boot_sig_tail = False
if len(args) != 2:
common.Usage(__doc__)
sys.exit(1)
/*此处为false*/
if OPTIONS.extra_script is not None:
    	OPTIONS.extra_script = open(OPTIONS.extra_script).read()

解压输入压缩包到一个中间文件夹,并把目录赋给OPTIONS.input_tem,同时创建一个输入压缩包的ZipFile(zipfile.py中)实例:input_zip

print "unzipping target target-files..."
  OPTIONS.input_tmp, input_zip = common.UnzipTemp(args[0])

把输入压缩包的解压目录赋给OPTIONS.target_tmp。

通过input_zip读取压缩包中的META/misc_info.txt文件,以及RECOVERY/RAMDISK/etc/recovery.fstab,SYSTEM/build.prop等文件,并把文件内容并转换成字典形式OPTIONS.info_dict,建立字典代码如下:

def LoadDictionaryFromLines(lines):

  d = {}

for line in lines:                                                  

line = line.strip()

if not line or line.startswith("#"):continue

if "=" in line:

name, value = line.split("=", 1)

d[name] = value

return d
OPTIONS.target_tmp = OPTIONS.input_tmp
  OPTIONS.info_dict = common.LoadInfoDict(input_zip)

如果OPTIONS.info_dict中存在key selinux_fc,则key对应键值为:tmpxxx/BOOT/RAMDISK/file_contexts

如果冗余模式为真,则打印字典内容,此处为真。

# 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")
 
if OPTIONS.verbose:
print "--- target info ---"
common.DumpInfoDict(OPTIONS.info_dict)

如果调用者利用-s/–device_specific指定了device-specific extension路径,则使用它

否者使用META/releasetools.py如果此文件存在target_files压缩包中

如果以上都没有,则通过查找字典中的key:‘tool_extension’,来作为device_specific

#If the caller explicitly specified the device-specific extensions
 # path via -s/--device_specific, use that.  Otherwise, use
  # META/releasetools.py if it is present inthe target target_files.
 # Otherwise, take the path of the file from 'tool_extensions' in the
  # info dict and look for that in the localfilesystem, relative to
  # the current directory.
 
ifOPTIONS.device_specific is None:
    from_input =os.path.join(OPTIONS.input_tmp, "META", "releasetools.py")
ifos.path.exists(from_input):
print"(using device-specific extensions from target_files)"
     OPTIONS.device_specific = from_input
else:
      OPTIONS.device_specific = OPTIONS.info_dict.get("tool_extensions",None)
 
ifOPTIONS.device_specific is not None:
OPTIONS.device_specific =os.path.abspath(OPTIONS.device_specific)

S1:此处OPTIONS.mtk_sec_boot_sig_tail为真,读取boot.sig ,recovery.sig内容

while True:
if OPTIONS.mtk_sec_boot_sig_tail:
      #After WriteIncrementalOTAPackage, input_zip seems scramble, so read *.sig at first
      boot_sig = input_zip.read("META/boot.sig")
      recovery_sig = input_zip.read("META/recovery.sig")

此处为false,所以走else语句:
创建一个temp_zip_file临时文件夹的_TemporaryFileWrapper实例

然后创建一个zipfile实例output_zip,以temp_zip_file为入参。其实就是对1中的临时文件进行一个zipfile实例化,最后可以利用zipfile中的属性方法对这个文件进行操作

if OPTIONS.no_signing:
if os.path.exists(args[1]): os.unlink(args[1])
      output_zip = zipfile.ZipFile(args[1], "w", compression=zipfile.ZIP_DEFLATED)
else:
      temp_zip_file = tempfile.NamedTemporaryFile()
      output_zip = zipfile.ZipFile(temp_zip_file, "w",
compression=zipfile.ZIP_DEFLATED)

判断是否带-i参数,有则进行差分包制作,无则进行全包制作. 此处没有, 所以走if语句为真,然后跳到WriteFullOTAPackage(input_zip,output_zip)中执行,可参考后文详细分析

if OPTIONS.incremental_source is None:
WriteFullOTAPackage(input_zip,output_zip)
ifOPTIONS.package_key is None:
        OPTIONS.package_key =OPTIONS.info_dict.get(
            "default_system_dev_certificate",
           "build/target/product/security/testkey")
ifnot OPTIONS.mtk_sec_boot_sig_tail:
break
else:
print"unzipping source target-files..."
      OPTIONS.source_tmp, source_zip =common.UnzipTemp(OPTIONS.incremental_source)
      OPTIONS.target_info_dict =OPTIONS.info_dict
      OPTIONS.source_info_dict =common.LoadInfoDict(source_zip)
if"selinux_fc" in OPTIONS.source_info_dict:
       OPTIONS.source_info_dict["selinux_fc"] =os.path.join(OPTIONS.source_tmp, "BOOT", "RAMDISK",
                                                             "file_contexts")
ifOPTIONS.package_key is None:
       OPTIONS.package_key = OPTIONS.source_info_dict.get(
           "default_system_dev_certificate",
            "build/target/product/security/testkey")
ifOPTIONS.verbose:
print"--- source info ---"
common.DumpInfoDict(OPTIONS.source_info_dict)
try:
WriteIncrementalOTAPackage(input_zip,source_zip, output_zip)
ifnot OPTIONS.mtk_sec_boot_sig_tail:
break
exceptValueError:
ifnot OPTIONS.fallback_to_full: raise
print"--- failed to build incremental; falling back to full ---"
        OPTIONS.incremental_source = None
        output_zip.close()

接下来调到全包升级主要接口

WriteFullOTAPackage(input_zip, output_zip)
def WriteFullOTAPackage(input_zip, output_zip):
  # TODO: how to determine this?  We don't know what version it will
  # be installed on top of.  For now, we expect the API just won't
  # change very often.

a1:创建一个脚本实例,这个脚本将来会被写入临时升级包中:/META-INF/com/google/android/updater-script

script = edify_generator.EdifyGenerator(3, OPTIONS.info_dict)
/*获取oem参数,没有这个key*/
  oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties")
/*获取recovery_mount_options key所对应的value*/
  recovery_mount_options = OPTIONS.info_dict.get("recovery_mount_options")
/*目前没有涉及oem信息,下面几行忽略*/
  oem_dict = None
if oem_props is not None and len(oem_props) > 0:
if OPTIONS.oem_source is None:
raise common.ExternalError("OEM source required for this build")
script.Mount("/oem", recovery_mount_options)
    oem_dict = common.LoadDictionaryFromLines(open(OPTIONS.oem_source).readlines())

M1:得到metadata字典,供最后一步使用:

post-build:alps/full_Android OTA升级(二)之ota_from_target_files文件分析_差分_02(PROJECT_NAME):5.1/LMY47D/1501228112:eng/test-keys

post-timestamp: 1501228303

pre-device: $(PROJECT_NAME)

metadata = {"post-build": CalculateFingerprint(
                               oem_props, oem_dict, OPTIONS.info_dict),
              "pre-device": GetOemProperty("ro.product.device", oem_props, oem_dict,
                                         OPTIONS.info_dict),
              "post-timestamp": GetBuildProp("ro.build.date.utc",
                                             OPTIONS.info_dict),
              }
 
  device_specific = common.DeviceSpecificParams(
      input_zip=input_zip,
      input_version=OPTIONS.info_dict["recovery_api_version"],
      output_zip=output_zip,
script=script,
      input_tmp=OPTIONS.input_tmp,
metadata=metadata,
      info_dict=OPTIONS.info_dict)
/判断是否有recovery path:target_files_zip.getinfo("SYSTEM/recovery-from-boot.p"),为真*/
  has_recovery_patch = HasRecoveryPatch(input_zip)
/*是否支持block,生成ota包时有带–block参数,所以此处为真*/
  block_based = OPTIONS.block_based and has_recovery_patch
/*不执行*/
if not OPTIONS.omit_prereq:
ts = GetBuildProp("ro.build.date.utc", OPTIONS.info_dict)
    ts_text = GetBuildProp("ro.build.date", OPTIONS.info_dict)
script.AssertOlderBuild(ts, ts_text)

升级脚本中添加:getprop(“ro.product.device”) ==“p92s_hd” || abort(“This package is for “p92s_hd"devices; this is a “” + getprop(“ro.product.device”) +””.");

AppendAssertions(script, OPTIONS.info_dict, oem_dict)
  device_specific.FullOTA_Assertions()

此处为false,不执行

if OPTIONS.two_step:
if not OPTIONS.info_dict.get("multistage_support", None):
assert False, "two-step packages not supported by this build"
fs = OPTIONS.info_dict["fstab"]["/misc"]
assert fs.fs_type.upper() == "EMMC", \
        "two-step packages only supported on devices with EMMC /misc partitions"
    bcb_dev = {"bcb_dev": fs.device}
common.ZipWriteStr(output_zip, "recovery.img", recovery_img.data)
if OPTIONS.info_dict.get("mtk_header_support", None):
      recovery_bthdr_img = common.GetBootableImage("recovery_bthdr.img", "recovery_bthdr.img",
                                                   OPTIONS.input_tmp, "RECOVERY")
common.ZipWriteStr(output_zip, "recovery_bthdr.img", recovery_bthdr_img.data)
script.AppendExtra("""
if get_stage("%(bcb_dev)s") == "2/3" then
""" % bcb_dev)
script.WriteRawImage("/recovery", "recovery.img")
script.AppendExtra("""
set_stage("%(bcb_dev)s", "3/3");
reboot_now("%(bcb_dev)s", "recovery");
else if get_stage("%(bcb_dev)s") == "3/3" then
""" % bcb_dev)
 
  device_specific.FullOTA_InstallBegin()
system_progress = 0.75
if OPTIONS.wipe_user_data:
    system_progress -= 0.1
if HasVendorPartition(input_zip):
    system_progress -= 0.1
if HasCustomPartition(input_zip):
    system_progress -= 0.1
/*把file_contexts写入升级包中*/
if "selinux_fc" in OPTIONS.info_dict:
WritePolicyConfig(OPTIONS.info_dict["selinux_fc"], output_zip)
/*得到recovery_mount_optionskey所对应的value*/
  recovery_mount_options = OPTIONS.info_dict.get("recovery_mount_options")
/*设置system分区对应的config文件为:META/filesystem_config.txt,留着后续使用*/
  system_items = ItemSet("system", "META/filesystem_config.txt")
/*在升级脚本中添加语句:show_progress(0.750000, 0);*/
script.ShowProgress(system_progress, 0)

此处为真,然后执行下面任务:

先从input_tem(原始包解压后的中间文件夹)中得到system.img
对system.img进行resetFileMap,具体不用深究
在中间文件中生成:system.transfer.list; system.new.dat ; system.patch.dat
最后再把这三个文件system.transfer.list; system.new.dat ; system.patch.dat写入到OTA包中,并添加script语句:

ui_print(“Patching system imageunconditionally…”);

block_image_update(“system”,package_extract_file(“system.transfer.list”),“system.new.dat”, “system.patch.dat”);

以上涉及到另外两个python文件:common.py ; blockimgdiff.py

if block_based:
    # Full OTA is done as an "incremental" against an empty source
    # image.  This has the effect of writing new data from the package
    # to the entire partition, but lets us reuse the updater code that
    # writes incrementals to do it.
    system_tgt = GetImage("system", OPTIONS.input_tmp, OPTIONS.info_dict)
    system_tgt.ResetFileMap()
    system_diff = common.BlockDifference("system", system_tgt, src=None)
    system_diff.WriteScript(script, output_zip)
else:
if OPTIONS.ubifs:
      cust_dir1 = GetBuildProp("ro.product.device", OPTIONS.info_dict)
      system_path = os.path.join("out/target/product",cust_dir1,"android.fixup.img")
if os.path.exists(system_path):
        system_img = open(system_path).read()
common.ZipWriteStr(output_zip, "system.img", system_img)
script.WriteRawImageUbifs("system", "system.img")
script.Mount("/system", recovery_mount_options)
if not has_recovery_patch:
script.UnpackPackageDir("recovery", "/system")
symlinks = CopyPartitionFiles(system_items, input_zip, output_zip)
else:
script.FormatPartition("/system")
script.Mount("/system", recovery_mount_options)
if not has_recovery_patch:
script.UnpackPackageDir("recovery", "/system")
script.UnpackPackageDir("system", "/system")
symlinks = CopyPartitionFiles(system_items, input_zip, output_zip)
script.MakeSymlinks(symlinks)
 
try: 
    custom_size = OPTIONS.info_dict["custom_size"]
except:
    custom_size = None
 
if custom_size is not None:
if (custom_size > 0) and (HasCustomPartition(input_zip) == True):
      custom_items = ItemSet("custom", "META/custom_filesystem_config.txt")
 
if block_based:
        custom_tgt = GetImage("custom", OPTIONS.input_tmp, OPTIONS.info_dict)
        custom_tgt.ResetFileMap()
        custom_diff = common.BlockDifference("custom", custom_tgt)
        custom_diff.WriteScript(script, output_zip)
else:
  	script.FormatPartition("/custom")
	script.Mount("/custom")
	script.UnpackPackageDir("custom", "/custom")
 
	symlinks = CopyPartitionFiles(custom_items, input_zip, output_zip)
	script.MakeSymlinks(symlinks)
 
	custom_items.GetMetadata(input_zip)
	custom_items.Get("custom").SetPermissions(script)

B1:从输入文件中获得boot.img(已存在),然后返回一个FILE class的实例,name:为镜像名;data:镜像的二进制内容,如下:common.py

class File(object):

def init(self,name, data):

self.name = name

self.data = data

self.size = len(data)

self.sha1 = sha1(data).hexdigest()

@classmethod

def FromLocalFile(cls,name, diskname):

f = open(diskname, "rb")

data = f.read()

f.close()

return File(name, data)

….

这个boot_img实例在后续会使用

boot_img = common.GetBootableImage("boot.img", "boot.img",
                                     OPTIONS.input_tmp, "BOOT")
 
if not block_based:
def output_sink(fn, data):
common.ZipWriteStr(output_zip, "recovery/" + fn, data)
      system_items.Get("system/" + fn, dir=False)
 
common.MakeRecoveryPatch(OPTIONS.input_tmp, output_sink,
                             recovery_img, boot_img)
 
    system_items.GetMetadata(input_zip)
    system_items.Get("system").SetPermissions(script)
 
if HasVendorPartition(input_zip):
    vendor_items = ItemSet("vendor", "META/vendor_filesystem_config.txt")
script.ShowProgress(0.1, 0)
 
if block_based:
      vendor_tgt = GetImage("vendor", OPTIONS.input_tmp, OPTIONS.info_dict)
      vendor_tgt.ResetFileMap()
      vendor_diff = common.BlockDifference("vendor", vendor_tgt)
      vendor_diff.WriteScript(script, output_zip)
else:
script.FormatPartition("/vendor")
script.Mount("/vendor", recovery_mount_options)
script.UnpackPackageDir("vendor", "/vendor")
 
symlinks = CopyPartitionFiles(vendor_items, input_zip, output_zip)
script.MakeSymlinks(symlinks)
 
      vendor_items.GetMetadata(input_zip)
      vendor_items.Get("vendor").SetPermissions(script)
 
if HasCustomPartition(input_zip):
    custom_items = ItemSet("custom", "META/custom_filesystem_config.txt")
script.ShowProgress(0.1, 0)
 
if block_based:
      custom_tgt = GetImage("custom", OPTIONS.input_tmp, OPTIONS.info_dict)
      custom_tgt.ResetFileMap()
      custom_diff = common.BlockDifference("custom", custom_tgt)
      custom_diff.WriteScript(script, output_zip)
else:
script.FormatPartition("/custom")
script.Mount("/custom")
script.UnpackPackageDir("custom", "/custom")
 
symlinks = CopyPartitionFiles(custom_items, input_zip, output_zip)
script.MakeSymlinks(symlinks)
 
      custom_items.GetMetadata(input_zip)
      custom_items.Get("custom").SetPermissions(script)

check boot.img镜像的大小,是否超过max_size,此处用到B1中boot_img class中的data

2.把boot_img.data内容写入到输出压缩包中,参考zipfile.py , tempfile.py

  1. 同时写入script脚本:

show_progress(0.050000,5);

assert(package_extract_file(“boot.img”,"/tmp/boot.img"),

write_raw_image("/tmp/boot.img", "bootimg"),

delete("/tmp/boot.img"));

common.CheckSize(boot_img.data, "boot.img", OPTIONS.info_dict)
common.ZipWriteStr(output_zip, "boot.img", boot_img.data)
 
script.ShowProgress(0.05, 5)
script.WriteRawImage("/boot", "boot.img")

下面几行主要是:获得SEC_VER.txt的路径,并把它写入输出的压缩包中

[SEC OTA] config : vendor/mediatek/proprietary/custom/$(PROJECT_NAME)/security/recovery/SEC_VER.txt

# security version
print "[SEC OTA] Adding security version (WriteFullOTAPackage)"
  cust_dir = GetBuildProp("ro.product.model", OPTIONS.info_dict)
  cust_dir_base = GetBuildProp("ro.product.device", OPTIONS.info_dict)
print "[SEC OTA] cust directory : ", cust_dir_base
  sec_ver_path = os.path.join("vendor/mediatek/proprietary/custom",cust_dir_base,"security/recovery/SEC_VER.txt")
print "[SEC OTA] security config : ", sec_ver_path
if os.path.exists(sec_ver_path):
    sec_ver = open(sec_ver_path).read()  
common.ZipWriteStr(output_zip, "SEC_VER.txt", sec_ver)

判断是否有:out/target/product/device/bror/p92s_hd/signed_bin/sig_info/boot.img.sig

如果有,写入输出压缩包中;没有则不需操作

boot_sec_sig_ext_path = os.path.join("out/target/product",cust_dir,"signed_bin/sig_info/boot.img.sig")
if not os.path.exists(boot_sec_sig_ext_path):
    cust_dir = GetBuildProp("ro.product.name", OPTIONS.info_dict)
    boot_sec_sig_ext_path = os.path.join("out/target/product",cust_dir,"signed_bin/sig_info/boot.img.sig")
if not os.path.exists(boot_sec_sig_ext_path):
    cust_dir = GetBuildProp("ro.mediatek.project.path", OPTIONS.info_dict)
    boot_sec_sig_ext_path = os.path.join("out/target/product",cust_dir,"signed_bin/sig_info/boot.img.sig")
 
print "[SEC OTA] security boot sig_ext : ", boot_sec_sig_ext_path
if os.path.exists(boot_sec_sig_ext_path):
    boot_sec_sig_ext = open(boot_sec_sig_ext_path).read()  
common.ZipWriteStr(output_zip, "boot.img.sig", boot_sec_sig_ext)

在output_zip中写入type.txt文件,内容为1.(全包为1,差分包为0)

#wschen start
common.ZipWriteStr(output_zip, "type.txt", "1")

把ota_scatter.txt写入output_zip中,命名为scatter.txt

cust_dir =GetBuildProp("ro.product.model", OPTIONS.info_dict)
scatter_path =os.path.join("out/target/product",cust_dir,"ota_scatter.txt")
if not os.path.exists(scatter_path):
   cust_dir = GetBuildProp("ro.product.device",OPTIONS.info_dict)
   scatter_path =os.path.join("out/target/product",cust_dir,"ota_scatter.txt")
if not os.path.exists(scatter_path):
   cust_dir = GetBuildProp("ro.product.name", OPTIONS.info_dict)
   cust_dir = cust_dir.split('full_')[-1]
   scatter_path =os.path.join("out/target/product",cust_dir,"ota_scatter.txt")
if not os.path.exists(scatter_path):
   cust_dir = GetBuildProp("ro.mediatek.project.path",OPTIONS.info_dict)
   cust_dir = cust_dir.split('/')[-1]
   scatter_path = os.path.join("out/target/product",cust_dir,"ota_scatter.txt")
ota_scatter = open(scatter_path).read()
common.ZipWriteStr(output_zip,"scatter.txt", ota_scatter)

下面这几步是判断是否进行preloader,logo,uboot,tee等分区进行升级,如不需要,则忽略

#  if OPTIONS.preloader is not None or OPTIONS.uboot is not None or OPTIONS.logo is not None:
#    script.AppendExtra('assert(run_program(\"/system/bin/dd\", \"if=/dev/zero\", \"of=/proc/driver/mtd_writeable\", \"bs=3\", \"count=1\"));')
 
if OPTIONS.logo is not None:
    logo_img = open(OPTIONS.logo).read()
common.ZipWriteStr(output_zip, "logo.img", logo_img)
script.WriteRawImage2("logo", "logo.img")
 
if OPTIONS.preloader is not None:
    preloader_img = open(OPTIONS.preloader).read()
common.ZipWriteStr(output_zip, "preloader.img", preloader_img)
script.WriteRawImage2("preloader", "preloader.img")
 
if OPTIONS.uboot is not None:
    uboot_img = open(OPTIONS.uboot).read()
common.ZipWriteStr(output_zip, "uboot.img", uboot_img)
script.WriteRawImage2("uboot", "uboot.img")
 
 
  #tonykuo start
if OPTIONS.tee is not None:
    tee_img = open(OPTIONS.tee).read()
common.ZipWriteStr(output_zip, "tee.img", tee_img)
script.WriteRawImage2("tee1", "tee.img")
  #tonykuo end
 
  #koshi start
if OPTIONS.trustonic is not None:
    trustonic_img = open(OPTIONS.trustonic).read()
common.ZipWriteStr(output_zip, "mobicore.bin", trustonic_img)
script.WriteRawImage2("tee1", "mobicore.bin")
  #koshi end

添加script语句:show_progress(0.200000, 10);

并声明WriteFull_ota制作结束

script.ShowProgress(0.2, 10)
device_specific.FullOTA_InstallEnd()

如果存在额外脚本,则把脚本内容填入升级script中,此处为None

if OPTIONS.extra_script is not None:
script.AppendExtra(OPTIONS.extra_script)

升级脚本中添加unmount所有mount_point语句,如下:

def UnmountAll(self):

for p insorted(self.mounts):

self.script.append(‘unmount("%s");’% (p,))

self.mounts = set()

但由于sef.mounts在全包升级中为空集合,所以此处没有添加unmount语句到脚本中

script.UnmountAll()

制作ota包命令行中是否带有"-f","–special_factory_reset"参数,没有则不执行下面语句,此处为False

#wschen
if OPTIONS.special_factory_reset:
script.AppendExtra('special_factory_reset();')

制作ota包命令行中是否带有"-w", "–wipe_user_data"参数,没有则不执行下面语句:

清楚data分区。此处为False

if OPTIONS.wipe_user_data:
script.ShowProgress(0.1, 10)
script.FormatPartition("/data")

添加升级脚本语句:

self.script.append((‘apply_sig(package_extract_file("%(sigfile_name)s"),"%(partion_name)s");’)

% {'sigfile_name':sigfile_name,'partion_name':partion_name})

==>apply_sig(package_extract_file(“sig/boot.sig”),“bootimg”);

if OPTIONS.mtk_sec_boot_sig_tail:
script.ApplySig("sig/boot.sig", "bootimg")

制作ota包命令行中是否带有"-2", "–two_step"参数,没有则不执行下面语句:

清楚data分区。此处为False

if OPTIONS.two_step:
script.AppendExtra("""
set_stage("%(bcb_dev)s", "");
""" % bcb_dev)
script.AppendExtra("else\n")
script.WriteRawImage("/boot", "recovery.img")
script.AppendExtra("""
set_stage("%(bcb_dev)s", "2/3");
reboot_now("%(bcb_dev)s", "");
endif;
endif;
""" % bcb_dev)

此处尤为重要,主要做两件事情:

将升级脚本语句script序列写入到升级包中:"META-INF/com/google/android/updater-script"
将升级工具(可执行文件)写入升级包中:"META-INF/com/google/android/update-binary"

代码如下:请参考edify_generator.py:

defAddToZip(self, input_zip, output_zip, input_path=None):

“”"Write the accumulated script to the output_zipfile. input_zip

isused as the source for the ‘updater’ binary needed to run

script. If input_path is not None, it will be used asa local

pathfor the binary instead of input_zip."""

self.UnmountAll()

common.ZipWriteStr(output_zip,“META-INF/com/google/android/updater-script”,

"\n".join(self.script) + "\n")

/此处为序列之间加入‘\n’,所以最终的文件中都是一行一行的呈现出来/

ifinput_path is None:

data= input_zip.read(“OTA/bin/updater”)

else:

data= open(input_path, “rb”).read()

common.ZipWriteStr(output_zip,“META-INF/com/google/android/update-binary”,

data,perms=0755)

script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary)

把上文M1处内容中的字典格式转换成‘%s=%s’格式,并写入到升级包中:

“META-INF/com/android/metadata”

如下文code:调用

defWriteMetadata(metadata, output_zip):

common.ZipWriteStr(output_zip,“META-INF/com/android/metadata”,

"".join(["%s=%s\n" % kv

                          for kv insorted(metadata.iteritems())]))

最终文件内容如下:

post-build=alps/full_p92s_hd/p92s_hd:5.1/LMY47D/1501228112:eng/test-keys

post-timestamp=1501228303

pre-device=p92s_hd

WriteMetadata(metadata, output_zip)

至此,writeFullOTAPackage函数运行完毕
接下来再继续跳到main函数剩余部分接着执行

if OPTIONS.incremental_source is None:
WriteFullOTAPackage(input_zip, output_zip)
if OPTIONS.package_key is None:
        OPTIONS.package_key = OPTIONS.info_dict.get(
            "default_system_dev_certificate",
            "build/target/product/security/testkey")
if not OPTIONS.mtk_sec_boot_sig_tail:
break
else:
print "unzipping source target-files..."
      OPTIONS.source_tmp, source_zip = common.UnzipTemp(OPTIONS.incremental_source)
      OPTIONS.target_info_dict = OPTIONS.info_dict
      OPTIONS.source_info_dict = common.LoadInfoDict(source_zip)
if "selinux_fc" in OPTIONS.source_info_dict:
        OPTIONS.source_info_dict["selinux_fc"] = os.path.join(OPTIONS.source_tmp, "BOOT", "RAMDISK",
                                                              "file_contexts")
if OPTIONS.package_key is None:
        OPTIONS.package_key = OPTIONS.source_info_dict.get(
            "default_system_dev_certificate",
            "build/target/product/security/testkey")
if OPTIONS.verbose:
print "--- source info ---"
common.DumpInfoDict(OPTIONS.source_info_dict)
try:
WriteIncrementalOTAPackage(input_zip, source_zip, output_zip)
if not OPTIONS.mtk_sec_boot_sig_tail:
break
except ValueError:
if not OPTIONS.fallback_to_full: raise
print "--- failed to build incremental; falling back to full ---"
        OPTIONS.incremental_source = None
        output_zip.close()

此处为真,接下来在临时升级文件包中写入sig/boot.sig; sig/recovery.sig文件

其中源data(boot_sig,recovery_sig)为上文S1步骤中的内容

if OPTIONS.mtk_sec_boot_sig_tail:
common.ZipWriteStr(output_zip, "sig/boot.sig", boot_sig)
common.ZipWriteStr(output_zip, "sig/recovery.sig", recovery_sig)
break;
  output_zip.close()

对中间升级文件包进行前签名,生成最终的升级包

1.running: openssl pkcs8 -inbuild/target/product/security/testkey.pk8 -inform DER -nocrypt

2.running: java -Xmx2048m -jarout/host/linux-x86/framework/signapk.jar -wbuild/target/product/security/testkey.x509.pembuild/target/product/security/testkey.pk8 /tmp/tmpBXjz5Z out/target/product/xxxx/xxxx-ota-eng.wan.zip

最终生成升级包

第2步中会调用build/tools/signapk/signapk.java文件,生成

META-INF/MANIFEST.MF,META-INF/CERT.RSA,META-INF/CERT.SF,

META-INF/com/android/otacert文件

if not OPTIONS.no_signing:
SignOutput(temp_zip_file.name, args[1])
    temp_zip_file.close()
 
print "done."

最终压缩包中的文件列表如下:

Android OTA升级(二)之ota_from_target_files文件分析_压缩包_03


着重看下META-INF/com/google/android下的两个升级文件:

Update-binary:升级工具,可执行文件

Updater-script:升级脚本,由制作升级包的过程中一步一步生成的,其内容如下:

Android OTA升级(二)之ota_from_target_files文件分析_sed_04

文章参考:
Android OTA升级(二)之ota_from_target_files文件分析


【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2023年11月24日 0

暂无评论

推荐阅读
  2oXoBYjmdHYq   2023年12月05日   39   0   0 sedsedMySQLMySQL
HvTJUzsxOBtS