在前面一篇文章中,咱們分析了Android模塊的編譯過程。當Android系統的全部模塊都編譯好以後,咱們就能夠對編譯出來的模塊文件進行打包了。 打包結果是得到一系列的鏡像文件,例如system.img、boot.img、ramdisk.img、userdata.img和 recovery.img等。這些鏡像文件最終能夠燒錄到手機上運行。在本文中,咱們就詳細分析Android系統的鏡像文件的打包過程。html
老羅的新浪微博:http://weibo.com/shengyangluo,歡迎關注!
java
Android系統鏡像文件的打包工做一樣是由Android編譯系統來完成的,如圖1所示:node
圖1 Android系統鏡像文件的打包過程python
從前面Android編譯系統環境初始化過程分析和Android源代碼編譯命令m/mm/mmm/make分析這 兩篇文章能夠知道,Android編譯系統在初始化的過程當中,會經過根目錄下的Makefile腳本加載build/core/main.mk腳本,接着 build/core/main.mk腳本又會加載build/core/Makefile腳本,而Android系統鏡像文件就是由build /core/Makefile腳本負責打包生成的。shell
在build/core/main.mk文件中,與Android系統鏡像文件打包過程相關的內容以下所示:bash
[plain] view plaincopyapp
...... 框架
ifdef FULL_BUILD less
# The base list of modules to build for this product is specified ide
# by the appropriate product definition file, which was included
# by product_config.make.
product_MODULES := $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_PACKAGES)
# Filter out the overridden packages before doing expansion
product_MODULES := $(filter-out $(foreach p, $(product_MODULES), \
$(PACKAGES.$(p).OVERRIDES)), $(product_MODULES))
$(call expand-required-modules,product_MODULES,$(product_MODULES))
product_FILES := $(call module-installed-files, $(product_MODULES))
......
else
# We're not doing a full build, and are probably only including
# a subset of the module makefiles. Don't try to build any modules
# requested by the product, because we probably won't have rules
# to build them.
product_FILES :=
endif
......
modules_to_install := $(sort \
$(ALL_DEFAULT_INSTALLED_MODULES) \
$(product_FILES) \
$(foreach tag,$(tags_to_install),$($(tag)_MODULES)) \
$(call get-tagged-modules, shell_$(TARGET_SHELL)) \
$(CUSTOM_MODULES) \
)
# Some packages may override others using LOCAL_OVERRIDES_PACKAGES.
# Filter out (do not install) any overridden packages.
overridden_packages := $(call get-package-overrides,$(modules_to_install))
ifdef overridden_packages
# old_modules_to_install := $(modules_to_install)
modules_to_install := \
$(filter-out $(foreach p,$(overridden_packages),$(p) %/$(p).apk), \
$(modules_to_install))
endif
......
# Install all of the host modules
modules_to_install += $(sort $(modules_to_install) $(ALL_HOST_INSTALLED_FILES))
# build/core/Makefile contains extra stuff that we don't want to pollute this
# top-level makefile with. It expects that ALL_DEFAULT_INSTALLED_MODULES
# contains everything that's built during the current make, but it also further
# extends ALL_DEFAULT_INSTALLED_MODULES.
ALL_DEFAULT_INSTALLED_MODULES := $(modules_to_install)
include $(BUILD_SYSTEM)/Makefile
modules_to_install := $(sort $(ALL_DEFAULT_INSTALLED_MODULES))
ALL_DEFAULT_INSTALLED_MODULES :=
......
.PHONY: ramdisk
ramdisk: $(INSTALLED_RAMDISK_TARGET)
......
.PHONY: userdataimage
userdataimage: $(INSTALLED_USERDATAIMAGE_TARGET)
......
.PHONY: bootimage
bootimage: $(INSTALLED_BOOTIMAGE_TARGET)
......
若是定義在FULL_BUILD這個變量,就意味着咱們是要對整個系統進行編譯,而且編譯完成以後 ,須要將編譯獲得的文件進行打包,以即可以獲得相應的鏡像文件,不然的話,就僅僅是對某些模塊進行編譯。
在前面Android編譯系統環境初始化過程分析一 篇文章中,咱們提到,變量INTERNAL_PRODUCT描述的是執行lunch命令時所選擇的產品所對應的產品Makefile文件,而變量 PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_PACKAGES描述的是在該產品Makefile文件中經過變量 PRODUCT_PACKAGES所定義的模塊名稱列表。所以,咱們獲得的變量product_MODULES描述的就是要安裝的模塊名稱列表。
咱們知道,Android源碼中自帶了不少默認的APK模塊。若是咱們想用本身編寫的一個APK來代替某一個系統自帶的APK,那麼就能夠經過變量 PACKAGES.<new>.OVERRIDES := <old>來講明。其中,<new>表示用來替換的APK,而<old>表示被替換的APK。在這種狀況下,被替換 的APK是不該該被打包到系統鏡像中去的。所以,咱們須要從上一步獲得的模塊名稱列表中剔除那些被替換的APK,這是經過Makefile腳本提供的 filter-out函數來完成的。
一個模塊能夠經過LOCAL_REQUIRED_MODULES變量來指定它所依賴的其它模塊,所以,當一個模塊被安裝的時候,它所依賴的其它模塊也會一 起被安裝。調用函數expand-required-modules得到的就是全部要安裝的模塊所依賴的其它模塊,而且將這些被依賴的模塊也一塊兒保存在變 量product_MODULES中。
注意,這時候咱們獲得的product_MODULES描述的僅僅是要安裝的模塊的名稱,可是咱們實際須要的這些模塊對應的具體文件,所以,須要進一步調 用函數module-installed-files來得到要安裝的模塊所對應的文件,也就是要安裝的模塊通過編譯以後生成的文件,這些文件就保存在變量 product_FILES中。
最終須要安裝的文件除了變量product_FILES所描述的文件以後, 還包含如下四類模塊文件:
1. 變量ALL_DEFAULT_INSTALLED_MODULES描述的文件。
2. 變量CUSTOM_MODULES描述的文件。
3. 與當前編譯類型對應的模塊文件。例如,若是當前設定的編譯類型爲debug,那麼全部經過將變量LOCAL_MODULE_TAGS將本身設置爲debug的模塊也會被打包到系統鏡像文件中去。
4. 與當前shell名稱對應的模塊文件。例如,若是當前使用的shell是mksh,那麼全部經過將變量LOCAL_MODULE_TAGS將本身設置爲shell_mksh的模塊也會被打包到系統鏡像文件中去。
最後,modules_to_install就描述當前須要打包到系統鏡像中去的模塊文件。實際上,咱們除了能夠經過 PACKAGES.$(p).OVERRIDES來描述要替換的APK以後,還能夠在一個模塊中經過LOCAL_OVERRIDES_PACKAGES來 描述它所替換的模塊。所以,咱們須要經過函數get-package-overrides來得到此類被替換的模塊文件,而且將它們從 modules_to_install中剔除,這樣獲得的模塊文件纔是最終須要安裝的。
肯定了要安裝的全部模塊文件以後,就能夠將build/core/Makefile文件加載進來了。注意,文件build/core/Makefile是 經過變量ALL_DEFAULT_INSTALLED_MODULES來得到當前全部要打包到系統鏡像文件中去的模塊文件的。
文件build/core/Makefile主要就是用來打包各類Android系統鏡像文件的,固然它也是經過make規則來執行各類Android系 統鏡像文件打包命令的。每個Android鏡像文件都對應有一個make僞目標。例如,在build/core/main.mk文件中,就定義了三個 make僞目標ramdisk、userdataimage和bootimage,它們分別依賴於變量 INSTALLED_USERDATAIMAGE_TARGET、INSTALLED_USERDATAIMAGE_TARGET和 INSTALLED_BOOTIMAGE_TARGET所描述的文件,而且它們分別表示的就是ramdisk.img、userdata.img和 boot.img文件。
變量INSTALLED_USERDATAIMAGE_TARGET、INSTALLED_USERDATAIMAGE_TARGET和 INSTALLED_BOOTIMAGE_TARGET都是在build/core/Makefile文件中定義的。此外,build/core /Makefile文件還定義了另外兩個鏡像文件system.img和recovery.img的生成規則。接下來,咱們就分別對這些鏡像文件的打包過 程進行分析。
一. system.img
system.img鏡像文件描述的是設備上的system分區,即/system目錄,它是在build/core/Makefile文件中生成的,相關的內容以下所示:
[plain] view plaincopy
# Rules that need to be present for the all targets, even
# if they don't do anything.
.PHONY: systemimage
systemimage:
......
INSTALLED_SYSTEMIMAGE := $(PRODUCT_OUT)/system.img
......
$(INSTALLED_SYSTEMIMAGE): $(BUILT_SYSTEMIMAGE) $(RECOVERY_FROM_BOOT_PATCH) | $(ACP)
@echo "Install system fs image: $@"
$(copy-file-to-target)
$(hide) $(call assert-max-image-size,$@ $(RECOVERY_FROM_BOOT_PATCH),$(BOARD_SYSTEMIMAGE_PARTITION_SIZE),yaffs)
systemimage: $(INSTALLED_SYSTEMIMAGE)
.PHONY: systemimage-nodeps snod
systemimage-nodeps snod: $(filter-out systemimage-nodeps snod,$(MAKECMDGOALS)) \
| $(INTERNAL_USERIMAGES_DEPS)
@echo "make $@: ignoring dependencies"
$(call build-systemimage-target,$(INSTALLED_SYSTEMIMAGE))
$(hide) $(call assert-max-image-size,$(INSTALLED_SYSTEMIMAGE),$(BOARD_SYSTEMIMAGE_PARTITION_SIZE),yaffs)
從這裏就能夠看出,build/core/Makefile文件定義了兩個僞目標來生成system.img:1. systemimg;2. systemimg-nodeps或者snod。僞目標systemimg表示在打包system.img以前,要根據依賴規則從新生成全部要進行打包的 文件,而僞目標systemimg-nodeps則不須要根據依賴規則從新生成全部須要打包的文件而直接打包system.img文件。所以,執行 systemimg-nodep比執行systemimg要快不少。一般,若是咱們在Android源碼中修改了某一個模塊,而且這個模塊不被其它模塊依 賴,那麼對這個模塊進行編譯以後,就能夠簡單地執行make systemimg-nodeps來從新打包system.img。可是,若是咱們修改的模塊會被其它模塊引用,例如,咱們修改了Android系統的核 心模塊framework.jar和services.jar,那麼就須要執行make systemimg來更新全部依賴於framework.jar和services.jar的模塊,那麼最後獲得的system.img纔是正確的鏡像。 不然的話,會致使Android系統啓動失敗。
接下來,咱們就主要關注僞目標systemimg生成system.img鏡像文件的過程。僞目標systemimg依賴於 INSTALLED_SYSTEMIMAGE,也就是最終生成的$(PRODUCT_OUT)/system.img文件。 INSTALLED_SYSTEMIMAGE又依賴於BUILT_SYSTEMIMAGE、RECOVERY_FROM_BOOT_PATCH和ACP。 注意,BUILT_SYSTEMIMAGE、RECOVERY_FROM_BOOT_PATCH和ACP之間有一個管道符號相隔。在一個make規則之 中,一個目標的依賴文件能夠劃分爲兩類。一個類是普通依賴文件,它們位於管道符號的左則,另外一類稱爲「order-only」依賴文件,它們位於管理符號 的右側。每個普通依賴文件發生修改後,目標都會被更新。可是"order-only"依賴文件發生修改時,卻不必定會致使目標更新。只有當目標文件不存 在的狀況下,"order-only"依賴文件的修改纔會更新目標文件。也就是說,只有在目標文件不存在的狀況下,「order-only」依賴文件纔會 參與到規則的執行過程當中去。
ACP描述的是一個Android專用的cp命令,在生成system.img鏡像文件的過程當中是須要用到的。普通的cp命令在不一樣的平臺(Mac OS X、MinGW/Cygwin和Linux)的實現略有差別,而且可能會致使一些問題,因而Android編譯系統就重寫了本身的cp命令,使得它在不一樣 平臺下執行具備統一的行爲,而且解決普通cp命令可能會出現的問題。例如,在Linux平臺上,當咱們把一個文件從NFS文件系統拷貝到本地文件系統時, 普通的cp命令老是會認爲在NFS文件系統上的文件比在本地文件系統上的文件要新,由於前者的時間戳精度是微秒,然後者的時間戳精度不是微秒。 Android專用的cp命令源碼能夠參考build/tools/acp目錄。
RECOVERY_FROM_BOOT_PATCH描述的是一個patch文件,它的依賴規則以下所示:
[plain] view plaincopy
# The system partition needs room for the recovery image as well. We
# now store the recovery image as a binary patch using the boot image
# as the source (since they are very similar). Generate the patch so
# we can see how big it's going to be, and include that in the system
# image size check calculation.
ifneq ($(INSTALLED_RECOVERYIMAGE_TARGET),)
intermediates := $(call intermediates-dir-for,PACKAGING,recovery_patch)
RECOVERY_FROM_BOOT_PATCH := $(intermediates)/recovery_from_boot.p
$(RECOVERY_FROM_BOOT_PATCH): $(INSTALLED_RECOVERYIMAGE_TARGET) \
$(INSTALLED_BOOTIMAGE_TARGET) \
$(HOST_OUT_EXECUTABLES)/imgdiff \
$(HOST_OUT_EXECUTABLES)/bsdiff
@echo "Construct recovery from boot"
mkdir -p $(dir $@)
PATH=$(HOST_OUT_EXECUTABLES):$$PATH $(HOST_OUT_EXECUTABLES)/imgdiff $(INSTALLED_BOOTIMAGE_TARGET) $(INSTALLED_RECOVERYIMAGE_TARGET) $@
endif
這個patch文件的名稱爲recovery_from_boot.p,保存在設備上system分區中,描述的是recovery.img與 boot.img之間的差別。也就是說,在設備上,咱們能夠經過boot.img和recovery_from_boot.p文件生成一個 recovery.img文件,使得設備能夠進入recovery模式。
INSTALLED_SYSTEMIMAGE描述的是system.img鏡像所包含的核心文件,它的依賴規則以下所示:
[plain] view plaincopy
systemimage_intermediates := \
$(call intermediates-dir-for,PACKAGING,systemimage)
BUILT_SYSTEMIMAGE := $(systemimage_intermediates)/system.img
# $(1): output file
define build-systemimage-target
@echo "Target system fs image: $(1)"
@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)
$(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)
endef
$(BUILT_SYSTEMIMAGE): $(FULL_SYSTEMIMAGE_DEPS) $(INSTALLED_FILES_FILE)
$(call build-systemimage-target,$@)
INSTALLED_SYSTEMIMAGE描述的是$(systemimage_intermediates)/system.img文件,它依賴於 FULL_SYSTEMIMAGE_DEPS和INSTALLED_FILES_FILE,而且是經過調用函數build-systemimage- target來生成,而函數build-systemimage-target實際上又是經過調用python腳本build_image.py來生成 system.img文件的。
INSTALLED_FILES_FILE的依賴規則以下所示:
[plain] view plaincopy
# -----------------------------------------------------------------
# installed file list
# Depending on anything that $(BUILT_SYSTEMIMAGE) depends on.
# We put installed-files.txt ahead of image itself in the dependency graph
# so that we can get the size stat even if the build fails due to too large
# system image.
INSTALLED_FILES_FILE := $(PRODUCT_OUT)/installed-files.txt
$(INSTALLED_FILES_FILE): $(FULL_SYSTEMIMAGE_DEPS)
@echo Installed file list: $@
@mkdir -p $(dir $@)
@rm -f $@
$(hide) build/tools/fileslist.py $(TARGET_OUT) > $@
INSTALLED_FILES_FILE描述的是$(PRODUCT_OUT)/installed-files.txt文件,該文件描述了要打包 在system.img鏡像中去的文件列表,同時它與INSTALLED_SYSTEMIMAGE同樣,也依賴於 FULL_SYSTEMIMAGE_DEPS。
FULL_SYSTEMIMAGE_DEPS的定義以下所示:
[plain] view plaincopy
FULL_SYSTEMIMAGE_DEPS := $(INTERNAL_SYSTEMIMAGE_FILES) $(INTERNAL_USERIMAGES_DEPS)
INTERNAL_USERIMAGES_DEPS描述的是製做system.img鏡像所依賴的工具。例如,若是要製做的system.img使用的是yaffs2文件系統,那麼對應工具就是mkyaffs2image。
INTERNAL_SYSTEMIMAGE_FILES描述的是用來製做system.img鏡像的文件,它的定義以下所示:
[plain] view plaincopy
INTERNAL_SYSTEMIMAGE_FILES := $(filter $(TARGET_OUT)/%, \
$(ALL_PREBUILT) \
$(ALL_COPIED_HEADERS) \
$(ALL_GENERATED_SOURCES) \
$(ALL_DEFAULT_INSTALLED_MODULES) \
$(PDK_FUSION_SYSIMG_FILES) \
$(RECOVERY_RESOURCE_ZIP))
從這裏就能夠看出,INTERNAL_SYSTEMIMAGE_FILES描述的就是從ALL_PREBUILT、 ALL_COPIED_HEADERS、ALL_GENERATED_SOURCES、ALL_DEFAULT_INSTALLED_MODULES、 PDK_FUSION_SYSIMG_FILES和RECOVERY_RESOURCE_ZIP中過濾出來的存放在TARGET_OUT目錄下的那些文 件,即在目標產品輸出目錄中的system子目錄下那些文件。
ALL_PREBUILT是一個過期的變量,用來描述要拷貝到目標設備上去的文件,如今建議使用PRODUCT_COPY_FILES來描述要拷貝到目標設備上去的文件。
ALL_COPIED_HEADERS描述的是要拷貝到目標設備上去的頭文件。
ALL_GENERATED_SOURCES描述的是要拷貝到目標設備上去的由工具自動生成的源代碼文件。
ALL_DEFAULT_INSTALLED_MODULES描述的就是要安裝要目標設備上的模塊文件,這些模塊文件是在build/core/main.mk文件中設置好而且傳遞給build/core/Makefile文件使用的。
PDK_FUSION_SYSIMG_FILES是從PDK(Platform Development Kit)提取出來的相關文件。PDK是Google爲了解決Android碎片化問題而爲手機廠商提供的一個新版本的、還未發佈的Android開發包, 目的是爲了讓手機廠商跟上官方新版Android的開發節奏。具體能夠參考這篇文章:http://www.xinwengao.net/release /af360/67079.shtml。
RECOVERY_RESOURCE_ZIP描述的是Android的recovery系統要使用的資源文件,對應於/system/etc目錄下的recovery-resource.dat文件。
注意,ALL_DEFAULT_INSTALLED_MODULES描述的文件除了在build/core/main.mk文件中定義的模塊文件以外,還包含如下這些文件:
1. 經過PRODUCT_COPY_FILES定義的要拷貝到目標設備上去的文件
[plain] view plaincopy
unique_product_copy_files_pairs :=
$(foreach cf,$(PRODUCT_COPY_FILES), \
$(if $(filter $(unique_product_copy_files_pairs),$(cf)),,\
$(eval unique_product_copy_files_pairs += $(cf))))
unique_product_copy_files_destinations :=
$(foreach cf,$(unique_product_copy_files_pairs), \
$(eval _src := $(call word-colon,1,$(cf))) \
$(eval _dest := $(call word-colon,2,$(cf))) \
$(call check-product-copy-files,$(cf)) \
$(if $(filter $(unique_product_copy_files_destinations),$(_dest)), \
$(info PRODUCT_COPY_FILES $(cf) ignored.), \
$(eval _fulldest := $(call append-path,$(PRODUCT_OUT),$(_dest))) \
$(if $(filter %.xml,$(_dest)),\
$(eval $(call copy-xml-file-checked,$(_src),$(_fulldest))),\
$(eval $(call copy-one-file,$(_src),$(_fulldest)))) \
<span style="color:#FF0000;"><strong>$(eval ALL_DEFAULT_INSTALLED_MODULES += $(_fulldest)) \</strong></span>
$(eval unique_product_copy_files_destinations += $(_dest))))
2. 由ADDITIONAL_DEFAULT_PROPERTIES定義的屬性所組成的default.prop文件
[plain] view plaincopy
INSTALLED_DEFAULT_PROP_TARGET := $(TARGET_ROOT_OUT)/default.prop
<span style="color:#FF0000;"><strong>ALL_DEFAULT_INSTALLED_MODULES += $(INSTALLED_DEFAULT_PROP_TARGET)</strong></span>
ADDITIONAL_DEFAULT_PROPERTIES := \
$(call collapse-pairs, $(ADDITIONAL_DEFAULT_PROPERTIES))
ADDITIONAL_DEFAULT_PROPERTIES += \
$(call collapse-pairs, $(PRODUCT_DEFAULT_PROPERTY_OVERRIDES))
ADDITIONAL_DEFAULT_PROPERTIES := $(call uniq-pairs-by-first-component, \
$(ADDITIONAL_DEFAULT_PROPERTIES),=)
$(INSTALLED_DEFAULT_PROP_TARGET):
@echo Target buildinfo: $@
@mkdir -p $(dir $@)
$(hide) echo "#" > $@; \
echo "# ADDITIONAL_DEFAULT_PROPERTIES" >> $@; \
echo "#" >> $@;
$(hide) $(foreach line,$(ADDITIONAL_DEFAULT_PROPERTIES), \
echo "$(line)" >> $@;)
build/tools/post_process_props.py $@
3. 由ADDITIONAL_BUILD_PROPERTIES等定義的屬性所組成的build.prop文件
[plain] view plaincopy
INSTALLED_BUILD_PROP_TARGET := $(TARGET_OUT)/build.prop
<span style="color:#FF0000;"><strong>ALL_DEFAULT_INSTALLED_MODULES += $(INSTALLED_BUILD_PROP_TARGET)</strong></span>
ADDITIONAL_BUILD_PROPERTIES := \
$(call collapse-pairs, $(ADDITIONAL_BUILD_PROPERTIES))
ADDITIONAL_BUILD_PROPERTIES := $(call uniq-pairs-by-first-component, \
$(ADDITIONAL_BUILD_PROPERTIES),=)
......
BUILDINFO_SH := build/tools/buildinfo.sh
$(INSTALLED_BUILD_PROP_TARGET): $(BUILDINFO_SH) $(INTERNAL_BUILD_ID_MAKEFILE) $(BUILD_SYSTEM)/version_defaults.mk $(wildcard $(TARGET_DEVICE_DIR)/system.prop)
@echo Target buildinfo: $@
@mkdir -p $(dir $@)
$(hide) TARGET_BUILD_TYPE="$(TARGET_BUILD_VARIANT)" \
TARGET_DEVICE="$(TARGET_DEVICE)" \
PRODUCT_NAME="$(TARGET_PRODUCT)" \
PRODUCT_BRAND="$(PRODUCT_BRAND)" \
PRODUCT_DEFAULT_LANGUAGE="$(call default-locale-language,$(PRODUCT_LOCALES))" \
PRODUCT_DEFAULT_REGION="$(call default-locale-region,$(PRODUCT_LOCALES))" \
PRODUCT_DEFAULT_WIFI_CHANNELS="$(PRODUCT_DEFAULT_WIFI_CHANNELS)" \
PRODUCT_MODEL="$(PRODUCT_MODEL)" \
PRODUCT_MANUFACTURER="$(PRODUCT_MANUFACTURER)" \
PRIVATE_BUILD_DESC="$(PRIVATE_BUILD_DESC)" \
BUILD_ID="$(BUILD_ID)" \
BUILD_DISPLAY_ID="$(BUILD_DISPLAY_ID)" \
BUILD_NUMBER="$(BUILD_NUMBER)" \
PLATFORM_VERSION="$(PLATFORM_VERSION)" \
PLATFORM_SDK_VERSION="$(PLATFORM_SDK_VERSION)" \
PLATFORM_VERSION_CODENAME="$(PLATFORM_VERSION_CODENAME)" \
BUILD_VERSION_TAGS="$(BUILD_VERSION_TAGS)" \
TARGET_BOOTLOADER_BOARD_NAME="$(TARGET_BOOTLOADER_BOARD_NAME)" \
BUILD_FINGERPRINT="$(BUILD_FINGERPRINT)" \
TARGET_BOARD_PLATFORM="$(TARGET_BOARD_PLATFORM)" \
TARGET_CPU_ABI="$(TARGET_CPU_ABI)" \
TARGET_CPU_ABI2="$(TARGET_CPU_ABI2)" \
TARGET_AAPT_CHARACTERISTICS="$(TARGET_AAPT_CHARACTERISTICS)" \
bash $(BUILDINFO_SH) > $@
$(hide) if [ -f $(TARGET_DEVICE_DIR)/system.prop ]; then \
cat $(TARGET_DEVICE_DIR)/system.prop >> $@; \
fi
$(if $(ADDITIONAL_BUILD_PROPERTIES), \
$(hide) echo >> $@; \
echo "#" >> $@; \
echo "# ADDITIONAL_BUILD_PROPERTIES" >> $@; \
echo "#" >> $@; )
$(hide) $(foreach line,$(ADDITIONAL_BUILD_PROPERTIES), \
echo "$(line)" >> $@;)
$(hide) build/tools/post_process_props.py $@
4. 用來描述event類型日誌格式的event-log-tags文件
[plain] view plaincopy
all_event_log_tags_file := $(TARGET_OUT_COMMON_INTERMEDIATES)/all-event-log-tags.txt
event_log_tags_file := $(TARGET_OUT)/etc/event-log-tags
# Include tags from all packages that we know about
all_event_log_tags_src := \
$(sort $(foreach m, $(ALL_MODULES), $(ALL_MODULES.$(m).EVENT_LOG_TAGS)))
# PDK builds will already have a full list of tags that needs to get merged
# in with the ones from source
pdk_fusion_log_tags_file := $(patsubst $(PRODUCT_OUT)/%,$(_pdk_fusion_intermediates)/%,$(filter $(event_log_tags_file),$(ALL_PDK_FUSION_FILES)))
$(all_event_log_tags_file): PRIVATE_SRC_FILES := $(all_event_log_tags_src) $(pdk_fusion_log_tags_file)
$(hide) mkdir -p $(dir $@)
$(hide) build/tools/merge-event-log-tags.py -o $@ $(PRIVATE_SRC_FILES)
# Include tags from all packages included in this product, plus all
# tags that are part of the system (ie, not in a vendor/ or device/
# directory).
event_log_tags_src := \
$(sort $(foreach m,\
$(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_PACKAGES) \
$(call module-names-for-tag-list,user), \
$(ALL_MODULES.$(m).EVENT_LOG_TAGS)) \
$(filter-out vendor/% device/% out/%,$(all_event_log_tags_src)))
$(event_log_tags_file): PRIVATE_SRC_FILES := $(event_log_tags_src) $(pdk_fusion_log_tags_file)
$(event_log_tags_file): PRIVATE_MERGED_FILE := $(all_event_log_tags_file)
$(event_log_tags_file): $(event_log_tags_src) $(all_event_log_tags_file) $(pdk_fusion_log_tags_file)
$(hide) mkdir -p $(dir $@)
$(hide) build/tools/merge-event-log-tags.py -o $@ -m $(PRIVATE_MERGED_FILE) $(PRIVATE_SRC_FILES)
event-log-tags: $(event_log_tags_file)
<strong><span style="color:#FF0000;">ALL_DEFAULT_INSTALLED_MODULES += $(event_log_tags_file)</span></strong>
關於Android系統的event日誌,能夠參考Android日誌系統驅動程序Logger源代碼分析、Android應用程序框架層和系統運行庫層日誌系統源代碼分析和Android日誌系統Logcat源代碼簡要分析這個系列的文章。
5. 因爲使用了BSD、GPL和Apache許可的模塊而必須在發佈時附帶的Notice文件
[plain] view plaincopy
target_notice_file_txt := $(TARGET_OUT_INTERMEDIATES)/NOTICE.txt
target_notice_file_html := $(TARGET_OUT_INTERMEDIATES)/NOTICE.html
target_notice_file_html_gz := $(TARGET_OUT_INTERMEDIATES)/NOTICE.html.gz
tools_notice_file_txt := $(HOST_OUT_INTERMEDIATES)/NOTICE.txt
tools_notice_file_html := $(HOST_OUT_INTERMEDIATES)/NOTICE.html
kernel_notice_file := $(TARGET_OUT_NOTICE_FILES)/src/kernel.txt
pdk_fusion_notice_files := $(filter $(TARGET_OUT_NOTICE_FILES)/%, $(ALL_PDK_FUSION_FILES))
......
# Install the html file at /system/etc/NOTICE.html.gz.
# This is not ideal, but this is very late in the game, after a lot of
# the module processing has already been done -- in fact, we used the
# fact that all that has been done to get the list of modules that we
# need notice files for.
$(target_notice_file_html_gz): $(target_notice_file_html) | $(MINIGZIP)
$(hide) $(MINIGZIP) -9 < $< > $@
installed_notice_html_gz := $(TARGET_OUT)/etc/NOTICE.html.gz
$(installed_notice_html_gz): $(target_notice_file_html_gz) | $(ACP)
$(copy-file-to-target)
# if we've been run my mm, mmm, etc, don't reinstall this every time
ifeq ($(ONE_SHOT_MAKEFILE),)
<span style="color:#FF0000;"><strong>ALL_DEFAULT_INSTALLED_MODULES += $(installed_notice_html_gz)</strong></span>
endif
6. 用來驗證OTA更新的證書文件
[plain] view plaincopy
<span style="color:#FF0000;"><strong>ALL_DEFAULT_INSTALLED_MODULES += $(TARGET_OUT_ETC)/security/otacerts.zip</strong></span>
$(TARGET_OUT_ETC)/security/otacerts.zip: KEY_CERT_PAIR := $(DEFAULT_KEY_CERT_PAIR)
$(TARGET_OUT_ETC)/security/otacerts.zip: $(addsuffix .x509.pem,$(DEFAULT_KEY_CERT_PAIR))
$(hide) rm -f $@
$(hide) mkdir -p $(dir $@)
$(hide) zip -qj $@ $<
.PHONY: otacerts
otacerts: $(TARGET_OUT_ETC)/security/otacerts.zip
二. boot.img
從前面的分析能夠知道,build/core/main.mk文件定義了boot.img鏡像文件的依賴規則,咱們能夠經過執行make bootimage命令來執行。其中,bootimage是一個僞目標,它依賴於INSTALLED_BOOTIMAGE_TARGET。
INSTALLED_BOOTIMAGE_TARGET定義在build/core/Makefile文件中,以下所示:
[plain] view plaincopy
ifneq ($(strip $(TARGET_NO_KERNEL)),true)
# -----------------------------------------------------------------
# the boot image, which is a collection of other images.
INTERNAL_BOOTIMAGE_ARGS := \
$(addprefix --second ,$(INSTALLED_2NDBOOTLOADER_TARGET)) \
--kernel $(INSTALLED_KERNEL_TARGET) \
--ramdisk $(INSTALLED_RAMDISK_TARGET)
INTERNAL_BOOTIMAGE_FILES := $(filter-out --%,$(INTERNAL_BOOTIMAGE_ARGS))
BOARD_KERNEL_CMDLINE := $(strip $(BOARD_KERNEL_CMDLINE))
ifdef BOARD_KERNEL_CMDLINE
INTERNAL_BOOTIMAGE_ARGS += --cmdline "$(BOARD_KERNEL_CMDLINE)"
endif
BOARD_KERNEL_BASE := $(strip $(BOARD_KERNEL_BASE))
ifdef BOARD_KERNEL_BASE
INTERNAL_BOOTIMAGE_ARGS += --base $(BOARD_KERNEL_BASE)
endif
BOARD_KERNEL_PAGESIZE := $(strip $(BOARD_KERNEL_PAGESIZE))
ifdef BOARD_KERNEL_PAGESIZE
INTERNAL_BOOTIMAGE_ARGS += --pagesize $(BOARD_KERNEL_PAGESIZE)
endif
INSTALLED_BOOTIMAGE_TARGET := $(PRODUCT_OUT)/boot.img
ifeq ($(TARGET_BOOTIMAGE_USE_EXT2),true)
tmp_dir_for_image := $(call intermediates-dir-for,EXECUTABLES,boot_img)/bootimg
INTERNAL_BOOTIMAGE_ARGS += --tmpdir $(tmp_dir_for_image)
INTERNAL_BOOTIMAGE_ARGS += --genext2fs $(MKEXT2IMG)
$(INSTALLED_BOOTIMAGE_TARGET): $(MKEXT2IMG) $(INTERNAL_BOOTIMAGE_FILES)
$(call pretty,"Target boot image: $@")
$(hide) $(MKEXT2BOOTIMG) $(INTERNAL_BOOTIMAGE_ARGS) --output $@
else # TARGET_BOOTIMAGE_USE_EXT2 != true
$(INSTALLED_BOOTIMAGE_TARGET): $(MKBOOTIMG) $(INTERNAL_BOOTIMAGE_FILES)
$(call pretty,"Target boot image: $@")
$(hide) $(MKBOOTIMG) $(INTERNAL_BOOTIMAGE_ARGS) $(BOARD_MKBOOTIMG_ARGS) --output $@
$(hide) $(call assert-max-image-size,$@,$(BOARD_BOOTIMAGE_PARTITION_SIZE),raw)
endif # TARGET_BOOTIMAGE_USE_EXT2
else # TARGET_NO_KERNEL
# HACK: The top-level targets depend on the bootimage. Not all targets
# can produce a bootimage, though, and emulator targets need the ramdisk
# instead. Fake it out by calling the ramdisk the bootimage.
# TODO: make the emulator use bootimages, and make mkbootimg accept
# kernel-less inputs.
INSTALLED_BOOTIMAGE_TARGET := $(INSTALLED_RAMDISK_TARGET)
endif
在介紹boot.img以前,咱們首先要介紹BootLoader。咱們都知道,PC啓動的時候,首先執行的是在BIOS上的代碼,而後再由BIOS負責將Kernel加載起來執行。在嵌入式世界裏,BootLoader的做用就至關於PC的BIOS。
BootLoader的啓動一般分爲兩個階段。第一階段在固態存儲中(Flash)執行,負責初始化硬件,例如設置CPU工做模式、時鐘頻率以及初始化內 存等,而且將第二階段拷貝到RAM去準備執行。第二階段就是在RAM中執行,所以速度會更快,主要負責創建內存映像,以及加載Kernel鏡像和 Ramdisk鏡像。
BootLoader的第二階段執行完成後,就開始啓動Kernel了。Kernel負責啓動各個子系統,例如CPU調度子系統和內存管理子系統等等。 Kernel啓動完成以後,就會將Ramdisk鏡像安裝爲根系統,而且在其中找到一個init文件,將其啓動爲第一個進程。init進程啓動就意味着系 統進入到用戶空間執行了,這時候各類用戶空間運行時以及守護進程就會被加載起來。最終完成整個系統的啓動過程。
BootLoader的第一階段是固化在硬件中的,而boot.img的存在就是爲BootLoader的第一階段提供第二階段、Kernel鏡像、 Ramdisk鏡像,以及相應的啓動參數等等。也就是說,boot.img主要包含有BootLoader的第二階段、Kernel鏡像和Ramdisk 鏡像。固然,BootLoader的第二階段是可選的。當不存在BootLoader的第二階段的時候,BootLoader的第一階段啓動完成後,就直 接進入到kernel啓動階段。
boot.img鏡像的文件格式定義在system/core/mkbootimg/bootimg.h中,以下所示:
[plain] view plaincopy
/*
** +-----------------+
** | boot header | 1 page
** +-----------------+
** | kernel | n pages
** +-----------------+
** | ramdisk | m pages
** +-----------------+
** | second stage | o pages
** +-----------------+
**
** n = (kernel_size + page_size - 1) / page_size
** m = (ramdisk_size + page_size - 1) / page_size
** o = (second_size + page_size - 1) / page_size
**
** 0. all entities are page_size aligned in flash
** 1. kernel and ramdisk are required (size != 0)
** 2. second is optional (second_size == 0 -> no second)
** 3. load each element (kernel, ramdisk, second) at
** the specified physical address (kernel_addr, etc)
** 4. prepare tags at tag_addr. kernel_args[] is
** appended to the kernel commandline in the tags.
** 5. r0 = 0, r1 = MACHINE_TYPE, r2 = tags_addr
** 6. if second_size != 0: jump to second_addr
** else: jump to kernel_addr
*/
它由4部分組成:boot header、kernel、ramdisk和second state。每個部分的大小都是以頁爲單位的,其中,boot header描述了kernel、ramdisk、sencond stage的加載地址、大小,以及kernel啓動參數等等信息。
boot header的結構一樣是定義在system/core/mkbootimg/bootimg.h中,以下所示:
[cpp] view plaincopy
struct boot_img_hdr
{
unsigned char magic[BOOT_MAGIC_SIZE];
unsigned kernel_size; /* size in bytes */
unsigned kernel_addr; /* physical load addr */
unsigned ramdisk_size; /* size in bytes */
unsigned ramdisk_addr; /* physical load addr */
unsigned second_size; /* size in bytes */
unsigned second_addr; /* physical load addr */
unsigned tags_addr; /* physical addr for kernel tags */
unsigned page_size; /* flash page size we assume */
unsigned unused[2]; /* future expansion: should be 0 */
unsigned char name[BOOT_NAME_SIZE]; /* asciiz product name */
unsigned char cmdline[BOOT_ARGS_SIZE];
unsigned id[8]; /* timestamp / checksum / sha1 / etc */
};
各個成員變量的含義以下所示:
magic:魔數,等於「ANDROID!」。
kernel_size:Kernel大小,以字節爲單位。
kernel_addr:Kernel加載的物理地址。
ramdisk_size:Ramdisk大小,以字節爲單位。
ramdisk_addr:Ramdisk加載的物理地址。
second_size:BootLoader第二階段的大小,以字節爲單位。
second_addr:BootLoader第二階段加載的物理地址。
tags_addr:Kernel啓動以前,它所須要的啓動參數都會填入到由tags_addr所描述的一個物理地址中去。
unused:保留之後使用。
page_size:頁大小。
name:產品名稱。
id:時間戳、校驗碼等等。
理解了BootLoader的啓動過程,以及boot.img的文件結構以後,就不難理解boot.img文件的生成過程了。
首先檢查變量TARGET_NO_KERNEL的值是否等於true。若是等於true的話,那麼就表示boot.img不包含有BootLoader第 二階段和Kernel,即它等同於Ramdisk鏡像。若是不等於true的話,那麼就經過 INSTALLED_2NDBOOTLOADER_TARGET、INSTALLED_KERNEL_TARGET和 INSTALLED_RAMDISK_TARGET得到BootLoader第二階段、Kernel和Ramdisk對應的鏡像文件,以及經過 BOARD_KERNEL_CMDLINE、BOARD_KERNEL_BASE和BOARD_KERNEL_PAGESIZE得到Kernel啓動命令 行參數、內核基地址和頁大小等參數。最後根據TARGET_BOOTIMAGE_USE_EXT2的值來決定是使用genext2fs仍是 mkbootimg工具來生成boot.img。
三. ramdisk.img
從前面的分析能夠知道,build/core/main.mk文件定義了ramdisk.img鏡像文件的依賴規則,咱們能夠經過執行make ramdisk命令來執行。其中,ramdisk是一個僞目標,它依賴於INSTALLED_RAMDISK_TARGET。
INSTALLED_RAMDISK_TARGET定義在build/core/Makefile文件中,以下所示:
[plain] view plaincopy
INTERNAL_RAMDISK_FILES := $(filter $(TARGET_ROOT_OUT)/%, \
$(ALL_PREBUILT) \
$(ALL_COPIED_HEADERS) \
$(ALL_GENERATED_SOURCES) \
$(ALL_DEFAULT_INSTALLED_MODULES))
BUILT_RAMDISK_TARGET := $(PRODUCT_OUT)/ramdisk.img
# We just build this directly to the install location.
INSTALLED_RAMDISK_TARGET := $(BUILT_RAMDISK_TARGET)
$(INSTALLED_RAMDISK_TARGET): $(MKBOOTFS) $(INTERNAL_RAMDISK_FILES) | $(MINIGZIP)
$(call pretty,"Target ram disk: $@")
$(hide) $(MKBOOTFS) $(TARGET_ROOT_OUT) | $(MINIGZIP) > $@
ALL_PREBUILT、ALL_COPIED_HEADERS、ALL_GENERATED_SOURCES和 ALL_DEFAULT_INSTALLED_MODULES這幾個變量的含義前面分析system.img的生成過程時已經介紹過了。所以,這裏咱們就 很容易知道,ramdisk.img鏡像實際上就是由這幾個變量所描述的、保存在TARGET_ROOT_OUT目錄中的文件所組成。與此相對應的 是,system.img由保存在TARGET_OUT目錄中的文件組成。
TARGET_ROOT_OUT和TARGET_OUT又分別是指向什麼目錄呢?假設咱們的編譯目標產品是模擬器,那麼 TARGET_ROOT_OUT和TARGET_OUT對應的目錄就分別爲out/target/product/generic/root和out /target/product/generic/system。
收集好對應的文件以後,就能夠經過MKBOOTFS和MINIGZIP這兩個變量描述的mkbootfs和minigzip工具來生成一個格式爲cpio 的ramdisk.img了。mkbootfs和minigzip這兩個工具對應的源碼分別位於system/core/cpio和external /zlib目錄中。
四. userdata.img
userdata.img鏡像描述的是Android系統的data分區,即/data目錄,裏面包含了用戶安裝的APP以及數據等等。
從前面的分析能夠知道,build/core/main.mk文件定義了userdata.img鏡像文件的依賴規則,咱們能夠經過執行make userdataimage命令來執行。其中,userdataimage是一個僞目標,它依賴於 INSTALLED_USERDATAIMAGE_TARGET。
INSTALLED_USERDATAIMAGE_TARGET定義在build/core/Makefile文件中,以下所示:
[plain] view plaincopy
INTERNAL_USERDATAIMAGE_FILES := \
$(filter $(TARGET_OUT_DATA)/%,$(ALL_DEFAULT_INSTALLED_MODULES))
$(info $(TARGET_OUT_DATA))
# If we build "tests" at the same time, make sure $(tests_MODULES) get covered.
ifdef is_tests_build
INTERNAL_USERDATAIMAGE_FILES += \
$(filter $(TARGET_OUT_DATA)/%,$(tests_MODULES))
endif
userdataimage_intermediates := \
$(call intermediates-dir-for,PACKAGING,userdata)
BUILT_USERDATAIMAGE_TARGET := $(PRODUCT_OUT)/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)
$(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)
$(hide) $(call assert-max-image-size,$(INSTALLED_USERDATAIMAGE_TARGET),$(BOARD_USERDATAIMAGE_PARTITION_SIZE),yaffs)
endef
# We just build this directly to the install location.
INSTALLED_USERDATAIMAGE_TARGET := $(BUILT_USERDATAIMAGE_TARGET)
$(INSTALLED_USERDATAIMAGE_TARGET): $(INTERNAL_USERIMAGES_DEPS) \
$(INTERNAL_USERDATAIMAGE_FILES)
$(build-userdataimage-target)
INSTALLED_USERDATAIMAGE_TARGET的值等於BUILT_USERDATAIMAGE_TARGET,後者指向的就是 userdata.img文件,它依賴於INTERNAL_USERDATAIMAGE_FILES描述的文件,即那些由 ALL_DEFAULT_INSTALLED_MODULES描述的、而且位於TARGET_OUT_DATA目錄下的文件。假設咱們的編譯目標產品是模 擬器,那麼TARGET_OUT_DATA對應的目錄就爲out/target/product/generic/data。此外,若是咱們在make userdataimage的時候,還帶有一個額外的tests目標,那麼那些將本身的tag設置爲tests的模塊也會被打包到 userdata.img鏡像中。
INSTALLED_USERDATAIMAGE_TARGET還依賴於INTERNAL_USERIMAGES_DEPS。前面在分析 system.img鏡像的生成過程時提到,INTERNAL_USERIMAGES_DEPS描述的是製做userdata.img鏡像所依賴的工具。 例如,若是要製做的userdata.img使用的是yaffs2文件系統,那麼對應工具就是mkyaffs2image。
INSTALLED_USERDATAIMAGE_TARGET規則由函數build-userdataimage-target來執行,該函數經過build_image.py腳原本生成userdata.img鏡像文件。
與system.img鏡像相似,userdata.img鏡像的生成也能夠經過一個沒有任何依賴的僞目標userdataimage-nodeps生成,以下所示:
[plain] view plaincopy
.PHONY: userdataimage-nodeps
userdataimage-nodeps: | $(INTERNAL_USERIMAGES_DEPS)
$(build-userdataimage-target)
當咱們執行make userdataimage-nodeps的時候,函數build-userdataimage-target就會被調用直接生成userdata.img文件。
五. recovery.img
recovery.img是設備進入recovery模式時所加載的鏡像。recovery模式就是用來更新系統的,咱們能夠認爲咱們的設備具備兩個系 統。一個是正常的系統,它由boot.img、system.img、ramdisk.img和userdata.img等組成,另一個就是 recovery系統,由recovery.img組成。平時咱們進入的都是正常的系統,只有當咱們須要更新這個正常的系統時,纔會進入到 recovery系統。所以,咱們能夠將recovery系統理解爲在Linux Kernel之上運行的一個小小的用戶空間運行時。這個用戶空間運行時能夠訪問咱們平時常用的那個系統的文件,從而實現對它的更新。
在build/core/Makefile文件中,定義了一個僞目標recoveryimage,用來生成recovery.img,以下所示:
[plain] view plaincopy
.PHONY: recoveryimage
recoveryimage: $(INSTALLED_RECOVERYIMAGE_TARGET) $(RECOVERY_RESOURCE_ZIP)
INSTALLED_RECOVERYIMAGE_TARGET描述的是組成recovery系統的模塊文件,而RECOVERY_RESOURCE_ZIP描述的是recovery系統使用的資源包,它的定義以下所示:
[plain] view plaincopy
INSTALLED_RECOVERYIMAGE_TARGET := $(PRODUCT_OUT)/recovery.img
recovery_initrc := $(call include-path-for, recovery)/etc/init.rc
recovery_kernel := $(INSTALLED_KERNEL_TARGET) # same as a non-recovery system
recovery_ramdisk := $(PRODUCT_OUT)/ramdisk-recovery.img
recovery_build_prop := $(INSTALLED_BUILD_PROP_TARGET)
recovery_binary := $(call intermediates-dir-for,EXECUTABLES,recovery)/recovery
recovery_resources_common := $(call include-path-for, recovery)/res
recovery_resources_private := $(strip $(wildcard $(TARGET_DEVICE_DIR)/recovery/res))
recovery_resource_deps := $(shell find $(recovery_resources_common) \
$(recovery_resources_private) -type f)
recovery_fstab := $(strip $(wildcard $(TARGET_DEVICE_DIR)/recovery.fstab))
# Named '.dat' so we don't attempt to use imgdiff for patching it.
RECOVERY_RESOURCE_ZIP := $(TARGET_OUT)/etc/recovery-resource.dat
......
INTERNAL_RECOVERYIMAGE_ARGS := \
$(addprefix --second ,$(INSTALLED_2NDBOOTLOADER_TARGET)) \
--kernel $(recovery_kernel) \
--ramdisk $(recovery_ramdisk)
# Assumes this has already been stripped
ifdef BOARD_KERNEL_CMDLINE
INTERNAL_RECOVERYIMAGE_ARGS += --cmdline "$(BOARD_KERNEL_CMDLINE)"
endif
ifdef BOARD_KERNEL_BASE
INTERNAL_RECOVERYIMAGE_ARGS += --base $(BOARD_KERNEL_BASE)
endif
BOARD_KERNEL_PAGESIZE := $(strip $(BOARD_KERNEL_PAGESIZE))
ifdef BOARD_KERNEL_PAGESIZE
INTERNAL_RECOVERYIMAGE_ARGS += --pagesize $(BOARD_KERNEL_PAGESIZE)
endif
# Keys authorized to sign OTA packages this build will accept. The
# build always uses dev-keys for this; release packaging tools will
# substitute other keys for this one.
OTA_PUBLIC_KEYS := $(DEFAULT_SYSTEM_DEV_CERTIFICATE).x509.pem
# Generate a file containing the keys that will be read by the
# recovery binary.
RECOVERY_INSTALL_OTA_KEYS := \
$(call intermediates-dir-for,PACKAGING,ota_keys)/keys
DUMPKEY_JAR := $(HOST_OUT_JAVA_LIBRARIES)/dumpkey.jar
$(RECOVERY_INSTALL_OTA_KEYS): PRIVATE_OTA_PUBLIC_KEYS := $(OTA_PUBLIC_KEYS)
$(RECOVERY_INSTALL_OTA_KEYS): extra_keys := $(patsubst %,%.x509.pem,$(PRODUCT_EXTRA_RECOVERY_KEYS))
$(RECOVERY_INSTALL_OTA_KEYS): $(OTA_PUBLIC_KEYS) $(DUMPKEY_JAR) $(extra_keys)
@echo "DumpPublicKey: $@ <= $(PRIVATE_OTA_PUBLIC_KEYS) $(extra_keys)"
@rm -rf $@
@mkdir -p $(dir $@)
java -jar $(DUMPKEY_JAR) $(PRIVATE_OTA_PUBLIC_KEYS) $(extra_keys) > $@
$(INSTALLED_RECOVERYIMAGE_TARGET): $(MKBOOTFS) $(MKBOOTIMG) $(MINIGZIP) \
$(INSTALLED_RAMDISK_TARGET) \
$(INSTALLED_BOOTIMAGE_TARGET) \
$(recovery_binary) \
$(recovery_initrc) $(recovery_kernel) \
$(INSTALLED_2NDBOOTLOADER_TARGET) \
$(recovery_build_prop) $(recovery_resource_deps) \
$(recovery_fstab) \
$(RECOVERY_INSTALL_OTA_KEYS)
@echo ----- Making recovery image ------
$(hide) rm -rf $(TARGET_RECOVERY_OUT)
$(hide) mkdir -p $(TARGET_RECOVERY_OUT)
$(hide) mkdir -p $(TARGET_RECOVERY_ROOT_OUT)/etc $(TARGET_RECOVERY_ROOT_OUT)/tmp
@echo Copying baseline ramdisk...
$(hide) cp -R $(TARGET_ROOT_OUT) $(TARGET_RECOVERY_OUT)
@echo Modifying ramdisk contents...
$(hide) rm -f $(TARGET_RECOVERY_ROOT_OUT)/init*.rc
$(hide) cp -f $(recovery_initrc) $(TARGET_RECOVERY_ROOT_OUT)/
$(hide) -cp $(TARGET_ROOT_OUT)/init.recovery.*.rc $(TARGET_RECOVERY_ROOT_OUT)/
$(hide) cp -f $(recovery_binary) $(TARGET_RECOVERY_ROOT_OUT)/sbin/
$(hide) cp -rf $(recovery_resources_common) $(TARGET_RECOVERY_ROOT_OUT)/
$(hide) $(foreach item,$(recovery_resources_private), \
cp -rf $(item) $(TARGET_RECOVERY_ROOT_OUT)/)
$(hide) $(foreach item,$(recovery_fstab), \
cp -f $(item) $(TARGET_RECOVERY_ROOT_OUT)/etc/recovery.fstab)
$(hide) cp $(RECOVERY_INSTALL_OTA_KEYS) $(TARGET_RECOVERY_ROOT_OUT)/res/keys
$(hide) cat $(INSTALLED_DEFAULT_PROP_TARGET) $(recovery_build_prop) \
> $(TARGET_RECOVERY_ROOT_OUT)/default.prop
$(hide) $(MKBOOTFS) $(TARGET_RECOVERY_ROOT_OUT) | $(MINIGZIP) > $(recovery_ramdisk)
$(hide) $(MKBOOTIMG) $(INTERNAL_RECOVERYIMAGE_ARGS) $(BOARD_MKBOOTIMG_ARGS) --output $@
$(hide) $(call assert-max-image-size,$@,$(BOARD_RECOVERYIMAGE_PARTITION_SIZE),raw)
@echo ----- Made recovery image: $@ --------
$(RECOVERY_RESOURCE_ZIP): $(INSTALLED_RECOVERYIMAGE_TARGET)
$(hide) mkdir -p $(dir $@)
$(hide) find $(TARGET_RECOVERY_ROOT_OUT)/res -type f | sort | zip -0qrj $@ -@
因爲recovery.img和boot.img都是用來啓動系統的,所以,它們的內容是比較相似,例如都包含有Kernel及其啓動參數、 Ramdisk,以及可選的BootLoader第二階段。此外,recovery.img還包含有如下的主要內容:
1. 用來設置系統屬性的文件build.prop和default.prop;
2. 進入recovery界面時用到的資源文件recovery_resources_common和recovery_resources_private;
3. 描述進入recovery模式時要安裝的文件系統腳本recovery.fstab;
4. 在recovery模式執行OTA更新要用到的證書OTA_PUBLIC_KEYS;
5. 負責升級系統的recovery程序,它的源代碼位於bootable/recovery目錄中。
經過對比Android正常使用時的系統和進行升級時使用的Recovery系統,咱們就能夠對運行Linux系統的移動設備(或者說嵌入式設備)世界窺 一斑而知全豹,實際上咱們只須要有一個BootLoader、一個Linux Kernel,以及一個Ramdisk,就能夠將它們運行起來。Ramdisk屬於用戶空間的範疇,它裏面主要包含了Linux Kernel啓動完成時所要加載的根文件系統。這個根文件系統的做用就是爲整個系統提供一個init程序,以便Linux Kernel能夠將控制權從內核空間轉移到用戶空間。系統提供什麼樣的功能給用戶使用主要取決於在用戶空間層運行了哪些服務和守護進程。這些服務守護進程 都是直接或者間接地由init進程啓動的。例如,對於recovery系統來講,它最主要提供的功能就是讓用戶升級系統,也就是它的主要用戶空間服務就是 負責執行長級任務的recovery程序。又如,咱們正常使用的Android系統,它的主要用戶空間服務和守護進程都是由system.img鏡像提供 的,此外,咱們還能夠經過userdata.img鏡像來安裝第三方應用來豐富手機的功能。所以,咱們也能夠說,init進程啓動的服務決定了系統的功 能。固然 ,這些功能是創建是硬件的基礎之上的。
至此,咱們就分析完成Android系統的主要鏡像文件system.img、boot.img、ramdisk.img、userdata.img和 recovery.img的製做過程了,但願經過這些鏡像文件的製做過程以及它們的做用的介紹,使得小夥伴後對Android系統有更進一步的認識。同 時,咱們也結束了對Android編譯系統的學習了。從新學習請參考Android編譯系統簡要介紹和學習計劃一文。想了解更多Android相關技術,歡迎關注老羅的新浪微博:http://weibo.com/shengyangluo。