天天看點

Android A/B System OTA分析(二)系統image的生成

原文:http://blog.csdn.net/guyongqiangx/article/details/71516768,感謝作者的辛勤付出

Android從7.0開始引入新的OTA更新方式,

A/B System Updates

,這裡将其叫做

A/B

系統,涉及的内容較多,分多篇對

A/B

系統的各個方面進行分析。本文為第二篇,系統image的生成。

image

這個詞的含義很多,這裡指編譯後可以燒寫到裝置的檔案,如

boot.img

system.img

等,統稱為鏡像檔案吧。

我一直覺得将

image

翻譯成鏡像檔案怪怪的,如果有更貼切詞彙,請一定要告訴我啊,十分感謝。

本文基于

AOSP 7.1.1_r23 (NMF27D)

代碼進行分析。

1. 

A/B

系統和傳統方式下鏡像内容的比較

傳統OTA方式下:

  1. boot.img内有一個boot ramdisk,用于系統啟動時加載system.img;
  2. recovery.img内有一個recovery ramdisk,作為recovery系統運作的ramdisk;
  3. system.img隻包含android系統的應用程式和庫檔案;

A/B

系統下:

  1. system.img除了包含android系統的應用程式和庫檔案還,另外含有boot ramdisk,相當于傳統OTA下boot.img内的ramdisk存放到system.img内了;
  2. boot.img内包含的是recovery ramdisk,而不是boot ramdisk。Android系統啟動時不再加載boot.img内的ramdisk,而是通過device mapper機制選擇system.img内的ramdisk進行加載;
  3. 沒有recovery.img檔案

要想知道系統的各個分區到底有什麼東西,跟傳統OTA的鏡像檔案到底有什麼差別,需要閱讀Makefile,看看每個鏡像裡面到底打包了哪些檔案。

在看系統編譯打封包件生成鏡像之前,先看看跟

A/B

相關的到底有哪些變量,以及這些變量有什麼作用。

2. 

A/B

系統相關的Makefile變量

這些變量主要有三類:

  • A/B

    系統必須定義的變量
    • AB_OTA_UPDATER := true

    • AB_OTA_PARTITIONS := boot system vendor

    • BOARD_BUILD_SYSTEM_ROOT_IMAGE := true

    • TARGET_NO_RECOVERY := true

    • BOARD_USES_RECOVERY_AS_BOOT := true

    • PRODUCT_PACKAGES += update_engine update_verifier

  • A/B

    系統可標明義的變量
    • PRODUCT_PACKAGES_DEBUG += update_engine_client

  • A/B

    系統不能定義的變量
    • BOARD_RECOVERYIMAGE_PARTITION_SIZE

    • BOARD_CACHEIMAGE_PARTITION_SIZE

    • BOARD_CACHEIMAGE_FILE_SYSTEM_TYPE

以下詳細說明這些變量。

  1. 必須定義的變量
    • AB_OTA_UPDATER := true

      A/B

      系統的主要開關變量,設定後:
      • recovery

        系統内不再具有操作

        cache

        分區的功能,

        bootable\recovery\device.cpp

      • recovery

        系統使用不同的方式來解析更新檔案,

        bootable\recovery\install.cpp

      • 生成

        A/B

        系統相關的META檔案
    • AB_OTA_PARTITIONS := boot system vendor

      • A/B

        系統可更新的分區寫入檔案

        $(zip_root)/META/ab_partitions.txt

    • BOARD_BUILD_SYSTEM_ROOT_IMAGE := true

      将boot ramdisk放到system分區内
    • TARGET_NO_RECOVERY := true

      不再生成

      recovery.img

      鏡像
    • BOARD_USES_RECOVERY_AS_BOOT := true

      将recovery ramdisk放到boot.img檔案内
    • PRODUCT_PACKAGES += update_engine update_verifier

      編譯

      update_engine

      update_verifier

      子產品,并安裝相應的應用
  2. 可選的變量
    • PRODUCT_PACKAGES_DEBUG += update_engine_client

      系統自帶了一個

      update_engine_client

      應用,可以根據需要選擇是否編譯并安裝
  3. 不能定義的變量
    • BOARD_RECOVERYIMAGE_PARTITION_SIZE

      系統沒有recovery分區,不需要設定

      recovery

      分區的

      SIZE

    • BOARD_CACHEIMAGE_PARTITION_SIZE

      系統沒有

      cache

      分區,不需要設定

      cache

      分區的

      SIZE

    • BOARD_CACHEIMAGE_FILE_SYSTEM_TYPE

      系統沒有

      cache

      分區,不需要設定

      cache

      分區的

      TYPE

