本文爲Android平臺開發人員和Android設備製造商提供了底層開發指導。如果你對Android的上層應用開發很感興趣,請訪問Android Developers Site。
關於這份指導書
這份指導書按照邏輯劃分爲幾個部分(見目錄)。在一個持續的開發過程中,Android是一個複雜的工程項目,隨着版本和API的改變,這份指導書將會不斷更新。
至使用者
對於精通嵌入式Linux的工程師而言,這本書非常有價值。但是,它的重點並不在普通的嵌入式Linux開發,而是更多提供Android平臺的特色。
初學Android
對於初識Android的人而言,建議閱讀以下文檔:
Android Develop site:這個網頁提供了高版本的SDK文檔;
Android Open Source Project site:這個網頁指導你如何獲取源代碼,建立開發環境和從事簡單的工程開發。
如果你準備在你的目標系統上定製和移植Android系統,請閱讀系統編譯概述。
1.配置和編譯
1.1系統編譯
Android用一個定製的編譯系統產生一系列工具,編譯文件和文檔。這一小節概述了Android編譯系統和如何建立一個簡單的編譯環境。
Android的編譯系統是以GNU Make爲基礎,並且要求最新的版本(注意:Android所使用的最新的GNU Make規則可能並不會出現在GNU Make的網頁上)。在開始之前,首先通過“make -v”檢查下你的編譯環境的版本。如果你不是3.80或者更高版本的話,你需要升級你的GNU Make版本。
理解makefile
makefile文件定義瞭如何去建立一個系統的編譯規則。典型的makefile包含以下一些元素:
1. 名稱:爲你的編譯目標取一個名稱(LOCAL_MODULE := <build_name>);
2. 局部變量:用CLEAR_VARS清除局部變量(include $ (CLESR_VARS));
3. 所需要編譯的文件:註明你的目標需要鏈接哪些源文件(LOCAL_SRC_FILES := main.c);
4. 標記:編譯選項(LOCAL_MODULE_TAGS := eng development);
5. 庫:定義你的目標所需要鏈接的庫文件(LOCAL_SHARED_LIBRARIES := cutils);
6. 模板文件:所包含的這些模板文件定義了生成目標的類型,同時包含了生成此種目標的編譯工具(include $(BUILD_EXECUTABLE))。
下面一段代碼舉例說明一個典型的makefile:
- LOCAL_PATH := $(my-dir)
- include $(CLEAR_VARS)
- LOCAL_MODULE := <buil_name>
- LOCAL_SRC_FILES := main.c
- LOCAL_MODULE_TAGS := eng development
- LOCAL_SHARED_LIBRARIES := cutils
- include $(BUILD_EXECUTABLE)
- (HOST_)EXECUTABLE,(HOST_)JAVA_LIBRARY,(HOST_)PREBUILT, (HOST_)SHARED_LIBRARY,(HOST_)STATIC_LIBRARY,PACKAGE,JAVADOC, RAW_EXECUTABLE, RAW_STATIC_LIBRARY,COPY_HEADERS, KEY_CHAR_MAP
爲了提高代碼的可讀性,上面這段代碼中包含了一些的書寫風格。
Layers
下表描述了編譯系統所包含的抽象層。
每一個抽象層都是以一對多的關係和上面一層相關。例如,一種處理器架構可以運行在很多種目標板上,而每一個目標板又有很多設備。你可以在給定的抽象層上定義一個元素作爲同一層元素的一個特例,這樣就避免了複製和簡化了維護。
Layer |
Example |
Description |
Product |
myProduct, myProduct_eu, myProduct_eu_fr, j2, sdk |
產品層定義了一個產品的詳細說明,包括編譯的模塊和配置。你可以基於一種特定的應用提供一種設備上的幾種不同版本。例如,基於攝像技術。 |
Device |
myDevice, myDevice_eu, myDevice_eu_lite |
設備層代表了構建在設備上的物理層。例如,南美的可能包含QWERTY鍵盤而法國的可能包含AZERTY鍵盤。連接到設備層的典型外設。 |
Board |
sardine, trout, goldfish |
開發板層代表了一個產品的縮減版。當然你可以連接一些外設上去。 |
Arch |
arm (arm5te) (arm6), x86, 68k |
架構層描述了你目標系統的處理器架構 |
編譯Android系統
這部分說明如何編譯缺省的Android版本。一旦你熟悉了普通的編譯過程,你就可以嘗試着編譯一個能運行在你自己的設備上的Android系統。
設備編碼
爲了做一個普通Android編譯過程,源碼中build/envsetup.sh中包含了一些環境變量和函數定義。例如:
|
爲了一個工程調試編譯,你也可以用eng取代user
這些編譯變量隨着調試選項和安裝包的不同而不同。
清除編譯結果
通過執行”m clean”來清除你剛編譯產生的目標文件。也可以通過”m clobber”來刪除所有目標編譯的輸出文件,也就相當於將整個/out目錄刪除。
加速重新編譯
每一個目標系統的編譯輸出文件都放在/out目錄下,每一次編譯都會快速的選擇目標而不需要重新編譯所有源碼。
但是如果編譯系統沒有將改動告訴環境變量或makefile,我們就有必要清除以往的編譯結果。如果這種情況經常發生,你就需要定義一個環境變量:
- %make –j4 PRODUCT-generic-eng
這樣做是爲了迫使編譯系統使用ccache編譯器,它能減少源碼的重複編譯。
ccache編譯器源碼已經提供(/prebuilt),不需要再次安裝。
問題定位
下面的錯誤很可能是由於JAVA版本過低所導致的:
- % export USE_CCACHE=1
- device Dex: core UNEXPECTED TOP-LEVEL ERROR:
- java.lang.NoSuchMethodError: method java.util.Arrays.hashCode with
- signature ([Ljava.lang.Object;)I was not found.
- at com.google.util.FixedSizeList.hashCode(FixedSizeList.java:66)
- at com.google.rop.code.Rop.hashCode(Rop.java:245)
- at java.util.HashMap.hash(libgcj.so.7)
Dx是一個Java工具,首次出現在java1.5版本中。通過”java -version’檢查你的java版本。
如果你有java1.5或者更高版本,你還會遇到這樣的錯誤,檢查你的PATH變量。
編譯Android的內核
這部分介紹如何編譯Android缺省的內核。一旦你熟悉了普通的Android內核編譯,你就可以嘗試着去配置你自己的Android驅動。
爲了編譯內核,選擇設備目錄(/home/joe/android/device),建立環境變量並且運行:
- % . envsetup.sh
- % partner_setup generic
然後選擇內核目錄/home/joe/android/kernel.
下載分支
缺省的代碼分支是Android。爲了下載不同的分支代碼,執行:
- % git checkout --track -b android-mydevice origin/android-mydevice
- //Branch android-mydevice set up to track remote branch
- % refs/remotes/origin/android-mydevice.
- //Switched to a new branch "android-mydevice"
爲了簡化代碼的管理,讓你的分支名字和它的主幹名字相同。通過執行”git checkout <branchname>”來選擇下載的代碼分支。
分支鑑定
要找出哪一個代碼分支存在和哪一個代碼分支是可用(標有asterisk),執行:
- % git branch -a
- android
- * android-mydevice
- origin/HEAD
- origin/android
- origin/android-mydevice
- origin/android-mychipset
編譯內核
執行:
- % make –j4
編譯選項
當我們需要針對目標系統編譯時,我們希望在最終版本中如果有幾種不同編譯選項的鏡像。下面有幾個編譯選項:
eng |
This is the default flavor. A plain make is the same as make eng. Installs modules tagged with: eng, debug, user, and/or development. Installs non-APK modules that have no tags specified. Installs APKs according to the product definition files, in addition to tagged APKs. ro.secure=0 ro.debuggable=1 ro.kernel.android.checkjni=1 adb is enabled by default |
user |
make user This is the flavor intended to be the final release bits. Installs modules tagged with user. Installs non-APK modules that have no tags specified. Installs APKs according to the product definition files; tags are ignored for APK modules. ro.secure=1 ro.debuggable=0 adb is disabled by default. |
userdebug |
make userdebug The same as user, except: Also installs modules tagged with debug. ro.debuggable=1 adb is enabled by default. |
1.1.1配置新產品
創建makefile
如何爲你的Android移動設備創建一個makefile?步驟如下:
1. 在//vendor/下,以產品的公司名字創建一個目錄
2. 在公司名字目錄下再創建一個產品的目錄
3. 創建一個產品的makefile文件,至少包含以下代碼
4. 將產品特性的變量添加到產品定義文件中
5. 在/products/目錄下,創建一個AndroidProducts.mk文件,用於鏈接個別產品的makefile文件
- mkdir vendor/<company_name>
- mkdir vendor/<company_name>/products/
- $(call inherit-product, $(SRC_TARGET_DIR)/product/generic.mk)
- #
- # Overrides
- PRODUCT_NAME := <first_product_name>
- PRODUCT_DEVICE := <board_name>
6. 在公司目錄下在創建一個目標板的目錄,這個目錄下的makefile文件可以被運行在目標板的任何一個產品訪問到:
7. 在目標板目錄下創建一個BoardConfig.mk文件:
|
8. 如果你想改進系統屬性,在<board_name>目錄下創建一個system.prop文件:
9. 在products/AndroidProducts.mk中聲明<second_product_name>.mk
10. 一個Anroid.mk文件(/vendor/<company_name>/<board_name>)必須包含以下代碼:
|
11. 對於同一個目標板的第二個產品,創建第二個makefile文件 vendor /companyname/product/<second_product_name>.mk:
|
現在,你就有了兩個產品,<first_product_name>and<second_product_name>。爲了驗證一個新的產品是否被成功配置,執行:
編譯後,你就會發現產生了一個新的目錄/out/target/product/<board_name>。
新產品的文件結構樹
在完成了上述步驟之後,在你的系統上你會看到如下目錄:
- ||--<company_name>
- |--<board_name>
- Android.mk
- poduct_config.mk
- system.prop
- |--<products>
- AndroidProducts.mk
- <first_product_name>.mk
- <second_product_name>.mk
產品變量定義文件
產品的屬性變量就定義在產品變量定義文件中。一個產品的變量定義文件可以從其他產品繼承,這樣就可以減少不必要的複製和易於代碼維護。
|
Parameter |
Description |
Example |
PRODUCT_NAME |
End-user-visible name for the overall product. Appears in the "About the phone" info |
|
PRODUCT_MODEL |
End-user-visible name for the end product |
|
PRODUCT_LOCALS |
A space-separated list of two-letter language code, two-letter country code pairs that describe several settings for the user, such as the UI language and time, date and currency formatting. The first locale listed in PRODUCT_LOCALES is is used if the locale has never been set before. |
en_GB de_DE es_ES fr_CA |
PRODUCT_PACKAGES |
Lists the APKs to install. |
Calendar Contacts |
PRODUCT_DEVICE |
Name of the industrial design |
dream |
PRODUCT_MANUFACTUER |
Name of the manufacturer |
acme |
PRODUCT_BRAND |
The brand (e.g., carrier) the software is customized for, if any |
|
PRODUCT_PROPERTY_OVERRIDES |
List of property assignments in the format "key=value" |
|
PRODUCT_COPY_FILES |
List of words like source_path:destination_path. The file at the source path should be copied to the destination path when building this product. The rules for the copy steps are defined in config/Makefile |
|
PRODUCT_OTA_PUBLIC_KEYS |
List of OTA public keys for the product |
|
PRODUCT_POLICY |
Indicate which policy this product should use |
|
PRODUCT_PACKAGE_OVERLAYS |
Indicate whether to use default resources or add any product specific overlays |
vendor/acme/overlay |
PRODUCT_CONTRIBUTORS_FILE |
HTML file containing the contributors to the project. |
|
PRODUCT_TAGS |
list of space-separated words for a given productb |
|
下面舉例說明一個典型的產品變量定義文件:
1.1.2編譯手冊
Android編譯手冊提供簡單的實例代碼幫助你很快的建立一些普通的編譯任務。
編譯一個簡單的APK文件
編譯一個依賴.jar庫的APK文件
- $(call inherit-product, build/target/product/generic.mk)
- #Overrides
- PRODUCT_NAME := MyDevice
- PRODUCT_MANUFACTURER := acme
- PRODUCT_BRAND := acme_us
- PRODUCT_LOCALES := en_GB es_ES fr_FR
- PRODUCT_PACKAGE_OVERLAYS := vendor/acme/overlay
編譯一個需要平臺密鑰文件簽名的APK文件
|
編譯一個需要定製代碼密鑰文件簽名的APK文件
- LOCAL_PATH := $(call my-dir)
- include $(CLEAR_VARS)
- # List of static libraries to include in the package
- LOCAL_STATIC_JAVA_LIBRARIES := static-library
- # Build all java files in the java subdirectory
- LOCAL_SRC_FILES := $(call all-subdir-java-files)
- # Name of the APK to build
- LOCAL_PACKAGE_NAME := LocalPackage
- # Tell it to build an APK
- include $(BUILD_PACKAGE)
增加一個預編譯的APK文件
|
增加一個靜態的JAVA庫
|
Android.mk變量
下面是一些Android.mk文件中常見的變量,按字母順序羅列。首先,注意變量的命名:
LOCAL_-這些變量被設置爲單獨的每個模塊。以”include $(CLEAR_VARS)”爲界限,可以通過它清空其他LOCAL_的聲明。大多數模塊中的變量都是LOCAL_變量;
PRIVATE_-這些變量用來編譯特定目標代碼。即他們僅用在模塊的命令當中。也意味着它不可能作用於定義在當前模塊後面的模塊;
HOST_和TARGET_-:這些變量包含對HOST和TARGET的說明和定義。在你的makefile文件中不要使用HOST_和TARGET_;
BUILD_和CLEAR_VARS-這些變量包含了模板makefile的名字。
其它的名字你都可以任意使用在你自己的makefile文件中。但是,記住這是一個非遞歸的編譯系統,很可能你的變量會被其它的Android.mk修改,導致你使用的時候它變得不同了。
(表格見http://source.android.com/porting/build_cookbook.html)
1.2密鑰文件和數字簽名
介紹
Android平臺要求每個應用程序有密鑰文件簽名以獲取系統權限,這樣應用程序就可以獲得共享的用戶ID或者運行在系統進程當中。Android平臺使用四種密鑰文件來維護系統的安全性:
·Platform:關於package的平臺密鑰文件;
·Shard:能分享/home/contacts進程的密鑰文件;
·Media:關於media/download中包的密鑰文件;
·Releasekey:除上面之外的缺省的密鑰文件。
這些密鑰文件爲發行的Image文件中的應用獲取數字簽名,他們並不是編譯所需要的。編譯系統使用測試密鑰文件(build/target/product/security/)進行數字簽名。這個測試密鑰文件是標準的Android平臺的一部分,所以不會被其他平臺產品和設備所使用。相應的,設備製造商們會提供他們設備的產品密鑰文件。
創建一個密鑰文件
設備製造商的產品密鑰文件應該放在/vendor/<vendor_name>/security/< product_name>下面。爲了創建一個簡單的密鑰文件,複製這個目錄下的mkkey.sh的腳本。爲了定製你自己的密鑰文件,修改AUTH這一行,更正你公司的信息:
|
mkkey.sh是一個產生密鑰文件的腳本。注意:你輸入的密碼在你的終端上是可見的。另外,這些密碼是用於數字簽名的。
爲了產生四種密鑰文件,需要運行mkkey.sh四次:
- LOCAL_PATH := $(call my-dir)
- include $(CLEAR_VARS)
- # Build all java files in the java subdirectory
- LOCAL_SRC_FILES := $(call all-subdir-java-files)
- # Any libraries that this library depends on
- LOCAL_JAVA_LIBRARIES := android.test.runner
- # The name of the jar file to create
- LOCAL_MODULE := sample
- # Build a static jar file.
- include $(BUILD_STATIC_JAVA_LIBRARY)
這樣你就有了自己的產品密鑰文件。
數字簽名
數字簽名包含兩個步驟:
1. 爲需要編譯的每個部分進行數字簽名;
2. 簽名好後放回Image文件中。
應用程序數字簽名
用build/tools/releasetools/sign_target_files_apks對target_file進行數字簽名。缺省情況下target_files不會編譯的,所以當你編譯是需要明確”dist”:
這條命令在out/dist目錄下創建一個文件<product_name>-target_files.zip。這就是腳本sign_target_files_apks所需要的文件。
如果在編譯過程中,有些apk你不想重新簽名,你就要在你的命令行中爲增加”-e Foo.apk=”。
Sign_target_files_apks有許多其他的命令選項,可以-h查看。
創建Image文件
一旦你有了signed-target-files.zip,就可以通過下面命令放到image中:
Sign-img.zip包含了所有的image文件。
2.定製
啓動畫面定製
在設備啓動過程中,Android會顯示一幅圖片。如果你希望更改默認的圖片:
1. 創建一個320*480的圖片(splashscreen.jpg);
2. 使用ImageMagick轉換你的圖片格式:
3. 使用rgb2565將圖片轉換爲565格式
4. 開機啓動我們製作的圖片
- #!/bin/sh
- AUTH='/C=US/ST=California/L=Mountain View/O=Android/OU=Android/CN=Android/[email protected]'
- if [ "$1" == "" ]; then
- echo "Create a test certificate key."
- echo "Usage: $0 NAME"
- echo "Will generate NAME.pk8 and NAME.x509.pem"
- echo " $AUTH"
- exit
- fi
- openssl genrsa -3 -out $1.pem 2048
- openssl req -new -x509 -key $1.pem -out $1.x509.pem -days 10000 /
- -subj "$AUTH"
- echo "Please enter the password for this key:"
- openssl pkcs8 -in $1.pem -topk8 -outform DER -out $1.pk8 -passout stdin
網絡定製平臺
網絡配置
Android的網絡配置信息作爲一種資源被編譯到目標文件中。它的xml文件在//android/framework/base/core/res/res/xml/apns.xml。這個文件中不包括APNS配置。一般不需要修改,但是在編譯的過程中你需要配置APNs。
編譯時APN的配置
爲一個產品配置APN,你需要增加一個apns-conf.xml(不需要修改系統缺省的APNs)。這樣就可以讓不同的產品擁有不同的APNs配置。
爲了在產品層次上配置APNs,在vendor/<vendor_name>/products/ myphone-us.mk:
系統運行時APN配置
系統運行時,Android從system/etc/apns-conf.xml中讀取配置。
Android提供下面幾種運行時的網絡配置:
自動配置:系統啓動時,Android從SIM卡的MCC和MNC中獲取的網絡配置信心並且自動完成所有配置。
手動配置:Android平臺也支持運行時用戶手動配置。
WAP/SMS配置:網絡配置是標準的Android資源。你可以通過安裝一個新的系統資源APK包來更新網絡配置。也可以通過添加一項服務,這項服務可以爲SMS(包含網絡配置信息)監聽SMS端口。
定製預加載的應用程序
爲產品定製應用程序開發包(包括application,inputmethods,providers,services等),需要在產品配置(product configuration)中設置PRODUCT_PACKAGES:
- sh mkkey.sh platform # enter password
- sh mkkey.sh media # enter password
- sh mkkey.sh shared # enter password
- sh mkkey.sh release # enter password
程序包名應該和Android.mk中LOCAL_PACKAGE_NAME名字對應。
例如:某個Android.mk文件:
|
注意:主屏幕(Launcher.apk)僅僅是一個Android的應用程序,可以通過修改源代碼的方式定製自己的HomeScreen。
定製瀏覽器書籤
瀏覽器書籤存儲在Brower應用程序的string資源當中://android/packages/ apps/Brower/res/values/strings.xml。書籤被定義爲一個簡單的字符串數組(bookmarks),第一部分代表他的書籤名,第二部分代表他的URL:
- Fastboot flash splash1 screen.565
- <apn carrier="T-Mobile US"
- mcc="310"
- mnc="260"
- apn=" wap.voicestream.com"
- user="none"
- server="*"
- password="none"
- proxy=" 216.155.165.50"
- port="8080"
- mmsc="http://216.155.174.84/servlets/mms"
- />
Android在平臺配置的基礎上交替下載Like和應用程序資源。爲了給一個特定的移動網絡配置書籤,請將你的strings.xml放在Mobile Network Code的資源文件夾下面。例如,Brower/res/values-mccXXX-mncYYY/strings.xml,其中XXX和YYY就代表MCC和MNC的值。
EmailProvider定製
缺省的email Provider設置存儲在//android/packages/apps/Email/res/xml/ providers.xml:
- PRODUCT_COPY_FILES := vendor/acme/etc/apns-conf-us.xml:system/etc/apns-conf.xml
- PRODUCT_PACKAGES := /
- <company_name>Mail /
- <company_name>IM /
- <company_name>HomeScreen /
- <company_name>Maps /
- <company_name>SystemUpdater
Android會在平臺配置的基礎上交替加載所有的應用程序資源。給一個特定的移動網絡配置email Provider,你需要見provider.xml放在Mobile Network Code的資源文件下。例如,Email/res/xml-mccXXX-mnxYYY/providers.xml。
平臺主題
平臺主題和風格在//android/framework/base/core/res/res/values/styles.xml中。
動畫
Android提供了一些窗口和視圖變換的動畫。系統的動畫放在:
//android/framework/base/core/res/res/anim中。