Android R中HIDL的使用

一、 Android R中hidl模板的创建

1. 前言

hidl的概念网上已经很多了,这里就不赘述了,大家可以自行查看,本篇文章记录一下如何在Android R的标准HAL中添加一个自定义的新接口。

2. 准备

需要一套可编译的Android源码以及可以进行测试的硬件环境,或者模拟器也可以。

3. 步骤

  1. 假设当前处于Android源码的根目录位置
mkdir -p ./hardware/interfaces/demo/1.0/default

这里是在Android标准HAL接口中创建了我们的测试用例接口demo-hidl需要的一些文件夹

  1. 编写IDemo.hal接口文件,这里比较简单,就一个helloworld接口
touch ./hardware/interfaces/demo/1.0/IDemo.hal   # 创建接口文件并写入下面内容
// IDemo.hal
package android.hardware.demo@1.0;
interface IDemo {
		helloWorld(string name) generates (string result);
};

上面接口中我们定义了一个helloWorld接口,一个string参数,并且返回string

  1. 通过Android的hidl-gen以及脚本生成一些文件
# 下面这句执行的时候Android代码的环境必须是lunch过的,编译过Android的应该知道
./hardware/interfaces/update-makefiles.sh # 执行该脚本会在1.0目录下生成一个Android.bp

# 这句是在default目录下生成.h和.cpp文件
hidl-gen -L c++-impl -o ./hardware/interfaces/demo/1.0/default/ -r demo.hardware:./hardware/interfaces android.hardware.demo@1.0

执行完得到以下结果:

// Demo.h
// FIXME: your file license if you have one
#ifndef ANDROID_HARDWARE_DEMO_V1_0_DEMO_H
#define ANDROID_HARDWARE_DEMO_V1_0_DEMO_H

#pragma once

#include <android/hardware/demo/1.0/IDemo.h>
#include <hidl/MQDescriptor.h>
#include <hidl/Status.h>

namespace android::hardware::demo::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;

struct Demo : public V1_0::IDemo {
    // Methods from ::android::hardware::demo::V1_0::IDemo follow.
    Return<void> helloWorld(const hidl_string& name, helloWorld_cb _hidl_cb) override;

    // Methods from ::android::hidl::base::V1_0::IBase follow.

};

// FIXME: most likely delete, this is only for passthrough implementations
// extern "C" IDemo* HIDL_FETCH_IDemo(const char* name);

}  // namespace android::hardware::demo::implementation

#endif /* ANDROID_HARDWARE_DEMO_V1_0_DEMO_H */

.h文件中extern注释掉的两行可以看出能选择passthrough模式,将这句注释解除掉选择passthrough模式,Android R中解除了这个注释没有编译通过,因此没有选择passthrough模式,后面有机会再看这个问题

// Demo.cpp
// FIXME: your file license if you have one

#include "Demo.h"

namespace android::hardware::demo::implementation {

// Methods from ::android::hardware::demo::V1_0::IDemo follow.
Return<void> Demo::helloWorld(const hidl_string& name, helloWorld_cb _hidl_cb) {
    // TODO implement
    char buf[512];
    memset(buf, 0, sizeof(buf));
	
	snprintf(buf, 512, "hello world, %s", name.c_str());
	hidl_string result(buf);
	
	_hidl_cb(result);

    return Void();
}


// Methods from ::android::hidl::base::V1_0::IBase follow.

// IDemo* HIDL_FETCH_IDemo(const char* /* name */) {
//     return new Demo();
// }

}  // namespace android::hardware::demo::implementation

在.cpp文件中我们对helloWorld接口进行了实现
HIDL_FETCH_IDemo方法的注释解除用于实现passthrough模式,passthrough模式暂不考虑

// This file is autogenerated by hidl-gen -Landroidbp.

hidl_interface {
    name: "android.hardware.demo@1.0",
    root: "android.hardware",
    vndk: {
        enabled: true,
    },
    srcs: [
        "IDemo.hal",
    ],
    interfaces: [
        "android.hidl.base@1.0",
    ],
    gen_java: true,
}
  1. 编写default下的Android.bp
    default目录下的bp文件没有自动生成需要创建一个,内容如下:
touch ./hardware/interfaces/demo/1.0/default/Android.bp
cc_library_shared {
    name: "android.hardware.demo@1.0-impl",
    relative_install_path: "hw",
    proprietary: true,
    srcs: [
        "Demo.cpp",
    ],
    shared_libs: [
        "libhidlbase",
        "libhidltransport",
        "libutils",
        "android.hardware.demo@1.0",
    ],
}

同样需要lunch之后到./hardware/interfaces/demo/1.0下执行mm进行编译

编译完成之后我们得到了HIDL的接口(android.hardware.demo@1.0.so
)以及接口的实现实例(android.hardware.demo@1.0-impl.so
),为了应用能够通过binder访问到我们写的接口,需要将服务端注册到HWServiceManager中

  1. 注册服务到HWServiceManager中

实现这一步需要两个文件

touch ./hardware/interface/demo/1.0/default/android.hardware.demo@1.0-service.rc
touch ./hardware/interface/demo/1.0/default/service.cpp

