编写 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

运行后会发现生成了一些文件,现在的目录应该长成这样:
hidl1
由于个人有点轻微强迫症,喜欢把头文件单独放在一个目录里,因此微调一下结构,把cpp文件都放进impl中,把h文件都放进impl/include中,调整后的目录结构如下:
hidl2
修改一下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模式:
binder化
直通模式:
passthrough
按照官方的说法,直通式是为了兼容旧版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>

烧录到系统中,看一下我们的服务是否正常启动了:
PS
编写一个测试程序调用一下接口:
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服务接口,以及接收回调:
screen
参考文章:
Google HIDL
写一个自己的 Android Hidl Service

  • 1
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值