3. 

A/B

系統鏡像檔案的生成

build\core\Makefile

定義了所需生成的鏡像目标和規則,各鏡像規則如下,我直接在代碼裡進行注釋了。

  1. recovery.img

    # A/B系統中,"TARGET_NO_RECOVERY := true",是以條件成立
    ifeq (,$(filter true, $(TARGET_NO_KERNEL) $(TARGET_NO_RECOVERY)))
    INSTALLED_RECOVERYIMAGE_TARGET := $(PRODUCT_OUT)/recovery.img
    else
    INSTALLED_RECOVERYIMAGE_TARGET :=
    endif
               
    由于

    A/B

    系統定了

    TARGET_NO_RECOVERY := true

    ,這裡

    INSTALLED_RECOVERYIMAGE_TARGET

    被設定為空,是以不會生成

    recovery.img

  2. boot.img

    # 定義boot.img的名字和存放的路徑
    INSTALLED_BOOTIMAGE_TARGET := $(PRODUCT_OUT)/boot.img
    
    #
    # 以下error表明:
    #     BOARD_USES_RECOVERY_AS_BOOT和BOARD_BUILD_SYSTEM_ROOT_IMAGE
    #     在A/B系統中需要同時被定義為true
    #
    # BOARD_USES_RECOVERY_AS_BOOT = true must have BOARD_BUILD_SYSTEM_ROOT_IMAGE = true.
    # BOARD_USES_RECOVERY_AS_BOOT 已經定義為true
    ifeq ($(BOARD_USES_RECOVERY_AS_BOOT),true) 
    ifneq ($(BOARD_BUILD_SYSTEM_ROOT_IMAGE),true)
      # 如果沒有定義BOARD_BUILD_SYSTEM_ROOT_IMAGE 則編譯終止,并顯示錯誤資訊
      $(error BOARD_BUILD_SYSTEM_ROOT_IMAGE must be enabled for BOARD_USES_RECOVERY_AS_BOOT.)
    endif
    endif
    
    ...
    
    # 好吧,這裡才是生成boot.img的地方
    ifeq ($(BOARD_USES_RECOVERY_AS_BOOT),true)
    # 對boot.img添加依賴:boot_signer,這裡不關心
    ifeq (true,$(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_SUPPORTS_BOOT_SIGNER))
    $(INSTALLED_BOOTIMAGE_TARGET) : $(BOOT_SIGNER)
    endif
    # 對boot.img添加依賴:vboot_signer.sh,這裡不關心
    ifeq (true,$(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_SUPPORTS_VBOOT))
    $(INSTALLED_BOOTIMAGE_TARGET) : $(VBOOT_SIGNER)
    endif
    # boot.img的其它依賴,并通過宏build-recoveryimage-target來生成boot.img
    $(INSTALLED_BOOTIMAGE_TARGET): $(MKBOOTFS) $(MKBOOTIMG) $(MINIGZIP) \
            $(INSTALLED_RAMDISK_TARGET) \
            $(INTERNAL_RECOVERYIMAGE_FILES) \
            $(recovery_initrc) $(recovery_sepolicy) $(recovery_kernel) \
            $(INSTALLED_2NDBOOTLOADER_TARGET) \
            $(recovery_build_prop) $(recovery_resource_deps) \
            $(recovery_fstab) \
            $(RECOVERY_INSTALL_OTA_KEYS)
            $(call pretty,"Target boot image from recovery: [email protected]")
            $(call build-recoveryimage-target, [email protected])
    endif
    
    #
    # 上面的規則中:
    #   INSTALLED_BOOTIMAGE_TARGET = $(PRODUCT_OUT)/boot.img
    # 其依賴的是recovery系統檔案,最後通過build-recoveryimage-target打包成boot.img
    # 這不就是把recovery.img換個名字叫boot.img麼?
    #
    
    #
    # 再來看看原本的recovery.img的生成規則:
    #  - A/B 系統下,INSTALLED_RECOVERYIMAGE_TARGET已經定義為空,什麼都不做
    #  - 非A/B 系統下,以下規則會生成recovery.img
    #
    $(INSTALLED_RECOVERYIMAGE_TARGET): $(MKBOOTFS) $(MKBOOTIMG) $(MINIGZIP) \
            $(INSTALLED_RAMDISK_TARGET) \
            $(INSTALLED_BOOTIMAGE_TARGET) \
            $(INTERNAL_RECOVERYIMAGE_FILES) \
            $(recovery_initrc) $(recovery_sepolicy) $(recovery_kernel) \
            $(INSTALLED_2NDBOOTLOADER_TARGET) \
            $(recovery_build_prop) $(recovery_resource_deps) \
            $(recovery_fstab) \
            $(RECOVERY_INSTALL_OTA_KEYS)
            $(call build-recoveryimage-target, [email protected])
               
    對比

    A/B

    系統下

    boot.img

    生成方式和非

    A/B

    系統下

    recovery.img

    的生成方式,基本上是一樣的,是以

    A/B

    系統下的

    boot.img

    相當于非

    A/B

    系統下的

    recovery.img

  3. system.img

    #
    # build-systemimage-target宏函數定義
    #     宏函數内部調用build_image.py,從$(TARGET_OUT)目錄,即$(PRODUCT_OUT)/system目錄建立鏡像檔案
    # 
    # $(1): output file
    define build-systemimage-target
      @echo "Target system fs image: $(1)"
      $(call create-system-vendor-symlink)
      @mkdir -p $(dir $(1)) $(systemimage_intermediates) && rm -rf $(systemimage_intermediates)/system_image_info.txt
      $(call generate-userimage-prop-dictionary, $(systemimage_intermediates)/system_image_info.txt, \
          skip_fsck=true)
      $(hide) PATH=$(foreach p,$(INTERNAL_USERIMAGES_BINARY_PATHS),$(p):)$$PATH \
    		      ./build/tools/releasetools/build_image.py \
    		      $(TARGET_OUT) $(systemimage_intermediates)/system_image_info.txt $(1) $(TARGET_OUT) \
    		      || ( echo "Out of space? the tree size of $(TARGET_OUT) is (MB): " 1>&2 ;\
    		           du -sm $(TARGET_OUT) 1>&2;\
    		           if [ "$(INTERNAL_USERIMAGES_EXT_VARIANT)" == "ext4" ]; then \
    		               maxsize=$(BOARD_SYSTEMIMAGE_PARTITION_SIZE); \
    		               if [ "$(BOARD_HAS_EXT4_RESERVED_BLOCKS)" == "true" ]; then \
    		                   maxsize=$$((maxsize - 4096 * 4096)); \
                   fi; \
                   echo "The max is $$(( maxsize / 1048576 )) MB." 1>&2 ;\
    		           else \
    		               echo "The max is $$(( $(BOARD_SYSTEMIMAGE_PARTITION_SIZE) / 1048576 )) MB." 1>&2 ;\
               fi; \
               mkdir -p $(DIST_DIR); cp $(INSTALLED_FILES_FILE) $(DIST_DIR)/installed-files-rescued.txt; \
               exit 1 )
    endef
    
    #
    # 調用build-systemimage-target,生成目标檔案$(BUILT_SYSTEMIMAGE)
    # 即:obj\PACKAGING\systemimage_intermediates\system.img檔案
    # 
    $(BUILT_SYSTEMIMAGE): $(FULL_SYSTEMIMAGE_DEPS) $(INSTALLED_FILES_FILE)
        # 站住,生成system.img的入口就在這裡了
        $(call build-systemimage-target,[email protected])
    
    # 定義system.img的名字和存放的路徑
    INSTALLED_SYSTEMIMAGE := $(PRODUCT_OUT)/system.img
    SYSTEMIMAGE_SOURCE_DIR := $(TARGET_OUT)
    
    ...
               

    看完這段代碼我開始有點崩潰了~~ 

    此前boot.img裡面的ramdisk是recovery系統的recovery ramdisk,這裡生成system.img也不見哪裡添加了ramdisk啊,那系統啟動時用recovery的ramdisk挂載system分區麼?顯然不是啊~~那boot ramdisk到底藏到哪裡去了啊?

    木有了辦法,那就老老實實看看宏

    build-systemimage-target

    的過程吧,調用指令:
    • 第一步,調用

      $(call create-system-vendor-symlink)

      建立符号連結
    • 第二步,建立檔案夾

      $(systemimage_intermediates)

      ,并删除其中的檔案

      system_image_info.txt

    • 第三步,調用

      call generate-userimage-prop-dictionary

      ,重新生成系統屬性檔案

      system_image_info.txt

    • 第四步,調用

      build_image.py

      ,根據系統屬性檔案

      system_image_info.txt

      system

      目錄

      $(PRODUCT_OUT)/system

      建立

      system.img

      檔案
    顯然重點就在第四步了,看看

    build_image.py

    到底是如何生成

    system.img

    的。

    build_image.py

    的主程式比較簡單:
    • 腳本入口
      # 運作build_image.py腳本的入口,轉到main函數
      if __name__ == '__main__':
        main(sys.argv[1:])
                 
    • 主程式

      main

      函數
      # 主程式
      def main(argv):
        if len(argv) != 4:
          print __doc__
          sys.exit(1)
      
        """
         * build_image.py的調用指令為:
         * ./build/tools/releasetools/build_image.py \
         *     $(TARGET_OUT) \
         *     $(systemimage_intermediates)/system_image_info.txt \
         *     $(systemimage_intermediates)/system.img \
         *     $(TARGET_OUT)
         *
        """
        in_dir = argv[0]           # 參數0:in_dir=$(TARGET_OUT)
        glob_dict_file = argv[1]   # 參數1:glob_dict_file=$(systemimage_intermediates)/system_image_info.txt
        out_file = argv[2]         # 參數2:outfile=$(systemimage_intermediates)/system.img
        target_out = argv[3]       # 參數3:target_out=$(TARGET_OUT)
      
        # 解析系統屬性的字典檔案system_image_info.txt
        glob_dict = LoadGlobalDict(glob_dict_file)
        if "mount_point" in glob_dict:
          # The caller knows the mount point and provides a dictionay needed by
          # BuildImage().
          image_properties = glob_dict
        else:
          image_filename = os.path.basename(out_file)
          mount_point = ""
          # 設定system.img的挂載點為system
          if image_filename == "system.img":
            mount_point = "system"
          ...
          else:
            print >> sys.stderr, "error: unknown image file name ", image_filename
            exit(1)
      
          image_properties = ImagePropFromGlobalDict(glob_dict, mount_point)
      
        # 調用BuildImage函數來建立檔案
        if not BuildImage(in_dir, image_properties, out_file, target_out):
          print >> sys.stderr, "error: failed to build %s from %s" % (out_file,
                                                                      in_dir)
          exit(1)
                 
    • BuildImage

      函數
      def BuildImage(in_dir, prop_dict, out_file, target_out=None):
        ...
      
        # 關鍵!!!前面改動過了in_dir,是以條件成立
        if in_dir != origin_in:
          # Construct a staging directory of the root file system.
          ramdisk_dir = prop_dict.get("ramdisk_dir")
          if ramdisk_dir:
            shutil.rmtree(in_dir)
            # 将字典system_image_info.txt裡"ramdisk_dir"指定的内容複制到臨時檔案夾in_dir中,并保持原有的符号連結
            shutil.copytree(ramdisk_dir, in_dir, symlinks=True)
          staging_system = os.path.join(in_dir, "system")
          # 删除in_dir/system目錄,即删除ramdisk_dir下system目錄
          shutil.rmtree(staging_system, ignore_errors=True)
          # 将origin_in目錄的内容複制到ramdisk_dir/system目錄下
          # 原來的origin_in是指定的$(PRODUCT_OUT)/system目錄
          # 是以這裡的操作是将ramdisk和system的内容合并生成一個完整的檔案系統
          shutil.copytree(origin_in, staging_system, symlinks=True)
      
        reserved_blocks = prop_dict.get("has_ext4_reserved_blocks") == "true"
        ext4fs_output = None
      
        # 繼續對合并後完整的檔案系統進行其它操作,最後打包為system.img
        ...
      
        return exit_code == 0
                 
      顯然,

      build_image.py

      腳本将ramdisk和system檔案夾下的内容合并成一個完整的檔案系統,最終輸出為system.img,再也不用擔心system.img沒有rootfs了。
  4. userdata.img

    # Don't build userdata.img if it's extfs but no partition size
    skip_userdata.img :=
    # 如果TARGET_USERIMAGES_USE_EXT4定義為true,則會進行以下定義:
    # INTERNAL_USERIMAGES_USE_EXT := true
    # INTERNAL_USERIMAGES_EXT_VARIANT := ext4
    # 在vendor相關的deivce下,BoradConfig.mk中會定義BOARD_USERDATAIMAGE_PARTITION_SIZE
    # 是以這裡最終skip_userdata.img仍然為空
    ifdef INTERNAL_USERIMAGES_EXT_VARIANT
    ifndef BOARD_USERDATAIMAGE_PARTITION_SIZE
    skip_userdata.img := true
    endif
    endif
    
    # skip_userdata.img為空,是以這裡會定義userdata.img并生成這個檔案
    ifneq ($(skip_userdata.img),true)
    userdataimage_intermediates := \
        $(call intermediates-dir-for,PACKAGING,userdata)
    BUILT_USERDATAIMAGE_TARGET := $(PRODUCT_OUT)/userdata.img
    
    # 具體生成userdata.img的宏函數
    define build-userdataimage-target
      $(call pretty,"Target userdata fs image: $(INSTALLED_USERDATAIMAGE_TARGET)")
      @mkdir -p $(TARGET_OUT_DATA)
      @mkdir -p $(userdataimage_intermediates) && rm -rf $(userdataimage_intermediates)/userdata_image_info.txt
      $(call generate-userimage-prop-dictionary, $(userdataimage_intermediates)/userdata_image_info.txt, skip_fsck=true)
      $(hide) PATH=$(foreach p,$(INTERNAL_USERIMAGES_BINARY_PATHS),$(p):)$$PATH \
          ./build/tools/releasetools/build_image.py \
          $(TARGET_OUT_DATA) $(userdataimage_intermediates)/userdata_image_info.txt $(INSTALLED_USERDATAIMAGE_TARGET) $(TARGET_OUT)
      $(hide) $(call assert-max-image-size,$(INSTALLED_USERDATAIMAGE_TARGET),$(BOARD_USERDATAIMAGE_PARTITION_SIZE))
    endef
    
    # 好吧,這裡才是真正調用build-userdataimage-target去生成userdata.img的規則
    # We just build this directly to the install location.
    INSTALLED_USERDATAIMAGE_TARGET := $(BUILT_USERDATAIMAGE_TARGET)
    $(INSTALLED_USERDATAIMAGE_TARGET): $(INTERNAL_USERIMAGES_DEPS) \
                                       $(INTERNAL_USERDATAIMAGE_FILES)
        # 生成userdata.img的入口就這裡了
        $(build-userdataimage-target)
               
    這裡的步驟跟生成system.img基本一緻,宏函數

    build-userdataimage-target

    内通過

    build_image.py

    來将

    $(PRODUCT_OUT)/data

    目錄内容打包生成

    userdata.img

    ,不同的是,這裡不再需要放入

    ramdisk

    的内容。

    顯然,userdata.img的生成跟是否是

    A/B

    系統沒有關系。
  5. cache.img

    # cache partition image
    # `A/B`系統中 BOARD_CACHEIMAGE_FILE_SYSTEM_TYPE 沒有定義,這裡條件不能滿足,是以不會生成cache.img
    ifdef BOARD_CACHEIMAGE_FILE_SYSTEM_TYPE
    INTERNAL_CACHEIMAGE_FILES := \
        $(filter $(TARGET_OUT_CACHE)/%,$(ALL_DEFAULT_INSTALLED_MODULES))
    
    cacheimage_intermediates := \
        $(call intermediates-dir-for,PACKAGING,cache)
    BUILT_CACHEIMAGE_TARGET := $(PRODUCT_OUT)/cache.img
    
    ...
    
    # We just build this directly to the install location.
    # 這裡是真正去生成cache.img的地方,可惜`A/B`系統下不會再有調用了
    INSTALLED_CACHEIMAGE_TARGET := $(BUILT_CACHEIMAGE_TARGET)
    $(INSTALLED_CACHEIMAGE_TARGET): $(INTERNAL_USERIMAGES_DEPS) $(INTERNAL_CACHEIMAGE_FILES)
        $(build-cacheimage-target)
    
    ...
    
    else # BOARD_CACHEIMAGE_FILE_SYSTEM_TYPE
    # we need to ignore the broken cache link when doing the rsync
    IGNORE_CACHE_LINK := --exclude=cache
    endif # BOARD_CACHEIMAGE_FILE_SYSTEM_TYPE
               
    于A/B系統定了沒有定義

    BOARD_CACHEIMAGE_FILE_SYSTEM_TYPE

    ,這裡

    BUILT_CACHEIMAGE_TARGET

    也不會定義,是以不會生成cache.img
  6. vendor.img

    # vendor partition image
    # 如果系統内有定義BOARD_VENDORIMAGE_FILE_SYSTEM_TYPE,則這裡會生成vendor.img
    ifdef BOARD_VENDORIMAGE_FILE_SYSTEM_TYPE
    # 定義vendor系統内包含的所有檔案
    INTERNAL_VENDORIMAGE_FILES := \
        $(filter $(TARGET_OUT_VENDOR)/%,\
          $(ALL_DEFAULT_INSTALLED_MODULES)\
          $(ALL_PDK_FUSION_FILES))
    
    # platform.zip depends on $(INTERNAL_VENDORIMAGE_FILES).
    $(INSTALLED_PLATFORM_ZIP) : $(INTERNAL_VENDORIMAGE_FILES)
    
    # vendor的檔案清單:installed-files-vendor.txt
    INSTALLED_FILES_FILE_VENDOR := $(PRODUCT_OUT)/installed-files-vendor.txt
    $(INSTALLED_FILES_FILE_VENDOR) : $(INTERNAL_VENDORIMAGE_FILES)
        @echo Installed file list: [email protected]
        @mkdir -p $(dir [email protected])
        @rm -f [email protected]
        $(hide) build/tools/fileslist.py $(TARGET_OUT_VENDOR) > [email protected]
    
    # vendor.img目标
    vendorimage_intermediates := \
        $(call intermediates-dir-for,PACKAGING,vendor)
    BUILT_VENDORIMAGE_TARGET := $(PRODUCT_OUT)/vendor.img
    
    # 定義生成vendor.img的宏函數build-vendorimage-target
    define build-vendorimage-target
      $(call pretty,"Target vendor fs image: $(INSTALLED_VENDORIMAGE_TARGET)")
      @mkdir -p $(TARGET_OUT_VENDOR)
      @mkdir -p $(vendorimage_intermediates) && rm -rf $(vendorimage_intermediates)/vendor_image_info.txt
      $(call generate-userimage-prop-dictionary, $(vendorimage_intermediates)/vendor_image_info.txt, skip_fsck=true)
      $(hide) PATH=$(foreach p,$(INTERNAL_USERIMAGES_BINARY_PATHS),$(p):)$$PATH \
          ./build/tools/releasetools/build_image.py \
          $(TARGET_OUT_VENDOR) $(vendorimage_intermediates)/vendor_image_info.txt $(INSTALLED_VENDORIMAGE_TARGET) $(TARGET_OUT)
      $(hide) $(call assert-max-image-size,$(INSTALLED_VENDORIMAGE_TARGET),$(BOARD_VENDORIMAGE_PARTITION_SIZE))
    endef
    
    # We just build this directly to the install location.
    # 生成vendor.img的依賴和規則
    INSTALLED_VENDORIMAGE_TARGET := $(BUILT_VENDORIMAGE_TARGET)
    $(INSTALLED_VENDORIMAGE_TARGET): $(INTERNAL_USERIMAGES_DEPS) $(INTERNAL_VENDORIMAGE_FILES) $(INSTALLED_FILES_FILE_VENDOR)
        $(build-vendorimage-target)
    
    .PHONY: vendorimage-nodeps
    vendorimage-nodeps: | $(INTERNAL_USERIMAGES_DEPS)
        $(build-vendorimage-target)
    
    # 如果定義了BOARD_PREBUILT_VENDORIMAGE,說明已經預備好了vendor.img,那就直接複制到目标位置
    else ifdef BOARD_PREBUILT_VENDORIMAGE
    INSTALLED_VENDORIMAGE_TARGET := $(PRODUCT_OUT)/vendor.img
    $(eval $(call copy-one-file,$(BOARD_PREBUILT_VENDORIMAGE),$(INSTALLED_VENDORIMAGE_TARGET)))
    endif
               
    顯然,vendor.img跟是否是

    A/B

    系統沒有關系,主要看系統是否定義了

    BOARD_VENDORIMAGE_FILE_SYSTEM_TYPE

