- 1:创建自定义HIDL接口
- 1.1:创建厂商的HIDL目录
- 1.2:编写HIDL接口
- 1.3:生成HIDL接口文件的hash
- 2:实现HIDL接口(服务端)
- 2.1:生成C++文件
- 2.2:用Android Studio来开发HAL
- 2.3:编写绑定式HAL服务
- 2.4:添加HAL服务到目标版本中
- 3:验证HAL服务(客户端)
- 3.1:编写客户端
- 3.2:开启服务端
- 3.3:运行客户端
本文将基于AOSP android-10.0.0_r41来创建绑定式HAL服务。并编写可执行文件的客户端进行测试。HIDL的详细语法请参阅官网文档。
创建自定义HIDL接口
创建厂商的HIDL目录
首先在AOSP中创建文件夹vendor/melrose/hardware/interfaces用来存放自己定义的HIDL接口,其中melrose为厂商名字。
然后将AOSP下的hareware/interfaces/Android.bp文件拷贝到自己定义的文件夹中:
cp hardware/interfaces/Android.bp vendor/melrose/hardware/interfaces/
修改拷贝后的Android.bp为:
hidl_package_root {
name: "vendor.melrose.hardware",
use_current: true,
}
编写HIDL接口
假设我们需要定义一系列的HIDL接口来控制LED灯的打开与关闭,在上面定义好的目录下创建led包和版本号:
vendor/melrose/hardware/interfaces/led/1.0
我们上面定义的HIDL的包名为:vendor.melrose.hardware,并在该文件夹下定义ILed.hal和types.hal两个文件:
package vendor.melrose.hardware.led@1.0;
interface ILed{
open() generates(LedStatus status);
close() generates(LedStatus status);
};
接下来我们croot到顶层目录为定义好的HIDL接口生成相应的Android.bp文件。
hidl-gen -L androidbp -r vendor.melrose.hardware:vendor/melrose/hardware/interfaces/ vendor.melrose.hardware.led@1.0
在执行完如上命令后会在vendor/melrose/hardware/interfaces/led/1.0目录中生成对应的Android.bp文件:
// This file is autogenerated by hidl-gen -Landroidbp.
hidl_interface {
name: "vendor.melrose.hardware.led@1.0",
root: "vendor.melrose.hardware",
product_specific: true,
srcs: [
"types.hal",
"ILed.hal",
],
interfaces: [
"android.hidl.base@1.0",
],
gen_java: true,
}
这样,我们就编写好了对应HIDL接口。
生成HIDL接口文件的hash
在我们确定接口不会进行改动时,我们就需要为编写完成的HIDL接口生成hash,如下命令会将生成的hash写入到vendor/melrose/hardware/interfaces/current.txt中:
hidl-gen -L hash -r vendor.melrose.hardware:vendor/melrose/hardware/interfaces/ vendor.melrose.hardware.led@1.0 -o vendor/melrose/hardware/interfaces/ >> vendor/melrose/hardware/interfaces/current.txt
这个命令会为我们编写的.hal文件生成对应的hash值,如果需要对编写的HIDL进行改动时都应该执行该命令来更新对应文件的hash值。
77e9ae2dc1785296078aac8577b1865e38991bf6ac1dd8210700f467fffd659c vendor.melrose.hardware.led@1.0::types
98b543f5060b0c20f5230d160b967a0d5381a9685ff58b646d6f6bedb3360eda vendor.melrose.hardware.led@1.0::ILed
实现HIDL接口(服务端)
生成C++文件
在编写完成HIDL接口后,一般会实现HIDL接口来控制驱动,我们在vendor/melrose/hardware/interfaces/led/1.0目录下创建default文件夹来生成HIDL对应的C++文件:
hidl-gen -L c++-impl -r vendor.melrose.hardware:vendor/melrose/hardware/interfaces/ vendor.melrose.hardware.led@1.0 -o vendor/melrose/hardware/interfaces/led
/1.0/default/
上面的命令会在default文件夹下生成对应的头文件和cpp文件:
-- led
`-- 1.0
|-- Android.bp
|-- ILed.hal
|-- default
| |-- Led.cpp
| `-- Led.h
`-- types.hal
为C++实现添加Android.bp文件:
hidl-gen -L androidbp-impl -r vendor.melrose.hardware:vendor/melrose/hardware/interfaces/ vendor.melrose.hardware.led@1.0 -o vendor/melrose/hardware/interfac
es/led/1.0/default/
这个命令会为c++实现生成直通式HAL的Android.bp文件。
// FIXME: your file license if you have one
cc_library_shared {
// FIXME: this should only be -impl for a passthrough hal.
// In most cases, to convert this to a binderized implementation, you should:
// - change '-impl' to '-service' here and make it a cc_binary instead of a
// cc_library_shared.
// - add a *.rc file for this module.
// - delete HIDL_FETCH_I* functions.
// - call configureRpcThreadpool and registerAsService on the instance.
// You may also want to append '-impl/-service' with a specific identifier like
// '-vendor' or '-<hardware identifier>' etc to distinguish it.
name: "vendor.melrose.hardware.led@1.0-impl",
relative_install_path: "hw",
// FIXME: this should be 'vendor: true' for modules that will eventually be
// on AOSP.
proprietary: true,
srcs: [
"Led.cpp",
],
shared_libs: [
"libhidlbase",
"libhidltransport",
"libutils",
"vendor.melrose.hardware.led@1.0",
],
}
接下来切换到vendor/melrose/hardware/interfaces/led/1.0目录下mm编译该模块来生成中间文件,会在目录out/soong/.intermediates/vendor/melrose/hardware/interfaces/led/1.0下生成各种文件。
default/
vendor.melrose.hardware.led-V1.0-java/
vendor.melrose.hardware.led-V1.0-java-shallow/
vendor.melrose.hardware.led-V1.0-java_gen_java/
'vendor.melrose.hardware.led@1.0'/
'vendor.melrose.hardware.led@1.0-adapter'/
'vendor.melrose.hardware.led@1.0-adapter-helper'/
'vendor.melrose.hardware.led@1.0-adapter-helper_genc++'/
'vendor.melrose.hardware.led@1.0-adapter-helper_genc++_headers'/
'vendor.melrose.hardware.led@1.0-adapter_genc++'/
'vendor.melrose.hardware.led@1.0-vts.driver'/
'vendor.melrose.hardware.led@1.0-vts.driver_genc++'/
'vendor.melrose.hardware.led@1.0-vts.driver_genc++_headers'/
'vendor.melrose.hardware.led@1.0-vts.profiler'/
'vendor.melrose.hardware.led@1.0-vts.profiler_genc++'/
'vendor.melrose.hardware.led@1.0-vts.profiler_genc++_headers'/
'vendor.melrose.hardware.led@1.0-vts.spec'/
'vendor.melrose.hardware.led@1.0_genc++'/
然后回到default目录下添加service.cpp,并将Android.bp按照其提示将其修改为绑定式HAL:
// FIXME: your file license if you have one
cc_binary {
// FIXME: this should only be -impl for a passthrough hal.
// In most cases, to convert this to a binderized implementation, you should:
// - change '-impl' to '-service' here and make it a cc_binary instead of a
// cc_library_shared.
// - add a *.rc file for this module.
// - delete HIDL_FETCH_I* functions.
// - call configureRpcThreadpool and registerAsService on the instance.
// You may also want to append '-impl/-service' with a specific identifier like
// '-vendor' or '-<hardware identifier>' etc to distinguish it.
name: "vendor.melrose.hardware.led@1.0-service",
relative_install_path: "hw",
// FIXME: this should be 'vendor: true' for modules that will eventually be
// on AOSP.
proprietary: true,
srcs: [
"Led.cpp",
"service.cpp",
],
shared_libs: [
"libhidlbase",
"libhidltransport",
"libutils",
"vendor.melrose.hardware.led@1.0",
],
}
用Android Studio来开发HAL
为了提高开发效率和提高代码的健壮性,我们可以使用Android Studio来对生成的c++文件进行开发:
- 创建Android C++项目
- 在CMakeLists添加之前编译hal生成的文件的绝对路径:
add_library( # Sets the name of the library.
native-lib
# Sets the library as a shared library.
SHARED
# Provides a relative path to your source file(s).
native-lib.cpp
/home/melrose/workspace/aosp/vendor/melrose/hardware/interfaces/led/1.0/default/Led.cpp
/home/melrose/workspace/aosp/vendor/melrose/hardware/interfaces/led/1.0/default/Led.h
/home/melrose/workspace/aosp/vendor/melrose/hardware/interfaces/led/1.0/default/Android.bp
/home/melrose/workspace/aosp/vendor/melrose/hardware/interfaces/led/1.0/default/service.cpp
)
- Build -> Refresh Linked C++ Projects就可以在IDE中看到我们添加的文件:
解决找不到头文件问题
当在IDE中打开Led.cpp时会提示找不到头文件,接下来我们就一个一个去解决这些问题:
1、<vendor/melrose/hardware/led/1.0/ILed.h>
该头文件位置在之前我们 out/soong/.intermediates/vendor/melrose/hardware/interfaces/led/1.0的vendor.melrose.hardware.led@1.0_genc++_headers/gen目录下,只需要将其添加include_directories中重新Refresh即可。
2、#include <hidl/MQDescriptor.h>和#include <hidl/Status.h>
这些头文件位于库libhidl中,具体位置为system/libhidl/base/include下,只需要将其添加include_directories中重新Refresh即可。
3、using ::android::sp
sp类位于StrongPointer.h文件中,目录为system/core/libutils/include下,只需要将其添加include_directories中重新Refresh即可。
include_directories(
/home/melrose/workspace/aosp/out/soong/.intermediates/vendor/melrose/hardware/interfaces/led/1.0/vendor.melrose.hardware.led@1.0_genc++_headers/gen
/home/melrose/workspace/aosp/system/libhidl/base/include
/home/melrose/workspace/aosp/system/libhidl/transport/include
/home/melrose/workspace/aosp/system/core/libutils/include
)
编写绑定式HAL服务
在AndroidStudio配置完成后,就可以对Led.cpp和service.cpp进行编辑。
Led.h
#pragma once
#include <vendor/melrose/hardware/led/1.0/ILed.h>
#include <hidl/MQDescriptor.h>
#include <hidl/Status.h>
#include <android/log.h>
namespace vendor {
namespace melrose {
namespace hardware {
namespace led {
namespace V1_0 {
namespace 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;
#define LOG_TAG "Melrose"
#define LOG_I(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
#define LOG_W(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)
#define LOG_D(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
struct Led : public ILed {
public:
Led();
~Led();
static Led* getInstance();
Return<::vendor::melrose::hardware::led::V1_0::LedStatus> open() override;
Return<::vendor::melrose::hardware::led::V1_0::LedStatus> close() override;
private:
static Led* mInstance;
};
} // namespace implementation
} // namespace V1_0
} // namespace led
} // namespace hardware
} // namespace melrose
} // namespace vendor
在Led.h的头文件中,添加了单例和构造、析构方法。因为使用了log,别忘了在Android.bp添加liblog动态库。
Led.cpp
#include "Led.h"
namespace vendor {
namespace melrose {
namespace hardware {
namespace led {
namespace V1_0 {
namespace implementation {
// Methods from ::vendor::melrose::hardware::led::V1_0::ILed follow.
Return<::vendor::melrose::hardware::led::V1_0::LedStatus> Led::open() {
LOG_I("led on");
return ::vendor::melrose::hardware::led::V1_0::LedStatus::ON;
}
Return<::vendor::melrose::hardware::led::V1_0::LedStatus> Led::close() {
LOG_I("led off");
return ::vendor::melrose::hardware::led::V1_0::LedStatus::OFF;
}
Led* Led::mInstance = nullptr;
Led::Led() {
LOG_I("led init");
}
Led::~Led() {
LOG_I("led release");
}
Led * Led::getInstance() {
if (mInstance == nullptr)
{
mInstance = new Led;
}
return mInstance;
}
// Methods from ::android::hidl::base::V1_0::IBase follow.
//ILed* HIDL_FETCH_ILed(const char* /* name */) {
//return new Led();
//}
//
} // namespace implementation
} // namespace V1_0
} // namespace led
} // namespace hardware
} // namespace melrose
} // namespace vendor
为了简便,在实现open和close方法时Logcat会打印对应的日志。
service.cpp
#include "Led.h"
#include <hidl/HidlTransportSupport.h>
using namespace vendor::melrose::hardware::led::V1_0;
using namespace vendor::melrose::hardware::led::V1_0::implementation;
using namespace android::hardware;
int main(){
//获取Led的强指针
android::sp<ILed> ptr = Led::getInstance();
if (ptr == nullptr){
return 1;
}
//配置线程池,需要添加头文件 system/libhidl/transport/include
configureRpcThreadpool(1,true);
//注册成一个服务
auto stat = ptr->registerAsService("led-service");
if (stat != android::OK){
LOG_I("start led service failed %d!",stat);
return 1;
}
LOG_I("start led service successful!");
//加入线程池
joinRpcThreadpool();
return 0;
}
配置一个绑定式服务只需要4步:
- 获取Led的强指针
- 配置线程池
- 注册成一个服务
- 加入线程池
添加HAL服务到目标版本中
在编写完服务后,我们需要为目标编译HAL服务并将其作为vendor镜像的一部分,在device/generic/goldfish中的manifest.xml中添加hal(interface里面的instance的值为注册服务的名字,默认为default):
<hal format="hidl">
<name>vendor.melrose.hardware.led</name>
<transport>hwbinder</transport>
<version>1.0</version>
<interface>
<name>ILed</name>
<instance>led-service</instance>
</interface>
</hal>
接下来,我们需要将之前自定义的厂商的HAL模块和LED模块添加到device/generic/goldfish/vendor.mk中:
PRODUCT_PACKAGES += \
vendor.melrose.hardware \
vendor.melrose.hardware.led@1.0-service
在添加完成后,make installclean后并m编译adb shell进入模拟器就可以在目录vendor/bin/hw下看到我们的服务:
验证HAL服务(客户端)
编写客户端
在完成服务端的编写后,可以编写简单的客户端去测试,首先,我们在厂商vendor/melrose下创建tests文件夹并创建对应的路径来放置客户端:
`-- melrose
|-- hardware
| `-- interfaces
| |-- Android.bp
| |-- current.txt
| `-- led
| `-- 1.0
| |-- Android.bp
| |-- ILed.hal
| |-- default
| | |-- Android.bp
| | |-- Led.cpp
| | |-- Led.h
| | `-- service.cpp
| `-- types.hal
`-- tests
`-- hardware
`-- interfaces
`-- led
`-- 1.0
执行如下命令来创建客户端的Android.bp:
hidl-gen -L androidbp-impl -r vendor.melrose.hardware:vendor/melrose/hardware/interfaces/ vendor.melrose.hardware.led@1.0 -o vendor/melrose/tests/hardware/interfaces/led/1.0/default/
将Android.bp修改为如下:
cc_binary {
name: "vendor.melrose.hardware.led-1.0-test",
relative_install_path: "test",
proprietary: true,
srcs: [
"LedTest.cpp",
],
shared_libs: [
"libhidlbase",
"libhidltransport",
"libutils",
"vendor.melrose.hardware.led@1.0",
],
}
创建对应的LedTest.cpp文件,并将创建的cpp文件和bp文件按照之前的方法添加到AndroidStudio中进行开发编辑:
#include <stdio.h>
#include <vendor/melrose/hardware/led/1.0/ILed.h>
using namespace vendor::melrose::hardware::led::V1_0;
int main(){
android::sp<ILed> led = ILed::getService("led-service");
if (led == nullptr){
printf("Failed to get service\n");
return 1;
}
led->open();
led->close();
return 0;
}
代码逻辑很简单,获取Led服务,并调用它的方法 ;
然后将测试模块添加到device/generic/goldfish/vendor.mk中:
PRODUCT_PACKAGES += \
vendor.melrose.hardware \
vendor.melrose.hardware.led@1.0-service \
vendor.melrose.hardware.led-1.0-test
开启服务端
在m编译后运行emulator即可打开模拟器,然后重开终端adb shell进入模拟器,直接执行该命令即可启动服务:
vendor/bin/hw/vendor.melrose.hardware.led@1.0-service
运行客户端
在服务端开启后,另外一个终端adb shell就可以直接运行该命令就能测试:
vendor/bin/test/vendor.melrose.hardware.led-1.0-test
直接运行该命令就可以在Logcat中看到服务端打印的日志。
界面很漂亮