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