HIDL Hal 开发指南3 —— HIDL HAL 实例程序

在分析 HAL 层源码之前,我们先实现一个 Binderized HALs,对 HIDL HAL 有一个相对细节的认知。

Binderized HAL 以进程的形式存在,内部有一个 HwBinder 服务端对象,对外提供 HwBinder 远程调用服务。Framework 通过 HwBinder 远程调用到 HAL 中的函数,这些函数直接访问具体的驱动。

接下来,我们给我们之前自定义的 Product 添加一个 HAL 模块,Product 不太清楚可以参考添加 Product

Hal 文件实现

Hal 层的实现一般放在 hardware、vendor 或者 device 目录下。

我们的示例就放在 vendor 目录下。

创建目录:

bash 复制代码
# 系统源码目录下
mkdir -p jelly/hardware/interfaces/hello_hidl/1.0

接着在 vendor/jelly/hardware/interface/hello_hidl/1.0 目录下创建 Hal 文件

java 复制代码
//定义包名,最后跟一个版本号
package jelly.hardware.hello_hidl@1.0;
//定义 hidl 服务对外提供的接口
interface IHello {
    //for test,generates 后面跟的是返回类型
    addition_hidl(uint32_t a,uint32_t b) generates (uint32_t total);
    //写 hello 驱动
    write(string name) generates (uint32_t result);
    //读 hello 驱动
    read() generates (string name);
};

这里的 IHello.hal 定义了我们的服务对外提供了哪些函数。可以认为这就是我们服务的对外协议。协议一般定义好就不会再修改,以保持对外的稳定性。 关于 hal 的写法,可以参考官方的文档,另外也可以参考 hardware 目录下系统自带的 hal 的写法。

Hal 文件生成 CPP 代码

接着我们使用 hidl-gen 命令将我们写的 hal 文件转换为 C++ 文件:

在系统源码下依次执行下面的命令:

bash 复制代码
source build/envsetup.sh
# 项目的完整包名
PACKAGE=jelly.hardware.hello_hidl@1.0 
# 生成代码的存放位置
LOC=vendor/jelly/hardware/interfaces/hello_hidl/1.0/default
# -o 选项指定生成的文件存放的位置
# -Lc++-impl 表示要生成 C++ 代码
# -rjelly.hardware:vendor/jelly/hardware/interfaces 用于指定包名与路径的对应关系
# 最后的 $PACKAGE 指定项目的完整的包名
hidl-gen -o $LOC -Lc++-impl -rjelly.hardware:vendor/jelly/hardware/interfaces $PACKAGE

执行完上面的命令后,在 vendor/jelly/hardware/interfaces/hello_hidl/1.0/default 目录下会生成 Hello.cpp 和 Hello.h。

接着修改 vendor/jelly/hardware/interfaces/hello_hidl/1.0/default 目录下生成的 Hello.cpp:

cpp 复制代码
#define LOG_TAG "HELLO_HAL"

#include "Hello.h"
#include <cutils/log.h>


