91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

升級包的制作與升級流程簡介

發布時間:2020-06-05 22:48:45 來源:網絡 閱讀:5598 作者:18061389 欄目:移動開發

#升級包的制作與升級的流程

一、升級包的制作

1、Makefile

make otapackage是一個.PHONY偽目標,查看\build\core\Makefile:

.PHONY: otapackage
otapackage:droidcore dist_files $(INTERNAL_OTA_PACKAGE_TARGET)

可以看到otapackage這個偽目標是依賴于$(INTERNAL_OTA_PACKAGE_TARGET)的

INTERNAL_OTA_PACKAGE_TARGET := $(PRODUCT_OUT)/$(name).zip
$(INTERNAL_OTA_PACKAGE_TARGET): KEY_CERT_PAIR := $(DEFAULT_KEY_CERT_PAIR)

$(INTERNAL_OTA_PACKAGE_TARGET): $(BUILT_TARGET_FILES_PACKAGE) $(DISTTOOLS)
    @echo "Package OTA: $@"
    @echo ./build/tools/releasetools/ota_from_target_files -v \
       $(amlogic_flag) \
       $(omit_prereq_flag) \
       $(UPDATE_DTB) \
       -n \
       -p $(HOST_OUT) \
       $(wipeopt) \
       $(baksupport) \  
       -k $(KEY_CERT_PAIR) \
       $(recovery_not_patch) \
       $(dm_verity) \
       $(BUILT_TARGET_FILES_PACKAGE) $@

可以看到,$(INTERNAL_OTA_PACKAGE_TARGET)的生成依賴于$(BUILT_TARGET_FILES_PACKAGE)和$(DISTTOOLS)等生成

$(DISTTOOLS)的編譯規則如下:

DISTTOOLS :=  $(HOST_OUT_EXECUTABLES)/minigzip \
      $(HOST_OUT_EXECUTABLES)/mkbootfs \
      $(HOST_OUT_EXECUTABLES)/mkbootimg \
      $(HOST_OUT_EXECUTABLES)/fs_config \
      $(HOST_OUT_EXECUTABLES)/mkyaffs2image \
      $(HOST_OUT_EXECUTABLES)/zipalign \
      $(HOST_OUT_EXECUTABLES)/bsdiff \
      $(HOST_OUT_EXECUTABLES)/imgdiff \
      $(HOST_OUT_JAVA_LIBRARIES)/dumpkey.jar \
      $(HOST_OUT_JAVA_LIBRARIES)/signapk.jar \
      $(HOST_OUT_EXECUTABLES)/mkuserimg.sh \
      $(HOST_OUT_EXECUTABLES)/make_ext4fs \
      $(HOST_OUT_EXECUTABLES)/simg2img \
      $(HOST_OUT_EXECUTABLES)/e2fsck

OTATOOLS := $(DISTTOOLS) \
      $(HOST_OUT_EXECUTABLES)/aapt

.PHONY: otatools
otatools: $(OTATOOLS)

變量$(HOST_OUT_EXECUTABLES)指代的是out/host/linux-x86/bin目錄,而變量$(HOST_OUT_JAVA_LIBRARIES)/表示的是out/host/linux-x86/framework目錄。可以看出,$(OTATOOLS)用于指定命令執行所需要的工具,例如:minigzip:用于gzip文件;make_ext4fs:將文件轉換為ext4類型;mkyaffs2image:用于yaffs文件系統;bsdiff,imgdiff用于差分;signapk.jar用于簽名等功能。

$(BUILT_TARGET_FILES_PACKAGE)主要是完成了兩件事:重新打包system.img文件和生成差分資源包。$(BUILT_TARGET_FILES_PACKAGE)的編譯規則如下所示:

# -----------------------------------------------------------------
# A zip of the directories that map to the target filesystem.
# This zip can be used to create an OTA package or filesystem image
# as a post-build step.
#
name := $(TARGET_PRODUCT)
ifeq ($(TARGET_BUILD_TYPE),debug)
  name := $(name)_debug
endif
name := $(name)-target_files-$(FILE_NAME_TAG)

intermediates := $(call intermediates-dir-for,PACKAGING,target_files)
BUILT_TARGET_FILES_PACKAGE := $(intermediates)/$(name).zip
$(BUILT_TARGET_FILES_PACKAGE): intermediates := $(intermediates)
$(BUILT_TARGET_FILES_PACKAGE): \
        zip_root := $(intermediates)/$(name)

