Android HIDL 服务实现
概述
引用Android 官方文档描述:
HAL 接口定义语言(简称 HIDL)是用于指定 HAL 和其用户之间的接口的一种接口描述语言 (IDL)。HIDL 允许指定类型和方法调用(会汇集到接口和软件包中)。从更广泛的意义上来说,HIDL 是指用于在可以独立编译的代码库之间进行通信的系统。从 Android 10 开始,HIDL 已废弃,Android 将在所有位置改用 AIDL 。
HIDL 旨在用于进程间通信 (IPC)。使用 HDL 创建的 HAL 称为绑定式 HAL,因为它们可以使用 Binder 进程间通信 (IPC) 调用与其他架构层进行通信。绑定式 HAL 在独立于使用它们的客户端的进程中运行。对于必须与进程相关联的代码库,还可以使用 透传模式 (在 Java 中不受支持)。
HIDL 可指定数据结构和方法签名,这些内容会整理归类到接口(与类相似)中,而接口会汇集到软件包中。尽管 HIDL 具有一系列不同的关键字,但 C++ 和 Java 程序员对 HIDL 的语法并不陌生。此外,HIDL 还使用 Java 样式的注解
HIDL 服务端实现(C++)
创建HAL 接口
创建目录,创建HAL 接口问题:
mkdir vendor/czx/hardware/interfaces/hello/1.0/default
touch vendor/czx/hardware/interfaces/hello/1.0/IHello.hal
在IHello.hal 中进行接口的声明:
package vendor.czx.hardware.hello@1.0;
interface IHello {
hello(string name) generates (string result);
foo(int32_t a, int32_t b) generates (int32_t result);
print(string s);
};
详细HAL 语法
数据类型 | Android 开源项目 | Android Open Source Project
建立HIDL根目录映射关系
在vendor/czx/hardware/interfaces/hello/1.0目录下创建一个Android.bp文件.
subdirs = [
"*",
]
hidl_package_root {
name: "vendor.czx.hardware",
path: "vendor/czx/hardware/interfaces",
}
生成模板代码
添加脚本update-makefiles.sh
将 hardware/interfaces/update-makefiles.sh 文件拷贝一份到vendor/czx/hardware/interfaces下,
并对update-makefiles.sh
进行修改. 将android.hardware:hardware/interfaces 映射替换成vendor.czx.hardware:vendor/czx/hardware/interfaces.
这里的映射关系要与上面的hidl_package_root的映射保持一致.
#!/bin/bash
source $ANDROID_BUILD_TOP/system/tools/hidl/update-makefiles-helper.sh
do_makefiles_update \
"vendor.czx.hardware:vendor/czx/hardware/interfaces" \
"android.hidl:system/libhidl/transport"
使用脚本生成hal Android.bp
#执行脚本, 生成更新Andorid.bp
./update-makefiles.sh
生成服务端实现代码
PACKAGE=vendor.czx.hardware.hello@1.0
LOC=vendor/czx/hardware/interfaces/hello/1.0/default
#生成Hello.h,Hello.cpp.
hidl-gen -o $LOC -Lc++-impl -rvendor.czx.hardware:vendor/czx/hardware/interfaces \
-randroid.hidl:system/libhidl/transport $PACKAGE
#生成Android.bp
hidl-gen -o $LOC -Landroidbp-impl -rvendor.czx.hardware:vendor/czx/hardware/interfaces \
-randroid.hidl:system/libhidl/transport $PACKAGE
代码树说明:
├── hello
│ └── 1.0
│ ├── Android.bp //update-makefiles.sh生成
│ ├── default
│ │ ├── Android.bp //hidl-gen -Landroidbp-impl 相关指令生成
│ │ ├── Hello.cpp //hidl-gen -Lc++-impl 相关指令生成
│ │ ├── Hello.h //hidl-gen -Lc++-impl 相关指令生成
│ └── IHello.hal
└── update-makefiles.sh // 脚本,用于更新,生成hal 接口so
模板代码生成完成后, 接下来需要对Hello.cpp 添加具体功能.
#include "Hello.h"
#include <utils/Log.h>
namespace vendor {
namespace czx {
namespace hardware {
namespace hello {
namespace V1_0 {
namespace implementation {
// Methods from ::vendor::czx::hardware::hello::V1_0::IHello follow.
Return<void> Hello::hello(const hidl_string& name, hello_cb _hidl_cb) {
ALOGD("Hello:hello() called");
_hidl_cb(name);
return Void();
}
Return<int32_t> Hello::foo(int32_t a, int32_t b) {
ALOGD("Hello::foo() called a = %d, b = %d ", a, b);
return int32_t { a + b };
}
Return<void> Hello::print(const hidl_string& s) {
ALOGD("Hello::print() called a = %s ", s.c_str());
return Void();
}
// Methods from ::android::hidl::base::V1_0::IBase follow.
//IHello* HIDL_FETCH_IHello(const char* /* name */) {
//return new Hello();
//}
//
} // namespace implementation
} // namespace V1_0
} // namespace hello
} // namespace hardware
} // namespace czx
} // namespace vendor
编译:mmm vendor/czx/hardware/interfaces/hello/1.0/
通过编译会生成主要关注的so库文件:
#hal 接口so, 由客户端使用
out/target/product/xxxx/system/lib64/vendor.czx.hardware.hello@1.0.so
# hal 接口功能实现,由服务端使用
out/target/product/xxxx/vendor/lib64/hw/vendor.czx.hardware.hello@1.0-impl.so
创建hal Service
在vendor/czx/hardware/interfaces/hello/1.0/default/ 目录下创建一个service.cpp
#include <stdio.h>
#include <android/log.h>
#include <hidl/HidlTransportSupport.h>
#include "Hello.h"
#undef LOG_TAG
#define LOG_TAG "HelloService"
using ::android::hardware::configureRpcThreadpool;
using ::android::hardware::joinRpcThreadpool;
using ::vendor::czx::hardware::hello::V1_0::implementation::Hello;
using ::vendor::czx::hardware::hello::V1_0::IHello;
using ::android::status_t;
int main(int /*argc*/, char * /*argv*/[])
{
android::sp<IHello> service = new Hello();
configureRpcThreadpool(4, true);
ALOGD("Hello HAL service starting");
status_t status = service->registerAsService();
if (status != ::android::OK) {
ALOGE("Unable to register Hello HAL service (%d)", status);
return 1;
}
ALOGI("Register Hello HAL Service successfully");
joinRpcThreadpool();
return 0;
}
在default 目录下Android.bp中添加cc_binary:
cc_binary {
name: "vendor.czx.hardware.hello@1.0-service",
relative_install_path: "hw",
proprietary: true,
srcs: [
"service.cpp",
"Hello.cpp",
],
shared_libs: [
"libhidlbase",
"libhidltransport",
"libutils",
"liblog",
"vendor.czx.hardware.hello@1.0",
],
}
编译生成:
out/target/product/xxxx/vendor/bin/hw/vendor.czx.hardware.hello@1.0-service
验证hal service
到此,我们可以将编译出来的产物push 到Android 设备中,
adb push vendor.czx.hardware.hello@1.0-service vendor/bin/hw/
adb push vendor.czx.hardware.hello@1.0-impl.so vendor/lib64/hw/
adb push vendor.czx.hardware.hello@1.0.so vendor/lib64/
启动hal service , 打印日志,观察hal service 是否启动成功
HIDL 客户端实现
客户端测试逻辑实现
创建test 目录,Hello_test.cpp, Android.bp
mkdir vendor/czx/hardware/interfaces/hello/1.0/default/test/
touch vendor/czx/hardware/interfaces/hello/1.0/default/test/Hello_test.cpp
touch vendor/czx/hardware/interfaces/hello/1.0/default/test/Android.bp
实现Hello_test.cpp 测试逻辑, 获取IHello 服务. 调用IHello hal接口
#include <vendor/czx/hardware/hello/1.0/IHello.h>
#include <log/log.h>
using android::sp;
using android::hardware::hidl_string;
using ::vendor::czx::hardware::hello::V1_0::IHello;
int main()
{
//获取IHello 服务.
sp<IHello> service = IHello::getService();
if (service == nullptr)
{
ALOGE("HELLO_TEST: Can't find IHello service...");
return -1;
}
printf("HELLO_TEST: found service @ %p\n", service.get());
//通过IHello 实例,调用其接口.
service->hello("test_client", [&](hidl_string result)
{ printf("HELLO_TEST: %s\n", result.c_str()); });
service->foo(10, 4);
service->print("Hello HIDL");
return 0;
}
添加编译规则, 进行编译,将在out/target/product/xxxx/vendor/bin/hw/生成一个可执行bin 文件Hello_test
cc_binary {
name: "Hello_test",
relative_install_path: "hw",
proprietary: true,
srcs: ["Hello_test.cpp"],
shared_libs: [
"liblog",
"libhidlbase",
"libhidltransport",
"libhwbinder",
"libutils",
"vendor.czx.hardware.hello@1.0",
],
}
验证客户端测试
将编译生成的bin文件push 到Android 设备
adb push Hello_test vendor/bin/hw
启动IHello 服务 并执行Hello_test
./vendor.czx.hardware.hello@1.0-service | ./Hello_test
从日志中可以看见, hwservicemanager 找不到IHello这个服务. 这是因为没有在VINTF 清单进行声明.
01-01 06:33:10.414 298 298 W hwservicemanager: getTransport: Cannot find entry vendor.czx.hardware.hello@1.0::IHello/default in either framework or device manifest.
01-01 06:33:10.415 8759 8759 E HELLO_TEST: Can't find IHello service...
将修改后清单文件重新push 到Android 设备, 重启,重新验证客户端测试
Hello_test 可以正常获取到IHello 服务,并调用其hal 接口.
VINTF 清单声明
将Android 设备中的清单文件pull 出来,修改,然后验证. 最后在项目中的manifest.xml下添加IHello的声明.
adb pull vendor/etc/vintf/manifest.xml
在清单文件中添加对IHello 的声明:
<hal format="hidl">
<name>vendor.czx.hardware.hello</name>
<transport>hwbinder</transport>
<version>1.0</version>
<interface>
<name>IHello</name>
<instance>default</instance>
</interface>
<fqname>@1.0::IHello/default</fqname>
</hal>
关于VINTF清单见: 清单 | Android 开源项目 | Android Open Source Project (google.cn)
如果
如何让hal service 在系统中正确启动
添加init rc
添加rc文件, 目的在开机时启动hal service.
service vendor.hello /vendor/bin/hw/vendor.czx.hardware.hello@1.0-service
class hal
user system
group system
修改Android.bp, 添加init_rc: [“vendor.czx.hardware.hello@1.0-service.rc”],
cc_binary {
name: "vendor.czx.hardware.hello@1.0-service",
relative_install_path: "hw",
init_rc: ["vendor.czx.hardware.hello@1.0-service.rc"],
proprietary: true,
srcs: [
"service.cpp",
"Hello.cpp",
],
shared_libs: [
"libhidlbase",
"libhidltransport",
"libutils",
"liblog",
"vendor.czx.hardware.hello@1.0",
],
}
但仅仅添加rc, 不能让服务正常启动,系统在启动时会进行Selinux 权限检查。 所以需要为其添加Selinux 权限.
01-01 08:00:03.341 0 0 E [kernel]init: Could not start service ‘vendor.hello’ as part of class ‘hal’: File /vendor/bin/hw/vendor.czx.hardware.hello@1.0-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? https://source.android.com/security/selinux/device-policy#label_new_services_and_address_denials
添加Selinux 权限
在device/xxxx/sepolicy目录下,新增hal_hello.te 文件
# hello service
type hal_hello, domain;
type hal_hello_exec, exec_type, vendor_file_type, file_type;
hwbinder_use(hal_hello)
init_daemon_domain(hal_hello)
add_hwservice(hal_hello, hal_hello_hwservice)
get_prop(hal_hello, hwservicemanager_prop)
在file_contexts文件中添加:
#hello service
/(vendor|system/vendor)/bin/hw/vendor\.czx\.hardware\.hello@1\.0-service u:object_r:hal_hello_exec:s0
在hwservice_contexts文件中添加:
vendor.czx.hardware.hello::IHello u:object_r:hal_hello_hwservice:s0
在hwservice.te 文件中添加:
type hal_hello_hwservice, hwservice_manager_type;
打包
将hello 相关编译产物打包进系统镜像
PRODUCT_PACKAGES += \
vendor.czx.hardware.hello@1.0-service \
vendor.czx.hardware.hello@1.0 \
vendor.czx.hardware.hello@1.0-impl \
更新current.txt
当HAL 接口稳定后,进行接口发布时,请更新current.txt. 在interfaces目录下创建current.txt,填写一个任意值
和接口名。 如下:
8989222 vendor.czx.hardware.hello@1.0::IHello
然后执行./update-makefiles.sh。 将会给出正确的hash 值,用此hash 替换掉原来的任意值即可.
以后HAL 接口更新时,请更新current.txt. 将新的hash 追加到current.txt 中即可.
代码树结构
.
└── interfaces
├── current.txt
├── hello
│ └── 1.0
│ ├── Android.bp
│ ├── default
│ │ ├── Android.bp
│ │ ├── Hello.cpp
│ │ ├── Hello.h
│ │ ├── service.cpp
│ │ ├── test
│ │ │ ├── Android.bp
│ │ │ └── Hello_test.cpp
│ │ └── vendor.czx.hardware.hello@1.0-service.rc
│ └── IHello.hal
└── update-makefiles.sh