1. 概述
hidl基本知识可以参考官网
安卓官网-hidl
也讲解了C++和Java实现hidl
本文讲解C++Hal服务的创建
2. 文件的创建
aosp整编过的代码,可以直接choosecombo后使用hidl-gen工具。如果没有整编过,可以单编hidl-gen工具。
hidl-gen工具可以用来协助创建hidl。
Process FQNAME, PACKAGE(.SUBPACKAGE)*@[0-9]+.[0-9]+(::TYPE)?, to create output.
-h: Prints this menu.
-L <language>: The following options are available:
check : Parses the interface to see if valid but doesn't write any files.
c++ : (internal) (deprecated) Generates C++ interface files for talking to HIDL interfaces.
c++-headers : (internal) Generates C++ headers for interface files for talking to HIDL interfaces.
c++-sources : (internal) Generates C++ sources for interface files for talking to HIDL interfaces.
export-header : Generates a header file from @export enumerations to help maintain legacy code.
c++-impl : Generates boilerplate implementation of a hidl interface in C++ (for convenience).
c++-impl-headers: c++-impl but headers only.
c++-impl-sources: c++-impl but sources only.
c++-adapter : Takes a x.(y+n) interface and mocks an x.y interface.
c++-adapter-headers: c++-adapter but helper headers only.
c++-adapter-sources: c++-adapter but helper sources only.
c++-adapter-main: c++-adapter but the adapter binary source only.
java : (internal) Generates Java library for talking to HIDL interfaces in Java.
java-constants : (internal) Like export-header but for Java (always created by -Lmakefile if @export exists).
vts : (internal) Generates vts proto files for use in vtsd.
makefile : (removed) Used to generate makefiles for -Ljava and -Ljava-constants.
androidbp : (internal) Generates Soong bp files for -Lc++-headers, -Lc++-sources, -Ljava, -Ljava-constants, and -Lc++-adapter.
androidbp-impl : Generates boilerplate bp files for implementation created with -Lc++-impl.
hash : Prints hashes of interface in `current.txt` format to standard out.
function-count : Prints the total number of functions added by the package or interface.
dependencies : Prints all depended types.
-O <owner>: The owner of the module for -Landroidbp(-impl)?.
-o <output path>: Location to output files.
-p <root path>: Android build root, defaults to $ANDROID_BUILD_TOP or pwd.
-R: Do not add default package roots if not specified in -r.
-r <package:path root>: E.g., android.hardware:hardware/interfaces.
-v: verbose output.
-d <depfile>: location of depfile to write to.
2.1 创建hal接口
三方创建的hal接口一般位于vendor/厂商名/interfaces下,然后每一个hidl模块创建一个子目录。
hal接口需要使用hidl中的数据类型,具体可参照安卓官网
package vendor.vendorname.hardware.hidltest@1.0;
interface ITest {
add(int32_t a, int32_t b) generates (int32_t result);
};
package指定软件包,interface指定hal接口,接口的全限定名称其实是软件包::接口名。
2.2 hash值写入current.txt
current.txt记录interfaces中所有接口的hash值,hal接口编译的时候会判断hash值,如果hash值改变,就会编译失败。其目的在于不允许改变接口文件,可以升级接口版本,依赖最初的接口版本,然后把新版本接口的hash值插入current.txt文件中,实现版本迭代。
hidl-gen -L hash -r vendor.vendorname.hardware:vendor/vendorname/interfaces vendor.vendorname.hardware.hidltest@1.0 >> interfaces/current.txt
借助hidl-gen工具生成hash值,并写入current.txt文件中,生成的hash值示例如下:
bac86584fe7fb75f0ccf0bc956428031f69074edfac20c4bd4106ccd41514dab vendor.vendorname.hardware.hidltest@1.0::ITest
2.3 创建hal接口的bp文件
借助hidl-gen工具生成hal接口的bp文件
hidl-gen -L androidbp -r vendor.vendorname.hardware:vendor/vendorname/interfaces vendor.vendorname.hardware.hidltest@1.0
生成的Androi.bp文件如下
// This file is autogenerated by hidl-gen -Landroidbp.
hidl_interface {
name: "vendor.vendorname.hardware.hidltest@1.0",
root: "vendor.vendorname.hardware",
product_specific: true,
srcs: [
"ITest.hal",
],
interfaces: [
"android.hidl.base@1.0",
],
gen_java: true,
}
product_specific: true指定生成的文件在product分区
root指定父路径,一般需要在interfaces路径下添加bp文件
vendor/vendorname/interfaces/Android.bp
hidl_package_root {
name: "vendor.vendorname.hardware",
path: "vendor/vendorname/interfaces",
}
subdir = [
"*",
]
这里指定了hidl的根路径。
2.4 生成C++文件
hidl-gen -o vendor/vendorname/interfaces/hidltest/1.0/default -L c++-impl -r vendor.vendorname.hardware:vendor/vendorname/interfaces vendor.vendorname.hardware.hidltest@1.0
通过-o参数来指定生成文件的目录
生成了一个头文件和一个源文件
Test.h
// FIXME: your file license if you have one
#pragma once
#include <vendor/vendorname/hardware/hidltest/1.0/ITest.h>
#include <hidl/MQDescriptor.h>
#include <hidl/Status.h>
namespace vendor {
namespace vendorname {
namespace hardware {
namespace hidltest {
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;
struct Test : public ITest {
// Methods from ::vendor::vendorname::hardware::hidltest::V1_0::ITest follow.
Return<int32_t> add(int32_t a, int32_t b) override;
// Methods from ::android::hidl::base::V1_0::IBase follow.
};
// FIXME: most likely delete, this is only for passthrough implementations
// extern "C" ITest* HIDL_FETCH_ITest(const char* name);
} // namespace implementation
} // namespace V1_0
} // namespace hidltest
} // namespace hardware
} // namespace vendorname
} // namespace vendor
Test.cpp
// FIXME: your file license if you have one
#include "Test.h"
namespace vendor {
namespace vendorname {
namespace hardware {
namespace hidltest {
namespace V1_0 {
namespace implementation {
// Methods from ::vendor::vendorname::hardware::hidltest::V1_0::ITest follow.
Return<int32_t> Test::add(int32_t a, int32_t b) {
// TODO implement
//return int32_t {}; 原生生成的
return a + b;
}
// Methods from ::android::hidl::base::V1_0::IBase follow.
//ITest* HIDL_FETCH_ITest(const char* /* name */) {
//return new Test();
//}
//
} // namespace implementation
} // namespace V1_0
} // namespace hidltest
} // namespace hardware
} // namespace vendorname
} // namespace vendor
可以看到软件包名会转换成namespace,并将hal接口生成了对应的实现,我们可以在实现中进行修改。
2.5 创建源码的bp文件
hidl-gen -o vendor/vendorname/interfaces/hidltest/1.0/default -L androidbp-impl -r vendor.vendorname.hardware:vendor/vendorname/interfaces vendor.vendorname.hardware.hidltest@1.0
同样通过-o指定生成的bp文件跟C++文件在同一个目录
原生的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.vendorname.hardware.hidltest@1.0-impl",
relative_install_path: "hw",
// FIXME: this should be 'vendor: true' for modules that will eventually be
// on AOSP.
proprietary: true,
srcs: [
"Test.cpp",
],
shared_libs: [
"libhidlbase",
"libhidltransport",
"libutils",
"vendor.vendorname.hardware.hidltest@1.0",
],
}
默认是生成so文件的直通式hal,开发绑定式hal需要对此bp文件进行修改
// FIXME: your file license if you have one
cc_binary {
name: "vendor.vendorname.hardware.hidltest@1.0-service",
relative_install_path: "hw",
defaults: ["hidl_defaults"],
proprietary: true,
srcs: [
"Test.cpp",
"service.cpp",
],
shared_libs: [
"libhidlbase",
"libhidltransport",
"libutils",
"libcutils",
"liblog",
"vendor.vendorname.hardware.hidltest@1.0",
],
init_rc: [
"vendor.vendorname.hardware.hidltest@1.0-service",
]
}
首先是改成cc_binary,让源代码编译成一个可执行文件。更改name,默认是生成直通式hal的impl结尾,结尾改成service。添加defaults导入hidl_defaults定义,里面有hidl相关定义。srcs添加service.cpp,并创建该文件在当前目录,后续介绍。shared_libs添加常用的so依赖。添加init_rc启动文件,后续介绍。
service.cpp
# define LOG_TAG "vendorname"
#include <android/log.h>
#include <hidl/HidlSupport.h>
#include <hidl/HidlTransportSupport.h>
#include <vendor/vendorname/hardware/hidltest/1.0/ITest.h>
#include <stdio.h>
#include <unistd.h>
#include "Test.h"
using vendor::vendorname::hardware::hidltest::V1_0::ITest;
using vendor::vendorname::hardware::hidltest::V1_0::implementation::Test;
using android::sp;
using android::hardware::configureRpcThreadpool;
using android::hardware::joinRpcThreadpool;
int main() {
ALOGI("begin vendorname hwbinder service");
sp<ITest> instance = new Test();
configureRpcThreadpool(1, true);
android::status_t status = instance->registerAsService();
if (status != android::OK)
{
ALOGE("Could not register vendorname Service!");
return -1;
}
while(true) {
ALOGI("vendorname hal service recycle");
sleep(1);
}
joinRpcThreadpool();
return 0;
}
service.cpp中的main函数是编译出的hal service二进制可执行文件的执行入口。而可执行文件的调用有rc文件负责。
rc文件
service hidltest_service vendor/bin/hw/vendor.vendorname.hardware.hidltest@1.0-service
class hal
user system
group system
系统启动时,会加载读取rc文件,然后service这一行会使系统加载后面路径下的bin文件
3 编译产物
在interfaces目录下mm编译,编译完成后会在out/target/product/{brandname}/vendor生成以下产物
需要注意的是,整编的时候,并不能打入手机的版本中。需要添加
PRODUCT_PACKAGES += \
vendor.vendorname.hardware.hidltest@1.0 \
vendor.vendorname.hardware.hidltest@1.0-service
在device/coral/coral/device.mk中添加自定义mk文件,编译出错
由此看是编译通过了,需要解决编译无法打入版本的问题,这里看问题是对打进版本包的文件有限制。参考网上的解决方式,在envsetup.sh中添加环境变量
@@ -323,6 +323,8 @@ function setpaths()
unset ANDROID_TARGET_OUT_TESTCASES
export ANDROID_TARGET_OUT_TESTCASES=$(get_abs_build_var TARGET_OUT_TESTCASES)
+ export DISABLE_ARTIFACT_PATH_REQUIREMENTS="true"
+
# needed for building linux on MacOS
# TODO: fix the path
#export HOST_EXTRACFLAGS="-I "$T/system/kernel_headers/host_include
如果是真机调试,发现刷机后手机中system下有了接口so文件,但是vendor下还是没有service。以pixel4xl为例,这是因为,真机刷机时需要下载谷歌的驱动,驱动解压到aosp的根目录,会使用一个固定的vendor.img镜像,而非是我们使用aosp编译的aosp镜像。流程如下
解压后的BoardConfig.mk中
device/google/coral/coral/BoardConfig.mk
include device/google/coral/BoardConfig-common.mk
device/google/coral/BoardConfig-common.mk
-include vendor/google_devices/coral/proprietary/BoardConfigVendor.mk
-include的意思是如果文件不存在,也不会报致命错误,而是所有加载完之后再重新检查,如果还是不存在,则报警告
vendor/google_devices/coral/proprietary/BoardConfigVendor.mk
ifneq ($(filter flame,$(TARGET_DEVICE)),)
-include vendor/google_devices/flame/BoardConfigPartial.mk
-include vendor/qcom/flame/BoardConfigPartial.mk
else
-include vendor/google_devices/coral/BoardConfigPartial.mk
-include vendor/qcom/coral/BoardConfigPartial.mk
endif
vendor/google_devices/coral/BoardConfigPartial.mk
BOARD_PREBUILT_VENDORIMAGE := vendor/google_devices/coral/proprietary/vendor.img
对比out目录下的vendor.img和该目录下的vendor.img的md5值,发现一样的。
如果想要定制自己的vendor镜像可参考博文
[Android]基于AOSP源码为Pixel3编译vendor.img
如果是手动加载,此时hal服务已经能够启动
但是对于开机自启动来说,并不能启动,开机加载hal服务的时候,需要给hal服务配置selinux规则,否则会启动失败。