Android系統映象檔案的打包過程分析
在前面一篇文章中,我們分析了Android模組的編譯過程。當Android系統的所有模組都編譯好之後,我們就可以對編譯出來的模組檔案進行打包了。打包結果是獲得一系列的映象檔案,例如system.img、boot.img、ramdisk.img、userdata.img和recovery.img等。這些映象檔案最終可以燒錄到手機上執行。在本文中,我們就詳細分析Android系統的映象檔案的打包過程。
《Android系統原始碼情景分析》一書正在進擊的程式設計師網(http://0xcc0xcd.com)中連載,點選進入!
Android系統映象檔案的打包工作同樣是由Android編譯系統來完成的,如圖1所示:
圖1 Android系統映象檔案的打包過程
從前面Android編譯系統環境初始化過程分析和Android原始碼編譯命令m/mm/mmm/make分析這兩篇文章可以知道,Android編譯系統在初始化的過程中,會通過根目錄下的Makefile指令碼載入build/core/main.mk指令碼,接著build/core/main.mk指令碼又會載入build/core/Makefile指令碼,而Android系統映象檔案就是由build/core/Makefile指令碼負責打包生成的。
在build/core/main.mk檔案中,與Android系統映象檔案打包過程相關的內容如下所示:
...... ifdef FULL_BUILD # The base list of modules to build for this product is specified # 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檔案中生成的,相關的內容如下所示:
# 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: [email protected]"
$(copy-file-to-target)
$(hide) $(call assert-max-image-size,[email protected] $(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 [email protected]: 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檔案,它的依賴規則如下所示:
# 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 [email protected])
PATH=$(HOST_OUT_EXECUTABLES):$$PATH $(HOST_OUT_EXECUTABLES)/imgdiff $(INSTALLED_BOOTIMAGE_TARGET) $(INSTALLED_RECOVERYIMAGE_TARGET) [email protected]
endif
這個patch檔案的名稱為recovery_from_boot.p,儲存在裝置上system分割槽中,描述的是recovery.img與boot.img之間的差異。也就是說,在裝置上,我們可以通過boot.img和recovery_from_boot.p檔案生成一個recovery.img檔案,使得裝置可以進入recovery模式。INSTALLED_SYSTEMIMAGE描述的是system.img映象所包含的核心檔案,它的依賴規則如下所示:
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,[email protected])
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的依賴規則如下所示:
# -----------------------------------------------------------------
# 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: [email protected]
@mkdir -p $(dir [email protected])
@rm -f [email protected]
$(hide) build/tools/fileslist.py $(TARGET_OUT) > [email protected]
INSTALLED_FILES_FILE描述的是$(PRODUCT_OUT)/installed-files.txt檔案,該檔案描述了要打包在system.img映象中去的檔案列表,同時它與INSTALLED_SYSTEMIMAGE一樣,也依賴於FULL_SYSTEMIMAGE_DEPS。 FULL_SYSTEMIMAGE_DEPS的定義如下所示:
FULL_SYSTEMIMAGE_DEPS := $(INTERNAL_SYSTEMIMAGE_FILES) $(INTERNAL_USERIMAGES_DEPS)
INTERNAL_USERIMAGES_DEPS描述的是製作system.img映象所依賴的工具。例如,如果要製作的system.img使用的是yaffs2檔案系統,那麼對應工具就是mkyaffs2image。
INTERNAL_SYSTEMIMAGE_FILES描述的是用來製作system.img映象的檔案,它的定義如下所示:
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定義的要拷貝到目標裝置上去的檔案
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)))) \
$(eval ALL_DEFAULT_INSTALLED_MODULES += $(_fulldest)) \
$(eval unique_product_copy_files_destinations += $(_dest))))
2. 由ADDITIONAL_DEFAULT_PROPERTIES定義的屬性所組成的default.prop檔案INSTALLED_DEFAULT_PROP_TARGET := $(TARGET_ROOT_OUT)/default.prop
ALL_DEFAULT_INSTALLED_MODULES += $(INSTALLED_DEFAULT_PROP_TARGET)
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: [email protected]
@mkdir -p $(dir [email protected])
$(hide) echo "#" > [email protected]; \
echo "# ADDITIONAL_DEFAULT_PROPERTIES" >> [email protected]; \
echo "#" >> [email protected];
$(hide) $(foreach line,$(ADDITIONAL_DEFAULT_PROPERTIES), \
echo "$(line)" >> [email protected];)
build/tools/post_process_props.py [email protected]
3. 由ADDITIONAL_BUILD_PROPERTIES等定義的屬性所組成的build.prop檔案INSTALLED_BUILD_PROP_TARGET := $(TARGET_OUT)/build.prop
ALL_DEFAULT_INSTALLED_MODULES += $(INSTALLED_BUILD_PROP_TARGET)
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: [email protected]
@mkdir -p $(dir [email protected])
$(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) > [email protected]
$(hide) if [ -f $(TARGET_DEVICE_DIR)/system.prop ]; then \
cat $(TARGET_DEVICE_DIR)/system.prop >> [email protected]; \
fi
$(if $(ADDITIONAL_BUILD_PROPERTIES), \
$(hide) echo >> [email protected]; \
echo "#" >> [email protected]; \
echo "# ADDITIONAL_BUILD_PROPERTIES" >> [email protected]; \
echo "#" >> [email protected]; )
$(hide) $(foreach line,$(ADDITIONAL_BUILD_PROPERTIES), \
echo "$(line)" >> [email protected];)
$(hide) build/tools/post_process_props.py [email protected]
4. 用來描述event型別日誌格式的event-log-tags檔案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 [email protected])
$(hide) build/tools/merge-event-log-tags.py -o [email protected] $(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 [email protected])
$(hide) build/tools/merge-event-log-tags.py -o [email protected] -m $(PRIVATE_MERGED_FILE) $(PRIVATE_SRC_FILES)
event-log-tags: $(event_log_tags_file)
ALL_DEFAULT_INSTALLED_MODULES += $(event_log_tags_file)
關於Android系統的event日誌,可以參考Android日誌系統驅動程式Logger原始碼分析、Android應用程式框架層和系統執行庫層日誌系統原始碼分析和Android日誌系統Logcat原始碼簡要分析這個系列的文章。5. 由於使用了BSD、GPL和Apache許可的模組而必須在釋出時附帶的Notice檔案
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 < $< > [email protected]
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),)
ALL_DEFAULT_INSTALLED_MODULES += $(installed_notice_html_gz)
endif
6. 用來驗證OTA更新的證書檔案ALL_DEFAULT_INSTALLED_MODULES += $(TARGET_OUT_ETC)/security/otacerts.zip
$(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 [email protected]
$(hide) mkdir -p $(dir [email protected])
$(hide) zip -qj [email protected] $<
.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檔案中,如下所示:
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: [email protected]")
$(hide) $(MKEXT2BOOTIMG) $(INTERNAL_BOOTIMAGE_ARGS) --output [email protected]
else # TARGET_BOOTIMAGE_USE_EXT2 != true
$(INSTALLED_BOOTIMAGE_TARGET): $(MKBOOTIMG) $(INTERNAL_BOOTIMAGE_FILES)
$(call pretty,"Target boot image: [email protected]")
$(hide) $(MKBOOTIMG) $(INTERNAL_BOOTIMAGE_ARGS) $(BOARD_MKBOOTIMG_ARGS) --output [email protected]
$(hide) $(call assert-max-image-size,[email protected],$(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中,如下所示:
/*
** +-----------------+
** | 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中,如下所示:
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檔案中,如下所示:
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: [email protected]")
$(hide) $(MKBOOTFS) $(TARGET_ROOT_OUT) | $(MINIGZIP) > [email protected]
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檔案中,如下所示:
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生成,如下所示:
.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,如下所示:
.PHONY: recoveryimage
recoveryimage: $(INSTALLED_RECOVERYIMAGE_TARGET) $(RECOVERY_RESOURCE_ZIP)
INSTALLED_RECOVERYIMAGE_TARGET描述的是組成recovery系統的模組檔案,而RECOVERY_RESOURCE_ZIP描述的是recovery系統使用的資源包,它的定義如下所示: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: [email protected] <= $(PRIVATE_OTA_PUBLIC_KEYS) $(extra_keys)"
@rm -rf [email protected]
@mkdir -p $(dir [email protected])
java -jar $(DUMPKEY_JAR) $(PRIVATE_OTA_PUBLIC_KEYS) $(extra_keys) > [email protected]
$(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 [email protected]
$(hide) $(call assert-max-image-size,[email protected],$(BOARD_RECOVERYIMAGE_PARTITION_SIZE),raw)
@echo ----- Made recovery image: [email protected] --------
$(RECOVERY_RESOURCE_ZIP): $(INSTALLED_RECOVERYIMAGE_TARGET)
$(hide) mkdir -p $(dir [email protected])
$(hide) find $(TARGET_RECOVERY_ROOT_OUT)/res -type f | sort | zip -0qrj [email protected] [email protected]
由於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。
相關推薦
Android系統映象檔案的打包過程分析
在前面一篇文章中,我們分析了Android模組的編譯過程。當Android系統的所有模組都編譯好之後,我們就可以對編譯出來的模組檔案進行打包了。打包結果是獲得一系列的映象檔案,例如system.img、boot.img、ramdisk.img、userdata.
android 原始碼的m、mm、mmm編譯命令的使用與重新打包android系統映象檔案。
一、android 原始碼的m、mm、mmm編譯命令的使用 m:編譯整個安卓系統 makes from the top of the tree mm:編譯當前目錄下的模組,當前目錄下需要有Android.mk這個makefile檔案,否則就往上找最近的A
轉自老羅 Android應用程式資源的編譯和打包過程分析
原文地址 http://blog.csdn.net/luoshengyang/article/details/8744683 轉載自老羅,轉載請說明 我們知道,在一個APK檔案中,除了有程式碼檔案之外,還有很多資原始檔。這些資原始檔是通過An
SimpleFs檔案系統初步三(Mount過程分析)
static int simplefs_init(void) { int ret; sfs_inode_cachep = kmem_cache_create("sfs_inode_cache",
Android執行時ART載入OAT檔案的過程分析
在前面一文中,我們介紹了Android執行時ART,它的核心是OAT檔案。OAT檔案是一種Android私有ELF檔案格式,它不僅包含有從DEX檔案翻譯而來的本地機器指令,還包含有原來的DEX檔案內容。這使得我們無需重新編譯原有的APK就可以讓它正常地在ART裡
Android應用程式資源的編譯和打包過程分析
我們知道,在一個APK檔案中,除了有程式碼檔案之外,還有很多資原始檔。這些資原始檔是通過Android資源打包工具aapt(Android Asset Package Tool)打包到APK檔案裡面的。在打包之前,大部分文字格式的XML資原始檔還會被編譯
Android系統程序Zygote啟動過程的原始碼分析
在Android系統中,所有的應用程式程序以及系統服務程序SystemServer都是由Zygote程序孕育(fork)出來的,這也許就是為什麼要把它稱為Zygote(受精卵)的原因吧。由於Zygote程序在Android系統中有著如此重要的地位,本文將詳細分
[Android] bindService的binder通訊過程分析
關於bindService方法 public class ContextWrapper extendsContext { Context mBase; public ContextWrapper(Conte
Android系統播放器MediaPlayer原始碼分析(一)
前言 對於MediaPlayer播放器的原始碼分析內容相對來說比較多,會從Java->JNI->C/C++慢慢分析,後面會慢慢更新。另外,部落格只作為自己學習記錄的一種方式,對於其他的不過多的評論。 MediaPlayerDemo public class MainA
Android系統架構特點及優劣分析
Android 架構分析 首先要注意到,Android系統有著極短的開發時間,因此Android在架構上有著四處借鑑的特點。 Android分為四個層,從低到高分別是linux核心層、系統執行庫層、應用程式框架層和應用程式層。在最底層,Android使用了L
各種官網系統映象檔案(Windows 7 ,Windows 10,Ubuntu 18.6,Centos 6.8 ,Centos 7.6 )
在以前的剛進去計算機行業的時候,學的第一件事就是裝系統,在網上苦於找不到正版的系統,這些是一直以來,見識的比較穩定的,有些是從官網下載的系統,給大家分享一哈。大家如果有用到其他好的系統,可以給我留言或者發給我一個連結,大家互相分享。 百度雲盤連結:https://pan.baidu.com/share
Android系統升級的完整過程
下面是HTC官方的一個圖片,展示了Android系統從釋出最終到使用者手中的一個完整的過程: Awesome Infographic: HTC Shows Us “The Anatomy of an Android OS Update” From PDK to OTA
各種官網系統映象檔案
在以前的剛進去計算機行業的時候,學的第一件事就是裝系統,在網上苦於找不到正版的系統,這些是一直以來,見識的比較穩定的,有些是從官網下載的系統,給大家分享一哈。大家如果有用到其他好的系統,可以給我留言或者發給我一個連結,大家互相分享。 百度雲盤連結:https://pan.baidu.com/share
Android系統映象改造指南
一、原始碼編譯 飛思卡爾板子編譯原始碼步驟: 1. # source build/envsetup.sh 2. # lunch jj_6dq-user 3. # make -j4 2>&1 | tee build_sabresd_6dq_android.log
如何使用Ghost製作系統映象檔案
如果採用傳統的系統安裝方法,即使擁有最先進的電腦,速度再快、效能再高, Windows的安裝速度也仍然是令人頭痛的。而且每次安裝完作業系統後,都要安裝各種軟體,進行各種設定,那麼每一次重新安裝系統就都會耗費大量的時間。有沒有什麼重灌系統的簡便方法呢?Ghost就是其中的一
Android6.0 按鍵kl檔案載入過程分析
在之前按鍵過程分析的幾篇部落格中,我分析過關於按鍵kl檔案的載入,但是講的不是非常詳細,這篇部落格主要把kl檔案載入過程單獨拉出來分析下。 1. 獲取InputDeviceIdentifier的name 以及 Device的建立 InputDeviceIdentifier
Android系統之Binder原始碼情景分析
寫在前面:看過很多大牛寫的Binder詳解,因為講得太過晦澀難懂,所以對於新手好像不太友好,為了讓新手對於Binder有一個大概的認識,故準備了半個月寫了這篇部落格,部落格的大概流程應該是正確的,希望看過的新手能夠有一些收穫。本文主要講解了三個部分:Servic
android系統映象:boot.img kernel.img ramdisk.img system.img userdata.img cache.img recovery.img
boot.img(kernel.img+ramdisk.img) ramdisk.img(/) system.img(/system) userdata.img(/data) cache.im
android 系統資料業務---模式切換分析(下)
5.3 RIL_REQUEST_DATA_REGISTRATION_STATE 在GsmServiceStateTracker的構造方法中,註冊了一些監聽事件, mCi.registerForAvailable(this, EVENT_RADIO_AVAILABLE, n
C/C++程式從編譯到最終生成可執行檔案的過程分析
*******************************************************篇一*******************************************************************************************