一个简单的HIDL开发笔记
个人学习总结
一、创造HIDL实例相关文件
创建自己的HAL层文件目录
这里我以我自己的源码目录platform/vendor/mediatek/hardware/interfaces目录为例,在该目录下创建cyhhidl目录;
1
2
cd vendor/mediatek/hardware/interfaces
mkdir -p cyhhidl/1.0/default
在版本目录(即1.0)中创建接口描述文件ICyhHidl.hal; 文件内容如下:
package vendor.mediatek.hardware.cyhhidl@1.0;
interface ICyhHidl {
helloWorld(string name) generates (string result);
};
内部实现了一个helloWorld方法,传入string类型对象并返回string类型对象。
使用工具和脚本生成所需文件
使用hidl-gen生成HAL相关文件,使用脚本更新Makefile文件,自动生成Android.bp,Android.mk;
根据自身情况设定好对应的值,其中TT是固定的,不用修改
1
2
3
4
LOC=vendor/mediate/hardware/interfaces/cyhhidl/1.0/default
PACKAGE=vendor.mediatek.hardware.cyhhidl@1.0
SS=vendor.mediatek.hardware:vendor/mediatek/hardware/interfaces
TT=android.hidl:system/libhidl/transport
hidl-gen工具代码路径在system/tools/hidl,通过mmm编译后会在out 下生成out/host/linux-x86/bin/hidl-gen,之后在命令行中执行(编译hidl-gen开始就在源码根目录,vendor目录下要修改相应的内容)
1
./out/host/linux-x86/bin/hidl-gen -o . -Landroidbp -r$SS -r$TT $PPACKAGE
这样就在1.0目录下生成了Android.bp文件,修改hal文件接口后,需要使用hidl-gen添加对应的哈希:
1
./out/host/linux-x86/bin/hidl-gen -L hash -r$SS -r$TT $PPACKAGE
一般是在vendor/mediatek/hardware/interfaces/current.txt文件内按字母升序添加,接下来生成default目录用于实现服务端的c++代码及bp描述文件,执行以下命令
1
./out/host/linux-x86/bin/hidl-gen -o $LOC -Lc++-impl -r$SS -r$TT $PPACKAGE
这样就在default目录生成了CyhHidl.cpp和CyhHidl.h文件,接着执行以下命令:
1
./out/host/linux-x86/bin/hidl-gen -o $LOC -Landroidbp-impl -r$SS -r$TT $PPACKAGE
这样就在default目录下生成了Android.bp文件。 命令行中的-o 需要视情况使用,将会覆盖生成代码,以免造成已修改的服务端代码被覆盖掉。 至此就生成了HIDL服务所需要的文件: 1.0/Android.bp和1.0/default/CyhHidl.h和1.0/default/CyhHidl.cpp,其中1.0/Android.bp内容如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
// This file is autogenerated by hidl-gen -Landroidbp.
hidl_interface {
name: "vendor.mediatek.hardware.cyhhidl@1.0",
root: "vendor.mediatek.hardware",
srcs: [
"ICyhHidl.hal",
],
interfaces: [
"android.hidl.base@1.0",
],
gen_java: true,
}
之后我们就要具体实现内部的方法
二、实现HAL
在CyhHidl.h文件内可以定义实现的实例是哪种模式(直通Passthrough模式和绑定Binderized模式,使用直通式HIDL 需要打开屏蔽了的HIDL_FETCH_name方法,并在实现返回。),之后实现对应的.cpp文件内的函数;这里不对CyhHidl.h做改动,使用Binderized模式。 1.0/default/CyhHidl.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#ifndef VENDOR_MEDIATEK_HARDWARE_CYHHIDL_V1_0_CYHHIDL_H
#define VENDOR_MEDIATEK_HARDWARE_CYHHIDL_V1_0_CYHHIDL_H
#include <vendor/mediatek/hardware/cyhhidl/1.0/ICyhHidl.h>
#include <hidl/MQDescriptor.h>
#include <hidl/Status.h>
namespace vendor {
namespace mediatek {
namespace hardware {
namespace cyhhidl {
namespace V1_0 {
namespace implementation {
using ::android::hardware::hidl_array;
using ::android::hardware::hidl_memory;
using ::android::hardware::hidl_string;
using ::android::hardware::hidl_vec;
using ::android::hardware::Return;
using ::android::hardware::Void;
using ::android::sp;
struct CyhHidl : public ICyhHidl {
// Methods from ::vendor::mediatek::hardware::cyhhidl::V1_0::ICyhHidl follow.
Return<void> helloWorld(const hidl_string& name, helloWorld_cb _hidl_cb) override;
// Methods from ::android::hidl::base::V1_0::IBase follow.
};
// FIXME: most likely delete, this is only for passthrough implementations
// extern "C" ICyhHidl* HIDL_FETCH_ICyhHidl(const char* name);
} // namespace implementation
} // namespace V1_0
} // namespace cyhhidl
} // namespace hardware
} // namespace mediatek
} // namespace vendor
#endif // VENDOR_MEDIATEK_HARDWARE_CYHHIDL_V1_0_CYHHIDL_H
1.0/default/CyhHidl.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include "CyhHidl.h"
namespace vendor {
namespace mediatek {
namespace hardware {
namespace cyhhidl {
namespace V1_0 {
namespace implementation {
// Methods from ::vendor::mediatek::hardware::cyhhidl::V1_0::ICyhHidl follow.
Return<void> CyhHidl::helloWorld(const hidl_string& name, helloWorld_cb _hidl_cb) {
// TODO implement
char buf[100];
::memset(buf, 0x00, 100);
::snprintf(buf, 100, "Hello World, %s", name.c_str());
hidl_string result(buf);
_hidl_cb(result);
return Void();
}
// Methods from ::android::hidl::base::V1_0::IBase follow.
//ICyhHidl* HIDL_FETCH_ICyhHidl(const char* /* name */) {
//return new CyhHidl();
//}
//
} // namespace implementation
} // namespace V1_0
} // namespace cyhhidl
} // namespace hardware
} // namespace mediatek
} // namespace vendor
之后通过1.0/default/Android.bp文件看出之后会通过这几个文件生成一个vendor.mediatek.hardware.cyhhide@1.0-impl.so的库,在vendor/lib64/hw/目录下。
1.0/default/Android.bp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
cc_library_shared {
name: "vendor.mediatek.hardware.cyhhidl@1.0-impl",
relative_install_path: "hw",
proprietary: true,
srcs: [
"CyhHidl.cpp",
],
shared_libs: [
"libhidlbase",
"libhidltransport",
"libutils",
"vendor.mediatek.hardware.cyhhidl@1.0",
],
}
三、调用HAL流程
HIDL软件包中自动生成的文件会链接到与软件包同名的单个共享库,该共享库还会导出单个头文件ICyhHidl.h,用于在binder客户端和服务端的接口文件,下图是官网对IFoo.hal(对应ICyhHidlTest.hal)编译后生成的文件走向:
- IFoo.h(对应ICyhHidl.h):描述C++类中的IFoo(ICyhHidl)接口;包含IFoo.hal(对应ICyhHidl.hal)文件中的IFoo(ICyhHidl)接口中所定义的方法和类型,必要时会转换成C++类型。不包含与用于实现此接口的RPC机制(例如HwBinder)相关的详细信息。类的命名空间包含软件包名称和版本号(例::android:hardware::samples::IFoo::V1_0)。客户端和服务端都包含此标头:客户端用它来调用方法,服务器用它来实现这些方法;
- IHwFoo.h:头文件,其中包含用于对接口中使用的数据类型进行序列化的函数声明。开发者不得包含其标头(它不包含任何类); 3.BpFoo.h:从IFoo继承的类,可描述接口的HwBinder代理(客户端)实现。开发者不得直接引用此类;
- BnFoo.h:保存对IFoo实现的应用类,可描述接口的HwBinder存根(服务器端)实现,开发者不得直接引用此类;
- FooAll.cpp:包含HwBinder代理和HwBinder存根的实现类。当客户端调用接口方法时,代理会自动从客户端封装参数,并将事务发送到绑定内核驱动程序,该内核驱动程序会将事务传送到另一端的存根(该存根随后会调用实际服务器实现)。
独立于HIDL使用的RPC机制的唯一一个自动生成的文件时IFoo.h,其他所有文件都与HIDL使用的HwBinder RPC机制相关联。因此客户端和服务端不得直接引用除IFoo之外的任何内容,所以需要只包含IFoo.h并链接到生成的共享库。
开发的实例会用到以下的几个模块
- vendor.mediatek.hardware.cyhhidl@1.0-impl.so:CyhHidl模块实现端的代码,编译生成,binder server端;
- vendor.mediatek.hardware.cyhhidl@1.0.so:cyhHidl模块调用端的代码,binder clinent端;
- cyhHidl_hal_service:通过直通式注册binder service,暴露接口给lclient调用;
- vendor.mediatek.hardware.cyhhidl@1.0-service.rc:Android natice进程入口
四、HIDL Service和Client端测试代码实现
Service端测试代码service.cpp 1.0/default/service.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#define LOG_TAG "vendor.mediatek.hardware.cyhhidl@1.0-service"
#define LOG_NDEBUG 0
#include <log/log.h>
#include <android-base/logging.h>
#include <hidl/LegacySupport.h>
#include <hidl/HidlTransportSupport.h>
#include "CyhHidl.h"
using vendor::mediatek::hardware::cyhhidl::V1_0::implementation::CyhHidl;
using android::hardware::configureRpcThreadpool;
using android::hardware::joinRpcThreadpool;
int main() {
configureRpcThreadpool(4, true);
CyhHidl cyhhidl;
auto status = cyhhidl.registerAsService();
CHECK_EQ(status, android::OK) << "Failed to register cyhhidl HAL implementation";
ALOGD("register cyhhidl HAL success");
joinRpcThreadpool();
return 0; // joinRpcThreadpool shouldn't exit
}
Clint端测试代码client.cpp 1.0/default/client.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#define LOG_TAG "vendor.mediatek.hardware.cyhhidl@1.0-client"
#include <vendor/mediatek/hardware/cyhhidl/1.0/ICyhHidl.h>
#include <hidl/LegacySupport.h>
#include <stdio.h>
// Generated HIDL files
using vendor::mediatek::hardware::cyhhidl::V1_0::ICyhHidl;
using android::sp;
using android::hardware::hidl_string;
int main() {
android::sp<ICyhHidl> service = ICyhHidl::getService();
if (service == nullptr) {
printf("Failed get cyhhidl service!\n");
return -1;
}
printf("Success get cyhhidl service!\n");
service->helloWorld("Cyh",[&](hidl_string result){
printf("%s\n",result.c_str());
});
return 0;
}
在Android.bp文件内加入编译配置 1.0/default/Android.bp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
cc_binary {
name: "vendor.mediatek.hardware.cyhhdil@1.0-service",
relative_install_path: "hw",
defaults: ["hidl_defaults"],
init_rc: ["vendor.mediatek.hardware.cyhhidl@1.0-service.rc"],
srcs: ["service.cpp","CyhHidl.cpp"],
proprietary: true,
shared_libs: [
"libbase",
"liblog",
"libhardware",
"libhidlbase",
"libhidltransport",
"libutils",
"vendor.mediatek.hardware.cyhhidl@1.0",
],
cflags: [
"-Werror",
"-Wno-unused-parameter",
],
}
// 添加客户端测试时使用
cc_binary {
name: "vendor.mediatek.hardware.cyhhidl@1.0-client",
relative_install_path: "hw",
defaults: ["hidl_defaults"],
srcs: ["client.cpp"],
shared_libs: [
"liblog",
"libbase",
"libdl",
"libutils",
"libhardware",
"libhidlbase",
"libhidltransport",
"vendor.mediatek.hardware.cyhhidl@1.0",
],
}
五、启动binder server进程 创建服务的rc文件 ,设定服务开机自启,vendor.mediatek.hardware.cyhhidl@1.0-service.rc,文件内容如下:
1
2
3
4
service hal-cyhhidl-1-0 /vendor/bin/hw/vendor.mediatek.hardware.cyhhidl@1.0-service
class hal
user system
group system
设定服务的selinux权限,在device/mediatek/mt2712/sepolicy/non_plat目录下: 在file_contexts文件上下文添加定义:
1
/system/bin/hw/android\.hardware\.cyhhidl@1.\0-service u:object_r:hal_cyhhidl_default_exec:s0
在hwservice_contexts文件上下文添加定义
1
vendor.mediatek.hardware.cyhhidl::ICyhHidl u:object_r:hal_cyhhidl_hwservice:s0
在attributes文件上下文添加属性定义
1
2
3
4
# hal cyhhidl
#attribute hal_cyhhidl;
#attribute hal_cyhhidl_client;
#attribute hal_cyhhidl_server;
新增hal_cyhhidl.te权限文件
1
2
3
4
5
6
7
8
# HwBinder IPC from client to server, and callbacks
binder_call(hal_cyhhidl_client, hal_cyhhidl_server);
binder_call(hal_cyhhidl_server, hal_cyhhidl_client);
add_hwservice(hal_cyhhidl_server, hal_cyhhidl_hwservice);
allow hal_cyhhidl_client hal_cyhhidl_hwservice:hwservice_manager find;
allow shell vendor_file:file { getattr };
allow shell hal_cyhhidl_hwservice:hwservice { find };
新增hal_cyhhidl_default.te**默认权限文件
1
2
3
4
5
type hal_cyhhidl_default, domain,coredomain;
hal_server_domain(hal_cyhhidl_default, hal_cyhhidl)
type hal_cyhhidl_default_exec, exec_type, file_type;
init_daemon_domain(hal_cyhhidl_default)
添加hidl配置,在device/mediatek/mt2712/manifest.xml和manifest_ab.xml中
1
2
3
4
5
6
7
8
9
<hal format="hidl">
<name>vendor.mediatek.hardware.cyhhidl</name>
<transport>hwbinder</transport>
<version>1.0</version>
<interface>
<name>ICyhHidl</name>
<instance>default</instance>
</interface>
</hal>
device.mk配置添加编译服务端
1
2
PRODUCT_PACKAGES += \
vendor.mediatek.hardware.cyhhidl@1.0-service
六、运行结果
开机执行
1
2
adb shell
ps -A | grep cyhhidl
输入
1
2
su
./vendor/bin/hw/vendor.mediatek.hardware.cyhhidl@1.0-client
执行客户端程序,显示如下:
过程中遇到的问题
VNDK验证问题
VNDK只针对hardware下生成的so库,在vendor下开发的HIDL不需要
编译时在设置SELinux权限时报错
1
2
libsepol.report_failure: neverallow on line 1016 of system/sepolicy/public/domain.te (or line 11338 of policy.conf) violated by allow hal_cyhhidl_default hal_cyhhidl_default_exec:file { execute };
libsepol.check_assertions: 1 neverallow failures occurred
这是hal_cyhhidl_default.te文件内部的语法错误导致,之后修改后就没有再出现。
服务编译不过,报出错误error: undefined reference to ‘VTT for android::hardware:xxxx
原因在于我的service.cpp引入了CyhHidl虚函数,但是没有引入对应的资源文件,
所以之后在Android.bp文件内对应的srcs改为 srcs: [“service.cpp”,”CyhHidl.cpp”],相当于把impl和service整合,其实就不需要impl了,可以去除;
之后会有error: unused parameter ‘_hidl_cb’ [-Werror,-Wunused-parameter]错误,在Android.bp内加入
1
2
3
4
cflags: [
"-Werror",
"-Wno-unused-parameter",
],
则成功编译通过
服务开机没有自启动
发现生成的文件是在vendor/bin/hw目录下,而rc文件内写成system/bin/hw下,这个生成目录位置要研究一下,修改下rc文件启动的文件目录即可启动service。
修改rc文件后开机没有自启
最后发现定义属性的时候要在/device/mediatek/mt2712/sepolicy/plat_public/attributes文件下,之前是在/device/mediatek/mt2712/sepolicy/non_plat/attributes,应该HIDL服务在vendor和hardware开发的区别,之后有待验证
再次编译后烧录,之后就可以看到服务正在运行