文件内容如下:

# android.hardware.demo@1.0-service.rc

service demo_hal_service /vendor/bin/hw/android.hardware.demo@1.0-service
		class hal
		user system
		group system
cc_library_shared {
    name: "android.hardware.demo@1.0-impl",
    relative_install_path: "hw",
    proprietary: true,
    srcs: [
        "Demo.cpp",
    ],
    shared_libs: [
        "libhidlbase",
        "libutils",
        "android.hardware.demo@1.0",
    ],
}

cc_binary {
    name: "android.hardware.demo@1.0-service",
    defaults: ["hidl_defaults"],
    proprietary: true,
    relative_install_path: "hw",
    srcs: [
        "service.cpp",
        "Demo.cpp",
    ],
    init_rc: [
        "android.hardware.demo@1.0-service.rc",
    ],
    shared_libs: [
        "libhidlbase",
        "libutils",
        "liblog",
        "android.hardware.demo@1.0",
        // "android.hardware.demo@1.0-impl",
    ],
}

这里是default路径下最终的bp文件。
注意
上面的bp文件中编译android.hardware.demo@1.0-service原本是在共享库中引入impl的,因为这个service需要使用到Demo.cpp中定义的接口,但是这种方式编译之后烧录发现该服务并没有被rc拉起,查看log发现报了如下错误:

	行 2666: B000001  01-01 08:00:21.590     0     0 I c7 init : starting service 'demo_hal_service'...
	行 2677: C000001  01-01 08:00:21.606   459   459 F linker  : CANNOT LINK EXECUTABLE "/vendor/bin/hw/android.hardware.demo@1.0-service": library "android.hardware.demo@1.0-impl.so" not found: needed by main executable
	行 2677: C000001  01-01 08:00:21.606   459   459 F linker  : CANNOT LINK EXECUTABLE "/vendor/bin/hw/android.hardware.demo@1.0-service": library "android.hardware.demo@1.0-impl.so" not found: needed by main executable
	行 2819: B000001  01-01 08:00:21.711     0     0 I c7 init : Service 'demo_hal_service' (pid 459) exited with status 1

这个是由于Android的system和vendor之间存在隔离导致的,找了一下,除了我上面代码提供的方法以外,还可以将这个impl的库文件拷贝一份放到system/lib64/hw目录下,但是我使用这个方法测试了一下没有成功,还是有相同的报错,查看Android的官方文档,发现应该是我的Android.bp文件写的有问题,如果写成同时支持vendor和system的版本,则不会存在报错,之前查到的复制到另一个目录中的方法应该也不是最全的解释,这个后面有时间再去折腾了。

  1. 编写测试应用
mkdir -p ./hardware/interfaces/demo/1.0/default/test
touch ./hardware/interfaces/demo/1.0/default/test/test.cpp
touch ./hardware/interfaces/demo/1.0/default/test/Android.bp

创建测试的文件以及编译的bp文件,内容如下:

#include <android/hardware/demo/1.0/IDemo.h>
#include <hidl/Status.h>
#include <utils/misc.h>
#include <hidl/HidlSupport.h>
#include <stdio.h>

using android::hardware::demo::V1_0::IDemo;
using android::sp;
using android::hardware::hidl_string;

int main()
{
    android::sp<IDemo> service = IDemo::getService();
    if (nullptr == service) {
        printf("Failed to get service\n");
        return -1;
    }

    service->helloWorld("templated hidl", [&](hidl_string result) {
        printf("%s\n", result.c_str());;
    });

    return 0;
}
cc_binary {
    name: "android.hardware.demo.test",
    srcs: [
        "test.cpp",
    ],
    shared_libs: [
        "libutils",
        "liblog",
        "libhidlbase",
        "android.hardware.demo@1.0",
    ],
}

修改成功之后使用mm或mmm命令进行编译,会在system/bin/下生成android.hardware.demo.test文件

4. 编译问题

4.1 编译报错处理

前面第三步的步骤操作下来已经获得了一个自定义的hidl服务和测试用的client,单编通过了可以push到硬件上进行测试,不过我这里选择的是整编进系统中然后刷机测试,但是整编的时候遇到了编译不通过的问题,报错如下:
报错信息
上图中圈出了需要修改的地方,在build/make/target/product/gsi/30.txt文件中加上VNDK-core:android.hardware.demo@1.0.so,编译问题解决
烧录之后通过log发现服务并没有启动,需要进行selinux相关的配置,见以下链接:
配置selinux

4.2 out路径下没有生成相关文件

解决了4.1的问题之后编译通过了,但是去out目录下只能找到android.hardware.demo@1.0.so的库文件,前面写的自启动的rc文件、service服务的库以及测试用的bin文件都没有编译进来,这个需要修改当前工程的编译系统,将这些内容加入进去,通常在device目录下,如我的在:

device/sprd/boardname/xxx/module/car/md.mk

在这个文件中加上如下修改:

PRODUCT_PACKAGES += \
			android.hardware.demo@1.0-service\
			android.hardware.demo.test\

5. 参考

参考文档1
参考文档2

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值