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