# $(1): Directory to copy
# $(2): Location to copy it to
# The "ls -A" is to prevent "acp s/* d" from failing if s is empty.
define package_files-copy-root
  if [ -d "$(strip $(1))" -a "$$(ls -A $(1))" ]; then \
    mkdir -p $(2) && \
    $(ACP) -rd $(strip $(1))/* $(2); \
  fi
endef

built_ota_tools := \
    $(call intermediates-dir-for,EXECUTABLES,applypatch)/applypatch \
    $(call intermediates-dir-for,EXECUTABLES,applypatch_static)/applypatch_static \
    $(call intermediates-dir-for,EXECUTABLES,check_prereq)/check_prereq \
    $(call intermediates-dir-for,EXECUTABLES,sqlite3)/sqlite3 \
    $(call intermediates-dir-for,EXECUTABLES,updater)/updater
$(BUILT_TARGET_FILES_PACKAGE): PRIVATE_OTA_TOOLS := $(built_ota_tools)

$(BUILT_TARGET_FILES_PACKAGE): PRIVATE_RECOVERY_API_VERSION := $(RECOVERY_API_VERSION)
$(BUILT_TARGET_FILES_PACKAGE): PRIVATE_RECOVERY_FSTAB_VERSION := $(RECOVERY_FSTAB_VERSION)

# Depending on the various images guarantees that the underlying
# directories are up-to-date.
#開始構建中間zip包
$(BUILT_TARGET_FILES_PACKAGE): \
        $(INSTALLED_BOOTIMAGE_TARGET) \
        $(INSTALLED_RADIOIMAGE_TARGET) \
        $(INSTALLED_RECOVERYIMAGE_TARGET) \
        $(INSTALLED_AMLOGIC_RECOVERY_TARGET) \
        $(INSTALLED_SYSTEMIMAGE) \
        $(INSTALLED_USERDATAIMAGE_TARGET) \
        $(INSTALLED_CACHEIMAGE_TARGET) \
        $(INSTALLED_VENDORIMAGE_TARGET) \
        $(INSTALLED_ANDROID_INFO_TXT_TARGET) \
        $(SELINUX_FC) \
        $(built_ota_tools) \
        $(APKCERTS_FILE) \
        $(HOST_OUT_EXECUTABLES)/fs_config \
        | $(ACP) \
        $(INSTALLED_AML_LOGO) \
        $(TARGET_AMLOGIC_KERNEL)
    @echo "Package target files: $@"
    # 刪除之前的zip文件
    $(hide) rm -rf $@ $(zip_root)
    $(hide) mkdir -p $(dir $@) $(zip_root)
    @# Components of the recovery image
    $(hide) mkdir -p $(zip_root)/RECOVERY
    $(hide) $(call package_files-copy-root, \
    $(TARGET_RECOVERY_ROOT_OUT),$(zip_root)/RECOVERY/RAMDISK)

    ifdef INSTALLED_KERNEL_TARGET
        $(hide) $(ACP) $(INSTALLED_KERNEL_TARGET) $(zip_root)/RECOVERY/kernel
    endif
    ifdef TARGET_AMLOGIC_RECOVERY_KERNEL
        $(hide) $(ACP) $(TARGET_AMLOGIC_RECOVERY_KERNEL) $(zip_root)/RECOVERY/kernel
    endif
    ifdef INSTALLED_2NDBOOTLOADER_TARGET
        $(hide) $(ACP) \
            $(INSTALLED_2NDBOOTLOADER_TARGET) $(zip_root)/RECOVERY/second
    endif
    ifdef INSTALLED_BOARDDTB_TARGET
        $(hide) $(ACP) $(INSTALLED_BOARDDTB_TARGET) $(zip_root)/RECOVERY/second
    endif
    ifdef BOARD_KERNEL_CMDLINE
        $(hide) echo "$(BOARD_KERNEL_CMDLINE)" > $(zip_root)/RECOVERY/cmdline
    endif
    ifdef BOARD_KERNEL_BASE
        $(hide) echo "$(BOARD_KERNEL_BASE)" > $(zip_root)/RECOVERY/base
    endif
    ifdef BOARD_KERNEL_PAGESIZE
        $(hide) echo "$(BOARD_KERNEL_PAGESIZE)" > $(zip_root)/RECOVERY/pagesize
    endif
    ifdef BOARD_KERNEL_OFFSET
        $(hide) echo "$(BOARD_KERNEL_OFFSET)" > $(zip_root)/RECOVERY/kernel_offset
    endif

        @# Components of the boot image
        $(hide) mkdir -p $(zip_root)/BOOT
        $(hide) $(call package_files-copy-root, \
            $(TARGET_ROOT_OUT),$(zip_root)/BOOT/RAMDISK)
    ifdef INSTALLED_KERNEL_TARGET
        $(hide) $(ACP) $(INSTALLED_KERNEL_TARGET) $(zip_root)/BOOT/kernel
    endif
    ifdef TARGET_AMLOGIC_KERNEL
        $(hide) $(ACP) $(TARGET_AMLOGIC_KERNEL) $(zip_root)/BOOT/kernel
    endif

    ......

    # 打包zip包
    #ifeq ($(PRODUCT_BUILD_SECURE_BOOT_IMAGE_DIRECTLY),true)
    $(hide) (cd $(zip_root) && zip -qry ../$(notdir $@) .)
    @# Run fs_config on all the system, boot ramdisk, and recovery ramdisk files in the zip, and save the output
    $(hide) zipinfo -1 $@ | awk 'BEGIN { FS="SYSTEM/" } /^SYSTEM\// {print "system/" $$2}' | $(HOST_OUT_EXECUTABLES)/fs_config -C -S $(SELINUX_FC) > $(zip_root)/META/filesystem_config.txt
    $(hide) zipinfo -1 $@ | awk 'BEGIN { FS="BOOT/RAMDISK/" } /^BOOT\/RAMDISK\// {print $$2}' | $(HOST_OUT_EXECUTABLES)/fs_config -C -S $(SELINUX_FC) > $(zip_root)/META/boot_filesystem_config.txt
    $(hide) zipinfo -1 $@ | awk 'BEGIN { FS="RECOVERY/RAMDISK/" } /^RECOVERY\/RAMDISK\// {print $$2}' | $(HOST_OUT_EXECUTABLES)/fs_config -C -S $(SELINUX_FC) > $(zip_root)/META/recovery_filesystem_config.txt
    $(hide) (cd $(zip_root) && zip -q ../$(notdir $@) META/*filesystem_config.txt)

.PHONY: target-files-package
target-files-package: $(BUILT_TARGET_FILES_PACKAGE)

這塊target-files-package目標主要實現的功能:

● 創建$(zip_root)目錄,$(zip_root)即out/target/product/<product-name>/obj/PACKAGING/target_files_from_intermedias/<product-name>-target_files-<version-name>

● 創建/$(zip_root)/RECOVERY目錄并將COPY相關文件,包括:kernel鏡像文件、RAMDISK目錄。此目錄最終用來生成recovery.img

● 創建/$(zip_root)/BOOT目錄并將COPY相關文件,包括:kernel鏡像文件、RAMDISK目錄、ramdisk鏡像。此目錄最終用來生成boot.img

● 創建其他目錄并COPY文件,包括SYSTEM目錄、OTA/bin目錄、META目錄

● 將$(zip_root)目錄壓縮為資源差分包

完成這些工作后調用ota_from_target_files做進一步的處理:

$(INTERNAL_OTA_PACKAGE_TARGET): $(BUILT_TARGET_FILES_PACKAGE) $(DISTTOOLS)
    @echo "Package OTA: $@"
    @echo ./build/tools/releasetools/ota_from_target_files -v \
       $(amlogic_flag) \
       $(omit_prereq_flag) \
       $(UPDATE_DTB) \
       -n \
       -p $(HOST_OUT) \
       $(wipeopt) \
       $(baksupport) \
       -k $(KEY_CERT_PAIR) \
       $(recovery_not_patch) \
       $(dm_verity) \
       $(BUILT_TARGET_FILES_PACKAGE) $@

2、ota_from_target_files

/build/tools/releasetools/ota_from_target_files.py

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

  -b  (--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.

  -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.

  -a  (--aslr_mode)  <on|off>
      Specify whether to turn on ASLR for the package (on by default).

用法:ota_from_target_files [flags] input_target_files output_ota_package

-i:生成增量OTA包時使用此選項

-k:簽名所使用的密鑰

-w:是否清除userdata分區

-e:是否有額外運行的腳本

-p:定義腳本用到的一些可執行文件的路徑

具體來看下這個腳本的執行過程:

def main(argv):

  #將用戶設定的option存入到一個OPTIONS類中
  def option_handler(o, a):
    if o in ("-b", "--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 ("-s", "--no_wipe_system"):
      OPTIONS.no_wipe_system = True
    elif o in ("-n", "--no_prereq"):
      OPTIONS.omit_prereq = True
    elif o in ("-e", "--extra_script"):
      OPTIONS.extra_script = a
    elif o in ("-c", "--wipe_cache"):
      OPTIONS.wipe_cache_enable = True
    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 ("--worker_threads"):
      OPTIONS.worker_threads = int(a)
    elif o in ("--backup_support"):
      OPTIONS.backup_support = True
    elif o in ("--recovery_not_patch"):
      OPTIONS.recovery_not_patch = True
    elif o in ("--dm_verity"):
      OPTIONS.dm_verity_enable = True
    elif o in ("--progress_iptv"):
      OPTIONS.progress_iptv = True
    elif o in ("--dtb"):
      OPTIONS.update_dtb = True
    else:
      return False
    return True

    #解析參數,將得到的參數和參數值傳回給args
    args = common.ParseOptions(argv, __doc__,
                             extra_opts="b:k:i:wsneca:",
                             extra_long_opts=["board_config=",
                                              "package_key=",
                                              "incremental_from=",
                                              "wipe_user_data",
                                              "no_wipe_system",
                                              "no_prereq",
                                              "extra_script=",
                                              "wipe_cache",
                                              "dm_verity",
                                              "progress_iptv",
                                              "dtb",
                                              "worker_threads=",
                                              "aslr_mode=",
                          "backup_support",
                          "recovery_not_patch",
                                              ],
                             extra_option_handler=option_handler)

common.py中的ParseOptions,主要是調用python中的getopt模塊中的getopt函數來解析參數

def ParseOptions(argv,
                 docstring,
                 extra_opts="", extra_long_opts=(),
                 extra_option_handler=None):
  """Parse the options in argv and return any arguments that aren't
  flags.  docstring is the calling module's docstring, to be displayed
  for errors and -h.  extra_opts and extra_long_opts are for flags
  defined by the caller, which are processed by passing them to
  extra_option_handler."""

  try:
    opts, args = getopt.getopt(
        argv, "ahvp:s:x:" + extra_opts,
        ["amlogic", "help", "verbose", "path=", "signapk_path=", "extra_signapk_args=",
         "java_path=", "public_key_suffix=", "private_key_suffix=",
         "device_specific=", "extra="] +
        list(extra_long_opts))
  except getopt.GetoptError, err:
    Usage(docstring)
    print "**", str(err), "**"
    sys.exit(2)

  path_specified = False

  for o, a in opts:
    if o in ("-h", "--help"):
      Usage(docstring)
      sys.exit()
    elif o in ("-a", "--amlogic"):
      OPTIONS.amlogic = True
    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 ("--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 not extra_option_handler(o, a):
        assert False, "unknown option \"%s\"" % (o,)

  os.environ["PATH"] = (os.path.join(OPTIONS.search_path, "bin") +
                        os.pathsep + os.environ["PATH"])

  return args

然后解壓目標文件包

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

#保存解壓目錄名稱
OPTIONS.target_tmp = OPTIONS.input_tmp
#根據target-files-package生成的zipfile對象
OPTIONS.info_dict = common.LoadInfoDict(input_zip)

其中LoadInfoDict主要是解析了三個文件:META/misc_info.txt、SYSTEM/build.prop、RECOVERY/RAMDISK/etc/recovery.fstab,并將文件內容以(k,v)鍵值對形式保存到OPTIONS.info_dict中。

隨后根據incremental_source的值,也就是之前的-i參數來判斷制作全量包還是增量包:

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")
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 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)
WriteIncrementalOTAPackage(input_zip, source_zip, output_zip)

先來分析全量包的制作WriteFullOTAPackage:

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.
  script = edify_generator.EdifyGenerator(3, OPTIONS.info_dict)

新建了updater-script文件,這是一個Android內置的Edify可擴展語言腳本,通過內建函數命令來執行操作,是整個升級包的核心。

隨后開始向updater-script寫入內容:

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)

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

if OPTIONS.progress_iptv:
script.ShowProgress(0.8, 40)
else:
script.ShowProgress(0.9, 95)

platform = GetBuildProp("ro.board.platform", OPTIONS.info_dict)
print "ro.board.platform: %s" % (platform)
if "meson3" in platform:
script.SetBootloaderEnv("upgrade_step", "0")
elif "meson6" in platform:
script.SetBootloaderEnv("upgrade_step", "0")
else:
script.SetBootloaderEnv("upgrade_step", "3")

if OPTIONS.wipe_user_data:
script.FormatPartition("/data")

if "selinux_fc" in OPTIONS.info_dict:
WritePolicyConfig(OPTIONS.info_dict["selinux_fc"], output_zip)

if OPTIONS.dm_verity_enable:
script.FormatPartition("/system")
if not OPTIONS.recovery_not_patch:
  OPTIONS.recovery_not_patch = True
  print 'enable write recovery.img in dm-verity'
else:
if not OPTIONS.no_wipe_system:
  script.FormatPartition("/system")
  if OPTIONS.backup_support:
    script.FormatPartition("/backup")
script.Mount("/system")

if not OPTIONS.dm_verity_enable:
script.UnpackPackageDir("recovery", "/system")
script.UnpackPackageDir("system", "/system")

symlinks = CopySystemFiles(input_zip, output_zip)
script.MakeSymlinks(symlinks)

然后執行:

# 將updater-script和update-binary寫入到output_zip
script.AddToZip(input_zip, output_zip)
#將metadata信息寫入到output_zip的META-INF/com/android/metadata文件中
WriteMetadata(metadata, output_zip)

這里的output_zip是tmp目錄下的臨時zip文件,最后回到main函數執行SignOutput:

SignOutput(temp_zip_file.name, args[1])

def SignOutput(temp_zip_name, output_zip_name):
  key_passwords = common.GetKeyPasswords([OPTIONS.package_key])
  pw = key_passwords[OPTIONS.package_key]

  common.SignFile(temp_zip_name, output_zip_name, OPTIONS.package_key, pw,
                  whole_file=True)

temp_zip_file.name就是output_zip對象對應的文件名稱,args[1]就是最終的OTA zip包的名稱。實際執行的就是我們平時手動簽名的命令。

到這里完成了全量包的制作,接下來對于差分包的制作WriteIncrementalOTAPackage進行一下分析:

def WriteIncrementalOTAPackage(target_zip, source_zip, output_zip):

    script = edify_generator.EdifyGenerator(source_version,
                                          OPTIONS.target_info_dict)

    #加載源、目標zip文件中的SYSTEM目錄文件
    print "Loading target..."
    target_data = LoadSystemFiles(target_zip)
    print "Loading source..."
    source_data = LoadSystemFiles(source_zip)

    matching_file_cache = {}
    for fn in source_data.keys():
    sf = source_data[fn]
    assert fn == sf.name
    matching_file_cache["path:" + fn] = sf
    # Only allow eligability for filename/sha matching
    # if there isn't a perfect path match.
    if target_data.get(sf.name) is None:
      matching_file_cache["file:" + fn.split("/")[-1]] = sf
      matching_file_cache["sha:" + sf.sha1] = sf

    for fn in sorted(target_data.keys()):
    tf = target_data[fn]
    assert fn == tf.name
    #在ClosestFileMatch構建源、目標文件對應關系
    sf = ClosestFileMatch(tf, matching_file_cache, renames)
    if sf is not None and sf.name != tf.name:
      print "File has moved from " + sf.name + " to " + tf.name
      renames[sf.name] = tf

    if sf is None or fn in OPTIONS.require_verbatim:
      # This file should be included verbatim
      if fn in OPTIONS.prohibit_verbatim:
        raise common.ExternalError("\"%s\" must be sent verbatim" % (fn,))
      print "send", fn, "verbatim"
      #寫入目標文件或者增量文件到輸出zip文件
      tf.AddToZip(output_zip)
      verbatim_targets.append((fn, tf.size))
    elif tf.sha1 != sf.sha1:
      # File is different; consider sending as a patch
      diffs.append(common.Difference(tf, sf))
    else:
      # Target file data identical to source (may still be renamed)
      pass

    common.ComputeDifferences(diffs)

    #檢查Fingerprint是否一致以及掛載/system分區(寫入腳本)
    source_fp = GetBuildProp("ro.build.fingerprint", OPTIONS.source_info_dict)
    target_fp = GetBuildProp("ro.build.fingerprint", OPTIONS.target_info_dict)
    metadata["pre-build"] = source_fp
    metadata["post-build"] = target_fp

    script.Mount("/system")
    script.AssertSomeFingerprint(source_fp, target_fp)

    #從源zip、目標zip中分別獲取boot.img、recovery.img
    if boot_img_exists:
    source_boot = common.GetBootableImage(
        "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT",
        OPTIONS.source_info_dict)
    target_boot = common.GetBootableImage(
        "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT")
    updating_boot = (source_boot.data != target_boot.data)

    if recovery_img_exists:
    source_recovery = common.GetBootableImage(
        "/tmp/recovery.img", "recovery.img", OPTIONS.source_tmp, "RECOVERY",
        OPTIONS.source_info_dict)
    target_recovery = common.GetBootableImage(
        "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
    updating_recovery = (source_recovery.data != target_recovery.data)

    #校驗所有源文件的SHA1(寫入腳本)
    script.Print("Verifying current system...")

    device_specific.IncrementalOTA_VerifyBegin()

    script.ShowProgress(0.1, 0)
    total_verify_size = float(sum([i[2].size for i in patch_list]) + 1)
    if updating_boot:
    total_verify_size += source_boot.size
    if updating_bootloader:
    total_verify_size += target_bootloader.size
    if updating_dtb:
    total_verify_size += target_dtb.size
    so_far = 0

    for fn, tf, sf, size, patch_sha in patch_list:
    script.PatchCheck("/"+fn, tf.sha1, sf.sha1)
    so_far += sf.size
    script.SetProgress(so_far / total_verify_size)

    if updating_bootloader:
    so_far += target_bootloader.size
    script.SetProgress(so_far / total_verify_size)

    if updating_logo:
    so_far += target_logo.size
    script.SetProgress(so_far / total_verify_size)

    if updating_dtb:
    so_far += target_dtb.size
    script.SetProgress(so_far / total_verify_size)

    #如果boot有更新,校驗boot的SHA1(寫入腳本)
    if updating_boot:

    boot_type, boot_device = common.GetTypeAndDevice("/boot", OPTIONS.info_dict)

    #script.PatchCheck("%s:%s:%d:%s:%d:%s" %
    print("update boot_img %s:%s:from %d:%s:to %d:%s" %
                      (boot_type, boot_device,
                       source_boot.size, source_boot.sha1,
                       target_boot.size, target_boot.sha1))
    so_far += target_boot.size
    script.SetProgress(so_far / total_verify_size)

    #檢驗cache剩余空間是否足夠(寫入腳本)
    if patch_list or updating_recovery or updating_boot or updating_bootloader or updating_logo or updating_dtb:
        script.CacheFreeSpaceCheck(largest_source_size)

    #刪除無需修改的文件(寫入腳本)
    script.Print("Removing unneeded files...")
    script.DeleteFiles(["/"+i[0] for i in verbatim_targets] +
                     ["/"+i for i in sorted(source_data)
                            if i not in target_data and
                            i not in renames] +
                     ["/system/recovery.img"])

    #應用patch(寫入腳本)
    script.Print("Patching system files...")
    deferred_patch_list = []
    for item in patch_list:
    fn, tf, sf, size, _ = item
    if tf.name == "system/build.prop":
      deferred_patch_list.append(item)
      continue
    script.ApplyPatch("/"+fn, "-", tf.size, tf.sha1, sf.sha1, "patch/"+fn+".p")
    so_far += tf.size

    if updating_boot:
    #如果boot有更新,寫入img(寫入腳本)
    script.Print("install boot image...");
    common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
    script.WriteRawImage("/boot", "boot.img")
    so_far += target_boot.size
    script.SetProgress(so_far / total_patch_size)
    print "boot image changed; including."
    else:
    print "boot image unchanged; skipping."

    if updating_recovery:
    #如果recovery有更新,寫入img(寫入腳本)      
    print("recovery_Image from:%d:%s: to %d:%s" %
                      (source_recovery.size, source_recovery.sha1,
                       target_recovery.size, target_recovery.sha1))
    script.Print("install recovery image...");
    common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
    script.WriteRawImage("/recovery", "recovery.img")
    so_far += target_recovery.size
    script.SetProgress(so_far / total_patch_size)
    print "recovery image changed; including."
    else:
    print "recovery image unchanged; skipping."

    ......

    #將升級腳本寫入到輸出zip
    script.AddToZip(target_zip, output_zip)
    #將metadata寫入到輸出zip
    WriteMetadata(metadata, output_zip)

至此,我們所需的差分包就制作完成了。

Unpacking new recovery...
Renaming files...
script aborted: Rename of system/wplayerplugins/com.pptv.gstplayer.apk() to system/app/com.pptv.gstplayer.apk() failed, error No such file or directory()
Rename of system/wplayerplugins/com.pptv.gstplayer.apk() to system/app/com.pptv.gstplayer.apk() failed, error No such file or directory()
E:the child process end code is 7
E:Error in /cache/update.zip
(Status 7)
Installation aborted

ui_print("Renaming files...");
rename("system/wplayerplugins/com.pptv.gstplayer.apk", "system/app/com.pptv.gstplayer.apk");

二、升級流程

recovery.cpp頭部的注釋:

/*
 * The recovery tool communicates with the main system through /cache files.
 *   /cache/recovery/command - INPUT - command line for tool, one arg per line
 *   /cache/recovery/log - OUTPUT - combined log file from recovery run(s)
 *   /cache/recovery/intent - OUTPUT - intent that was passed in
 *
 * The arguments which may be supplied in the recovery.command file:
 *   --send_intent=anystring - write the text out to recovery.intent
 *   --update_package=path - verify install an OTA package file
 *   --wipe_data - erase user data (and cache), then reboot
 *   --wipe_cache - wipe cache (but not user data), then reboot
 *   --set_encrypted_filesystem=on|off - enables / diasables encrypted fs
 *   --just_exit - do nothing; exit and reboot
 *
 * After completing, we remove /cache/recovery/command and reboot.
 * Arguments may also be supplied in the bootloader control block (BCB).
 * These important scenarios must be safely restartable at any point:
 *
 * FACTORY RESET
 * 1. user selects "factory reset"
 * 2. main system writes "--wipe_data" to /cache/recovery/command
 * 3. main system reboots into recovery
 * 4. get_args() writes BCB with "boot-recovery" and "--wipe_data"
 *    -- after this, rebooting will restart the erase --
 * 5. erase_volume() reformats /data
 * 6. erase_volume() reformats /cache
 * 7. finish_recovery() erases BCB
 *    -- after this, rebooting will restart the main system --
 * 8. main() calls reboot() to boot main system
 *
 * OTA INSTALL
 * 1. main system downloads OTA package to /cache/some-filename.zip
 * 2. main system writes "--update_package=/cache/some-filename.zip"
 * 3. main system reboots into recovery
 * 4. get_args() writes BCB with "boot-recovery" and "--update_package=..."
 *    -- after this, rebooting will attempt to reinstall the update --
 * 5. install_package() attempts to install the update
 *    NOTE: the package install must itself be restartable from any point
 * 6. finish_recovery() erases BCB
 *    -- after this, rebooting will (try to) restart the main system --
 * 7. ** if install failed **
 *    7a. prompt_and_wait() shows an error icon and waits for the user
 *    7b; the user reboots (pulling the battery, etc) into the main system
 * 8. main() calls maybe_install_firmware_update()
 *    ** if the update contained radio/hboot firmware **:
 *    8a. m_i_f_u() writes BCB with "boot-recovery" and "--wipe_cache"
 *        -- after this, rebooting will reformat cache & restart main system --
 *    8b. m_i_f_u() writes firmware image into raw cache partition
 *    8c. m_i_f_u() writes BCB with "update-radio/hboot" and "--wipe_cache"
 *        -- after this, rebooting will attempt to reinstall firmware --
 *    8d. bootloader tries to flash firmware
 *    8e. bootloader writes BCB with "boot-recovery" (keeping "--wipe_cache")
 *        -- after this, rebooting will reformat cache & restart main system --
 *    8f. erase_volume() reformats /cache
 *    8g. finish_recovery() erases BCB
 *        -- after this, rebooting will (try to) restart the main system --
 * 9. main() calls reboot() to boot main system
 */

