本文为Android平台开发人员和Android设备制造商提供了底层开发指导。如果你对Android的上层应用开发很感兴趣,请访问Android Developers Site。
关于这份指导书
这份指导书按照逻辑划分为几个部分(见目录)。在一个持续的开发过程中,Android是一个复杂的工程项目,随着版本和API的改变,这份指导书将会不断更新。
至使用者
对于精通嵌入式Linux的工程师而言,这本书非常有价值。但是,它的重点并不在普通的嵌入式Linux开发,而是更多提供Android平台的特色。
初学Android
对于初识Android的人而言,建议阅读以下文档:
Android Develop site:这个网页提供了高版本的SDK文档;
Android Open Source Project site:这个网页指导你如何获取源代码,建立开发环境和从事简单的工程开发。
如果你准备在你的目标系统上定制和移植Android系统,请阅读系统编译概述。
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 $ (CLEAR_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. |
创建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. 一个Android.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 |
|
下面举例说明一个典型的产品变量定义文件:
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)
介绍
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文件。
启动画面定制
在设备启动过程中,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/emailAddress=android@android.com' 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中。