Android系統移植與平臺開發(五)- 編譯Android原始碼
2.3編譯Android原始碼
Android原始碼體積非常龐大,由Dalvik虛擬機器、Linux核心、編譯系統、框架程式碼、Android定製C庫、測試套件、系統應用程式等部分組成,在編譯Android原始碼之前,必須要先掌握Android原始碼的組成。
2.3.1Android原始碼目錄結構
在Android原始碼中,按照不同功能程式碼被放在不同的目錄下:
目錄 |
描述 |
bionic |
針對Android系統定製的仿生標準C庫、連結器等所在目錄,Android系統並沒有使用Linux的glibc庫,bioinc C庫針對嵌入式系統做了優化,添加了一些Android特定的函式API |
bootable |
Android系統引導啟動程式碼,用來引導系統、更新系統、恢復系統。 |
build |
Android的編譯系統目錄,裡面包含大量的Makefile,用來編譯目標系統、Host主機開發環境等。 |
cts |
相容性測試工具目錄。 |
dalvik |
Dalvik虛擬機器,Android系統得以執行的虛擬執行環境。 |
development |
程式開發所需要的模板和工具。 |
external |
Android系統使用的其它開原始碼目錄,如jpeg圖片解碼開源庫、opencore開原始碼等。 |
frameworks |
框架層程式碼,frameworks/base目錄下存放目標系統的框架庫,frameworks/policies/base下存放應用程式框架程式碼。 |
hardware |
HAL(Hardware Abstraction Layer)硬體抽象層程式碼。 |
kernel |
Linux核心目錄,預設下載的Android原始碼裡沒有,需單獨下載。 |
packages |
Android系統級應用程式原始碼目錄,如攝像應用、電話應用等。 |
prebuilt |
主機編譯工具目錄,如arm-linux-gcc交叉系統工具鏈等。 |
sdk |
SDK及模擬器。 |
system |
init程序、藍芽、無線 |
devices |
廠商裝置配置目錄,針對不同裝置,由不同的子目錄來分別管理,用來裁剪實現不同裝置上Android目標系統。 |
在external目錄下存放著大量的外部開原始碼:
外部開源專案 |
描述 |
外部開源專案 |
描述 |
aes |
AES加密 |
libxml2 |
xml解析庫 |
apache-http |
網頁伺服器 |
make |
|
asm |
netbeans-visual |
||
bluez |
藍芽相關、協議棧 |
netcat |
simple Unix utility which reads and writes dataacross network connections |
ccache |
netperf |
網路效能測量工具 |
|
clearsilver |
neven |
看程式碼和JNI相關 |
|
dbus |
低延時、低開銷、高可用性的IPC機制 |
opencore |
多媒體框架 |
dhcpcd |
DHCP服務 |
openssl |
SSL加密相關 |
dropbear |
SSH2的server |
oprofile |
OProfile是Linux核心支援的一種效能分析機制 |
eclipse |
ppp |
pppd撥號命令,好像還沒有chat |
|
elfcopy |
複製ELF的工具 |
protobuf |
a flexible, efficient, automated mechanism for serializing structured data |
elfutils |
ELF工具 |
qemu |
arm模擬器 |
embunit |
Embedded Unit Project |
safe-iop |
functions for performing safe integer operations |
emma |
java程式碼覆蓋率統計工具 |
sdl |
|
esd |
Enlightened Sound Daemon,將多種音訊流混合在一個裝置上播放 |
skia |
skia圖形引擎 |
expat |
Expat is a stream-oriented XML parser |
sonivox |
sole MIDI solution for Google Android Mobile Phone Platform |
fdlibm |
FDLIBM (Freely Distributable LIBM) |
sqlite |
資料庫 |
Flex |
srec |
Nuance 公司提供的開源連續非特定人語音識別 |
|
freetype |
字型庫 |
strace |
trace工具 |
gdata |
google的無線資料相關 |
swing-worker |
|
diflib |
swt |
||
googleclient |
google使用者庫 |
tagsoup |
TagSoup是一個Java開發符合SAX的HTML解析器 |
icu4c |
ICU(International Component for Unicode)在C/C++下的版本 |
tcpdump |
抓TCP包的軟體 |
iptables |
防火牆 |
tinyxml |
TinyXml is a simple, small, C++ XML parser |
Jdiff |
generate a report describing the difference between two public Java APIs |
toolchain |
|
jfreechart |
tremor |
I stream and file decoder provides an embeddable,integer-only library |
|
jpeg |
jpeg庫 |
webkit |
瀏覽器核心 |
kxml2 |
wpa_supplicant |
無線網絡卡管理 |
|
libffi |
libffi is a foreign function interface library. |
yaffs2 |
yaffs檔案系統 |
libpcap |
網路資料包捕獲函式 |
zlib |
a general purpose data compression library |
在packages/app目錄下存放著大量系統級應用程式,我們可以拿到這些應用程式程式碼分析、理解,編寫出效率更高,效能更好的應用:
系統應用程式 |
描述 |
AlarmClock |
鬧鐘 |
Browser |
瀏覽器 |
Calculator |
計算器 |
Calendar |
日曆 |
Camera |
攝像頭 |
Contacts |
聯絡人 |
|
郵件 |
GoogleSearch |
Google搜尋 |
HTML Viewer |
瀏覽器附屬介面,被瀏覽器應用呼叫,同時提供儲存記錄功能 |
IM |
即時通訊,為手機提供訊號傳送、接收、通訊的服務 |
Launcher |
Android的桌面 |
Mms |
彩信業務 |
Music |
音樂播放器 |
PackageInstaller |
應用程式安裝、解除安裝器 |
Phone |
電話應用 |
Settings |
系統設定 |
SoundRecorder |
錄音機 |
Stk |
簡訊接收和傳送 |
Sync |
同步資料 |
Updater |
更新 |
VoiceDialer |
語音識別通話 |
在package/providers目錄下存放的是系統級內容提供器(Content Provider):
系統內容提供器 |
描述 |
CalendarProvider |
日曆提供器 |
ContactsProvider |
聯絡人提供器 |
DownloadProvider |
下載管理提供器 |
DrmProvider |
DRM受保護資料儲存服務,建立和更新資料庫時呼叫 |
GoogleContactsProvider |
谷歌聯絡人提供器 |
GoogleSubscribedFeedsProvider |
Google同步功能 |
ImProvider |
即時通訊提供器 |
MediaProvider |
媒體提供器、提供儲存資料 |
SettingsProvider |
系統設定提供器 |
SubscribedFeedsProvider |
|
TelephonyProvider |
彩信提供器 |
2.3.2編譯Android
按照Android官方網站給出的步驟,編譯Android原始碼過程如下:
Ø 初始化編譯環境
在編譯Android之前,編譯系統需要載入一些編譯指令碼命令到環境變數中,通過下面的指令來初始化編譯環境:
$ sourcebuild/envsetup.sh
在執行完上述命令後,可以通過執行help命令來檢視所有載入的命令。
$ help
Invoke ". build/envsetup.sh" from your shell to add the following functions to your environment:
- croot: Changes directory to the top of the tree.
- m: Makes from the top of the tree.
- mm: Builds all of the modules in the current directory.
- mmm: Builds all of the modules in the supplied directories.
- cgrep: Greps on all local C/C++ files.
- jgrep: Greps on all local Java files.
- resgrep: Greps on all local res/*.xml files.
- godir: Go to the directory containing a file.
Look at the source to view more functions. The complete list is:
add_lunch_combo cgrep check_product check_variant choosecombo chooseproduct choosetype choosevariant cproj croot findmakefile gdbclient get_abs_build_var getbugreports get_build_var getprebuilt gettop godir help isviewserverstarted jgrep lunch m mm mmm pid printconfig print_lunch_menu resgrep runhat runtest setpaths set_sequence_number set_stuff_for_environment settitle smoketest startviewserver stopviewserver systemstack tapas tracedmdump
常用指令碼命令:
指令碼命令 |
描述 |
Help |
幫助資訊,列印所有命令 |
add_lunch_combo |
新增新目標編譯項 |
print_lunch_menu |
列印所有目標編譯項 |
lunch |
選擇目標編譯項 |
m |
從原始碼樹頂級目錄向下編譯原始碼,相當於執行make |
mm |
從當前目錄向下編譯原始碼 |
mmm |
從指定目錄向下編譯原始碼,通常用來編譯某個模組 |
cgrep |
從所有的C,C++檔案裡查詢指定字串 |
jgrep |
從所有的Java檔案裡查詢指定字串 |
Ø 選擇編譯選項
由於Android原始碼是一個開源的系統,然要匹配很多裝置產品,也就是說一個版本的Android原始碼,可以編譯出針對不同產品的系統。通過選擇一個目標編譯項,來決定編譯出針對哪個產品的系統,我們可以通過執行下面的命令來選擇要編譯的目標系統:
$ lunch
You're building on Linux
generic-eng simulator
Lunch menu... pick a combo:
1. generic-eng
2. simulator
Which would you like? [generic-eng]
通過lunch命令可知,讓使用者輸入目標編譯項,我們可以選擇編譯項前的數字,也可以直接輸入編譯項的名字。
…接前面終端輸出資訊
Which would you like? [generic-eng]1 [回車]
============================================
PLATFORM_VERSION_CODENAME=REL
PLATFORM_VERSION=2.1-update1
TARGET_PRODUCT=generic
TARGET_BUILD_VARIANT=eng
TARGET_SIMULATOR=false
TARGET_BUILD_TYPE=release
TARGET_ARCH=arm
HOST_ARCH=x86
HOST_OS=linux
HOST_BUILD_TYPE=release
BUILD_ID=ERE27
============================================
由上面結果可知,當用戶輸入:1或generic-eng時,會打印出上面的資訊,這些資訊是Android的編譯系統必須依賴的環境變數,只有設定了這些變數,才能決定Android系統如何編譯,編譯成什麼平臺,編譯成什麼版本。
目標編譯項格式:產品名-版本變數名,目標編譯項可以由使用者新增(見xxx章節),產品名是目標裝置的產品名,由廠商自己定義,generic產品是通用產品,它是Android預設裝置的產品名,它包含了常用的手機的所有功能,自己定義的產品可以繼承generic,並重寫它的功能,達到定製產品的目的(見xxx章節)。
版本變數名由以下幾個組成:
- eng:工程版本,
- user:終端使用者版本
- userdebug:除錯版本
- tests:測試版本
其中,eng版本產品其實就是手機行業的工程機,它不是最終銷售的產品,而是產品在定型下線之前放出的一些測試用機器,用於檢測和標準的認證,這些工程機上安裝的系統為eng版本,user是終端使用者機發行版本,userdebug是除錯版本,它比使用者機添加了一些除錯功能,如adb除錯預設開啟等,tests測試版本,該版本會安裝一些測試程式,用於測試系統。
上述四種版本的分類作用,其一:用於區分目標系統裡的所有的應用程式、庫、測試程式等,將它們打上對應的Tags,當選擇一個版本編譯時,擁有對應Tags及低級別的Tags的程式會被編譯安裝到目標裝置上,應用程式Tags的包含關係如下圖:。其二:根據不同的版本,系統會有不同的設定,如adbd在使用者版本里是關閉的,在其它版本中是預設開啟的,ro.secure屬性使用者版本值為1,其它版本為0。
Ø 編譯原始碼
執行完前面的命令後,我們可以輸入make指令開始編譯目標系統:
$make
編譯的時長與機器的硬體配置有關係,當第一次編譯時一般需要數小時以上。後續編譯,相對快多了,編譯完的效果如下圖所示:
通過上面的輸出資訊可知,Android系統編譯完後,在out/target/product/generic/目錄下產出了三個檔案:system.img、ramdisk.img、userdata.img。
- system.img:android系統的檔案系統,裡面包含了android系統的應用程式(apk),系統用到的各種庫(jar, so)和資源,配置檔案(etc目錄下),系統命令(bin,usr/bin, xbin),該映像檔案是由out/target/product/generic/system目錄打包生成的,我們可以對這個目錄裡的東西進行定製化,比如,你要想讓android系統預設安裝一個應用程式,那麼可以將要安裝的apk檔案拷貝到out/target/product/generic/system/app目錄下。
- userdata.img:使用者資料映像,裡面包含有程式安裝資訊等,好比如是windows的C:/Program Files/目錄。
- ramdisk.img:記憶體磁碟映像。linux核心啟動起來,要掛載一個檔案系統作為自己的根檔案系統,裡面含有Linux核心啟動過程中依賴的一些程式和配置檔案。ramdisk.img就是一個最小化的根檔案系統,它被載入到記憶體中作為Android的根檔案系統。該映像是由out/target/product/generic/root目錄打包生成的。前面所述的userdata.img和system.img映像,在linux系統啟動起來後掛載到ramdisk.img中的data,system目錄下。
其實,Android手機的ROM包(通常為update.zip檔案),就是主要由上述三個映像檔案構成的:
ROM包檔案 |
說明 |
android-info.txt |
ROM版本及刷寫配置資訊 |
boot.img |
Linux核心zImage、ramdisk.img |
system.img |
Android系統映像 |
userdata.img |
使用者資料映像 |
… |
其它映像 |
只要我們拿到手機的原始碼,就可以自己編譯出自己的ROM,不過,一般手機廠商不會開源自己產品原始碼,都是第三方愛好者自己下載,修改編譯的,如:業界著名的CM團隊:http://www.cyanogenmod.com/。
由於完全編譯Android系統耗時很長,並且Android原始碼由很多模組組成,我們可以通過下面一些編譯命令來減少編譯時間:
編譯命令 |
說明 |
make snod |
打包生成system.img,不檢查依賴關係 |
make bootimage |
打包生成ramdisk.img |
mmm |
指定編譯某個目錄下的模組 |
上述三個命令經常在我們原始碼開發時使用,希望大家記住。
2.3.3編譯Linux核心
Android使用Linux核心,在原始碼級開發過程中,有時要修改核心程式碼,通常核心程式碼是和目標裝置相關的,我們使用的是模擬器的核心,即使沒有硬體裝置也可以完成實驗。
編譯Android的核心,需要用到交叉編譯器,我們可以直接使用Android原始碼裡自帶的arm-eabi-gcc編譯器,為了編譯出針對模擬器的核心(模擬器的CPU為Goldfish),還要配置核心(如果不知道如何配置核心,請讀者閱讀核心裁剪相關資料),為了方便我們編譯Goldfish核心,我們編寫了如下指令碼,方便編譯。
$ cd /home/linux/android/android_source/kernel/goldfish/
$ vi build_kernel.sh
新增如下內容:
@ /home/linux/android/android_source/kernel/goldfish/build_kernel.sh
#!/bin/bash
export PATH=/home/linux/android/android_source/prebuilt/linux-x86/toolchain/arm-eabi-4.4.3/bin:$PATH
export ARCH=arm
export SUBARCH=arm
export CROSS_COMPILE=arm-eabi-
if [ ! –f .config ] ; then
make goldfish_armv7_defconfig
fi
make
注:當Andorid原始碼目錄發生改變時,要修改PATH的路徑,讓它指向對應的交叉編譯器。
給指令碼加上可執行許可權,然後執行該指令碼:
$ chmod a+x build_kernel.sh
$ ./build_kernel.sh
核心編譯完成如圖x-x所示:
圖x-x 核心編譯結果