這部分的注釋詳細說明了recovery的兩大功能:FACTORY RESET和OTA INSTALL的流程。

這段注釋告訴我們,執行恢復出廠設置或者OTA升級,我們需要先向/cache/recovery/command中寫入命令:

 * The arguments which may be supplied in the recovery.command file:
 *   --send_intent=anystring - write the text out to recovery.intent
 *   --update_package=path - verify install an OTA package file
 *   --wipe_data - erase user data (and cache), then reboot
 *   --wipe_cache - wipe cache (but not user data), then reboot
 *   --set_encrypted_filesystem=on|off - enables / diasables encrypted fs

所以先來看下制作完成的升級包是如何進入到升級流程中的。

以升級U盤中的update.zip為例,插入U盤檢測到升級包之后,我們進入Settings開始進入升級流程。無論上層應用如何操作升級包的來源,最終都是調用RecoverySystem.installPackage來執行升級操作。

public static void installPackage(Context context, File packageFile)
        throws IOException {
    String arg = getInstallPackageArg(context, packageFile);
    bootCommand(context, arg, true);
}

這里的getInstallPackageArg是比較關鍵的,它就是處理/cache/recovery/command中命令的方法:

private static String getInstallPackageArg(Context context ,File packageFile)
        throws IOException {
    String filename = packageFile.getCanonicalPath();
    String strExt2Path = Environment.getExternalStorage2Directory().toString();
    String arg = null;

    if(filename.startsWith(strExt2Path)) {
        if(Environment.isExternalStorageBeSdcard()) {
            String newpath = filename.substring(4); 
            Log.w(TAG, "!!! REBOOTING TO INSTALL 1 " + newpath + " !!!");
            arg = "--update_package=" + newpath;

    String ibEnableStatus = SystemProperties.get("persist.sys.instaboot.enable");
    if ("enable".equals(ibEnableStatus) || "prepare".equals(ibEnableStatus)) {
        Log.w(TAG, "clean instaboot image");
        InstabootManager im = new InstabootManager(context);
        im.disable();
    }
            arg += "\n--locale=" + Locale.getDefault().toString();
        } else {
            String newpath = new String("/sdcard") + filename.substring(strExt2Path.length()); 
            Log.w(TAG, "!!! REBOOTING TO INSTALL 2 " + newpath + " !!!");
            arg = "--update_package=" + newpath;
            arg += "\n--locale=" + Locale.getDefault().toString();
        }
    } else if(filename.startsWith(Environment.getExternalStorageDirectory().toString())) {
        if(Environment.isExternalStorageBeSdcard()) {
            String absPath = packageFile.getAbsolutePath();
            if(SystemProperties.getInt("vold.fakesdcard.enable",0)==1 && absPath.startsWith("/mnt/sda1/")) {
                String newpath =new String("/udisk/")+absPath.substring(10); 
                Log.w(TAG, "!!! REBOOTING TO INSTALL 3-1 " + newpath + " !!!");
                arg = "--update_package=" + newpath;
                arg += "\n--locale=" + Locale.getDefault().toString();
            } else {
                String newpath = filename.substring(4); 
                Log.w(TAG, "!!! REBOOTING TO INSTALL 3-2 " + newpath + " !!!");
                arg = "--update_package=" + newpath;
                arg += "\n--locale=" + Locale.getDefault().toString();
            }
        } else {
            String newpath = new String("/media/" + packageFile.getName()); 
            Log.w(TAG, "!!! REBOOTING TO INSTALL 4 " + newpath + " !!!");
            arg = "--update_package=" + newpath;
            arg += "\n--locale=" + Locale.getDefault().toString();
        }
    } else if(filename.startsWith(Environment.getInternalStorageDirectory().toString())) {
        String newpath = new String("/media/"+packageFile.getName()); 
        Log.w(TAG, "!!! REBOOTING TO INSTALL 5 " + newpath + " !!!");
        arg = "--update_package=" + newpath;
        arg += "\n--locale=" + Locale.getDefault().toString();
    } else if(filename.startsWith("/udisk")) {
        String newpath =new String("/udisk/")+filename.substring(7);
        Log.w(TAG, "!!! REBOOTING TO INSTALL 6 " + newpath + " !!!");
        arg = "--update_package=" + newpath;
        arg += "\n--locale=" + Locale.getDefault().toString();
    } else {
        Log.w(TAG, "!!! REBOOTING TO INSTALL 7 " + filename + " !!!");
        arg = "--update_package=" + filename;
        arg += "\n--locale=" + Locale.getDefault().toString();
    }
    return arg;
}