到此為止,我們已經分析了除更新包

update.zip

外的主要檔案的生成,包括

recovery.img

boot.img

system.img

userdata.img

cache.img

vendor.img

總結:

  • recovery.img

    ,不再單獨生成,傳統方式的

    recovery.img

    現在叫做

    boot.img

  • boot.img

    ,包含

    kernel

    recovery

    模式的

    ramdisk

  • system.img

    ,傳統方式下

    system.img

    $(PRODUCT_OUT)/system

    檔案夾打包而成,

    A/B

    系統下,制作時将

    $(PRODUCT_OUT)/root

    $(PRODUCT_OUT)/system

    合并到一起,生成一個完整的帶有

    rootfs

    system.img

  • userdata.img

    ,跟原來一樣,打包

    $(PRODUCT_OUT)/data

    檔案夾而成
  • cache.img

    A/B

    系統下不再單獨生成

    cache.img

  • vendor.img

    ,檔案的生成跟是否

    A/B

    系統無關,主要有廠家決定

現在的情況是,裝置啟動後

bootloader

解析

boot.img

得到

kernel

檔案,啟動

Linux

進入系統,然後加載

android

主系統

system

,但是

boot.img

system.img

兩個鏡像内都有

rootfs

,這個啟動是如何啟動,那這個到底是怎麼搞的呢?下一篇會對這個啟動流程詳細分析。