欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

编写 android hidl service

程序员文章站 2022-07-07 20:11:40
HIDL什么是HIDL?按照谷歌官方的说法如下:HAL 接口定义语言(简称 HIDL,发音为“hide-l”)是用于指定 HAL 和其用户之间的接口的一种接口描述语言 (IDL)。HIDL 允许指定类型和方法调用(会汇集到接口和软件包中)。从更广泛的意义上来说,HIDL 是用于在可以独立编译的代码库之间进行通信的系统。HIDL 旨在用于进程间通信 (IPC)。进程之间的通信采用 Binder 机制。对于必须与进程相关联的代码库,还可以使用直通模式(在 Java 中不受支持)。HIDL 可指定数据结构...

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

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

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

本文地址:https://blog.csdn.net/snakezy1988/article/details/107179544