繼續往下看,進入了bootCommand方法中:

private static void bootCommand(Context context, String arg, Boolean update) throws IOException {
    RECOVERY_DIR.mkdirs();  // 創建/cache/recovery
    COMMAND_FILE.delete();  // 清除/cache/recovery/command
    LOG_FILE.delete();

    FileWriter command = new FileWriter(COMMAND_FILE);
    FileOutputStream fos = new FileOutputStream(COMMAND_FILE);
    try {
        command.write(arg);//寫入傳進來的arg
        command.write("\n");
    } finally {
        command.close();
        FileUtils.sync(fos);
    }

    ......

    // Having written the command file, go ahead and reboot
    PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
    if(update)
        pm.reboot("update");//重啟升級
    else 
        pm.reboot("recovery");

    throw new IOException("Reboot failed (no permissions?)");
}

然后進入了PMS的reboot,接著shutdownOrRebootInternal,繼而ShutdownThread.reboot,接著rebootOrShutdown,最后回到了PMS的lowLevelReboot:

public static void lowLevelReboot(String reason) {
    if (reason == null) {
        reason = "";
    }
    SystemProperties.set("sys.powerctl", "reboot," + reason);
    try {
        Thread.sleep(20000);
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
    }
}

最后帶著reason=update進入kernel開始重啟。

