HAL: 将 HIDL 接口改造为 Stable AIDL
目录
2.1 使用update-api freeze-api管理接口版本
2.2 配置 Framework Compatibility Matrix (FCM,兼容性矩阵)
4.2 创建 hal_sensorscalibrate_default.te
5. java层调用service(含sepolicy配置)
欢迎指正交流,谢谢!
1. 定义新的AIDL接口
目标:将旧接口全部转换为新接口
验证目标:肉眼检查所有的接口文件已转换,即可
我们使用AOSP自带的转换工具:hidl2aidl,将旧版hidl接口转换为aidl接口。
1.1 编译hidl2aidl工具
编译可以在任意位置执行,这里以vendor根目录为例
source /opt/conf/openjdk18.conf
source build/envsetup.sh
lunch 项目名-userdebug
m hidl2aidl
1.2 执行转换
运行hidl2aidl工具。它会读取我们指定的hidl接口,并生成转换后的aidl。
在命令里,我们需要指定hidl包的所在位置,不然工具会找不到对应的hidl包。
hidl2aidl -o 输出路径 -r hidl包名:hidl包根目录(即/1.0的上层目录) hidl包名@要转换的hidl版本
其中的输出路径,我们创建一个aidl文件夹,此文件夹与hidl接口(1.0、2.0等文件夹)同级。冒号两侧不能有空格。
例如:
hidl2aidl -o vendorabcd/hardware/interfaces/sensorscalibrate/aidl/ -r vendorabcd.hardware.sensorscalibrate:vendorabcd/hardware/interfaces/sensorscalibrate vendorabcd.hardware.sensorscalibrate@1.0
1.3 调整编译规则(bp文件)
首先检查转换生成的文件。转换会生成 .aidl 和 Android.bp 两种文件。
检查所在路径,要与包名匹配。例如包名:vendorabcd.hardware.sensorscalibrate,文件路径应为: aidl/vendorabcd/hardware/sensorscalibrate/ISensorsCalibrate.aidl
生成的bp文件需要修改,示例如下:(红色部分为需要修改的部分)
aidl_interface {
name: "vendorabcd.hardware.sensorscalibrate",
vendor_available: true, //这里设置为true,因为是vendor目录下的模块
owner: "vendorabcd", //作为vendor模块,需要设定owner才可以通过编译
class="lazy" data-srcs: ["vendorabcd/hardware/sensorscalibrate) {
return new SensorsCalibrate();
}//hidl的FETCH方法现在不需要,删除
} // namespace implementation
} // namespace V1_0} // namespace sensorscalibrate
} // namespace hardware
} // namespace vendorabcd
} // namespace aidl
3.2 创建service编译规则
要配置编译规则,我们在default目录下,继续添加以下三个文件:Android.mk/bp .rc .xml
指定service的编译规则。
具体使用mk文件还是bp文件,可以和hidl service保持一致。bp是更现代的方式;但如果要引用mk定义的模块,则也必须使用mk。因此,和hidl版本保持一致是最方便的选择。
例如,hidl版本此service的Android.mk文件如下。我们直接在其上进行修改,用作aidl版本service的mk文件:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := vendorabcd.hardware.sensorscalibrate@1.0-serviceLOCAL_MODULE := vendorabcd.hardware.sensorscalibrate-service
LOCAL_PROPRIETARY_MODULE := true
LOCAL_MODULE_CLASS := EXECUTABLES
LOCAL_MODULE_RELATIVE_PATH := hw
LOCAL_INIT_RC := vendorabcd.hardware.sensorscalibrate@1.0-service.rcLOCAL_INIT_RC := vendorabcd.hardware.sensorscalibrate-service.rc
//声明此service对aidl接口的依赖。之后,我们将创建这个文件。
LOCAL_VINTF_FRAGMENTS := vendorabcd.hardware.sensorscalibrate.xml
LOCAL_class="lazy" data-src_FILES := \
SensorsCalibrate.cpp \
service.cpp
LOCAL_SHARED_LIBRARIES := \
libhidlbase \
libhidltransport \libbinder_ndk \ //Stable aidl必需
libutils \
libdl \
liblog \
libcutils \
libhardware \
libbase \
vendorabcd.hardware.sensorscalibrate@1.0 \// 包名-V1-ndk_platform
vendorabcd.hardware.sensorscalibrate-V1-ndk_platform \
libhfmanager
include $(BUILD_EXECUTABLE)
文件名:vendorabcd.hardware.sensorscalibrate-service.rc
配置开机启动。
此rc文件将被init.rc链接。我们可以在此文件中,指定开机时启动aidl service。示例如下:
service vendor.sensorscalibrate-hal /vendor/bin/hw/vendorabcd.hardware.sensorscalibrate-service
//service + 注册名(可随意起一个) + 编译后的模块所在路径
class hal
user system
group system
文件名:vendorabcd.hardware.sensorscalibrate.xml
我把aidl接口的manifest转移到此处,因为service依赖于aidl。如果未来不需此service,则aidl接口也不编译。这一依赖关系,我们已在Android.mk中的LOCAL_VINTF_FRAGMENTS声明过,此项配置指向的xml文件即为我们现在创建的xml文件。
把2.3步中的aidl manifest删除,将其中内容转移到default目录下的此文件中。
内容原样复制即可:
<manifest version="1.0" type="device">
vendorabcd.hardware.sensorscalibrate
1
ISensorsCalibrate
default
在default目录下运行mm进行编译。(请记得先在vendor根目录下执行source build/envsetup.sh 和 lunch)现在应该能够完成模块编译。
编译成功后,在out/target/product/
3.3 将service添加进系统
修改device.mk文件,以包含此service。
我们首先找到hidl版本的编译声明,通常位置为device/vendorabcd/
例如,我找到这样的hidl编译配置:
PRODUCT_PACKAGES += vendorabcd.hardware.sensorscalibrate@1.0-service \
vendorabcd.hardware.sensorscalibrate@1.0-service.rc \
vendorabcd.hardware.sensorscalibrate@1.0 \
vendorabcd.hardware.sensorscalibrate-V1.0-java \
vendorabcd-hardware-sensorscalibrate.xml
DEVICE_MANIFEST_FILE += device/vendorabcd/chipset/mgvi/manifest_sensorscalibrate.xml
升级到aidl以后,只需配置好service,对应的xml即会自动加入编译。
把以上的内容全部删除。替换为:
PRODUCT_PACKAGES += vendorabcd.hardware.sensorscalibrate-service
(可选)现在可以全编译一下,确认能够正常完成整个系统的全编译。
3章报错速查
FAILED: ninja: '.xml', needed by 'out_hal/(...)/.xml', missing and no known rule to make it
解释:ninja错误:第2个路径xml中,需求了第1个路径的xml,但是找不到对应的文件
错误原因:可能因为manifest中的旧版本没有删除干净,导致编译器寻找已被删除的xml。
解决方法:检查 DEVICE_MANIFEST_FILE 中旧版的xml配置,并删除干净
ld.lld: error: undefined symbol: >>> the vtable symbol may be undefined because the class is missing its key function
解释:clang错误:没有找到对应的类,因为没有实现对应的虚函数。
错误原因:cpp编写有误,例如实现函数时未声明namespace :SensorsCalibrate::SensorsCal() 错写成 SensorsCal()
解决方法:检查cpp文件并修改
4. 确保service开机启动
目标:开机时service能够自启动
验证目标:开机后,执行adb shell service list,能看到新的service
本章将要配置SEpolicy以确保service开机启动。SEpolicy是引入了SELinux(kernel)后所需求的一种安全策略。SELinux会默认阻止一些关键操作(例如文件操作,ioctl等),我们需要逐条声明后,才能合法进行这些操作。
4.0 开始配置SEpolicy
在第3章最后,我们能编译得到一个新的系统版本。但在这个版本中,service不会随开机启动,因为我们还没有配置SEpolicy。一种更具有演示性的做法是,我们收集log中的拒绝信息,再对应配置SEpolicy。然而,由于全编译一次的时间成本过高,在这里,我们先配置完所需的SEpolicy,再检查是否能自动启动。
需要注意的是,在配置SEpolicy相关文件时,每个文件末尾必须另有一行空白行,否则会在编译时遇到无法预知的错误。具体原因我也不太清楚。
4.1 添加新feature目录
文件vendor/vendorabcd/security/sepolicy_vendor/features/sepolicy_features_vendor.mk 中定义了vendorabcd的vendor相关sepolicy。我们按格式增加一个feature。
添加如下一行:
BOARD_SEPOLICY_DIRS += $(LOCAL_PATH)/sensorscalibrate/vendor
并在此目录下创建/sensorscalibrate/vendor目录。进入这个新创建的目录。
4.2 创建 hal_sensorscalibrate_default.te
创建一个文件,名为hal_sensorscalibrate_default.te,内容如下:
type hal_sensorscalibrate_default, domain;
type hal_sensorscalibrate_default_exec, exec_type, vendor_file_type, file_type;
init_daemon_domain(hal_sensorscalibrate_default)
其中的init_daemon_domain是一段宏,可以自动帮助我们完成与init相关的policy声明。这段宏的括号中的输入x应为一个domain(即本文件,在第一行定义),并有名为x_exec的对象被定义为一个executable(在第二行定义)。第一行和第二行满足了init_daemon_domain对输入的需求。
我们现在还没有把 hal_sensorscalibrate_default_exec 与实际文件相绑定。于是另创建一个文件,名为file_contexts。
4.3 创建 file_contexts
file_contexts负责声明实际文件为某个SEpolicy中的对象。
我们将手机中service可执行文件的路径,声明为hal_sensorscalibrate_default_exec。
创建一个文件,名为file_contexts,内容如下:
/(vendor|system/vendor)/bin/hw/vendorabcd\.hardware\.sensorscalibrate-service u:object_r:hal_sensorscalibrate_default_exec:s0
4.1 无法启动时的排查方法
1. 确认service本身可正常运行
我们首先尝试手动启动服务,以收集相关log。
在adb shell内,切换到su用户。将SEpolicy调整到宽容模式,放行未声明的操作。
su root
setenforce 0
getenforce #检查是否打开宽容模式(输出Permissive为宽容模式)
启动服务:
/vendor/bin/hw/vendorabcd.hardware.sensorscalibrate-service
检查服务是否正常启动。不要关闭前一个终端窗口,另开一个窗口,在adb shell中执行
service list | grep sensorscalibrate
有如下输出,说明启动成功:
159 vendorabcd.hardware.sensorscalibrate.ISensorsCalibrate/default: [vendorabcd.hardware.sensorscalibrate.ISensorsCalibrate]
如果方括号[ ]内没有内容,可能是未成功打开宽容模式。
2. 检查启动阶段的log
dmesg是linux提供的内核log工具,与系统启动相关的log在其中有记录。
打开终端,进入sudo模式,收集本次启动的dmesg:
adb root
adb shell dmesg > dmesg.txt
如果看到类似于以下的dmesg内容,说明系统启动时,由于SEpolicy阻拦,service启动失败:
[ 10.220035] init: init 27: [10038][0]Could not start service 'vendor.sensorscalibrate-hal' as part of class 'hal': File /vendor/bin/hw/vendorabcd.hardware.sensorscalibrate-service(labeled "u:object_r:vendor_file:s0") has incorrect label or no domain transition from u:r:init:s0 to another SELinux domain defined. Have you configured your service correctly?
3 查看系统运行阶段的log
在宽容模式下,系统不会阻止未声明的操作,但仍记录这些操作的拒绝记录。我们可以通过拒绝记录来添加对应的声明。
在adb shell中执行
logcat | grep sensorscalibrate
若看到有如下log,记录了被拒绝的的操作:
11-10 16:29:41.311 633 633 E SELinux : avc: denied { find } for pid=3223 uid=2000 name=vendorabcd.hardware.sensorscalibrate.ISensorsCalibrate/default scontext=u:r:shell:s0 tcontext=u:object_r:default_android_service:s0 tclass=service_manager permissive=1
#这个log的意思是我试图从shell启动service但是失败了
log的格式解释如下:
avc: denied { 操作权限 } for pid=7201 comm=“进程名” scontext=u:r:源类型:s0 tcontext=u:r:目标类型:s0 tclass=访问类别 permissive=0
修改步骤:
找相应的“源类型.te ”文件
按如下格式在该文件中添加:
allow 源类型 目标类型:访问类别 {权限};
allow vendorabcd_app hal_sensorscalibrate_default:binder {call};
5. java层调用service(含sepolicy配置)
后面写不动了,先到这里吧。
在msi镜像中,也要定义同样的aidl接口,这样才能进行aidl通信。文件内容使用和vendor中完全相同即可。
推荐阅读:
sepolicy进阶小记 - LibXZR 的小本本 解释了许多关键的sepolicy宏,很有帮助
AIDL for HALs - Code Inside Out 一个AIDL for HAL全流程的文档,比较细
来源地址:https://blog.csdn.net/weixin_60253080/article/details/127810200
免责声明:
① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。
② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341