1. 需求描述
一般OEM厂商进行开发时,不会和芯片原厂lunch同一个device。因为一款芯片平台上不太可能就做一款产品,一般会做多款产品,就像手机一样,qcom平台上比如865,手机厂商发布865平台手机一般至少2款以上,有的要达到4款甚至更多,那为了代码复用方便,一般不会给某个产品单独拉分支,因为多分支的维护实在太耗费时间和人力。而添加产品自己device就是一个最好的方案。新增device的优势:
(1). 可以实现同芯片平台上,不同产品根据产品形态进行个性化配置,common部分复用,个性化配置则配置在自己添加的device mk。
(2). 子仓库编译过滤,比如有些子仓库中的代码逻辑产品1需要,产品2不需要,那么在该子仓库编译时就可以通过TARGET_DEVICE或者TARGET_PRODUCT进行过滤区分不参与编译,实现上更为灵活。
(3). Android系统层公共部分逻辑有可复用性,降低研发人力维护成本。
2. 需求实现思路
阅读本部分之前,请先阅读Android框架探索篇之source lunch配置原理篇章,更有利于理解,且流程性的重复内容不再赘述。
RK原始的RKDOC文件夹下有相关新增device的指导文档,但是本文不采用文档描述的方案;原因是按照RKDOC下进行的device新增目录结构不清晰,因为我们相当于OEM厂商角色,所添加的device在目录结构上应该和芯片device做到分离且清晰,RKDOC中的方案是直接把新增device放入到对应rk自己芯片lunch 的device里面了,这样就导致目录结构上特别不清晰。所以本文不采用RKDOC中方案实现。以一个逻辑图表述的话如下图1所示:, 为RKDOC的实现逻辑
图1 RKDOC方案目录结构
实际产品device的对应资源都放入了rk3568s目录里,并在里面新创建device文件夹实现。
实际个人考量后,将相应产品device资源目录分离出来,比如这里以rk3568平台上增加一个device名为lux为例。实际拆分后的目录结构如图2所示:
图2 修改后目录框图
一般新增的device目录在源码SDK里结构层级按照如下目录添加device/公司名/device名, 这里样例就是device/dami/lux
3. 需求开发
以篇章Android框架探索篇之source lunch配置原理总结为基础, 核心所必要的文件包含AndroidProduct.mk, lux.mk, BoardConfig.mk,接下来分别给出这几个核心文件的内容以及相应说明。AndroidProduct.mk文件如下图3所示:
PRODUCT_MAKEFILES := \
$(LOCAL_DIR)/lux.mk
COMMON_LUNCH_CHOICES := \
lux-userdebug \
lux-user
图3 AndroidProduct.mk文件
COMMON_LUNCH_CHOICES 是被编译框架直接使用get_build_var获取到,进行lunch选项枚举,所以这里我们把自己的产品 lunch添加进去;PRODUCT_MAKEFILES则是在编译框架中进行解析并赋值给all_product_config,并在后续编译框架中得到current_product_makefile, 因为我们的产品名为lux,所以这里的PRODUCT_MAKEFILES中就一定要是lux.mk,因为在编译框架内部会做匹配性检查, 赋值成别的mk会直接报错拦截掉。
接下来是lux.mk内容,如图4所示:
include device/rockchip/rk368/rk3568_s/rk3568_s.mk
include device/dami/lux/BoardConfig.mk
PRODUCT_NAME := lux
PRODUCT_DEVICE := lux
PRODUCT_BRAND := lux
PRODUCT_MODEL := lux
PRODUCT_MANUFACTURER := dami
图4 lux.mk文件内容
注意因为我们做目录分离了,所以lux.mk上来的第一步就是要包含芯片device的配置,所以需要先包含原来的rk3568_s中的mk文件, 同时也包含自己的BoardConfig.mk文件,之后PRODUCT_NAME和PRODUCT_DEVICE被赋值为lux,同样这两个变量也会被编译系统解析用来做匹配一致性检查。不可以赋值为其他的值。 所以其他模块研发如果想用来进行区分不同产品时,除了可以使用TARGET_PRODUCT, TARGET_DEVICE,也可以使用PRODUCT_NAME, PRODUCT_DEVICE。
这里为了统一PRODUCT_BRAND和PRODUCT_MODEL也同样赋值为lux,此刻抛出一个问题,问题1:其他模块研发同事是否可以使用PRODUCT_BRAND或者PRODUCT_MODEL用来作为判断不同产品的依据。反正图中的配置不也都是产品名lux吗?
答案是不要用,即使一样也不要用;甚至在手机厂是禁止使用PRODUCT_BRAND 或PRODUCT_MODEL 来区分不同产品标志。为何, 原因就是由编译框架探索篇章可知,PRODUCT_NAME以及PRODUCT_DEVICE在编译框架中被强制用来做匹配性判断,所以这两个值在系统中是绝不会被更改为其他值的,因为只要更改就一定会在lunch阶段报错了,但是PRODUCT_BRAND和PRODUCT_MODEL这两个值则不被做强制检查,这也就代表着这两个值是可以被OEM自行定义的。所以不能用来做不同产品区分的依据。
为何着重突出在手机厂是禁用呢,是因为手机由于产品形态和领域决定,保密性要求比较高,前期研发期间为了保密,PRODUCT_BRAND和PRODUCT_MODEL是会和PRODUCT_NAME保持一致的,因为如果在研发阶段就直接配成产品实际名就会造成泄密风险, 毕竟此时产品刚处于启动阶段,如果直接配置成产品名,则参与研发同事就直接知道目前做的是下一代哪款手机了,泄密风险大大提高;同样这就给刚接触Android开发的同事造成了误导以为PRODUCT_BRAND 和 PRODUCT_MODEL也能用来进行不同产品区分,结果临近发布前,这两个值被修改了,修改成了对应的手机实际品牌,比如Huawai nova, 或者是xiaomi MIX3, 或者oppo findx7等等类似其他Android手机产品;这时之前其他模块同事使用这两个值做filter过滤时的逻辑就出现偏差了。导致原本本该包含进去的功能由于值被修改了使得Android.mk中的判断逻辑被过滤掉仓库功能代码包含不进去,造成质量回溯。
接下来就是产品的BoardConfig.mk文件,如下图5所示:
BOARD_USES_AB_IMAGE := false
图5 BoardConfig.mk
没有过多需要补充的, 除此之外还有Android.mk以及AndroidBoard.mk文件,AndroidBoard.mk可以直接包含rk3568s中的,Android.mk则是通用格式,内容分别如图6和如图7所示:
include device/rockchip/rk3568/rk3568_s/AndroidBoard.mk
图6 AndridBoard.mk
LOCAL_PATH := $(call my-dir)
include $(call all-makefiles-under,$(LOCAL_PATH))
图7 Android.mk
主要文件添加好后,我们需要实际进行自己device编译烧录测试看下系统是否正常。
4.编译问题处理
上述核心文件添加完成后,进行实际编译时有编译包报错,特此记录。
编译问题1:
FAILED: ninja: 'device/dami/lux/bt_vendor.conf', needed by 'out/target/product/lux/vendor/etc/bluetooth/bt_vendor.conf', missing and no known rule to make it
原因分析:
因为我们自己添加的device也会包含common目录下编译资源,所以common目录下的相关编译逻辑也是同样在运行。关键点如下:
文件路径:
device/rockchip/common/device.mk
关键内容如图8所示:
PRODUCT_COPY_FILES += \
$(TARGET_DEVICE_DIR)/bt_vendor.conf:/vendor/etc/bluetooth/bt_vendor.conf
图8 device.mk内容
其中TARGET_DEVICE_DIR 在编译框架中会被强制赋值为当前device路径,赋值位置如下图9所示:
文件路径:
build/core/board_config.mk
TARGET_DEVICE_DIR := $(patsubst %/,%,$(dir $(board_config_mk)))
图9 TARGET_DEVICE_DIR 赋值
board_config_mk的路径进行分割赋值给TARGET_DEVICE_DIR , 所以当我们lunch自己添加的新device lux时,TARGET_DEVICE_DIR变量的是就是device/dami/lux,并且使用PRODUCT_COPY_FILES 要实现bt_vendor.conf配置文件的copy,而文件源缺失引起报错。
解决措施:
从之前rk3568s中将对应bt_vendor.conf重新copy一份到lux目录下即可
编译问题2;
FAILED: ninja: 'device/dami/lux/media_profiles_default.xml', needed by 'out/target/product/lux/vendor/etc/media_profiles_V1_0.xml', missing and no known rule to make it
原因同问题1不再赘述
5. 测试验证与运行问题处理
开始整编Android,实际烧录进行测试
source build/envsetup.sh
lunch lux-userdebug
./build.sh -UKAup
5.1 运行问题
烧录后发现卡在Recover阶段不动,抓取日志关键信息如下:
E:[libfs_mgr]ReadFstabFromFile(): cannot open file: '/etc/recovery.fstab': No such file or directory
I:[libfs_mgr]ReadDefaultFstab(): failed to find device default fstab
E:failed to read default fstab
:Check failed: !fstab.empty()
libc: Fatal signal 6 (SIGABRT), code -1 (SI_QUEUE) in tid 254 (recovery), pid 254 (recovery)
[ 225.150394] init: Service 'recovery' (pid 254) received signal 6
[ 225.150456] init: Sending signal 9 to service 'recovery' (pid 254) process group...
[ 225.150643] libprocessgroup: Successfully killed process cgroup uid 0 pid 254 in 0ms
[ 225.151501] init: Untracked pid 256 exited with status 1
[ 230.079919] init: starting service 'recovery'...
[ 230.081953] init: Created socket '/dev/socket/recovery', mode 422, user 1000, group 1000
[ 230.086966] init: cpuset cgroup controller is not mounted!
E:[libfs_mgr]ReadFstabFromFile(): cannot open file: '/etc/recovery.fstab': No such file or directory
I:[libfs_mgr]ReadDefaultFstab(): failed to find device default fstab
E:Failed to read default fstab
exit=================
Recovery系统下缺失fstab文件导致无法启动,fstab是包含分区挂载相关信息的配置文件,正常系统启动时由init进程负责解析处理完成相应挂载动作,Recovery模式下则有对应Recovery系统下的相应逻辑进行解析处理。既然没有,那我们就追踪下对应fstab文件在哪里赋值copy的,由于我们是非AB系统,所以仅追踪recovery.fstab即可。
可以分别在device目录以及build目录下进行快速检测,或者有opengrok也可以检索,最后在build/core目录如下位置检索到:
文件路径:
build/core/Makefile:
关键代码逻辑如图10所示:
recovery_fstab := $(strip $(wildcard $(TARGET_DEVICE_DIR)/recovery.fstab))
图10 recovery_fstab赋值
可以看到recovery_fstab赋值的位置同样是和 TARGET_DEVICE_DIR变量强相关,这就同样要求我们自己的device下面需要包含文件 recovery.fstab, 既然如此的话,就从rk3568s那侧将对应的非编译框架下文件都copy过来,防止有后续问题。
修改后重新编译烧录已经正常,device名同步变更且系统启动正常,如图11所示:
图11 hsr效果
最后整体修改文件清单如下:
AndroidBoard.mk
Android.mk
AndroidProducts.mk
BoardConfig.mk
bt_vendor.conf
config.cfg
config.cfg_ab_gki
dt-overlay.in
lux.mk
media_profiles_default.xml
ota
recovery.fstab
recovery.fstab_AB
其中bt_vendor.conf config.cfg config.cfg_ab_gki dt-overlay.in media_profiles_default.xml recovery.fstab recovery.fstab_AB文件以及ota文件夹由于是从rk3568s侧copy过来的,内容不必进行展示,仅展示修改以及新增的相关mk文件,patch修改如下图12所示:
diff --git a/lux/Android.mk b/lux/Android.mk
new file mode 100644
index 0000000..7a16792
--- /dev/null
+++ b/lux/Android.mk
@@ -0,0 +1,3 @@
+LOCAL_PATH := $(call my-dir)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/lux/AndroidBoard.mk b/lux/AndroidBoard.mk
new file mode 100644
index 0000000..7760ff5
--- /dev/null
+++ b/lux/AndroidBoard.mk
@@ -0,0 +1,3 @@
+# generate fstab file for device
+-include device/rockchip/rk356x/rk3568_s/AndroidBoard.mk
+
diff --git a/lux/AndroidProducts.mk b/lux/AndroidProducts.mk
new file mode 100644
index 0000000..eb30112
--- /dev/null
+++ b/lux/AndroidProducts.mk
@@ -0,0 +1,22 @@
+#
+# Copyright 2014 The Android Open-Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+PRODUCT_MAKEFILES := \
+ $(LOCAL_DIR)/lux.mk
+
+COMMON_LUNCH_CHOICES := \
+ lux-userdebug \
+ lux-user
diff --git a/lux/BoardConfig.mk b/lux/BoardConfig.mk
new file mode 100644
index 0000000..a4b26ca
--- /dev/null
+++ b/lux/BoardConfig.mk
@@ -0,0 +1,19 @@
+#
+# Copyright 2014 The Android Open-Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# AB image definition
+BOARD_USES_AB_IMAGE := false
diff --git a/lux/lux.mk b/lux/lux.mk
new file mode 100644
index 0000000..174756e
--- /dev/null
+++ b/lux/lux.mk
@@ -0,0 +1,25 @@
+#
+# Copyright 2014 The Android Open-Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# First lunching is S, api_level is 31
+include device/rockchip/rk356x/rk3568_s/rk3568_s.mk
+include device/dami/lux/BoardConfig.mk
+
+PRODUCT_NAME := lux
+PRODUCT_DEVICE := lux
+PRODUCT_BRAND := lux
+PRODUCT_MODEL := lux
+PRODUCT_MANUFACTURER := dami
图12 相关device/dami/lux目录下 patch如图12所示
6. 总结
补充一点,新增device满足了在编译阶段进行不同产品的区分,那如果研发想在程序运行阶段进行不同产品区分要如何来实现呢?这个就需要另外一套独立的方案去实现了,一般芯片厂家或者OEM开发厂能够支持HWID和productID功能, HWID用来区分同产品不同批次,productID就用来在程序运行阶段在同芯片平台上区分不同产品, 当然这需要在对应主板上多个产品中同个gpio上的引脚设计不同,比如外接的电阻阻值不同或者其他硬件实现,这样在uboot启动阶段就能通过电压落域区间得出不同产品名,进而设计方案透传到内核,之后到Android系统层,在系统层就可以使用property等属性方式来区分,这部分HWID or PRODUCTID RKDOC中有相似介绍,读者可自行阅读。
新增编译device需求至此结束,如有不当,欢迎指正。