經歷了重啟之后,kernel加載recovery.img,起來后執行的第一個進程就是init,此進程讀取init.rc啟動相應的服務:

......

on init
    //設置環境變量
    sysclktz 0
    loglevel 7
    export PATH /sbin:/system/sbin:/system/bin:/system/xbin
    export LD_LIBRARY_PATH /system/lib
    export ANDROID_ROOT /system
    export ANDROID_DATA /data
    export EXTERNAL_STORAGE /sdcard

    //建立連接
    symlink /system/etc /etc
    mkdir /system/bin
    symlink /sbin/sh /system/bin/sh

    //建立目錄
    mkdir /sdcard
    mkdir /system
    mkdir /data
    mkdir /cache
    //掛載/tmp為內存文件系統tmpfs
    mount /tmp /tmp tmpfs

......

//啟動recovery服務  
service recovery /sbin/recovery

//啟動adbd服務
service adbd /sbin/adbd recovery
    disabled
    socket adbd stream 660 system system

......

這樣就回到了開頭所說了recovery模式的入口:recovery.cpp,我們從main函數開始分析

//將recovery產生的log信息重定向到/tmp/recovery.log這個文件里
freopen(TEMPORARY_LOG_FILE, "a", stdout); setbuf(stdout, NULL);
freopen(TEMPORARY_LOG_FILE, "a", stderr); setbuf(stderr, NULL);

