目录
一 ,准备
二,添加hal文件
三,实现hal文件
四,添加service
五,启动rc文件
六,生成Android.bp文件
七,SELinux权限
八,编译
android设备添加一个外围设备,不像嵌入式那么简单,系统越复杂加入的方式越杂,但是万物都有规矩可寻,有方法可走。这篇文章就说说HAL和用户之间的接口HIDL。
Google:https://source.android.google.cn/devices/architecture/hidl
google 官方这样解释 :HAL 接口定义语言(简称 HIDL,发音为“hide-l”)是用于指定 HAL 和其用户之间的接口的一种接口描述语言 (IDL)。
HIDL 允许指定类型和方法调用(会汇集到接口和软件包中)。从更广泛的意义上来说,HIDL 是用于在可以独立编译的代码库之间进行通信的系统。
HIDL 旨在用于进程间通信 (IPC)。进程之间的通信采用 Binder 机制。对于必须与进程相关联的代码库,还可以使用直通模式(在 Java 中不受支持)。
HIDL 可指定数据结构和方法签名,这些内容会整理归类到接口(与类相似)中,而接口会汇集到软件包中。尽管 HIDL 具有一系列不同的关键字,但 C++ 和 Java 程序员对 HIDL 的语法并不陌生。此外,HIDL 还使用 Java 样式的注释。
在Android8.0开始,google引入了Treble机制,为了方便Android系统的快速移植、升级、稳定,Android引入了HAL Binder机制,把framework和HAL进行隔离,减少了之间的耦合性,使得framework可以被覆盖升级,不用对HAL编译升级。
用图片展示一下两者的差别:

