深刻Android系統
這本書是以Android5.0爲基礎講解,但本人使用的是Android9.0的源碼,因此和原書內容會有些出入。html
對於Android的構建系統,在
Android7.0
以後Google就已經使用Soong構建系統,旨在取代 Make。它利用 Kati GNU Make 克隆工具和 Ninja 構建系統組件來加速 Android 的構建。這裏是官方構建傳送門java
讀書是一個引導學習的過程,讀此書的目的是全面瞭解Android系統,當有一個全面瞭解後再來看新版特性吧。5.0 確實老了點。哈哈哈(PS:公司的項目都是9.0的,兩個版本對比學習吧)linux
Android的Build系統
是基於GNU Make
和Shell
構建的一套編譯環境。因爲Android是一個龐大的系統,包含了不少模塊,爲了管理整套源碼的編譯,Android專門開發了本身的Build系統
。android
Android的Build系統
能夠作:c++
從大的方面講,Android的Build系統
能夠分紅三部分:算法
Build系統
的框架和核心,位於build/core目錄下Android.mk
,位於各個模塊的源文件目錄下Build系統
核心編譯源碼時經常使用的三條指令是:shell
source build/envsetup.sh
lunch eva_userdebug
make
複製代碼
咱們就順着這三條指令,一步步分析整個編譯過程。編程
envsetup.sh
文件的做用執行Android系統的編譯,必須先運行envsetup.sh
腳本,這個腳本會創建Android的編譯環境。咱們看下envsetup.sh
的部分代碼:api
function add_lunch_combo() # 忽略實現
function print_lunch_menu() # 忽略實現
function lunch() # 忽略實現
function m() # 忽略實現
function findmakefile() # 忽略實現
function mm() # 忽略實現
function mmm() # 忽略實現
function mma() # 忽略實現
function mmma() # 忽略實現
function croot() # 忽略實現
function cproj() # 忽略實現
複製代碼
咱們看到envsetup.sh
文中定義了不少shell命令,上面列出的是一部分經常使用指令的定義,在執行完envsetup.sh
腳本後,這些指令就能夠調用了。數組
而envsetup.sh
文件中會執行的命令是:
# add the default one here
add_lunch_combo aosp_arm-eng
add_lunch_combo aosp_arm64-eng
add_lunch_combo aosp_mips-eng
add_lunch_combo aosp_mips64-eng
add_lunch_combo aosp_x86-eng
add_lunch_combo aosp_x86_64-eng
# Execute the contents of any vendorsetup.sh files we can find.
for f in `test -d device && find -L device -maxdepth 4 -name 'vendorsetup.sh' 2> /dev/null | sort` \
`test -d vendor && find -L vendor -maxdepth 4 -name 'vendorsetup.sh' 2> /dev/null | sort` \
`test -d product && find -L product -maxdepth 4 -name 'vendorsetup.sh' 2> /dev/null | sort`
do
echo "including $f"
. $f
done
unset f
複製代碼
這部分代碼中:
add_lunch_combo
增長了6條編譯選項device
、product
、vendor
目錄下的vendorsetup.sh
文件,並執行它們以公司的業務爲例,在vendor/formovie/goblin/vendorsetup.sh
中的內容以下:
add_lunch_combo goblin-eng
add_lunch_combo goblin-user
add_lunch_combo goblin-userdebug
add_lunch_combo goblin_fact-userdebug
複製代碼
這樣,envsetup.sh
作了三件事情:
shell
命令vendorsetup.sh
add_lunch_combo
那麼add_lunch_combo
是在幹啥呢?
咱們看下實現:
unset LUNCH_MENU_CHOICES
function add_lunch_combo()
{
local new_combo=$1
local c
# for循環查找數組中是否已存在新元素
for c in ${LUNCH_MENU_CHOICES[@]} ; do
if [ "$new_combo" = "$c" ] ; then
return
fi
done
# 將新元素加入 LUNCH_MENU_CHOICES 中
LUNCH_MENU_CHOICES=(${LUNCH_MENU_CHOICES[@]} $new_combo)
}
複製代碼
add_lunch_combo
的功能是將調用該命令時傳入的參數存放到一個全局變量數組LUNCH_MENU_CHOICES
中。代碼中有註釋哈。==shell 的語法規則看着很隨意啊,沒有俺們Java嚴禁呢==
lunch
指令打印的就是這個數組的內容
既然envsetup.sh
定義了那麼編譯指令,咱們看下都是幹啥用的吧:
命令 | 說明 |
---|---|
lunch | 指定當前編譯的產品 |
tapas | 設置Build環境變量 |
croot | 快速切換到源碼的根目錄 |
m | 編譯整個源碼,無需切換到根目錄 |
mm | 編譯當前目錄下的全部模塊,可是不編譯它們的依賴 |
mma | 編譯當前目錄下的全部模塊,同時編譯它們的依賴 |
mmm | 編譯指定目錄下的全部模塊,可是不編譯它們的依賴 |
mmma | 編譯指定目錄下的全部模塊,同時編譯它們的依賴 |
cgrep | 對系統全部的c/c++文件執行grep命令 |
ggrep | 對系統全部本地的Gradle文件執行grep命令 |
jgrep | 對系統全部的Java文件執行grep命令 |
resgrep | 對系統中全部res目錄下的xml文件執行grep命令 |
sgrep | 對系統中的全部源文件執行grep命令 |
godir | 根據godir後的參數文件名在整個源碼目錄中查找,而後切換到該目錄 |
lunch
命令的功能咱們看下lunch
的源碼(加註釋的那種):
function lunch()
{
local answer
# 判斷lunch指令後是否有跟隨參數
if [ "$1" ] ; then
answer=$1
else
# 無參數打印可用項目列表
print_lunch_menu
# 強行推薦一波
echo -n "Which would you like? [aosp_arm-eng] "
# 等待用戶輸入並賦值到 answer
read answer
fi
local selection=
# 若是answer 是空,給個默認值aosp_arm-eng
if [ -z "$answer" ]
then
selection=aosp_arm-eng
# 判斷是不是數字,做者這是把 grep 用到了極致了啊
# grep -q 表示quite模式,有匹配當即返回 0
# grep -e 後面跟隨一個匹配表達式,多個表達式須要多個-e
elif (echo -n $answer | grep -q -e "^[0-9][0-9]*$")
then
# answer 在數組範圍中,就給selection賦值
if [ $answer -le ${#LUNCH_MENU_CHOICES[@]} ]
then
selection=${LUNCH_MENU_CHOICES[$(($answer-1))]}
fi
else
# 不是數字,不爲空,直接使用傳入的參數
selection=$answer
fi
export TARGET_BUILD_APPS=
local product variant_and_version variant version
#從左向右剔除出現第一個`-`及其後面的字符,賦值給 product
product=${selection%%-*} # Trim everything after first dash
#從左向右剔除出現第一個`-`及其前面的字符,賦值給 variant_and_version
variant_and_version=${selection#*-} # Trim everything up to first dash
# 這裏的邏輯是要取出variant 的類型是eng、user、userdebug
if [ "$variant_and_version" != "$selection" ]; then
variant=${variant_and_version%%-*}
# version解析,沒有解析到的話也不會報錯
if [ "$variant" != "$variant_and_version" ]; then
version=${variant_and_version#*-}
fi
fi
# 若是 product 爲空的話,返回錯誤
if [ -z "$product" ]
then
echo
echo "Invalid lunch combo: $selection"
return 1
fi
TARGET_PRODUCT=$product \
TARGET_BUILD_VARIANT=$variant \
TARGET_PLATFORM_VERSION=$version \
# 設置build變量
build_build_var_cache
# 判斷 build_build_var_cache 的返回值,若是不爲0,退出
if [ $? -ne 0 ]
then
return 1
fi
export TARGET_PRODUCT=$(get_build_var TARGET_PRODUCT)
export TARGET_BUILD_VARIANT=$(get_build_var TARGET_BUILD_VARIANT)
if [ -n "$version" ]; then
# 字符串長度不爲0,設置 TARGET_PLATFORM_VERSION
export TARGET_PLATFORM_VERSION=$(get_build_var TARGET_PLATFORM_VERSION)
else
# 字符串長度爲0,取消設置 TARGET_PLATFORM_VERSION
unset TARGET_PLATFORM_VERSION
fi
export TARGET_BUILD_TYPE=release
echo
# 設置一些編譯時的環境,好比輸出路徑、輸出鏡像的序列號等
set_stuff_for_environment
#打印配置信息
printconfig
# 清除本地緩存
destroy_build_var_cache
}
複製代碼
lunch 代碼主要做用是根據用戶輸入或者選擇的產品名來設置與具體產品相關的環境變量。
lunch中賦值的相關變量主要是(以goblin_fact-userdebug
爲例):
TARGET_PRODUCT
:對應goblin_fact
TARGET_BUILD_VARIANT
:對應userdebug
TARGET_BUILD_TYPE
:對應release
執行完lunch命令後,輸出以下:
...
此處顯示add_lunch_combo添加的編譯項目,並標有序列號
...
Which would you like? [aosp_arm-eng]
複製代碼
等待確認使用哪一個項目進行編譯,咱們使用65號goblin_fact-userdebug
項目,環境變量信息輸出以下:
Which would you like? [aosp_arm-eng] 65
============================================
PLATFORM_VERSION_CODENAME=REL
PLATFORM_VERSION=9
TARGET_PRODUCT=goblin_fact
TARGET_BUILD_VARIANT=userdebug
TARGET_BUILD_TYPE=release
TARGET_ARCH=arm
TARGET_ARCH_VARIANT=armv7-a-neon
TARGET_CPU_VARIANT=cortex-a9
HOST_ARCH=x86_64
HOST_OS=linux
HOST_OS_EXTRA=Linux-4.15.0-70-generic-x86_64-Ubuntu-16.04.6-LTS
BUILD_ID=PQ3B.190705.003
OUT_DIR=out
複製代碼
這些環境變量將影響編譯過程。咱們簡單看下變量表明的含義:
PLATFORM_VERSION_CODENAME
:表示平臺版本的名稱,一般是AOSP(Android Open Source Project)
PLATFORM_VERSION
:Android平臺的版本號TARGET_PRODUCT
:所編譯產品的名稱TARGET_BUILD_VARIANT
:編譯產品的類型,可能有eng
、user
、userdebug
TARGET_BUILD_TYPE
:表示編譯的類型,包括release
和debug
。當選擇debug
時會加入一些調試信息。TARGET_ARCH
:編譯目標的CPU架構TARGET_ARCH_VARIANT
:編譯目標的CPU架構版本TARGET_CPU_VARIANT
:編譯目標的CPU架構代號HOST_ARCH
:編譯平臺的架構HOST_OS
:編譯平臺的操做系統HOST_OS_EXTRA
:編譯平臺操做系統的額外信息BUILD_ID
:BUILD_ID
會出如今編譯的版本信息中,能夠利用這個環境變量來定義公司特有標識OUT_DIR
:指定編譯結果的輸出目錄Build系統
的層次關係執行完lunch
命令後,就可使用make
命令來執行編譯Build
。
咱們先從編譯的角度描述下Build系統的功能:
刷機
的各類image
文件。過程當中能夠進行:
知道了功能,那咱們再來看下Build系統
是怎麼實現這些功能的。
執行make
命令後的調用流程:
graph LR
build/envsetup.sh-make-->build/soong/soong_ui.bash
build/soong/soong_ui.bash-->build/soong/ui/build/kati.go
build/soong/ui/build/kati.go --> build/make/core/main.mk
複製代碼
這部分其實和書本上差異挺大的,9.0多了一些東西
Android.mk
轉換成ninja file。Android.bp
轉換成ninja file。Android.bp
是jason形式組織的文件,未來逐步取代Android.mk
咱們這裏明確一點就好,從build/make/core/main.mk
開始,經過include
命令將其它的.mk
文件包含進來,最終造成一個包括全部編譯腳本的集合。這個集合至關於一個巨大的Makefile文件
雖然Makefile看上去很龐大,其實主要由三種內容構成:
Android的Makefile簡要體系以下:
針對上圖橘色標識的三個部分,Build系統會在這裏分別引入具體產品的配置文件AndroidProduct.mk
和BoardConfig.mk
。以及各模塊的編譯文件Android.mk
。
build/make/core/combo
目錄下的mk文件定義了GCC編譯器
的版本和參數。
build/make/core/clang
目錄下的mk文件定義了LLVM編譯器
clang的路徑和參數。
Build系統主要.mk
文件簡介:
文件名 | 說明 |
---|---|
main.mk |
Android Build系統的主控文件。該文件的主要做用是include其餘mk文件,以及定義幾個重要的編譯目標,如droid、sdk、ndk等。同時檢查編譯工具的版本,如make、gcc、javac等。 |
config.mk |
Android Build系統的配置文件,主要定義了許多常量來負責不一樣類型模塊的編譯,定義編譯器參數並引入產品的BoardConfig.mk 文件來配置產品參數,同時也定義了一些編譯工具的路徑,如aapt、mkbooting、javajar等。 |
envsetup.mk |
包含進product_config.mk 文件,並根據其內容設置編譯產品所須要的環境變量,如TARGET_PRODUCT 、TARGET_BUILD_VARIANT 、HOST_OS 等。並檢查變量值的合法性,指定編譯結果的輸出路徑。 |
product_config.mk |
包含進了系統中全部的AndroidProduct.mk 文件,並根據當前產品的配置文件來設置產品編譯相關的變量 |
product.mk |
定義product_config.mk 文件中使用的各類函數 |
definitions.mk |
定義了大量Build系統中使用的函數。 |
dexpreopt.mk |
定義與dex優化有關的路徑和參數 |
Makefile |
定義了系統最終完成編譯完成所須要的各類目標和規則 |
==書中5.0的編譯腳本和9.0比變化太大。。。。。不深究細節實現了,複雜的事情交給腳本吧,先簡單瞭解主要.mk
文件的include關係==
產品配置文件的做用是按照Build系統的要求,將生成產品的各類image文件所須要的配置信息(如版本號、各類參數等)、資源(圖片、字體等)、二進制文件(apk、jar包、so庫等)組織起來,同時能夠靈活增長或刪除一些模塊。
一般產品配置文件放在device目錄下,而vendor目錄下放一些硬件的HAL庫和放第三方廠商的定製代碼。
一般device目錄中分爲如下幾個子目錄:
針對goblin項目,配置文件集中在device/formovie/goblin
路徑下,核心文件包括:vendorsetup.sh
、AndroidProduct.mk
、BoardConfig.mk
。咱們逐個看下。
vendorsetup.sh
前面提到過,vendorsetup.sh
會在初始化編譯環境時被envsetup.sh
文件包含進去。它主要的做用是調用add_lunch_combo
來添加產品名稱。
以goblin爲例,它的vendorsetup.sh
內容以下:
add_lunch_combo goblin-eng
add_lunch_combo goblin-user
add_lunch_combo goblin-userdebug
add_lunch_combo goblin_fact-userdebug
複製代碼
AndroidProduct.mk
AndroidProduct.mk
會在Build系統的product_config.mk
文件中被包含進去,這個文件最重要的做用是定義一個變量PRODUCT_MAKEFILES
,這個變量定義的是本配置目錄中全部編譯的入口文件。
以goblin爲例,它的AndroidProduct.mk
內容以下:
ifeq ($(TARGET_PRODUCT),goblin)
PRODUCT_MAKEFILES := $(LOCAL_DIR)/goblin.mk
else ifeq ($(TARGET_PRODUCT),goblin_fact)
PRODUCT_MAKEFILES := $(LOCAL_DIR)/goblin_fact.mk
複製代碼
根據lunch
的不一樣產品執行不一樣的.mk
文件。
BoardConfig.mk
BoardConfig.mk
會被Build系統的envsetup.sh
包含進去。這個文件主要定義了和設備硬件(包括CPU、WIFI、CPS等)相關的一些參數。咱們看下部分編譯變量的做用:
TARGET_CPU_ABI
:表示CPU的編程接口。好比armeabi-v7a
。==ABI
是啥?下面詳解==TARGET_CPU_ABI2
:表示CPU的編程接口。TARGET_CPU_SMP
:表示CPU是否爲多核CPU。TARGET_ARCH
:定義CPU的架構。好比arm
。TARGET_ARCH_VARIANT
:定義CPU的架構版本。好比:armv7-a-neon
TARGET_CPU_VARIANT
:定義CPU的代號。好比:contex-a9
TARGET_NO_BOOTLOADER
:若是爲true,編譯出的image不包含bootloader。BOARD_KERNEL_BASE
:裝載kernel鏡像時的基地址。好比:0x0
BOARD_KERNEL_OFFSET
:kernel鏡像的偏移地址。好比:0x1080000
BOARD_KERNEL_CMDLINE
:裝載kernel時傳給kernel的命令行參數。好比:--cmdline "root=/dev/mmcblc0p20"
BOARD_MKBOOTIMG_ARGS
:使用mkbooting工具生成boot.img時的參數。好比:--kernel_offset 0x1080000
BOARD_USES_ALSA_AUDIO
:爲true時,表示主板聲音系統使用的是ALSA架構。BOARD_HAVE_BLUETOOTH
:爲true時,表示主板支持藍牙。BOARD_WLAN_DEVICE
:定義WiFi設備名稱。TARGET_BOARD_PLATFORM
:表示主板平臺的型號。好比tl1
TARGET_USERIMAGES_USE_EXT4
:爲true時,表示目標文件系統採用EXT4格式。TARGET_NO_RADIOIMAGE
:爲true時,表示編譯的鏡像中沒有射頻部分。WPA_SPPLICANT_VERSION
:定義 WIFI WPA 的版本。BOARD_WPA_SUPPLICANT_DRIVER
:指定一種WPA_SUPPLICANT
的驅動。BOARD_HOSTAPD_DRIVER
:指定WiFi熱點的驅動。WIFI_DRIVER_FW_PATH_PARAM
:指定WiFi驅動的參數路徑。WIFI_DRIVER_FW_PATH_AP
:定義WiFi熱點firmware文件的路徑。以上是相對核心的變量,Build系統中的變量實在是太多了。。。。
咱們解釋下
ABI
是啥?大部分來自百度百科
首先,咱們看下API
的解釋:應用程序接口(Application Programming Interface,API),又稱爲應用編程接口,就是軟件系統不一樣組成部分銜接的約定。因爲軟件的規模日益龐大,經常須要把複雜的系統劃分紅小的組成部分,編程接口的設計十分重要。程序設計的實踐中,編程接口的設計首先要使軟件系統的職責獲得合理劃分。良好的接口設計能夠下降系統各部分的相互依賴,提升組成單元的內聚性,下降組成單元間的耦合程度,從而提升系統的維護性和擴展性。
ABI
是Application Binary Interface的縮寫,叫作應用程序二進制接口
。不一樣於API
,API
定義了源代碼和庫之間的接口,所以一樣的代碼能夠在支持這個API
的任何系統中編譯 ,然而ABI
容許編譯好的目標代碼在使用兼容ABI
的系統中無需改動就能運行。同時 ABI
掩蓋了各類細節,例如:調用約定控制着函數的參數如何傳送以及如何接受返回值;系統調用的編碼和一個應用如何向操做系統進行系統調用;以及在一個完整的操做系統ABI
中,對象文件的二進制格式、程序庫等等。
它描述了應用程序與OS之間的底層接口。ABI
涉及了程序的各個方面,好比:目標文件格式、數據類型、數據對齊、函數調用約定以及函數如何傳遞參數、如何返回值、系統調用號、如何實現系統調用等。
一套完整的ABI
(好比:Intel Binary Compatibility Standard (iBCS)),可讓程序在全部支持該ABI
的系統上運行,而無需對程序進行修改。
.mk
在device/formovie/goblin
路徑下還有幾個和Build相關的.mk
goblin_fact.mk goblin.mk device.mk
複製代碼
goblin_fact.mk
和goblin.mk
就是咱們在AndroidProduct.mk
中定義的兩個入口文件。而device.mk
主要作下面幾個事情:
咱們看下部份內容:
PRODUCT_DIR := goblin
PRODUCT_NAME := goblin_fact #產品名稱
PRODUCT_DEVICE := goblin #設備名稱,很是關鍵
PRODUCT_BRAND := 產品品牌
PRODUCT_MODEL := 產品型號
PRODUCT_MANUFACTURER := 製造商
# 增長編譯模塊 Settings 和 Launcher
PRODUCT_PACKAGES += \
Settings \
Launcher
# 拷貝一個c++庫到 vendor/lib/ 下
PRODUCT_COPY_FILES += \
device/formovie/goblin/files/libstdc++.so:vendor/lib/libstdc++.so
# include 一個 device.mk 腳本
$(call inherit-product, device/formovie/$(PRODUCT_DIR)/device.mk)
########################################################
# device.mk #
########################################################
# 指定系統的overlay目錄
DEVICE_PACKAGE_OVERLAYS := \
device/formovie/$(PRODUCT_DIR)/overlay
# 設置系統屬性值
PRODUCT_PROPERTY_OVERRIDES += \
persist.sys.usb.config=mtp
# 定義系統支持的分辨率
PRODUCT_AAPT_CONFIG := xlarge hdpi xhdpi tvdpi
PRODUCT_AAPT_PREF_CONFIG := hdpi
複製代碼
上面一些重要的編譯變量解釋以下:
PRODUCT_COPY_FILES
:將編譯目錄下的一個文件複製到目標文件系統,若是文件夾不存在,會自動建立。PRODUCT_PACKAGES
:用來定義產品的模塊列表,全部在模塊列表中定義的模塊都會被編譯進去。PRODUCT_AAPT_CONFIG
:指定系統支持的屏幕密度類型(dip)。所謂支持,是指在編譯時,會將相應的資源文件添加到framework_res.apk
文件中。PRODUCT_AAPT_PREF_CONFIG
:指定系統實際的屏幕密度類型。DEVICE_PACKAGE_OVERLAYS
:很重要的變量,指定了系統的overlay
目錄。系統編譯時會使用overlay
目錄下存放的資源文件替換系統或者末班原有的資源文件。這樣在不改動原生資源文件的狀況下,就能實現產品的個性化。並且overlay
的目錄能夠有多個,它們會按照在變量中的前後順序來替換資源文件,利用這個特性能夠定義公共的overlay
目錄,以及各個產品專屬的overlay
目錄,最大限度的重用資源文件。PRODUCT_PROPERTY_OVERRIDES
:定義系統的屬性值。若是屬性以ro.
開頭,那麼這個屬性是隻讀屬性。一旦設置,屬性將不能改變。若是屬性以persisit.
開頭,當設置這個屬性時,它的值將寫入/data/property
中。eng
、user
和userdebug
eng
類型默認的編譯類型。執行make
至關於執行make eng
。
編譯時會將下列模塊編譯進系統:
Android.mk
中用LOCAL_MODULE_TAGS
定義了標籤:eng
、debug
、shell_$(TARGET_SHELL)
、user
和development
的模塊編譯的系統帶有下列屬性:
ro.secure=0
ro.debuggable=1
ro.kernel.android.checkjni=1
編譯的系統中默認狀況下adb
是打開的
user
類型編譯時會將下面這些模塊編譯進系統:
Android.mk
中用LOCAL_MODULE_TAGS
定義了標籤:shell_$(TARGET_SHELL)
和user
的模塊編譯後的系統屬性:
ro.secure=1
ro.debuggable=0
編譯的系統缺省狀況下adb
是不可用的,須要在設置中手動打開
userdebug
類型編譯時會將下列模塊編譯進系統:
Android.mk
中用LOCAL_MODULE_TAGS
定義了標籤:debug
、shell_$(TARGET_SHELL)
和user
的模塊編譯後的系統屬性:
ro.secure=1
ro.debuggable=1
編譯的系統缺省狀況下adb
是不可用的,須要在設置中手動打開
image
文件Android 編譯完成後會生成一個image
文件,包括:
boot.img
:包含經過 mkbootimg
組合在一塊兒的內核映像和 RAM 磁盤system,img
:主要包含 Android 框架recovery.img
:用於存儲在 OTA 過程當中啓動的恢復映像。cache.img
==:用於存儲臨時數據,若是設備使用 A/B 更新,則能夠不要此分區。misc.img
==:供恢復映像使用,存儲空間不能小於 4KB。userdata.img
:包含用戶安裝的應用和數據,包括自定義數據。metadata.img
==:若是設備被加密,則須要使用 metadata 分區,該分區的存儲空間不能小於 16MB。vendor.img
==:包含全部不可分發給 Android 開源項目 (AOSP) 的二進制文件。若是沒有專有信息,則能夠省略此分區。(應該是第三方廠商的相關數據)radio.img
==:包含無線裝置映像。只有包含無線裝置且在專用分區中包含無線裝置專用軟件的設備才須要此分區。tos.img
==:用於存儲 Trusty 操做系統的二進制映像文件,僅在設備包含 Trusty 時使用。黃色標識是書中未說起的,簡單記錄下。
boot.img
boot.img
是Android自定義的文件格式。該格式包括了一個2*1024
的文件頭,頭文件後面使用gzip壓縮過的kernel映像,後面是一個ramdisk映像,最後是一個載入器程序(可選部分)。
boot.img
結構以下:
- kernel.img
- ramdisk.img
-/
- init.rc
- init
- etc -> /system/etc
- system/ (mount point)
- vendor/ (mount point)
- odm/ (mount point)
...
複製代碼
boot.img
以page
爲單位存放數據,編譯時經過BOARD_KERNEL_PAGESIZE
指定。一般值爲2048。
ramdisk.img
是一個小型文件系統,它包括了初始化Linux系統所須要的所有核心文件。
recovery.img
recovery.img
至關於一個小型文本界面的Linux系統,它有本身的內核和文件系統。
recovery.img
的做用是恢復或升級系統。所以,在sbin目錄下會有一個recovery程序;同時也包括adbd
和系統配置文件init.rc
。不過這幾個文件和boot.img中的不相同的。
system.img
system.img
就是設備中system目錄的鏡像,裏面包含了Android系統的主要目錄和文件。目錄包括:
- system.img
-/
- app #存放通常的apk文件
- bin #存放一些Linux的工具,大部分是toolbox的連接
- etc #存放系統的配置文件
- fonts #存放系統的字體文件
- framework #存放系統平臺的全部jar包和資源文件包
- lib #存放系統的共享庫
- media #存放系統的多媒體資源,主要是鈴聲。
- priv-app #存放系統核心的apk文件
- tts #存放系統的語音合成文件
- usr #存放各類鍵盤佈局、時間區域文件
- vendor #存放一些第三方廠商的配置文件(現有版本已經拎出來做爲一個單獨的分區了)
- xbin #存放系統管理工具,至關於標準Linux文件系統中的sbin
- build.prop #系統屬性的定義文件
複製代碼
userdata.img
userdata.img
是設備中/data
目錄的鏡像,初始時通常不包含任何文件。在Android系統初始化時會在/data
目錄下建立一些子目錄和文件。
儘量大的內存
SSD提高文件讀寫速度
編譯優化,增長緩存。
export USE_CACHE = 1
export CCACHE_DIR=/<path_of_your_choice>/.ccache
prebuilts/misc/linux-x86/ccache/ccache -M 50G
複製代碼
須要注意的是CCCache並不能提升第一次編譯的速度。原理是將一些系統庫的編譯結果緩存起來,下次編譯時若是檢測到庫沒有變化,就直接使用緩存的文件(印象中坑比較多)。
操做以下:
. build/envsetup.sh
lunch sdk-eng
make
複製代碼
編譯完成後
emulator
複製代碼
對於x86的PC,啓動模擬器以前,能夠先執行位於SDK的extras/intel/Hardware_Accelerated_Execution_Manager
下的intelhaxm
來提高運行速度。
和Android相關的編譯變量到底有哪些,都是幹啥的,官方講解在
build/make/core/build-system.html
中。 迷茫的時候就看官方解釋吧。
Android 中的各類模塊,不管是apk應用、可執行程序仍是jar包,均可以經過Build系統編譯生成。在每一個模塊的源碼目錄下,都有一個Android.mk
文件,裏面包含了模塊代碼的位置、模塊的名稱、須要連接的動態庫等一系列的定義。
以Settings爲例:
# 設置LOCAL_PATH爲當前目錄
LOCAL_PATH:= $(call my-dir)
# 清除"LOCAL_PATH"外,全部的"LOCAL_"變量
include $(CLEAR_VARS)
######上面這部分每一個module直接抄過來就好######
# 指定模塊的名稱
LOCAL_PACKAGE_NAME := Settings
# 爲true時,會使用sdk的hide的api來編譯
LOCAL_PRIVATE_PLATFORM_APIS := true
# 指定模塊簽名使用platform簽名
LOCAL_CERTIFICATE := platform
# 爲true時表示apk將安裝到priv-app下
LOCAL_PRIVILEGED_MODULE := true
# 定義模塊的標籤爲optional
LOCAL_MODULE_TAGS := optional
# 使用AAPT2優化版本
LOCAL_USE_AAPT2 := true
# 指定依賴的共享 Java 類庫
LOCAL_JAVA_LIBRARIES := \
bouncycastle \
telephony-common \
ims-common
# 指定依賴的靜態 Java 類庫
LOCAL_STATIC_JAVA_LIBRARIES := \
android-arch-lifecycle-runtime \
android-arch-lifecycle-extensions \
guava \
jsr305 \
settings-logtags \
# 聲明調用android的包
LOCAL_STATIC_ANDROID_LIBRARIES := \
android-slices-builders \
android-slices-core \
android-slices-view \
android-support-compat \
android-support-v4 \
android-support-v13 \
android-support-v7-appcompat \
android-support-v7-cardview \
android-support-v7-preference \
android-support-v7-recyclerview \
android-support-v14-preference \
# 定義源文件列表
LOCAL_SRC_FILES := \
$(call all-logtags-files-under, src)
# 指定混淆的標誌
LOCAL_PROGUARD_FLAG_FILES := proguard.flags
# 指定編譯模塊類型爲 APK
include $(BUILD_PACKAGE)
# 將源碼目錄下其他的Android.mk都包含進來
include $(call all-makefiles-under,$(LOCAL_PATH))
複製代碼
Android.mk
文件能編譯出不一樣的模塊,是經過include
某個模塊編譯文件來實現的。好比編譯apk時的指令:
include $(BUILD_PACKAGE)
複製代碼
通過一系列的grep
,發現這些模塊編譯變量都是在build/make/core/config.mk
文件中定義的。咱們看下部分變量的聲明(不少都沒見過。。。用到的時候再查吧):
# ###############################################################
# Build system internal files
# ###############################################################
# 清除Local_變量
CLEAR_VARS:= $(BUILD_SYSTEM)/clear_vars.mk
# 產生編譯平臺使用的本地靜態庫
BUILD_HOST_STATIC_LIBRARY:= $(BUILD_SYSTEM)/host_static_library.mk
# 產生編譯平臺使用的本地共享庫
BUILD_HOST_SHARED_LIBRARY:= $(BUILD_SYSTEM)/host_shared_library.mk
# 產生目標平臺使用的本地靜態庫
BUILD_STATIC_LIBRARY:= $(BUILD_SYSTEM)/static_library.mk
BUILD_HEADER_LIBRARY:= $(BUILD_SYSTEM)/header_library.mk
BUILD_AUX_STATIC_LIBRARY:= $(BUILD_SYSTEM)/aux_static_library.mk
BUILD_AUX_EXECUTABLE:= $(BUILD_SYSTEM)/aux_executable.mk
# 產生編譯平臺使用的共享庫
BUILD_SHARED_LIBRARY:= $(BUILD_SYSTEM)/shared_library.mk
# 產生目標系統使用的Linux可執行程序
BUILD_EXECUTABLE:= $(BUILD_SYSTEM)/executable.mk
# 產生編譯平臺下可以使用的可執行程序
BUILD_HOST_EXECUTABLE:= $(BUILD_SYSTEM)/host_executable.mk
# 產生apk文件
BUILD_PACKAGE:= $(BUILD_SYSTEM)/package.mk
BUILD_PHONY_PACKAGE:= $(BUILD_SYSTEM)/phony_package.mk
BUILD_RRO_PACKAGE:= $(BUILD_SYSTEM)/build_rro_package.mk
BUILD_HOST_PREBUILT:= $(BUILD_SYSTEM)/host_prebuilt.mk
# 定義預編譯模塊目標,目的是將預編譯模塊引入系統
BUILD_PREBUILT:= $(BUILD_SYSTEM)/prebuilt.mk
# 定義多個預編譯模塊目標
BUILD_MULTI_PREBUILT:= $(BUILD_SYSTEM)/multi_prebuilt.mk
# 產生Java共享庫
BUILD_JAVA_LIBRARY:= $(BUILD_SYSTEM)/java_library.mk
# 產生Java靜態庫
BUILD_STATIC_JAVA_LIBRARY:= $(BUILD_SYSTEM)/static_java_library.mk
# 產生用於編譯平臺下的Java共享庫
BUILD_HOST_JAVA_LIBRARY:= $(BUILD_SYSTEM)/host_java_library.mk
BUILD_DROIDDOC:= $(BUILD_SYSTEM)/droiddoc.mk
BUILD_APIDIFF:= $(BUILD_SYSTEM)/apidiff.mk
# 用來將 LOCAL_COPY_HEADERS 變量定義的文件拷貝到 LOCAL_COPY_HEADERS_TO 定義的路徑中
BUILD_COPY_HEADERS := $(BUILD_SYSTEM)/copy_headers.mk
BUILD_NATIVE_TEST := $(BUILD_SYSTEM)/native_test.mk
BUILD_NATIVE_BENCHMARK := $(BUILD_SYSTEM)/native_benchmark.mk
BUILD_HOST_NATIVE_TEST := $(BUILD_SYSTEM)/host_native_test.mk
BUILD_FUZZ_TEST := $(BUILD_SYSTEM)/fuzz_test.mk
BUILD_HOST_FUZZ_TEST := $(BUILD_SYSTEM)/host_fuzz_test.mk
複製代碼
因此 include $(BUILD_PACKAGE)
其實就是
include build/make/core/package.mk
複製代碼
Google爸爸很貼心哈。
感興趣的同窗能夠深刻研究下這些.mk
文件喲。
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
# 指定依賴的共享Java庫
LOCAL_JAVA_LIBRARIES :=
# 指定依賴的靜態Java庫
LOCAL_STATIC_JAVA_LIBRARIES :=
# 指定模塊的標籤
LOCAL_MODULE_TAGS := optional/user/userdebug/eng
# 指定模塊的名稱
LOCAL_PACKAGE_NAME := apkName
# 指定模塊的簽名方式
LOCAL_CERTIFICATE := platform
# 不使用代碼混淆的工具進行代碼混淆
LOCAL_PROGUARD_ENABLED:= disabled
# 使用hide api編譯
LOCAL_PRIVATE_PLATFORM_APIS := true
# 安裝在priv-app目錄下
LOCAL_PRIVILEGED_MODULE := true
# 指定 AndroidManifest.xml 文件路徑
LOCAL_MANIFEST_FILE := app/src/main/AndroidManifest.xml
# 指定資源路徑
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/app/src/main/res
# 指定源碼路徑
LOCAL_SRC_FILES := \
$(call all-java-files-under, src)
include $(BUILD_PACKAGE)
複製代碼
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
# 編譯模塊的標籤
LOCAL_MODULE_TAGS := optional/user/userdebug/eng
# 編譯模塊的名稱
LOCAL_MODULE := java_lib_name
# 源碼編譯路徑
LOCAL_SRC_FILES := $(call all-java-files-under,java)
include $(BUILD_JAVA_LIBRARY)
複製代碼
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
# 編譯模塊的名稱
LOCAL_MODULE := java_static_lib_name
# 源碼編譯路徑
LOCAL_SRC_FILES := $(call all-java-files-under,java)
include $(BUILD_STATIC_JAVA_LIBRARY)
複製代碼
資源部也是一個 apk 文件,可是沒有代碼。
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
# 使用 AAPT2 打包資源
LOCAL_USE_AAPT2 := true
# 值爲true的話,說明手動寫依賴的jar包了。不管是系統的仍是用戶本身的
LOCAL_NO_STANDARD_LIBRARIES := true
# 指定簽名類型
LOCAL_CERTIFICATE := platform
# 指定 AAPT 工具參數(如今使用AAPT2優化版本)
LOCAL_AAPT_FLAGS := -x
# 定義模塊名字
LOCAL_PACKAGE_NAME := java_res_name_lib
# 定義模塊標籤爲 user
LOCAL_MODULE_PATH := user
# 定義模塊編譯後的輸出路徑
LOCAL_MODULE_PATH := $(TARGET_OUT_JAVA_LIBRARIES)
# 值爲true時,其餘的apk能夠引用本模塊的資源
LOCAL_EXPORT_PACKAGE_RESOURCES := true
include $(BUILD_PACKAGE)
複製代碼
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
# 定義源碼路徑
LOCAL_SRC_FIFLES := Executor.cpp
# 指定模塊須要連接的動態庫
LOCAL_SHARE_LIBRARIES := libutils libbinder
# 定義編譯標誌
ifeq ($(TARGET_OS),linux)
LOCAL_CFLAGS += -DXP_UNIX
endif
# 指定模塊的名稱
LOCAL_MODULE := executor_name
include $(BUILD_EXECUTABLE)
複製代碼
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional/user/userdebug/eng
LOCAL_MODULE := lib_native_share_name
# 指定須要編譯的源文件
LOCAL_SRC_FIFLES := Native.cpp
# 指定模塊須要連接的靜態庫
LOCAL_SHARE_LIBRARIES := \
libutils \
libbinder
# 指定模塊依賴的靜態庫
LOCAL_STATIC_LIBRARIES := lib_native_static_name
#指定頭文件查找路徑
LOCAL_C_INCLUDES += \
$(JNI_H_INCLUDE) \
$(LOCAL_PATH)/../include
# 定義編譯標誌
LOCAL_CFLAGS += -O
include $(BUILD_SHARED_LIBRARY)
複製代碼
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional/user/userdebug/eng
LOCAL_MODULE := lib_native_static_name
# 指定模塊的源文件
LOCAL_SRC_FIFLES := \
NativeStatic.cpp
# 指定頭文件查找路徑
LOCAL_C_INCLUDES:= \
# 定義編譯標誌
LOCAL_CFLAGS :=-D_ANDROID_
include $(BUILD_STATIC_LIBRARY)
複製代碼
在實際開發中,有不少apk、文件、jar包等都是預先編譯好的。編譯系統時須要將這些二進制文件複製到生成的image文件中。
經常使用的方法是經過PRODUCT_COPY_FILES
變量將文件複製到生成的image文件中,像這樣:
PRODUCT_COPY_FILES += \
$(LOCAL_PATH)/media/autovideo_265.mkv:/system/factory/autovideo_265.mkv
複製代碼
可是對於apk、或jar包,須要使用系統的簽名才能正常運行;另一些動態庫文件多是源碼中某些模塊依賴的。這樣用上述的方式就行不通了。
Android 提供了預編譯模塊的方法來對策這個問題,大致流程以下:
LOCAL_SRC_FILES
指定二進制文件(apk或jar包)的路徑LOCAL_MODULE_CLASS
指定編譯模塊的類型include $(BUILD_PREBUILT)
執行預編譯LOCAL_MODULE_CLASS
能夠指定哪些類型呢?首先,Google爸爸說:
LOCAL_MODULE_CLASS
Which kind of module this is. This variable is used to construct other variable names used to locate the modules. See base_rules.mk and envsetup.mk.
複製代碼
那咱們就從這兩個文件看下
base_rules.mk:
EXECUTABLES
SHARED_LIBRARIES
STATIC_LIBRARIES
NATIVE_BENCHMARK
HEADER_LIBRARIES
NATIVE_TESTS
JAVA_LIBRARIES
APPS
複製代碼
不過envsetup.mk
並無明確的聲明。
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := Factory
LOCAL_SRC_FILES := release/factory_test-release.apk
LOCAL_PRIVILEGED_MODULE := true
LOCAL_MODULE_CLASS := APPS
LOCAL_CERTIFICATE := platform
LOCAL_MODULE_TAGS := optional
include $(BUILD_PREBUILT)
複製代碼
LOCAL_
變量再次說明,變量含義這部分在官方
build/make/core/build-system.html
都有說明哈,那裏的更專業和詳細。
上面的模塊編譯中用到了那麼多LOCAL_
變量,方便查找先把經常使用的幾個記錄一下:
變量名 | 說明 |
---|---|
LOCAL_ASSET_FILES |
編譯 APK 文件時用於指定資源列表,一般寫成LOCAL_ASSET_FILES += $(call find-subdir-assets) |
LOCAL_CC |
自定義C編譯器來替換默認的編譯器 |
LOCAL_CXX |
自定義C++編譯器來替換默認的編譯器 |
LOCAL_CFLAGS |
定義額外的C/C++編譯器參數 |
LOCAL_CPPFLAGS |
定義額外的C++編譯器參數,不用在C編譯器中 |
LOCAL_CPP_EXTENSION |
自定義C++源文件的後綴。例如:LOCAL_CPP_EXTENSION := .cc 。一旦定義,模塊中的全部源文件都必須使用改後綴,目前不支持混合使用。 |
LOCAL_C_INCLUDES |
指定頭文件的搜索路徑。 |
LOCAL_FORCE_STATIC_EXECUTABLE |
若是編譯時須要連接的庫有共享和靜態兩種。此變量爲true時會優先連接靜態庫。一般這種狀況只會在編譯root/sbin 目錄下的應用纔會用到,由於它們執行時間比較早,文件系統的其餘部分尚未加載 |
LOCAL_GENERATED_SOURCES |
Files that you add to LOCAL_GENERATED_SOURCES will be automatically generated and then linked in when your module is built.(書中寫的不時很理解,直接上原文) |
LOCAL_MODULE_TAGS |
定義模塊標籤,Build系統根據標籤來決定哪些模塊須要編譯。 |
LOCAL_REQUIRED_MODULES |
指定依賴的模塊,一旦當前模塊被安裝,經過此變量指定的模塊也會被安裝 |
LOCAL_JAVACFLAGS |
定義額外的javac編譯器的參數 |
LOCAL_JAVA_LIBRARIES |
指定模塊依賴的Java共享庫 |
LOCAL_LDFLAGS |
定義鏈接器ld的參數 |
LOCAL_LDLIBS |
指定模塊鏈接時依賴的庫。若是庫文件不存在,並不會引起它們的編譯。(LOCAL_LDLIBS allows you to specify additional libraries that are not part of the build for your executable or library. Specify the libraries you want in -lxxx format; they're passed directly to the link line. However, keep in mind that there will be no dependency generated for these libraries. It's most useful in simulator builds where you want to use a library preinstalled on the host. The linker (ld) is a particularly fussy beast, so it's sometimes necessary to pass other flags here if you're doing something sneaky. Some examples:LOCAL_LDLIBS += -lcurses -lpthread ,LOCAL_LDLIBS += -Wl,-z,origin |
LOCAL_NO_MANIFEST |
爲true時,表示軟件包中沒有AndroidManifest.xml。(資源包中經常使用) |
LOCAL_PACKAGE_NAME |
指定APP應用名稱 |
LOCAL_PATH |
指定Android.mk 文件所在目錄 |
LOCAL_POST_PROCESS_COMMAND |
在編譯host相關模塊時,能夠用此變量定義一條命令在link完成後執行 |
LOCAL_PREBUILT_LIBS |
指定預編譯C/C++動態和靜態庫列表,用於預編譯模塊 |
LOCAL_PREBUILT_JAVA_LIBRARIES |
指定預編譯Java庫列表,用於預編譯模塊 |
LOCAL_SHARED_LIBRARIES |
指定模塊依賴的C/C++共享庫列表 |
LOCAL_SRC_FILES |
指定源文件列表 |
LOCAL_STATIC_LIBRARIES |
指定模塊依賴的C/C++靜態庫列表 |
LOCAL_MODULE |
除apk模塊使用LOCAL_PACKAGE_NAME 指定模塊名外,其他的模塊都使用LOCAL_MODULE 指定模塊名 |
LOCAL_MODULE_PATH |
指定模塊在目標系統的安裝路徑。可執行文件和動態庫模塊通常會搭配LOCAL_UNSTRIPPED_PATH 使用 |
LOCAL_UNSTRIPPED_PATH |
指定模塊的unstrpped版本在out目錄下保存的路徑 |
LOCAL_ADDITIONAL_DEPENDENCIES |
模塊須要依賴其餘任何實際未內置的模塊,則能夠將這些make目標添加到 LOCAL_ADDITIONAL_DEPENDENCIES 。一般,這是針對其餘一些不會自動建立的依賴項的解決方法。 |
LOCAL_MODULE_CLASS |
定義模塊的分類。根據分類,生成的模塊會被安裝到對應的目錄下。例如:APPS,安裝到/system/app;SHARED_LIBRARIES ,安裝到/system/lib;EXECUTABLE,安裝到/system/bin下;ETC,安裝到/system/etc;可是若是同時使用LOCAL_MODULE_PATH 定義了路徑,則安裝到該路徑。 |
LOCAL_MODULE_NAME |
指定模塊名 |
LOCAL_MODULE_SUFFIX |
指定當前模塊的後綴。一旦指定,系統在產生目標文件時,會以模塊名+後綴來建立 |
LOCAL_STRIP_MODULE |
指定模塊是否須要strip,通常在編譯可執行文件和動態庫時才使用該變量 |
在Android系統中,全部安裝到系統中的apk都須要簽名,所謂簽名就是給應用附加一個數字證書。雖然數字證書有不少用途,可是在Android中,它惟一的做用就是代表製做者的身份。
Android 使用Java工具keytool生成數字證書,命令以下:
keytool -genkey -v -keystore lee.keystore -alias lee.keystore -keyalg RSA -validity 30000
複製代碼
參數解釋:
-keystore lee.keystore
表示證書文件名-alias lee.keystore
表示證書的別名-keyalg RSA
表示採用的加密算法-validity 30000
表示證書的有效期執行命令後,終端提示須要設置輸入的信息以下:
keystore
的密碼,並重復輸入確認。yes
確認信息。key
的密碼,並重復輸入確認。keystore
)的密碼。用指令查看下剛纔建立的證書容器:
keytool -list -keystore lee.keystore
複製代碼
此時,就須要輸入第一個設置的密碼了,輸入後打印以下:
lijie@leeMBP:~$ keytool -list -keystore lee.keystore
輸入密鑰庫口令:
密鑰庫類型: jks
密鑰庫提供方: SUN
您的密鑰庫包含 1 個條目
lee.keystore, 2020-7-19, PrivateKeyEntry,
證書指紋 (SHA1): BD:6A:85:18:22:34:91:25:F9:38:1F:CB:A2:BA:EC:91:78:3C:67:9F
複製代碼
簽名命令
jarsigner -verbose -keystore lee.keystore -signedjar android-release-signed.apk android-release-unsigned.apk lee.keystore
複製代碼
-verbose
表示要打印簽名過程-keystore
用來指定 keystore 文件-signedjar
指定簽名後的apk名稱android-release-unsigned.apk
待簽名文件lee.keystore
是證書的別名執行過程遇到了以下錯誤:
lijie@leeMBP:~/Desktop$ jarsigner -verbose -keystore ~/lee.keystore -signedjar Feng-Factory-signed.apk Feng-Factory.apk lee.keystore
輸入密鑰庫的密碼短語:
輸入lee.keystore的密鑰口令:
jarsigner: 沒法對 jar 進行簽名: java.util.zip.ZipException: invalid entry compressed size (expected 26171 but got 26705 bytes)
複製代碼
由於Feng-Factory.apk是被簽過名的,此時須要把META-INF
文件夾刪掉(去掉舊的簽名文件),再次執行簽名,OK了。輸出以下:
....
>>> 簽名者
X.509, CN=lee, OU=hualee, O=hualee.top, L=PK, ST=PK_HD, C=CN
[可信證書]
jar 已簽名。
警告:
簽名者證書爲自簽名證書
複製代碼
簽名完成後,咱們來查看下簽名信息:
jarsigner -verify -verbose -certs Feng-Factory-signed.apk
複製代碼
會獲得以下返回:
....
- 由 "CN=lee, OU=hualee, O=hualee.top, L=PK, ST=PK_HD, C=CN" 簽名
摘要算法: SHA-256
簽名算法: SHA256withRSA, 2048 位密鑰
jar 已驗證。
複製代碼
通過簽名修改後,在應用發佈前還須要進行APK對齊
,命令以下:
zipalign -f -v infile.apk outfile.apk
複製代碼
APK對齊
第一次接觸。。。。。根據書中的描述,是爲了提高資源訪問效率。
對齊後的文件可使用以下指令檢查:
zipalign -c -v 4 test.apk
複製代碼
Android 系統簽名的方法和應用簽名不大同樣。在build/make/target/product/security
目錄中存放着4組後綴名爲.x509.pem
和.pk8
的文件,以下:
- security
- Android.mk
- README
- media.pk8
- media.x509.pem
- platform.pk8
- platform.x509.pem
- shared.pk8
- shared.x509.pem
- testkey.pk8
- testkey.x509.pem
- verity.pk8
- verity.x509.pem
- verity_key
複製代碼
*.pk8
文件是私鑰*.x509.pem
文件是公鑰須要使用源碼中的development/tools/make_key
命令。
經過make_key
生成系統簽名指令以下:
make_key testkey '/C=CN/ST=PK/L=OK_HD/O=hualee/OU=hualee.top/CN=lee/emailAddress=lee@mail.com'
複製代碼
media
、platform
、shared
、testkey
經過make_key
生成的簽名文件,
若是全部產品都想使用同一種簽名,能夠直接用這些文件覆蓋掉build/make/target/product/security
目錄下的文件。
若是不一樣產品還須要不一樣的簽名,能夠新建一個目錄,經過編譯變量PRODUCT_DEFAULT_DEV_CERTIFICATE
來指定。例如:
PRODUCT_DEFAULT_DEV_CERTIFICATE := # your security files path
複製代碼
而在Build系統編譯時,簽名是自動完成的。Build中的簽名是經過signapk.jar
完成的,指令以下:
java -jar signapk.jar platform.x509.pem platform.pk8 test.apk test_signed.apk
複製代碼
當咱們開發系統應用時,爲了方便調試,可能須要在AS中添加系統簽名文件。
下面記錄下如何生成系統簽名的keystore
文件:
取源碼目錄中build/make/target/product/security
取platform.pk8
platform.x509.pem
放到一個目錄下
生成shared.priv.pem
,指令: openssl pkcs8 -in platform.pk8 -inform DER -outform PEM -out shared.priv.pem -nocrypt
生成pkcs12
,指令:openssl pkcs12 -export -in platform.x509.pem -inkey shared.priv.pem -out shared.pk12 -name androiddebugkey
生成debug.keystore
,指令:keytool -importkeystore -deststorepass android -destkeypass android -destkeystore debug.keystore -srckeystore shared.pk12 -srcstoretype PKCS12 -srcstorepass android -alias androiddebugkey
Android Build系統終於看到了一個大概,雖然Google早就已經使用Soong
來進行系統構建了,可是Android的歷史遺留問題多滴很那。一步一步來吧,原理啥的應該都仍是共同的。
寫完本篇,瞭解了Android系統的龐大,之前看不懂的include
倒也瞭然了一些,哈哈哈,加油!
奧奧,還有個事情,想知道源碼怎麼下載麼?在圖片下面喲:
不謝 ( ̄_, ̄ )