//判斷是否使用adb的sideload來傳入,通過參數--adbd來判斷
if (argc == 2 && strcmp(argv[1], "--adbd") == 0) {
    adb_main();
    return 0;
}

//初始化并裝載recovery的分區表recovery.fstab
load_volume_table();

//在recovery中掛載/cache/recovery/last_log這個文件
ensure_path_mounted(LAST_LOG_FILE);
// Rename last_log -> last_log.1 -> last_log.2 -> ... -> last_log.$max
rotate_last_logs(10);

只有掛載到了對應的分區,才能訪問前面的command文件,然后才能正確打開升級包,上面這步也是非常重要的。

掛載完相應的分區以后,就需要獲取命令參數:

//從/cache/recovery/command獲取參數
get_args(&argc, &argv);

int arg;
while ((arg = getopt_long(argc, argv, "", OPTIONS, NULL)) != -1) {
    switch (arg) {
    case 'q': aml_update_version = optarg; break;
    case 'p': previous_runs = atoi(optarg); break;
    case 's': send_intent = optarg; break;
    case 'u': update_package = optarg; break;
    case 'x': update_patch = optarg; break;
#ifdef RECOVERY_HAS_PARAM
    case 'w': wipe_data = wipe_cache = wipe_param = 1; break;
#else
    case 'w': wipe_data = wipe_cache = 1; break;
#endif
    case 'g': restore_system = 1; break;
    case 'c': wipe_cache = 1; break;
    case 't': show_text = 1; break;
    //case 'x': just_exit = true; break;
    case 'l': locale = optarg; break;
    case 'f': reboot_to_factorymode = 1; break;
    case 'n': usb_burning = 1; break;
    case 'o': without_format = 1; break;    
case 'z': file_copy_from_partition_args = optarg; break;
#ifdef RECOVERY_HAS_MEDIA
    case 'm': wipe_media = 1; break;
#endif /* RECOVERY_HAS_MEDIA */
#ifdef RECOVERY_HAS_PARAM
    case 'P': wipe_param = 1; break;
#endif /* RECOVERY_HAS_PARAM */

#ifdef RECOVERY_HAS_EFUSE
    case 'v': set_efuse_version = 1; efuse_version = optarg; break;
    case 'd': set_efuse_ethernet_mac = 1; break;
    case 'b': set_efuse_bluetooth_mac = 1; break;
#ifdef EFUSE_LICENCE_ENABLE
    case 'a': set_efuse_audio_license = 1; break;
#endif /* EFUSE_LICENCE_ENABLE */

#endif /* RECOVERY_HAS_EFUSE */

#ifdef RECOVERY_WRITE_KEY
    case 'B': flash_write_mac = 1; break;
    case 'C': flash_write_mac_force = 1; break;
    case 'D': flash_write_mac_bt = 1; break;
    case 'E': flash_write_mac_bt_force = 1; break;
    case 'F': flash_write_mac_wifi = 1; break;
    case 'G': flash_write_mac_wifi_force = 1; break;
    case 'H': flash_write_hdcp = 1; break;
    case 'I': flash_write_hdcp_force = 1; break;
    case 'J': flash_write_usid = 1; break;
    case 'K': flash_write_usid_force = 1; break;
#endif /* RECOVERY_WRITE_KEY */

    case 'R': restore_systembackup = 1; break;
    case 'e': just_exit = true; break;
    case 'r': run_cmd = 1; cmd_args = optarg; break;
    case 'h': keep_file_path = optarg; wipe_data = wipe_cache = 1; break;
    case 'y': erase_data = 1; break; //Add for The data partition is full when OTA upgrades
    case '?':
        LOGE("Invalid command argument\n");
        continue;
    }
}