namespace jelly {
namespace hardware {
namespace hello_hidl {
namespace V1_0 {
namespace implementation {

// Methods from ::jelly::hardware::hello_hidl::V1_0::IHello follow.
Return<uint32_t> Hello::addition_hidl(uint32_t a, uint32_t b) {
    ALOGD("addition_hidl....a :%d,b:%d",a,b);
    return uint32_t { a+ b };
}

Return<uint32_t> Hello::write(const hidl_string& name) {
    ALOGD("write %");
    return uint32_t {};
}

Return<void> Hello::read(read_cb _hidl_cb) {
    ALOGD("read");
    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_hidl
}  // namespace hardware
}  // namespace jelly

这里就简单打印点信息,实际的 HAL 实现,在这里回去访问具体的驱动程序。

服务端实现

接着我们需要写一个 Server 端来向 HwServiceManager 注册我们的服务。在 vendor/jelly/hardware/interfaces/hello_hidl/1.0/default 目录下添加 service.cpp:

cpp 复制代码
#include <hidl/HidlTransportSupport.h>
#include <utils/Looper.h>
#include <utils/StrongPointer.h>
#include <log/log.h>
#include "Hello.h"

using android::hardware::configureRpcThreadpool;
using android::hardware::joinRpcThreadpool;
using jelly::hardware::hello_hidl::V1_0::IHello;
using jelly::hardware::hello_hidl::V1_0::implementation::Hello;

int main() {
    ALOGD("hello-hidl is starting...");

    // 配置 Binder 的线程数
    configureRpcThreadpool(4, true /* callerWillJoin */);

    // 初始化一个 Hello 服务端对象
    android::sp<IHello> service = new Hello();
    // 注册服务
    android::status_t ret = service->registerAsService();

    if (ret != android::NO_ERROR) {
    }

    // 当前线程成为 HwBinder 线程
    joinRpcThreadpool();

    return 0;
    //Passthrough模式
    //return defaultPassthroughServiceImplementation<IHello>(4);
}

这里使用的是 libhwbinder 库来做实现,接口上与 libbinder 库大体类似。

我们的服务端需要在开机时启动,创建 vendor/jelly/hardware/interfaces/hello_hidl/1.0/default/jelly.hardware.hello_hidl@1.0-service.rc 文件:

bash 复制代码
service vendor_hello_hidl_service /vendor/bin/hw/jelly.hardware.hello_hidl@1.0-service
	class hal
	user system
	group system

接着我们需要添加 VINTF 对象,对于注册到 hwservicemanager 的服务都需要添加一个 VINTF 对象。对于编码来说 VINTF 对象就是一个 xml 文件,创建 vendor/jelly/hardware/interfaces/hello_hidl/1.0/default/jelly.hardware.hello_hidl@1.0-service.xml 文件:

xml 复制代码
<manifest version="1.0" type="device">
  <hal format="hidl">
        <name>jelly.hardware.hello_hidl</name>
        <transport>hwbinder</transport>
        <version>1.0</version>
        <interface>
            <name>IHello</name>
            <instance>default</instance>
        </interface>
    </hal>
</manifest>

接着我们使用 hidl-gen 命令来生成对应的 Android.bp 文件:

bash 复制代码
# 注意和前面使用同一个终端
hidl-gen -o $LOC -Landroidbp-impl -rjelly.hardware:vendor/jelly/hardware/interfaces $PACKAGE

这个命令会在 vendor/jelly/hardware/interfaces/hello_hidl/1.0/default 目录下生成一个 Android.bp,我们在生成的基础上稍作修改如下:

json 复制代码
// FIXME: your file license if you have one

cc_library_shared {
    name: "jelly.hardware.hello_hidl@1.0-impl",
    relative_install_path: "hw",
    proprietary: true,
    srcs: [
        "Hello.cpp",
    ],
    shared_libs: [
        "libhidlbase",
        "libhidltransport",
        "libutils",
        "jelly.hardware.hello_hidl@1.0",
        "liblog",
    ],
}

cc_binary {
    name: "jelly.hardware.hello_hidl@1.0-service",
    init_rc: ["jelly.hardware.hello_hidl@1.0-service.rc"],
    // 这种方式添加 vintf ,在 Android11 以后才支持
    vintf_fragments: ["jelly.hardware.hello_hidl@1.0-service.xml"],
    defaults: ["hidl_defaults"],
    relative_install_path: "hw",
    vendor: true,
    srcs: ["service.cpp", "Hello.cpp"],
    shared_libs: [
        "jelly.hardware.hello_hidl@1.0",
        "libhardware",
        "libhidlbase",
        "libhidltransport",
        "libutils",
        "liblog",
    ],
}

上面生成的 Android.bp 里面有一个依赖 jelly.hardware.hello_hidl@1.0,目前编译系统中还没有这个库,接着我们来生成 jelly.hardware.hello_hidl@1.0 库对应的 Android.bp。

hardware/interfaces 目录下,将 update-makefiles.sh 拷贝到 vendor/jelly/hardware/interfaces/ 目录下,并修改如下:

bash 复制代码
#!/bin/bash

source $ANDROID_BUILD_TOP/system/tools/hidl/update-makefiles-helper.sh

do_makefiles_update \
  "jelly.hardware:vendor/jelly/hardware/interfaces"

接着在系统源码目录下执行:

bash 复制代码
./vendor/jelly/hardware/interfaces/update-makefiles.sh

就会生成 vendor/jelly/hardware/interfaces/hello_hidl/1.0/Android.bp

json 复制代码
hidl_interface {
    name: "jelly.hardware.hello_hidl@1.0",
    root: "jelly.hardware",
    product_specific: true,
    srcs: [
        "IHello.hal",
    ],
    interfaces: [
        "android.hidl.base@1.0",
    ],
    gen_java: true,
}

其中的 hidl_interface 是 hidl 独有的,当编译源码时,它会将 out/soong/.intermediates/vendor/jelly/hardware/interfaces/hello_hidl/1.0/jelly.hardware.hello_hidl@1.0_genc++/gen/jelly/hardware/hello_hidl/1.0 和 out/soong/.intermediates/vendor/jelly/hardware/interfaces/hello_hidl/1.0/jelly.hardware.hello_hidl@1.0_genc++_headers/gen/jelly/hardware/hello_hidl/1.0 目录下的源码编译为 jelly.hardware.hello_hidl@1.0.so 文件,并预制到手机的 /vendor/lib 和 /vendor/lib64/ 目录下。

为了使编译通过,新建 vendor/jelly/hardware/interfaces/Android.bp 文件:

json 复制代码
hidl_package_root {
    name: "jelly.hardware",
    path: "vendor/jelly/hardware/interfaces",
}

这个 Android.bp 的作用是告诉编译系统包名与路径的映射关系。

接着新建 vendor/jelly/hardware/interfaces/current.txt 文件,current.txt 记录了所有 hal 接口的 hash 值,接口有变化时,同时需要更新 current.txt 中的 hash 值,这是我们先随便设置一个 hash 值:

json 复制代码
123456 jelly.hardware.hello_hidl@1.0::IHello

再执行一遍 update-makefiles.sh,这个时候就会发现提示 hash 值不正确了,同时会给出正确的 hash 值,我们把正确的 hash 值替换到 current.txt 即可。

最后修改 device/generic/goldfish/manifest.xml,在其中添加:

xml 复制代码
    <hal format="hidl">
        <name>jelly.hardware.hello_hidl</name>
        <transport>hwbinder</transport>
        <version>1.0</version>
        <interface>
            <name>IHello</name>
            <instance>default</instance>
        </interface>
    </hal>

客户端实现

在 vendor/jelly/hardware/interfaces/hello_hidl/1.0/default 目录下创建如下的文件和文件夹:

其中 hello_hidl_test.cpp

cpp 复制代码
#include <jelly/hardware/hello_hidl/1.0/IHello.h>
#include <hidl/LegacySupport.h>

#define LOG_TAG "hello_hidl"
#include <log/log.h>

using android::sp;
using jelly::hardware::hello_hidl::V1_0::IHello;
using android::hardware::Return;
using android::hardware::hidl_string;

int main(){
    // 获取服务代理端对象
    android::sp<IHello> hw_device = IHello::getService();
    if (hw_device == nullptr) {
              ALOGD("failed to get hello-hidl");
              return -1;
        }
    ALOGD("success to get hello-hidl....");
    // 通过代理端对象发起远程调用
    Return<uint32_t> total = hw_device->addition_hidl(3,4);
    hw_device->write("hello");
    hw_device->read([&](hidl_string result){
        ALOGD("%s\n", result.c_str());
    });
    return 0;
} 

调用接口上与 binder 大体一致。

Android.bp:

json 复制代码
cc_binary {
    name: "hello_hidl_test",
    srcs: ["hello_hidl_test.cpp"],
    vendor: true,
    shared_libs: [
        "liblog",
        "jelly.hardware.hello_hidl@1.0",
        "libhidlbase",
        "libhidltransport",
        "libhwbinder",
        "libutils",
    ],
}

selinux 配置

在 device/Jelly/Rice14/sepolicy 目录下添加:

hwservice.te:

bash 复制代码
type hello_hidl_hwservice, hwservice_manager_type;

hello_hidl.te:

scss 复制代码
type hello_hidl, domain;
type hello_hidl_exec, exec_type, vendor_file_type, file_type;

init_daemon_domain(hello_hidl);
add_hwservice(hello_hidl, hello_hidl_hwservice)
hwbinder_use(hello_hidl)

allow hello_hidl hidl_base_hwservice:hwservice_manager { add };
binder_call(hello_hidl,hwservicemanager)
get_prop(hello_hidl,hwservicemanager_prop)

hwservice_contexts:

arduino 复制代码
jelly.hardware.hello_hidl::IHello      u:object_r:hello_hidl_hwservice:s0

hello_hidl_test.te:

scss 复制代码
type  hello_hidl_test, domain;
type  hello_hidl_test_exec, exec_type, vendor_file_type, file_type;

domain_auto_trans(shell, hello_hidl_test_exec, hello_hidl_test);

get_prop(hello_hidl_test, hwservicemanager_prop)
allow hello_hidl_test hello_hidl_hwservice:hwservice_manager find;
hwbinder_use(hello_hidl_test);

在 file_contexts 中添加:

css 复制代码
/vendor/bin/hw/jelly\.hardware\.hello_hidl@1\.0-service    u:object_r:hello_hidl_exec:s0

编译执行

接着在 device/Jelly/Rice14/Rice14.mk 中添加如下内容:

Makefile 复制代码
BOARD_SEPOLICY_DIRS += \
    device/Jelly/Rice14/sepolicy

PRODUCT_PACKAGES += \
    jelly.hardware.hello_hidl@1.0-service \
    hello_hidl_test \
    jelly.hardware.hello_hidl@1.0-impl \

然后整编系统:

bash 复制代码
source build/envsetup.sh
lunch rice14-eng
make -j16

最后测试:

bash 复制代码
# 执行客户端程序
hello_hidl_test &
# 查看 log
# logcat | grep hello 
3-28 18:31:40.460  1542  1542 D         : hello-hidl is starting...
03-28 18:31:40.461  1542  1542 I ServiceManagement: Registered jelly.hardware.hello_hidl@1.0::IHello/default (start delay of 77ms)
03-28 18:31:40.461  1542  1542 I ServiceManagement: Removing namespace from process name jelly.hardware.hello_hidl@1.0-service to hello_hidl@1.0-service.
03-28 18:32:35.299  3050  3050 D hello_hidl: success to get hello-hidl....
03-28 18:32:35.299  1542  1561 D HELLO_HAL: write hello
03-28 18:32:35.300  3050  3050 D hello_hidl: test
相关推荐
Winston Wood23 分钟前
Android Parcelable和Serializable的区别与联系
android·序列化
清风徐来辽27 分钟前
Android 项目模型配置管理
android
帅得不敢出门1 小时前
Gradle命令编译Android Studio工程项目并签名
android·ide·android studio·gradlew
problc2 小时前
Flutter中文字体设置指南:打造个性化的应用体验
android·javascript·flutter
帅得不敢出门12 小时前
安卓设备adb执行AT指令控制电话卡
android·adb·sim卡·at指令·电话卡
我又来搬代码了14 小时前
【Android】使用productFlavors构建多个变体
android
德育处主任15 小时前
Mac和安卓手机互传文件(ADB)
android·macos
芦半山15 小时前
Android“引用们”的底层原理
android·java
迃-幵16 小时前
力扣:225 用队列实现栈
android·javascript·leetcode
大风起兮云飞扬丶16 小时前
Android——从相机/相册获取图片
android