2.3 Android编译的板级支持
Android的开源代码默认是支持仿真器的。如果需要使用Android的开源代码支持一个特定的设备,除了具体的代码实现之外,还需要对Android的编译系统做出一些更改。这种编译的板级支持可以作为附加的方式被添加到Android系统之中。
↘ 2.3.1 支持一个板的工作
从Android开源代码到一个板(特定设备)的支持,变化的部分就是更改部分代码和增加板级支持目录。更改完成后,Android编译系统按照一定的顺序进行处理。
Android对特定板的支持的处理流程如图2-1所示。
图2-1 Android编译的板级支持的处理流程
Android对特定板的编译支持主要分成以下几个步骤。
● Make的时候传入TARGET_PRODUCT宏。
● Android的编译系统根据TARGET_PRODUCT宏找到目标板的配置目录。
● 在目标板配置目录中的板级配置文件得到若干配置宏。
● (系统编译流程)各个模块根据配置宏进行编译。
● 处理目标板中的其他特定配置文件。
Android编译流程主要涉及以下的一些文件。
● buildspec.mk:根目录中的文件,指定板名称。
● vendersetup.sh:专用设置文件。
● BoardConfig.mk:板级全局配置。
● AndroidBoard.mk:当前板的特殊配置。
● AndroidProducts.mk:产品的配置。
buildspec.mk和vendersetup.sh两个文件执行的是同一种功能,也就是目标产品名称的定义,二者可以取其一(实际上也可以有其他定义方式)。
在make配置后,系统可以开始进行构建。构建的前面就是找到目标产品的配置目录,并使用其中的几个文件:BoardConfig.mk中定义的内容可以被系统的每一个部分使用(传递到每一个Android.mk);AndroidBoard.mk和AndroidProducts.mk是配置文件,可以引用默认的内容。板级的编译支持目录中也可以具有自己的Android.mk文件,与普通的用法类似。
↘ 2.3.2 buildspec.mk文件的作用
将buildspec.mk增加到源代码TOP目录,可以完成配置工作。Android在执行make的时候,将找到TOP目录中的buildspec.mk,取出其中定义的内容进行配置。buildspec.mk的作用实际上就是为了定义几个宏,作为Make第一步使用的环境变量。
一个buildspec.mk的写法如下所示:
ifndef TARGET_PRODUCT #TARGET_PRODUCT:=generic Endif ifndef TARGET_BUILD_VARIANT #TARGET_BUILD_VARIANT:=user #TARGET_BUILD_VARIANT:=userdebug #TARGET_BUILD_VARIANT:=eng endif
其中,最重要的一个宏是TARGET_PRODUCT,表示目标产品的名称;TARGET_BUILD_VARIANT则表示编译的类型,分成user(用户模式)、userdebug(用户调试模式)、eng(工程模式),这个宏将主要影响系统是否具有root权限等方面。
当编译不进行定义时,或者TARGET_PRODUCT的数值本身就是“generic”,将使用“generic”板(也就是仿真器)的配置,其路径为:build/target/product。
● 提示:Android的仿真器默认情况下也是ARM体系结构的目标,仿真器QEMU仿真执行了ARM代码,因此generic目标和实际的产品差别并不大。
当TARGET_PRODUCT宏被正式定义时,将在自定义板的路径找到名称为其值的目录,作为板级编译支持目录。可以使用的板级编译支持目录有以下两个。
● device/*/<TARGET_PRODUCT>
● vendor/*/<TARGET_PRODUCT>
其含义为TOP目录中的device或者vendor目录中的某个子目录中的下一级目录作为板级编译支持目录。也就是说板级编译支持目录必须是Android三级子目录,且必须在device或者vendor目录中,二级子目录没有要求,一般是公司的名称。
两个产品的板级编译支持目录如下所示。
● device/htc/passion/:Nexus One手机(Google和HTC合作出品)。
● device/samsung/crespo/:Nexus S手机(Google和三星合作出品)。
↘ 2.3.3 vendersetup.sh和lunch命令
vendersetup.sh文件可以提供另外一种编译配置方法,这也是较新版本的Android系统推荐的方法。其职能实际上也是配置几个宏,作为Make时的环境变量。
在编译之前,可以使用envsetup.sh脚本进行编译的配置,配置的过程中,将会调用每一个板的vendersetup.sh文件,内容如下所示:
$ . build/envsetup.sh including device/htc/passion/vendorsetup.sh including device/samsung/crespo/vendorsetup.sh
此时的配置,实际是一个“遍历”的过程,将会调用device/*/和vendor/*/目录中每一个板的vendersetup.sh文件。只要板级支持目录中有vendersetup.sh文件,就会被调用。调用后实际上将为增加系统可以选择的目标产品,这个影响将在使用lunch命令进行选择产品时有所体现,内容如下所示:
$ lunch You're building on Linux Lunch menu... pick a combo: 1. generic-eng 2. simulator 3. full_passion-userdebug 4. full_crespo-userdebug Which would you like? [generic-eng] 1 ============================================ PLATFORM_VERSION_CODENAME=REL PLATFORM_VERSION=2.3.4 TARGET_PRODUCT=generic TARGET_BUILD_VARIANT=eng TARGET_SIMULATOR= TARGET_BUILD_TYPE=release TARGET_BUILD_APPS= TARGET_ARCH=arm HOST_ARCH=x86 HOST_OS=linux HOST_BUILD_TYPE=release BUILD_ID=GRJ22 ============================================
在lunch时出现的菜单项目(combo)中,出现了4个选项,可以使用数字进行选择。从中可见,除了默认的generic-eng和simulator两个选择之外,full_passion-userdebug和full_crespo-userdebug就是来自于自定义板级支持的文件。
device/htc/passion/中的vendersetup.sh文件如下所示:
add_lunch_combo full_passion-userdebug
device/samsung/crespo/中的vendersetup.sh文件如下所示:
add_lunch_combo full_crespo-userdebug
其中full_passion和full_crespo也就是所定义的TARGET_PRODUCT宏的名称。在lunch时选择了数字后,实际上得到的结果也是设置TARGET_PRODUCT等宏。
↘ 2.3.4 BoardConfig.mk文件的作用
BoardConfig.mk文件是一个平台全局配置的文件,这些配置的内容可以在各个工程的Android.mk文件中得到。BoardConfig.mk只是进行配置的定义,其中不能包含实际执行的命令。
例如,BoardConfig.mk文件可以进行定义:
BOARD_USES_GENERIC_AUDIO := false
BOARD_USES_GENERIC_AUDIO等宏的数值就可以在每一个编译文件(Android.mk)中被读出。
BoardConfig.mk文件中也可以使用-include包含其他的板级配置宏。
例如,device/samsung/crespo/中的BoardConfig.mk文件包含了如下的片断:
-include vendor/samsung/crespo/BoardConfigVendor.mk TARGET_CPU_ABI := armeabi-v7a # CPU体系结构的二进制格式 BOARD_HAVE_BLUETOOTH := true # 是否具有蓝牙 TARGET_NO_BOOTLOADER := true # 是否具有BootLoader TARGET_NO_KERNEL := false # 是否具有Linux内核
BoardConfig.mk文件中既可以使用TARGET_CPU_ABI等编译系统使用的宏,也可以定义自己使用的宏。
↘ 2.3.5 AndroidProducts.mk文件
AndroidProducts.mk为当前板的配置文件,其中可以使用一些宏定制最终在目标系统中需要的安装内容。在实际的板级支持过程中,通常需要在AndroidProducts.mk中包含同类文件,进行大部分默认的定义,然后再对不同配置进行覆盖(Override)定义。
AndroidProducts.mk需要定义几个宏,用于表示当前产品的信息: PRODUCT_NAME(产品名称),PRODUCT_DEVICE(产品设备)。
其中build/target/product/目录中的AndroidProducts.mk是默认的产品配置文件,内容如下所示。
ifneq ($(TARGET_BUILD_APPS),) PRODUCT_MAKEFILES := \ $(LOCAL_DIR)/core.mk \ $(LOCAL_DIR)/generic.mk else PRODUCT_MAKEFILES := \ $(LOCAL_DIR)/core.mk \ $(LOCAL_DIR)/generic.mk \ $(LOCAL_DIR)/generic_x86.mk \ $(LOCAL_DIR)/full.mk \ $(LOCAL_DIR)/sdk.mk \ $(LOCAL_DIR)/sim.mk endif
其中指定的PRODUCT_MAKEFILES实际上就是被其包含的文件。full.mk也在build/target/product/目录中,其中具有如下内容。
PRODUCT_NAME := full PRODUCT_BRAND := generic PRODUCT_DEVICE := generic PRODUCT_MODEL := Full Android
这里的PRODUCT_NAME等宏的定义也就是产品名称等信息,这里进行了定义后,在系统中通过部分的函数可以读取这些信息,并可以提供给程序和用户。
其中另一个功能是定义系统预安装的包。例子如下所示:
PRODUCT_PACKAGES := \ OpenWnn \ PinyinIME \ VoiceDialer \ libWnnEngDic \ libWnnJpnDic \ libwnndict
在包被增加到PRODUCT_PACKAGES宏之后,这个包将会被预装到系统中,作为系统映像的一部分。
系统预装文件使用PRODUCT_COPY_FILES宏来定义。例子如下所示:
PRODUCT_COPY_FILES := \ development/data/etc/apns-conf.xml:system/etc/apns-conf.xml \ development/data/etc/vold.conf:system/etc/vold.conf
在PRODUCT_COPY_FILES的各项内容中,“:”前面的为源文件,“:”后面的为目标机的文件(为out/target/product/<产品名称>目录加上后面的路径),编译时将源文件复制为目标文件。如果目标文件的目录没有,也会进行自动创建。因此,这是另外一种像目标系统中预装文件的方法。
另外一个可以定义的内容是系统语言区域的定义,在build/target/product/目录中的languages_<XXX>.mk文件中进行定义,类似具有如下的内容:
PRODUCT_LOCALES := en_US en_GB fr_FR it_IT de_DE es_ES
这里使用的是ISO 639-1定义的语言码和ISO 3166-1-alpha-2定义的区域码。
例如,device/samsung/crespo/中的AndroidProducts.mk文件包含了同目录的full_crespo.mk文件,full_crespo.mk的主要内容如下所示:
$(call inherit-product, $(SRC_TARGET_DIR)/product/languages_full.mk) $(call inherit-product, device/samsung/crespo/device.mk) $(call inherit-product, $(SRC_TARGET_DIR)/product/full.mk) PRODUCT_NAME := full_crespo PRODUCT_DEVICE := crespo PRODUCT_MODEL := Full Android on Crespo
此时除了PRODUCT_NAME等宏的定义之外,也可以包含其他的同类文件,包含的languages_full.mk和full.mk就是系统默认的文件,在这里被复用,而device.mk是当前目录中的另外一个文件。
↘ 2.3.6 AndroidBoard.mk文件
AndroidBoard.mk用于板级一些编译内容的定义,这个文件通常是可选的。AndroidBoard.mk的功能和语法类似一般的Android.mk文件。
例如,build/target/board/generic/中的AndroidBoard.mk文件如下所示:
LOCAL_PATH := $(call my-dir) file := $(TARGET_OUT_KEYLAYOUT)/tuttle2.kl ALL_PREBUILT += $(file) $(file) : $(LOCAL_PATH)/tuttle2.kl | $(ACP) $(transform-prebuilt-to-target) include $(CLEAR_VARS) LOCAL_SRC_FILES := tuttle2.kcm include $(BUILD_KEY_CHAR_MAP)
这里进行了两个工作:一个是将按键布局的文件tuttle2.kl复制到目标系统中,一个是调用BUILD_KEY_CHAR_MAP模板对按键字符映像的tuttle2.kcm文件进行编译处理。由于每一个Android板的输入设备通常都不同,因此在一个特定设备的构建过程中,按键相关的定制处理也是经常需要进行的工作。
AndroidBoard.mk不是必需的,例如,在crespo的配置目录device/samsung/crespo/中,就没有AndroidBoard.mk文件,而具有Android.mk文件。
↘ 2.3.7 编译中的层叠加
在一个Android的板级构建过程中,有一种不更改源代码文件,而实现构建出不同内容的方法,这就是通过编译中层叠加(overlay)的机制来实现的。使用这种方式可以基于同样的源代码,为了不同的硬件或不同产品构建出不同的系统。
在板级配置目录中,可以有一个名称为overlay的子目录,这个目录表示的就是一个代码叠加层。这个overlay目录对应于Android源代码的TOP目录,在编译的过程中将使用overlay目录中的内容替换TOP目录中的内容。
例如,在Nexus S(crespo)的板级配置的目录device/samsung/crespo/overlay/下面的一个目录结构如下所示:
device/samsung/crespo/overlay/frameworks/base/core/ `-- res `-- res |-- drawable-hdpi | `-- default_wallpaper.jpg |-- values | `-- config.xml `-- xml `-- power_profile.xml
此时,将把TOP目录中的frameworks/base/core/res/res/drawable-hdpi中的default_wallpaper.jpg替换成overlay对应目录的同名文件。这个文件实际上是系统默认的墙纸,由此,进行完如此的定义后,相当于crespo编译的系统将替换默认的墙纸。
Android系统中很多模块比较适合使用这种替换方式,例如应用程序包中的资源文件等。尤其值得注意的是,一旦使用overlay进行层替换,原有的源文件将不再起作用。