獲取到對應的命令,就會執行對應的標志,后面會根據標志來執行對應的操作。做完以上的流程后,下面就是設置語言信息,創建設備,初始化recovery的UI界面,設置Selinux權限等操作:

//設置語言
if (locale == NULL) {
    load_locale_from_cache();
}

//創建設備
Device* device = make_device();
//獲取UI
ui = device->GetUI();
//設置當前的UI
gCurrentUI = ui;
//UI初始化
ui->Init();

......

接下來根據上面傳入的命令參數,開始真正進入到重要的執行環節:

if (update_package != NULL) {
    unlink("/data/property/persist.sys.needcheckdata");
    ui->SetBackground(RecoveryUI::INSTALLING_UPDATE);
    ui->SetProgressType(RecoveryUI::DETERMINATE);

    //執行install_package進行升級
    status = install_package(update_package, &wipe_cache, TEMPORARY_INSTALL_FILE);

    if (status != INSTALL_SUCCESS) {
        ui->Print("Installation aborted.\n");
        ui->ShowText(true);
    } else {
        ui->SetBackground(RecoveryUI::UPDATED);
        sleep(3);
    }
}

if (wipe_data) {

    ......

} else if (wipe_cache) {

    ......

}

這里就是執行OTA升級和恢復出廠設置的地方了,恢復出廠設置也就是手機雙清,清除/data分區和/cache分區,代碼流程還是比較清楚詳細的,可以跟一下,這邊先不展開了。

