编写 android hidl service
HIDL
什么是HIDL?按照谷歌官方的说法如下:
HAL 接口定义语言(简称 HIDL,发音为“hide-l”)是用于指定 HAL 和其用户之间的接口的一种接口描述语言 (IDL)。HIDL 允许指定类型和方法调用(会汇集到接口和软件包中)。从更广泛的意义上来说,HIDL 是用于在可以独立编译的代码库之间进行通信的系统。
HIDL 旨在用于进程间通信 (IPC)。进程之间的通信采用 Binder 机制。对于必须与进程相关联的代码库,还可以使用直通模式(在 Java 中不受支持)。
HIDL 可指定数据结构和方法签名,这些内容会整理归类到接口(与类相似)中,而接口会汇集到软件包中。尽管 HIDL 具有一系列不同的关键字,但 C++ 和 Java 程序员对 HIDL 的语法并不陌生。此外,HIDL 还使用 Java 样式的注释。
HIDL的设计目的是为了可以在无需重新构建 HAL 的情况下替换框架,HAL 将由供应商或 SOC 制造商构建,并放置在设备的 /vendor 分区中,这样一来,就可以在框架自己的分区中通过 OTA 替换框架,而无需重新编译 HAL。
HIDL的语法合AIDL类似,例如:
ROOT =
PACKAGE IMPORTS PREAMBLE { ITEM ITEM ... } // not for types.hal
| PACKAGE IMPORTS ITEM ITEM... // only for types.hal; no method definitions
ITEM =
ANNOTATIONS? oneway? identifier(FIELD, FIELD ...) GENERATES?;
| safe_union identifier { UFIELD; UFIELD; ...};
| struct identifier { SFIELD; SFIELD; ...}; // Note - no forward declarations
| union identifier { UFIELD; UFIELD; ...};
| enum identifier: TYPE { ENUM_ENTRY, ENUM_ENTRY ... }; // TYPE = enum or scalar
| typedef TYPE identifier;
VERSION = integer.integer;
PACKAGE = package android.hardware.identifier[.identifier[...]]@VERSION;
PREAMBLE = interface identifier EXTENDS
EXTENDS = <empty> | extends import_name // must be interface, not package
GENERATES = generates (FIELD, FIELD ...)
// allows the Binder interface to be used as a type
// (similar to typedef'ing the final identifier)
IMPORTS =
[empty]
| IMPORTS import import_name;
TYPE =
uint8_t | int8_t | uint16_t | int16_t | uint32_t | int32_t | uint64_t | int64_t |
float | double | bool | string
| identifier // must be defined as a typedef, struct, union, enum or import
// including those defined later in the file
| memory
| pointer
| vec<TYPE>
| bitfield<TYPE> // TYPE is user-defined enum
| fmq_sync<TYPE>
| fmq_unsync<TYPE>
| TYPE[SIZE]
FIELD =
TYPE identifier
UFIELD =
TYPE identifier
| safe_union identifier { FIELD; FIELD; ...} identifier;
| struct identifier { FIELD; FIELD; ...} identifier;
| union identifier { FIELD; FIELD; ...} identifier;
SFIELD =
TYPE identifier
| safe_union identifier { FIELD; FIELD; ...};
| struct identifier { FIELD; FIELD; ...};
| union identifier { FIELD; FIELD; ...};
| safe_union identifier { FIELD; FIELD; ...} identifier;
| struct identifier { FIELD; FIELD; ...} identifier;
| union identifier { FIELD; FIELD; ...} identifier;
SIZE = // Must be greater than zero
constexpr
ANNOTATIONS =
[empty]
| ANNOTATIONS ANNOTATION
ANNOTATION =
| @identifier
| @identifier(VALUE)
| @identifier(ANNO_ENTRY, ANNO_ENTRY ...)
ANNO_ENTRY =
identifier=VALUE
VALUE =
"any text including \" and other escapes"
| constexpr
| {VALUE, VALUE ...} // only in annotations
ENUM_ENTRY =
identifier
| identifier = constexpr
下面记录一下如何写一个HIDL的hello world.
Step 1 建立服务目录,自动生成代码
- 在 vendor/rex/下新建目录,hidltest/interfaces/rexhello,此目录作为我们hidl服务的根目录
mkdir -p vendor/rex/hidltest/interfaces/rexhello
- 新建 1.0目录作为ver1.0版本,以及默认实现
mkdir -p vendor/rex/hidltest/interfaces/rexhello/1.0/default/impl
- 在根目录 vendor/rex/hidltest/interfaces/rexhello下创建Android.bp,内容如下:
subdirs = [
"1.0",
"1.0/default",
]
hidl_package_root {
//hidl接口名
name: "vendor.rex.rexhello",
//hidl服务根路径
path: "vendor/rex/hidltest/interfaces/rexhello"
}
- 在1.0目录下创建hal接口描述文件,按照命名规范,应该以I开头。假定我们的服务如下:
IRexHello.hal,service提供设置int32_t 数值的接口,获取该值的接口,以及 注册/反注册回调的接口
IRexHelloCallback.hal,回调定义,由service主动发送消息给client
types.hal 类型定义
内容如下:
IRexHello.hal:
package vendor.rex.rexhello@1.0;
import IRexHelloCallback;
interface IRexHello {
setHelloNum(int32_t num);
getHelloNum() generates(int32_t num);
registerCallback(IRexHelloCallback callback);
unRegisterCallback(IRexHelloCallback callback);
};
IRexHelloCallback.hal:
package vendor.rex.rexhello@1.0;
interface IRexHelloCallback {
OnHelloEvent(HelloEvent event);
};
types.hal:
package vendor.rex.rexhello@1.0;
struct HelloEvent {
string reply;
int32_t num;
};
以上准备工作完成后,在hidltest/interfaces/rexhello目录下新建一个shell脚本,hidl-gen.sh
LOC=1.0/default
PKG=vendor.rex.rexhello@1.0
hidl-gen -Landroidbp -rvendor.rex.rexhello:vendor/rex/hidltest/interfaces/rexhello $PKG
hidl-gen -o $LOC -Lc++-impl -rvendor.rex.rexhello:vendor/rex/hidltest/interfaces/rexhello $PKG
hidl-gen -o $LOC -Landroidbp-impl -rvendor.rex.rexhello:vendor/rex/hidltest/interfaces/rexhello $PKG
运行后会发现生成了一些文件,现在的目录应该长成这样:
由于个人有点轻微强迫症,喜欢把头文件单独放在一个目录里,因此微调一下结构,把cpp文件都放进impl中,把h文件都放进impl/include中,调整后的目录结构如下:
修改一下1.0/default/Android.bp的文件内容,注释掉relative_install_path: “hw”,表示我们的库需要直接安装在 vendor/lib以及vendor/lib64下,否则会安装在lib下面的hw里面。此外还需要修改一下源文件路径,头文件搜索路径,需要链接的库等。调整后的Android.bp内容如下:
cc_library_shared {
// FIXME: this should only be -impl for a passthrough hal.
// In most cases, to convert this to a binderized implementation, you should:
// - change '-impl' to '-service' here and make it a cc_binary instead of a
// cc_library_shared.
// - add a *.rc file for this module.
// - delete HIDL_FETCH_I* functions.
// - call configureRpcThreadpool and registerAsService on the instance.
// You may also want to append '-impl/-service' with a specific identifier like
// '-vendor' or '-<hardware identifier>' etc to distinguish it.
name: "vendor.rex.rexhello@1.0-impl",
// relative_install_path: "hw",
// FIXME: this should be 'vendor: true' for modules that will eventually be
// on AOSP.
vendor: true,
proprietary: true,
srcs: [
"impl/RexHello.cpp",
"impl/RexHelloCallback.cpp",
],
local_include_dirs: [
"impl/include",
],
export_include_dirs: [
"impl/include",
],
shared_libs: [
"libbase",
"libcutils",
"libhidlbase",
"libhidltransport",
"libutils",
"liblog",
"libhwbinder",
"vendor.rex.rexhello@1.0",
],
}
Step 2 编写服务
打开自动生成的头文件1.0/default/impl/include/RexHello.h,会发现有一段注释掉的代码:
// FIXME: most likely delete, this is only for passthrough implementations
// extern "C" IRexHello* HIDL_FETCH_IRexHello(const char* name);
网上有些教程会提示放开此部分代码,开启所谓的“直通模式”。其实这是谷歌设计的两种hidl通信方式,“直通化”、“binder化”。两者区别如下:
术语 | 描述 |
---|---|
Binder 化 | 表示 HIDL 用于进程之间的远程过程调用,并通过类似 Binder 的机制来实现。 |
直通式 | HIDL 的一种模式,使用这种模式时,服务器是共享库,由客户端进行 dlopen 处理。在直通模式下,客户端和服务器是相同的进程,但代码库不同。此模式仅用于将旧版代码库并入 HIDL 模型。 |
binder模式:
直通模式:
按照官方的说法,直通式是为了兼容旧版HAL的,以后的趋势是binder化,即HAL接口的调用都属于进程间调用。所以在此例子中,我们不需要打开HIDL_FETCH_***方法。
简单的写一下服务端的实现代码,并开启了一个线程周期性发送回调给client.
RexHello.h:
#ifndef VENDOR_REX_REXHELLO_V1_0_REXHELLO_H
#define VENDOR_REX_REXHELLO_V1_0_REXHELLO_H
#include <vendor/rex/rexhello/1.0/IRexHello.h>
#include <vendor/rex/rexhello/1.0/types.h>
#include <hidl/MQDescriptor.h>
#include <hidl/Status.h>
#include <mutex>
#include <thread>
namespace vendor {
namespace rex {
namespace rexhello {
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;
using ::android::wp;
using ::android::hidl::base::V1_0::IBase;
struct RexHello : public IRexHello, android::hardware::hidl_death_recipient {
// Methods from ::vendor::rex::rexhello::V1_0::IRexHello follow.
public:
RexHello();
virtual ~RexHello();
Return<void> setHelloNum(int32_t num) override;
Return<int32_t> getHelloNum() override;
Return<void> registerCallback(const sp<::vendor::rex::rexhello::V1_0::IRexHelloCallback>& callback) override;
Return<void> unRegisterCallback(const sp<::vendor::rex::rexhello::V1_0::IRexHelloCallback>& callback) override;
void serviceDied(uint64_t cookie, const wp<IBase>& who) override;
void sendEvent(::vendor::rex::rexhello::V1_0::HelloEvent& event);
// Methods from ::android::hidl::base::V1_0::IBase follow.
private:
bool unRegisterCallbackInternal(const sp<IBase>& cb);
void helloEventThreadFunc();
std::mutex mRequestMutex;
std::mutex mCallbackMutex;
std::mutex mEventMutex;
std::vector<sp<::vendor::rex::rexhello::V1_0::IRexHelloCallback>> mHelloCallbacks;
int32_t mHelloNum;
std::thread mHelloEventThread;
};
// FIXME: most likely delete, this is only for passthrough implementations
// extern "C" IRexHello* HIDL_FETCH_IRexHello(const char* name);
} // namespace implementation
} // namespace V1_0
} // namespace rexhello
} // namespace rex
} // namespace vendor
#endif // VENDOR_REX_REXHELLO_V1_0_REXHELLO_H
RexHello.cpp:
#define LOG_NDEBUG 0
#define LOG_TAG "RexHello"
#include <utils/Log.h>
#include <hidl/HidlTransportSupport.h>
#include "RexHello.h"
namespace vendor {
namespace rex {
namespace rexhello {
namespace V1_0 {
namespace implementation {
RexHello::RexHello():mHelloNum(0) {
ALOGD("RexHello created");
mHelloEventThread = std::thread(&RexHello::helloEventThreadFunc,this);
ALOGD("HelloEvent thread started");
}
RexHello::~RexHello() {
}
// Methods from ::vendor::rex::rexhello::V1_0::IRexHello follow.
Return<void> RexHello::setHelloNum(int32_t num) {
std::lock_guard<std::mutex> lock(mRequestMutex);
mHelloNum += num;
ALOGD("setHelloNum %d, current helloNum %d", num,mHelloNum);
return Void();
}
Return<int32_t> RexHello::getHelloNum() {
return mHelloNum;
}
Return<void> RexHello::registerCallback(const sp<::vendor::rex::rexhello::V1_0::IRexHelloCallback>& callback) {
if(callback == NULL){
return Void();
}
{
std::lock_guard<std::mutex> lock(mCallbackMutex);
for(auto& it : mHelloCallbacks) {
if(android::hardware::interfacesEqual(it,callback)){
ALOGW("duplicate callback register, return");
return Void();
}
}
mHelloCallbacks.push_back(callback);
}
auto linkRet = callback->linkToDeath(this,0u);
if(!linkRet.withDefault(false)){
ALOGW("can't linkToDeath: %d, description: %s", linkRet.isOk(),linkRet.description().c_str());
}
return Void();
}
Return<void> RexHello::unRegisterCallback(const sp<::vendor::rex::rexhello::V1_0::IRexHelloCallback>& callback) {
unRegisterCallbackInternal(callback);
return Void();
}
void RexHello::serviceDied(uint64_t cookie __unused, const wp<IBase>& who) {
(void)unRegisterCallbackInternal(who.promote());
}
bool RexHello::unRegisterCallbackInternal(const sp<IBase>& cb){
if(cb == NULL){
return false;
}
bool removed = false;
std::lock_guard<std::mutex> lock(mCallbackMutex);
for(auto it = mHelloCallbacks.begin(); it != mHelloCallbacks.end();){
if(android::hardware::interfacesEqual(*it,cb)){
it = mHelloCallbacks.erase(it);
removed = true;
}else {
++it;
}
}
if(removed){
cb->unlinkToDeath(this);
}else{
ALOGW("Ignore the unRegister interface");
}
return removed;
}
void RexHello::helloEventThreadFunc(){
while(true){
if(mHelloCallbacks.size() == 0){
ALOGD("callback list is empty, hold on the callback evnet...");
}else
{
::vendor::rex::rexhello::V1_0::HelloEvent event;
event.reply = ::android::hardware::hidl_string("hello,hidl rex");
event.num = mHelloNum;
sendEvent(event);
}
sleep(2);
}
}
void RexHello::sendEvent(::vendor::rex::rexhello::V1_0::HelloEvent& event){
std::lock_guard<std::mutex> lock(mEventMutex);
for(auto& callback : mHelloCallbacks){
callback->OnHelloEvent(event);
}
}
// Methods from ::android::hidl::base::V1_0::IBase follow.
//IRexHello* HIDL_FETCH_IRexHello(const char* /* name */) {
//return new RexHello();
//}
//
} // namespace implementation
} // namespace V1_0
} // namespace rexhello
} // namespace rex
} // namespace vendor
接下来在1.0/default目录下编写RexHelloService.cpp来启动服务,以及rc文件。
RexHelloService.cpp
#define LOG_TAG "RexHello@1.0-service"
#include <utils/Log.h>
#include <hidl/HidlTransportSupport.h>
#include "RexHello.h"
using namespace android;
using namespace vendor::rex::rexhello::V1_0::implementation;
int main(int argc __unused, char const *argv[] __unused)
{
ALOGD("RexHello@1.0-service start now.");
android::hardware::configureRpcThreadpool(8,true);
sp<RexHello> rexHello = new RexHello();
status_t status = rexHello->registerAsService();
if(status != OK){
ALOGE("unable to start RexHello@1.0-service, %d",status);
return -1;
}
ALOGD("RexHello@1.0-service is running now.");
android::hardware::joinRpcThreadpool();
return 0;
}
vendor.rex.rexhello@1.0-service.rc:
service rex-hal-1.0 /vendor/bin/hw/vendor.rex.rexhello@1.0-service
class hal
user system
group system inet
on post-fs-data
start rex-hal-1.0
在Android.bp中添加binary的编译目标:
1.0/default/Android.bp:
cc_library_shared {
// FIXME: this should only be -impl for a passthrough hal.
// In most cases, to convert this to a binderized implementation, you should:
// - change '-impl' to '-service' here and make it a cc_binary instead of a
// cc_library_shared.
// - add a *.rc file for this module.
// - delete HIDL_FETCH_I* functions.
// - call configureRpcThreadpool and registerAsService on the instance.
// You may also want to append '-impl/-service' with a specific identifier like
// '-vendor' or '-<hardware identifier>' etc to distinguish it.
name: "vendor.rex.rexhello@1.0-impl",
// relative_install_path: "hw",
// FIXME: this should be 'vendor: true' for modules that will eventually be
// on AOSP.
vendor: true,
proprietary: true,
srcs: [
"impl/RexHello.cpp",
"impl/RexHelloCallback.cpp",
],
local_include_dirs: [
"impl/include",
],
export_include_dirs: [
"impl/include",
],
shared_libs: [
"libbase",
"libcutils",
"libhidlbase",
"libhidltransport",
"libutils",
"liblog",
"libhwbinder",
"vendor.rex.rexhello@1.0",
],
}
cc_binary {
name: "vendor.rex.rexhello@1.0-service",
relative_install_path: "hw",
vendor: true,
srcs:[
"RexHelloService.cpp",
],
init_rc:[
"vendor.rex.rexhello@1.0-service.rc",
],
shared_libs:[
"liblog",
"libhidlbase",
"libhidltransport",
"libutils",
"vendor.rex.rexhello@1.0",
"vendor.rex.rexhello@1.0-impl",
],
}
至此hidl服务编写完成。为了让服务正常运行,还需要添加te权限处理文件,此处忽略。
最后还有关键的一步,注册我们刚刚编写的HIDL服务,否则客户端调用不到此hidl服务
举例:在device/vendor厂商/产品路径/manifest.xml添加:
<hal format="hidl" optional="true">
<name>vendor.rex.rexhello</name>
<transport>hwbinder</transport>
<version>1.0</version>
<interface>
<name>IRexHello</name>
<instance>default</instance>
</interface>
</hal>
在vendor_framework_compatibility_matrix.xml中添加:
<hal format="hidl" optional="true">
<name>vendor.rex.rexhello</name>
<transport>hwbinder</transport>
<version>1.0</version>
<interface>
<name>IRexHello</name>
<instance>default</instance>
</interface>
</hal>
烧录到系统中,看一下我们的服务是否正常启动了:
编写一个测试程序调用一下接口:
RexHelloTest.cpp:
#define LOG_TAG "RexHelloTest"
#include <vendor/rex/rexhello/1.0/IRexHelloCallback.h>
#include <vendor/rex/rexhello/1.0/IRexHello.h>
#include <vendor/rex/rexhello/1.0/types.h>
#include <utils/Log.h>
using android::sp;
using android::hardware::hidl_string;
using android::hardware::Return;
using android::hardware::Void;
using vendor::rex::rexhello::V1_0::IRexHello;
using vendor::rex::rexhello::V1_0::IRexHelloCallback;
class HelloBack : public IRexHelloCallback{
Return<void> OnHelloEvent(const ::vendor::rex::rexhello::V1_0::HelloEvent& event){
ALOGD("HelloBack got event: %s, helloNum is %d",event.reply.c_str(),event.num);
return Void();
}
};
int main(int argc, char const *argv[])
{
sp<IRexHello> mHelloService = IRexHello::getService();
sp<HelloBack> mCallback = new HelloBack();
if(mHelloService != NULL){
mHelloService->registerCallback(mCallback);
} else {
ALOGW("can't get hidle IRexHello service");
}
int32_t val = 1;
while (true)
{
mHelloService->setHelloNum(val);
sleep(1);
}
return 0;
}
Android.bp:
cc_binary {
name: "rexhellotest",
vendor: true,
srcs:[
"RexHelloTest.cpp",
],
shared_libs: [
"liblog",
"libhidlbase",
"libhidltransport",
"libutils",
"vendor.rex.rexhello@1.0",
],
}
可以看到客户端能正常调用到hidl服务接口,以及接收回调:
参考文章:
Google HIDL
写一个自己的 Android Hidl Service
本文地址:https://blog.csdn.net/snakezy1988/article/details/107179544
上一篇: Mybatis一级缓存与二级缓存