Android系统开发--5.添加product

前面我们都是编译 android 内置的 product 进行验证的,但真正做产品的时候,我们是需要添加自己的 product的。
一个 product 有四个要素:

  • vendorsetup.sh : 把 product 添加到 lunch 选择项中
  • BoardConfig.mk : 芯片硬件相关配置,分区设置等
  • AndroidProducts.mk : 指定 product 配置
  • product.mk : 一个产品的软件相关的配置,比如内置哪些软件模块

下面我们参考 aosp_x86-eng 这个 product 来一步一步创建一个我们自己的 product。

1. 创建 device 目录

上面我们说了一个 product 有四个要素,那这四个要素总得有个容身之处。
在 Android 8.1 系统上,product 的四要素放在两个地方, 一个是 device 目录,用来给各厂商放 product 的。
另一个是 build/target 目录,用来放 google 官方内置的 aosp product。 build/target/product 里面放 AndroidProducts.mk, 跟 product.mk, build/target/board 里面放 BoardConfig.mk 等。个人感觉 build/target 这个目录结构有点零乱。

我们在 device 目录添加 product 就行。先创建 device 目录。
目录结构按这种形式创建: device/[company]/[device]

1
mkdir -p device/qiushao/generic_pure

注意,这里我们说的是 device 目录, 而不是 prodcut 目录。
我们可以这么理解, device 是硬件相关的芯片平台级配置, product 是软件相关的配置。
一个 device 下面可以有多个 product,即同一块硬件板子上,可以内置不同的软件模块。

这里还有个小细节, 我们的 device 名是 generic_pure, device 名以 generic_ 前缀开始。
这是因为我们的 product 计划继承 aosp_x86-eng 这个 product。
如果不以 generic_ 前缀开始的话, vndk-sp 相关的模块不会编译到系统,导致 emulator 启动失败。
这个也是折腾了两天才发现的问题。
具体配置在 device/generic/common/vndk/Android.mk 中:

1
2
3
4
ifneq ($(filter generic_%,$(TARGET_DEVICE)),)
LOCAL_PATH := $(call my-dir)
include $(LOCAL_PATH)/vndk-sp-libs.mk
...

当然我们也可以把 ifneq ($(filter generic_%,$(TARGET_DEVICE)),) 这个条件去掉, 或者把vndk相关的配置 copy 到我们的 device 目录。这样 device 名就可以随意了。

2. 新建 vendorsetup.sh

vendorsetup.sh 的作用是把 product 加入到 lunch 的选择列表。内容如下:

1
add_lunch_combo generic_pure-eng

其中 generic_pure 为 product name, 寓意纯净。这里我们可以添加多个 product name,这里为了简单起见,只添加一个就行。
eng 为 build type。有以下几种选择:

  • eng : 对应到工程版。编译打包所有模块。同时ro.secure=0, ro.debuggable=1, ro.kernel.android.checkjni=1,表示adbd处于ROOT状态,所有调试开关打开
  • userdebug : 对应到用户调试版。同时ro.debuggable=1,打开调试开关,但并没有放开ROOT权限
  • user : 对应到用户版。同时ro.secure=1,ro.debuggable=0,关闭调试开关,关闭ROOT权限。最终发布到用户手上的版本,通常都是user版。

3. 新建 BoardConfig.mk

BoardConfig.mk 包含了硬件芯片架构配置,分区大小配置等信息
这里我们直接使用 aosp_x86 的 BoardConfig.mk 就行

1
include $(SRC_TARGET_DIR)/board/generic_x86/BoardConfig.mk

4. 新建 product 配置

product 配置文件的名字要与 product 名保持一致,比如说上面我们在 vendorsetup.sh 中定的名字为 generic_pure,所以这里我们的 product 配置文件就是 generic_pure.mk。其中定义了 PRODUCT_NAME, PRODUCT_DEVICE 等变量。 这两个变量的命名是有要求的:

  • PRODUCT_NAME 要与 vendorsetup.sh 里面的产品名保持一致
  • PRODUCT_DEVICE 要与 device 目录名保持一致。编译系统会根据 PRODUCT_DEVICE,include 对应目录下的 BoardConfig.mk。

这里我们直接继承 aosp_x86 的 product 配置,再稍作修改就行

1
2
3
4
5
6
$(call inherit-product, $(SRC_TARGET_DIR)/product/aosp_x86.mk)

PRODUCT_NAME := generic_pure
PRODUCT_DEVICE := generic_pure

PRODUCT_PACKAGES += vndk-sp

这里我们简单的把 PRODUCT_NAME 跟 PRODUCT_DEVICE 都设置为一样了。
还需要把 vndk-sp 加入到系统,不然 emulator 会启动失败。
这里有点想不明白的是 为什么 aosp_x86 不用加 PRODUCT_PACKAGES += vndk-sp
我的 product 继承于 aosp_x86 却要加。有同学搞明白的可以指导一下,非常感谢~

5. 新建 AndroidProducts.mk

AndroidProducts.mk 中定义了变量 PRODUCT_MAKEFILES,在系统编译时会把所有的 PRODUCT_MAKEFILES 都 include 进来。

1
2
PRODUCT_MAKEFILES := \
$(LOCAL_DIR)/generic_pure.mk

这个就是我们在上一步创建的 product 配置文件了。
如果我们的 device 下有多个 product, 就把所有的 product 配置都加到 PRODUCT_MAKEFILES 变量里就行。

6. 编译验证

至此,我们就定义好了一个最简单的 product 出来了, 目录结构如下:

1
2
3
4
5
6
qiushao
└── generic_pure
├── AndroidProducts.mk
├── BoardConfig.mk
├── generic_pure.mk
└── vendorsetup.sh

接下来编译验证一下看看能否用虚拟机跑起来,如果跑不起来请仔细核对一下以上步骤是否有问题。
还有参考的 product 也要选择正解,一开始我选择继承 mini-emulator-x86 产品的配置的,但编译完是跑不起来的。具体原因还搞不清楚。
具体编译,验证步骤参考前两个章节 Android系统开发–3.Android源码编译