接下來的重點就是在install_package方法上面了:

int
install_package(const char* path, int* wipe_cache, const char* install_file)
{
    FILE* install_log = fopen_path(install_file, "w");
    if (install_log) {
        fputs(path, install_log);
        fputc('\n', install_log);
    } else {
        LOGE("failed to open last_install: %s\n", strerror(errno));
    }
    int result;

    //設置安裝掛載對應的節點,也就是判斷掛載的路徑是/tmp或者/cache
    if (setup_install_mounts() != 0) {
        LOGE("failed to set up expected mounts for install; aborting\n");
        set_upgrade_step("2");
        result = INSTALL_ERROR;
    } else {
        result = really_install_package(path, wipe_cache);
    }
    if (install_log) {
        fputc(result == INSTALL_SUCCESS ? '1' : '0', install_log);
        fputc('\n', install_log);
        fclose(install_log);
    }
    return result;
}
向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

沐川县| 深泽县| 旬阳县| 简阳市| 遵义市| 华宁县| 揭西县| 文登市| 河间市| 韶山市| 集贤县| 高雄市| 大洼县| 沽源县| 大石桥市| 仪陇县| 太保市| 和田县| 静安区| 万宁市| 安顺市| 庄浪县| 改则县| 武乡县| 台湾省| 舟曲县| 开远市| 太白县| 诸城市| 南充市| 沛县| 门头沟区| 平谷区| 长海县| 米易县| 武宁县| 绩溪县| 西乌珠穆沁旗| 海安县| 聊城市| 平原县|