Android8.0之前,HAL的调用方式为Legacy HAL, HAL都是编译成so,然后动态链接到各个frameworks service中去,到了8.0,google为了给厂商一些修改时间,保持了Android的向前兼容性,另外设计了一套直通式(Passthrough)的HAL,保留动态库的HAL调用方式,
binderservice链接动态库Hal的实现。
8.0之后,google规定厂商新增的HAL都需要切换到绑定式HAL(Binderized)。
在从
libhardware
HAL 转换为 HIDL HAL 的过程中,HAL 实现成为服务器,而调用 HAL 的进程则成为客户端。默认实现可提供直通和 Binder 化 HAL,并可能会随着时间而发生变化:
Hal的发展历程
Legacy Hal:Android 8.0 之前版本的 HAL 都是编译成 so,然后动态链接到各个 frameworks service 中去。
Passthrough Hal:该模式是为了兼容旧版的 HAL,旧版 HAL 实现仍以动态库的方式提供,只是 binder service 链接了动态库 HAL 实现,即 binder service 通过 hw_get_module 链接了旧版的 hal 实现,而用户端通过与 binder service IPC 通信,间接实现了与旧版 HAL 的交互。
Binderized HAL:HAL 与 用户调用在不同的进程中,HAL 被写成 binder service,而用户接口如 frameworks 作为 binder client 通过 IPC 机制实现跨进程接口调用。
概念介绍到这里,了解更清楚的话,可以去google官网阅读相关内容。
实现一个回调案例
代码结构
├── bearpi
│ └── 1.0
│ ├── Android.bp
│ ├── current.txt
│ ├── default
│ │ ├── Android.bp
│ │ ├── [email protected]
│ │ ├── BearPi.cpp
│ │ ├── BearPi.h
│ │ └── service.cpp
│ ├── IBearPiClientCallback.hal
│ ├── IBearPi.hal
│ ├── test
│ │ ├── Android.bp
│ │ └── BearPiTest.cpp
│ └── types.hal
一 ,准备
我们需要用的hidl-gen 工具去生成文件,这个工具可以通过编译得到
source build/envsetup.sh
lunch xxx
make
先整编一下需要添加的aosp。
二,添加hal文件
在hardware/interfaces目录下新建bearpi,我们从1.0版本开始,default目录为实现hal的存放路径
mkdir -p bearpi/1.0/default
很多现有的HAL实现会与硬件异步通信,需要以异步的方式通知客户端发生的事件,比如,指令下发后返回执行状态。HIDL接口可以用作异步回调。
在bearpi/1.0路径下创建两个hal文件,
IBearPi.hal
package [email protected]; ----------------------------------------------------------------------------------------------->软件包名
import IBearPiClientCallback; --------------------------------------------------------------------------------------------------------------->
import
语句是用于访问其他软件包中的软件包接口和类型的 HIDL 机制
interface IBearPi{
setNotify(IBearPiClientCallback callback) generates (uint64_t bearpiId); --------------------------------------------------->设置回调
test() generates (uint64_t testresult);
};
IBearPiClientCallback.hal
package [email protected];
interface IBearPiClientCallback {
oneway onResult(uint64_t deviceId, BearPiAcquiredInfo acquiredInfo, uint32_t vendorCode);------------------------>回调函数
};
types.hal --------------------------------------------------------------------------------------------------------------------------------------------->
types.hal
文件并不定义接口,而是定义软件包中每个接口可以访问的数据类型
package [email protected];
enum BearPiAcquiredInfo : int32_t {
ACQUIRED_GOOD = 0,
ACQUIRED_PARTIAL = 1,
ACQUIRED_INSUFFICIENT = 2,
ACQUIRED_IMAGER_DIRTY = 3,
ACQUIRED_TOO_SLOW = 4,
ACQUIRED_TOO_FAST = 5,
ACQUIRED_VENDOR = 6
};
注意软件包前缀对应的位置,android.hardware.* -----hardware/interfaces) {---------------------------------------------->打开就是直通模式,注释使用绑定式
//return new BearPi();
//}
//
} // namespace implementation
} // namespace V1_0
} // namespace bearpi
} // namespace hardware
} // namespace android
四,添加service
在default路径下创建service.cpp
#define LOG_TAG "fht"
#include <android/log.h>
#include <hidl/HidlSupport.h>
#include <hidl/HidlTransportSupport.h>
#include <android/hardware/bearpi/1.0/IBearPi.h>
#include <android/hardware/bearpi/1.0/types.h>
#include "BearPi.h"
using android::hardware::bearpi::V1_0::IBearPi;
using android::hardware::bearpi::V1_0::implementation::BearPi;
using android::hardware::configureRpcThreadpool;
using android::hardware::joinRpcThreadpool;
using android::sp;
int main(){
ALOGD("Bearpi service start ");
android::sp<IBearPi> bear = BearPi::getInstance();
//绑定式服务
// This function must be called before you join to ensure the proper
// number of threads are created. The threadpool will never exceed
// size one because of this call.
//如果“callerWillJoin”为true,则表示调用方将加入线程池,因此内核需要少创建一个线程。
//如果“callerWillJoin”为false,我们仍然会在本地生成一个线程,我们还应该告诉内核创建的线程比这里请求的少一个。
configureRpcThreadpool(1, true ); // 用于设置 当前进程用于hwbinder通信的最大线程数
if (bear != nullptr) {
if (::android::OK != bear->registerAsService()) { //注册服务
return 1;
}
} else {
ALOGE("Can't create instance of bearpi, nullptr");
}
ALOGD("Bearpi service registerAsService ");
// Adds this thread to the threadpool, resulting in one total
// thread in the threadpool. We could also do other things, but
// would have to specify 'false' to willJoin in configureRpcThreadpool
joinRpcThreadpool();
return 0; // should never get here
//直通式服务,
defaultPassthroughServiceImplementation
将对提供的
-impl
库执行
dlopen()
操作,并将其作为 Binder 化服务提供
//return defaultPassthroughServiceImplementation<IBearPi>("BearPi");
}
五,启动rc文件
在default路径下新建 [email protected]
service vendor.bear_hal /vendor/bin/hw/[email protected]
# "class hal" causes a race condition on some devices due to files created
# in /data. As a workaround, postpone startup until later in boot once
# /data is mounted.
class hal
user system
group system system
六,生成Android.bp文件
文件创建成功后,需要加入编译,下面我们使用hidl-gen生成对应的Android.bp文件
生成1.0框架接口库
hidl-gen -Landroidbp -randroid.hardware:hardware/interfaces -randroid.hidl:system/libhidl/transport [email protected]
生成impl 库的 Android.bp
hidl-gen -Landroidbp-impl -o /home/ubuntu/data/LG/hardware/interfaces/bearpi/1.0/default/ -randroid.hardware:hardware/interfaces -randroid.hidl:system/libhidl/transport [email protected]
修改default下面Android.bp,生成service bin
cc_binary {
name: "[email protected]",
defaults: ["hidl_defaults"],
init_rc: ["[email protected]"],
vendor: true,
relative_install_path: "hw",
srcs: [
"BearPi.cpp",
"service.cpp",
],
shared_libs: [
"libcutils",
"liblog",
"libhidlbase",
"libhardware",
"libutils",
- "[email protected]",
],
}
七,SELinux权限
以mtk平台为例,添加bearpi相应权限
路径:device/mediatek/sepolicy/basic/non_plat
1,新建bearpi.te
type bearpi, domain;
type bearpi_exec, exec_type, vendor_file_type, file_type;
hwbinder_use(bearpi);
init_daemon_domain(bearpi)
add_hwservice(bearpi, hal_bearpi_hwservice)
allow bearpi hwservicemanager_prop:file { read open getattr map };
2,file_contexts中添加
/(vendor|system/vendor)/bin/hw/android\.hardware\[email protected]\.0-service u:object_r:bearpi_exec:s0
3,hwservice.te中定义hwservice manager类型
type hal_bearpi_hwservice, hwservice_manager_type;
4,hwservice_contexts中添加接口访问权限
android.hardware.bearpi::IBearPi u:object_r:hal_bearpi_hwservice:s0
八,编译
1,添加系统编译,使系统整编后能主动编译,在device/xx/device.mk中添加包编译
PRODUCT_PACKAGES += \
[email protected] \
[email protected] \
test_bearpi \
bearpi.default
DEVICE_MANIFEST_FILE += device/xiaomi/lancelot/manifest_bearpi.xml
2,在项目地下新建manifest_bearpi.xml接口注册文件,如果没有注册文件,客户端和服务端将无法通信,getService()将返回null。
<manifest version="1.0" type="device">
<hal format="hidl">
<name>android.hardware.bearpi</name>
<transport>hwbinder</transport>
<version>1.0</version>
<interface>
<name>IBearPi</name>
<instance>default</instance>
</interface>
</hal>
</manifest>
3,添加启动服务,在init.project.rc内添加bearpi 的rc文件,保证开机能够启动服务
import /vendor/etc/init/[email protected]
----------------------------------------------------------我------------是-------------分-----------割------------线---------------------------------------------------------------
如上服务端写好后,我们写客户端测试下
在1.0路径下新建test目录
创建BearPiTest.cpp
//
// Created by ubuntu on 2020/12/25.
//
#define LOG_TAG "fht"
#define LOG_VERBOSE "fht"
#include <android/hardware/bearpi/1.0/IBearPi.h>
#include <hidl/Status.h>
#include <log/log.h>
#include <android/log.h>
using android::hardware::bearpi::V1_0::BearPiAcquiredInfo;
using android::hardware::bearpi::V1_0::IBearPi;
using android::hardware::bearpi::V1_0::IBearPiClientCallback;
using android::sp;
using android::hardware::hidl_string;
using android::hardware::Return; // 命名空间
using android::hardware::Void; // Void函数
//实现回调
class BearPiClientCallback : public IBearPiClientCallback {
public:
BearPiClientCallback() {}
~BearPiClientCallback() {}
Return<void> onResult(uint64_t deviceId, const ::android::hardware::bearpi::V1_0::BearPiAcquiredInfo acquiredInfo,uint32_t vendorCode) override {
ALOGD("onResult deviceid =%lu, vendorCode = %d, info = %d", deviceId, vendorCode, acquiredInfo);
return Void();
};
};
int main() {
//获得服务
sp <IBearPi> bear = IBearPi::getService();
if (bear == nullptr) {
ALOGE("Can't find IBearPi service ...");
return -1;
}
ALOGD("IBearPi ON");
sp <BearPiClientCallback> callback = new BearPiClientCallback();
//注册回调
bear->setNotify(callback);
bear->test();
return 0;
}
在test目录下新建Android.bp
cc_binary {
name: "test_bearpi",
srcs: [
"BearPiTest.cpp",
],
shared_libs: [
"libcutils",
"liblog",
"libhidlbase",
"libhardware",
"libutils",
"[email protected]",
],
}