转载
概要
此部分为全包升级主要实现过程,涉及到ota_from_target_files 文件,这个也是制作全包和差分包的主要工具,接下来我们就着重分析怎么利用这个工具制作full_ota_package的。
主要流程
源码分析
上节中 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
Deprecated.
-k (--package_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)
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)
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)
Insert the contents of file at the end of the update script.
-r (--preloader)
Specify \'preloader.img\' to upgrade.
-l (--logo)
Specify \'logo.img\' to upgrade.
-u (--uboot)
Specify \'uboot.img/lk.img\' to upgrade.
-a (--aslr_mode)
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)
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)
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)
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_(PROJECTNAME)/(PROJECT_NAME)/(PROJECTNAME)/(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)
12345678910111213141516
/判断是否有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 = dat
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
- 把
boot_img.data
内容写入到输出压缩包中,参考zipfile.py
,tempfile.py
同时写入
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()
对中间升级文件包进行前签名,生成最终的升级包。
- running:
openssl pkcs8 -inbuild/target/product/security/testkey.pk8 -inform DER -nocrypt
- 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.\"
着重看下 META-INF/com/google/android
下的两个升级文件:
Update-binary
:升级工具,可执行文件Updater-script
:升级脚本,由制作升级包的过程中一步一步生成的,其内容如下: