Android HIDL
本篇仅简单介绍 hidl ,关于 hidl 的更为详细的介绍,请阅读 Google 官方文档 HIDL (虽然写的很乱)。
简介
HAL 接口定义语言(简称 HIDL,发音为“hide-l”)是用于指定 HAL 和其用户之间的接口的一种接口描述语言 (IDL)。HIDL 允许指定类型和方法调用(会汇集到接口和软件包中)。从更广泛的意义上来说,HIDL 是用于在可以独立编译的代码库之间进行通信的系统。 HIDL 旨在用于进程间通信 (IPC)。进程之间的通信经过 Binder 化。对于必须与进程相关联的代码库,还可以使用直通模式(在 Java 中不受支持)。 说白了,就是为了实现进程间通信。在说通俗点,其实就是对 binder 进行进一步封装,好吧,又是 binder,所以在学习 hidl 流程时,需要先去了解 binder 的流程
例子1(支持直通式 passthrough)
这里用 bootcontrol 举例,代码路径在 hardware/interfaces/boot/1.0 ,这里分析的是 c++ 的 hidl。另外 passthrough 和 Binderized 的模式只是表示的注册时候的方式。
├── Android.bp
├── Android.mk
├── default
│ ├── android.hardware.boot@1.0-service.rc
│ ├── Android.mk
│ ├── BootControl.cpp
│ ├── BootControl.h
│ └── service.cpp
├── IBootControl.hal
├── types.hal
└── vts
├── Android.bp
├── Android.mk
└── functional
├── Android.bp
└── VtsHalBootV1_0TargetTest.cpp
vts 那部分先不看,主要用来 vts 测试的,主要是用来保证接口和 google 要求的保证一致。所以我们先来看一下 types.hal 和 IBootControl.hal。
//申明包
package android.hardware.boot@1.0;
//定义数据类型
struct CommandResult {
bool success;
string errMsg;
};
typedef uint32_t Slot;
enum BoolResult : int32_t {
FALSE = 0,
TRUE = 1,
INVALID_SLOT = -1
};
package android.hardware.boot@1.0;
interface IBootControl {
getNumberSlots() generates (uint32_t numSlots);
getCurrentSlot() generates (Slot slot);
markBootSuccessful() generates (CommandResult error);
setActiveBootSlot(Slot slot) generates (CommandResult error);
setSlotAsUnbootable(Slot slot) generates (CommandResult error);
isSlotBootable(Slot slot) generates (BoolResult bootable);
isSlotMarkedSuccessful(Slot slot) generates (BoolResult successful);
getSuffix(Slot slot) generates (string slotSuffix);
};
注册服务
#define LOG_TAG "android.hardware.boot@1.0-service"
#include <android/hardware/boot/1.0/IBootControl.h>
#include <hidl/LegacySupport.h>
using ::android::hardware::boot::V1_0::IBootControl;
using android::hardware::defaultPassthroughServiceImplementation;
int main (int /* argc */, char * /* argv */ []) {
return defaultPassthroughServiceImplementation<IBootControl>();
}
IBootControl 这个类是在哪实现的?其实是由 .hal 文件经过 update-makefiles.sh 脚本(hardware/interface 下有,framework 里面也有)编译生成的,它会生成 Android.bp Android.mk 文件,然后在 out 目录你可以查找一下 IBootControl.h ,里面定义了相关的结构体。
namespace android {
namespace hardware {
namespace boot {
namespace V1_0 {
struct IBootControl : public ::android::hidl::base::V1_0::IBase {
virtual bool isRemote() const override { return false; }
.......
defaultPassthroughServiceImplementation 是一个模板函数,最终调用的是 IBootControl::getService() ,这个也是脚本编译生成的,在out/soong/下搜 IBootControlAll.cpp.看函数名称是不是很熟悉,都是在 binder 中出现过的,这边关注的 getTransport 函数。
::android::sp<IBootControl> IBootControl::getService(const std::string &serviceName, const bool getStub) {
using ::android::hardware::defaultServiceManager;
using ::android::hardware::details::waitForHwService;
using ::android::hardware::getPassthroughServiceManager;
using ::android::hardware::Return;
using ::android::sp;
using Transport = ::android::hidl::manager::V1_0::IServiceManager::Transport;
sp<IBootControl> iface = nullptr;
const sp<::android::hidl::manager::V1_0::IServiceManager> sm = defaultServiceManager();
if (sm == nullptr) {
ALOGE("getService: defaultServiceManager() is null");
return nullptr;
}
Return<Transport> transportRet = sm->getTransport(IBootControl::descriptor, serviceName);
if (!transportRet.isOk()) {
ALOGE("getService: defaultServiceManager()->getTransport returns %s", transportRet.description().c_str());
return nullptr;
}
Transport transport = transportRet;
const bool vintfHwbinder = (transport == Transport::HWBINDER);
const bool vintfPassthru = (transport == Transport::PASSTHROUGH);
代码路径在 system/hwservicemanager/Vintf.cpp ,注意到代码中的 GetFrameworkHalManifest 以及 GetDeviceHalManifest,分别从 /system/manifest.xml 以及 /vendor/manifest.xml 文件读取相关的 hidl 类型。system 下的 xml 文件由 Google 提供,路径在system/libhidl/manifest.xml,vendor 则由 soc 厂商提供,例如高通平台 /device/qcom/xxx/manifest.xml
vintf::Transport getTransport(const std::string &interfaceName, const std::string &instanceName) {
FQName fqName(interfaceName);
..................
vintf::Transport tr = getTransportFromManifest(fqName, instanceName,
vintf::VintfObject::GetFrameworkHalManifest());
if (tr != vintf::Transport::EMPTY) {
return tr;
}
tr = getTransportFromManifest(fqName, instanceName,
vintf::VintfObject::GetDeviceHalManifest());
if (tr != vintf::Transport::EMPTY) {
return tr;
}
LOG(WARNING) << __FUNCTION__ << ": Cannot find entry "
<< fqName.string() << "/" << instanceName
<< " in either framework or device manifest.";
return vintf::Transport::EMPTY;
}
Android N 时,加载 hal 模块的时候,大都在 framework 中显式调用,引入 hidl 后,调用放在了 hwservicemanager 中。
if (getStub || vintfPassthru || vintfLegacy) {
const sp<::android::hidl::manager::V1_0::IServiceManager> pm = getPassthroughServiceManager();
if (pm != nullptr) {
Return<sp<::android::hidl::base::V1_0::IBase>> ret =
pm->get(IBootControl::descriptor, serviceName);
if (ret.isOk()) {
sp<::android::hidl::base::V1_0::IBase> baseInterface = ret;
if (baseInterface != nullptr) {
iface = IBootControl::castFrom(baseInterface);
if (!getStub || trebleTestingOverride) {
iface = new BsBootControl(iface);
}
}
}
}
}
ServiceManagement.cpp 中的 get 函数实现了加载。看一下 openLibs 函数,会根据参数通过函数 dlopen 加载 /vendor/lib/hw/android.hardware.boot@1.0-impl.so
Return<sp<IBase>> get(const hidl_string& fqName,
const hidl_string& name) override {
sp<IBase> ret = nullptr;
openLibs(fqName, [&](void* handle, const std::string &lib, const std::string &sym) {
IBase* (*generator)(const char* name);
*(void **)(&generator) = dlsym(handle, sym.c_str());
if(!generator) {
const char* error = dlerror();
LOG(ERROR) << "Passthrough lookup opened " << lib
<< " but could not find symbol " << sym << ": "
<< (error == nullptr ? "unknown error" : error);
dlclose(handle);
return true;
}
ret = (*generator)(name.c_str());
if (ret == nullptr) {
dlclose(handle);
return true; // this module doesn't provide this instance name
}
registerReference(fqName, name);
return false;
});
return ret;
}
通过 dlsym 函数加载 HIDL_FETCH_IBootControl,HIDL_FETCH_IBootControl 结构体终于找到了我们熟悉的 hw_get_module ,这样子 hidl 就和 hal 紧紧联系在了一起。
IBootControl* HIDL_FETCH_IBootControl(const char* /* hal */) {
int ret = 0;
boot_control_module_t* module = NULL;
hw_module_t **hwm = reinterpret_cast<hw_module_t**>(&module);
ret = hw_get_module(BOOT_CONTROL_HARDWARE_MODULE_ID, const_cast<const hw_module_t**>(hwm));
if (ret)
{
ALOGE("hw_get_module %s failed: %d", BOOT_CONTROL_HARDWARE_MODULE_ID, ret);
return nullptr;
}
module->init(module);
return new BootControl(module);
}
hal 层细节实现,通过该结构体进行绑定。代码路径在 hardware/qcom/bootctrl
boot_control_module_t HAL_MODULE_INFO_SYM = {
.common = {
.tag = HARDWARE_MODULE_TAG,
.module_api_version = 1,
.hal_api_version = 0,
.id = BOOT_CONTROL_HARDWARE_MODULE_ID,
.name = "Boot control HAL",
.author = "Code Aurora Forum",
.methods = &boot_control_module_methods,
},
.init = boot_control_init,
.getNumberSlots = get_number_slots,
.getCurrentSlot = get_current_slot,
.markBootSuccessful = mark_boot_successful,
.setActiveBootSlot = set_active_boot_slot,
.setSlotAsUnbootable = set_slot_as_unbootable,
.isSlotBootable = is_slot_bootable,
.getSuffix = get_suffix,
.isSlotMarkedSuccessful = is_slot_marked_successful,
};
framework 调用
bool BootControlAndroid::Init() {
module_ = IBootControl::getService();
if (module_ == nullptr) {
LOG(ERROR) << "Error getting bootctrl HIDL module.";
return false;
}
LOG(INFO) << "Loaded boot control hidl hal.";
return true;
}
小结: goole 官方对于直通和绑定式服务介绍的都不是很清晰,但是可以肯定的是,是 hwbinder 还是 passthrough 都是在 mainfrest.xml 里面控制的。这里有一个疑问,那 defaultPassthroughServiceImplementation 存在的意义是什么?为何不用默认的纯绑定式进行注册服务?
例子2 (绑定式 )
这里分析的是纯绑定式的,这里以 fingerprint 为例
int main() {
android::sp<IBiometricsFingerprint> bio = BiometricsFingerprint::getInstance();
configureRpcThreadpool(1, true /*callerWillJoin*/);
if (bio != nullptr) {
bio->registerAsService();
} else {
ALOGE("Can't create instance of BiometricsFingerprint, nullptr");
}
joinRpcThreadpool();
return 0; // should never get here
}
总结
hidl 的存在,并不是把整个 hal 层进行了打包封装,而是通过定义这种结构语言,将 hal 层操作放在了 hidl 中,而 hidl 又通过 binder 实现了进程间通信机制,framework 需要调用 hal 的函数,直接通过 hwservicemanager 实现,这样保证了 framework 和 hal 运行在不同进程,在进行 OTA 时,便可实现只升级 framework 。
对于码农来说,我们需要掌握 hidl 的东西在这篇内容已经够用了。包括启动 (android.hardware.boot@1.0-service.rc),注册(service.cpp hwservicemanager),client获取(IBootControl::getService)。