一、 Android R中hidl模板的创建
1. 前言
hidl的概念网上已经很多了,这里就不赘述了,大家可以自行查看,本篇文章记录一下如何在Android R的标准HAL中添加一个自定义的新接口。
2. 准备
需要一套可编译的Android源码以及可以进行测试的硬件环境,或者模拟器也可以。
3. 步骤
- 假设当前处于Android源码的根目录位置
mkdir -p ./hardware/interfaces/demo/1.0/default
这里是在Android标准HAL接口中创建了我们的测试用例接口demo-hidl需要的一些文件夹
- 编写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
- 通过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,
}
- 编写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中
- 注册服务到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的版本,则不会存在报错,之前查到的复制到另一个目录中的方法应该也不是最全的解释,这个后面有时间再去折腾了。
- 编写测试应用
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\