什么是HIDL?

官方回答:

HAL 接口定义语言(简称 HIDL,发音为“hide-l”)是用于指定 HAL 和其用户之间的接口的一种接口描述语言 (IDL)。HIDL 允许指定类型和方法调用(会汇集到接口和软件包中)。从更广泛的意义上来说,HIDL 是用于在可以独立编译的代码库之间进行通信的系统。

HIDL 旨在用于进程间通信 (IPC)。进程之间的通信采用 Binder 机制。对于必须与进程相关联的代码库,还可以使用直通模式(在 Java 中不受支持)。

HIDL 可指定数据结构和方法签名,这些内容会整理归类到接口(与类相似)中,而接口会汇集到软件包中。尽管 HIDL 具有一系列不同的关键字,但 C++ 和 Java 程序员对 HIDL 的语法并不陌生。此外,HIDL 还使用 Java 样式的注释。

我的理解:

其实就是谷歌为了解耦硬件与上层软件,摆脱传统的HAL需要依赖上层与底层硬件,这样分离开来便于底层上层人员各司其职,专于自己部分的开发。同时也便于代码的管理,当定制硬件发生改变的时候,不需要改动谷歌相关的代码,只需要芯片厂商或者odm修改硬件相关的代码即可,做ota升级时也可单独升级硬件。这样做还是很多好处的。这里还有一个比较晦涩的概念就是直通模式和binder模式,刚开始接触的时候一脸懵逼,其实后来的理解就是一个在同一个进程当中(直通模式)相当于传统hal模式的直接调用库的方式,这时候不牵扯到进程间通信。另一种就是不在同一个进程中(binder模式),相当于使用aidl模式的客户端与服务端的通信,必须要有一个底层的binder驱动来支持,当然hidl有自己的binder驱动叫做hwbinder其实大同小异。两种模式的共同目标都是用来实现对底层硬件的调用,具体的硬件实现就交给各个芯片厂商odm厂商自己去实现。感觉谷歌的目的是想做一个纯软件公司,硬件相关的东西都给剥离出来。

实现自己的一个HIDL

本文以Android10为例子进行编写,由于大部分硬件开发过程中都是以C++形式,故本文目前为止仅仅介绍C++形式的hidl实现。

我们假设自己新推出了一款硬件叫做Chao,现在需要将这个硬件添加到谷歌的工程当中,这时候我们需要做的第一步就是将这个硬件添加到hidl当中,首先我们需要在hardware/interfaces/中创建一个目录叫做chao

创建完这个目录以后创建我们的1.0版本,hidl是有严格的版本管理机制的方便以后的兼容,我们同样是要在chao目录下再创建一个1.0的目录,然后在1.0这个目录下才是代码创建的开始。同时这个目录下需要创建一个default的目录用来存放后续自动生成以及需要自己实现的C++代码,整个目录结构如下图:

首先需要创建的就是hal文件,这也是hidl诞生而来的新的文件格式,在这个文件中我们定义自己的函数,类似于在aidl中首先需要定义一个aidl后缀接口文件一样。

首先我们在IChao.hal中定义硬件函数内容如下:

注意其中的包名以及函数定义方式,具体的语法格式以及定义请查阅谷歌官网https://source.android.com/devices/architecture/hidl

然后我们需要的一些变量以及结构体的定义放到types.hal中内容如下:

package android.hardware.chao@1.0;enum Status : int32_t {SUCCESS,CHAO_NOT_SUPPORTED,UNKNOWN,
};enum Type : int32_t {BACKLIGHT,KEYBOARD,BUTTONS,BATTERY,NOTIFICATIONS,ATTENTION,BLUETOOTH,WIFI,CHAO,COUNT,
};struct ChaoState {uint32_t chaoRen;uint32_t ironMan;
};

当我们创建完以上的文件以后就需要下一步的自动代码生成了:

我们需要到源码目录下先source lunch,然后定义两个全局变量指定我们的包名以及代码路径,需要用到的命令如下:

felix@felix:~/workspace/SA800U_Android10.0_R03_r200$ PACKAGE=android.hardware.chao@1.0
felix@felix:~/workspace/SA800U_Android10.0_R03_r200$ LOC=hardware/interfaces/chao/1.0/default/
felix@felix:~/workspace/SA800U_Android10.0_R03_r200$ m -j hidl-gen

最后一个拉起hidl-gen工具。

当我们执行完上述命令以后,然后才是真正生成c++代码的命令如下:

felix@felix:~/workspace/SA800U_Android10.0_R03_r200$ hidl-gen -o $LOC -Lc++-impl -randroid.hardware:hardware/interfaces -randroid.hidl:system/libhidl/transport $PACKAGEfelix@felix:~/workspace/SA800U_Android10.0_R03_r200$ hidl-gen -o $LOC -Landroidbp-impl -randroid.hardware:hardware/interfaces -randroid.hidl:system/libhidl/transport $PACKAGE

(felix@felix:~/workspace/SA800U_Android10.0_R03_r200$为作者的电脑源码路径请自行忽略)

当我们执行完上述所有的命令以后可以去default目录查看已经生成了相关的c++代码以及Android.bp文件如下:

同时我们需要在1.0目录下同样生成一个Android.bp文件便于以后的编译命令如下:

hidl-gen -Landroidbp  -r android.hidl:system/libhidl/transport -r android.hardware:hardware/interfaces android.hardware.chao@1.0

此时我们的C++代码已经生成,这时候基本可以生成一个共享库,但是这个库没有什么功能,需要我们自己去添加,一般我们也是在这个生成的cpp文件中去连接旧版hal层的库文件,或者直接进行底层硬件的操作,这样可以跟旧版本的HAL进行兼容,在这里去调用旧版本承上启下。下一步我们还需要创建一个服务,用来绑定这个库,以便于客户端来调用他,一般我们习惯给这个服务源文件取名为service.cpp,在这个文件里才是真正区别于直通式还是绑定式的服务。

先把自动生成的文件做稍微的修改,由于本例当中并没有真正的硬件操作,我们到此为止,简单进行函数的返回,修改后的代码如下:

Chao.cpp

// FIXME: your file license if you have one#include "Chao.h"namespace android {
namespace hardware {
namespace chao {
namespace V1_0 {
namespace implementation {// Methods from ::android::hardware::chao::V1_0::IChao follow.
Return<::android::hardware::chao::V1_0::Status> Chao::setChao(::android::hardware::chao::V1_0::Type type, const ::android::hardware::chao::V1_0::ChaoState& state) {if(type != Type::CHAO && state.chaoRen != 1){}// TODO implementreturn ::android::hardware::chao::V1_0::Status::SUCCESS;
}// Methods from ::android::hidl::base::V1_0::IBase follow.IChao* HIDL_FETCH_IChao(const char* /* name */) {return new Chao();
}
//
}  // namespace implementation
}  // namespace V1_0
}  // namespace chao
}  // namespace hardware
}  // namespace android

此处没有去操作真正的硬件,读者需自己添加硬件操作,本例只做演示使用

Chao.h

// FIXME: your file license if you have one#pragma once#include <android/hardware/chao/1.0/IChao.h>
#include <hidl/MQDescriptor.h>
#include <hidl/Status.h>namespace android {
namespace hardware {
namespace chao {
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 Chao : public IChao {// Methods from ::android::hardware::chao::V1_0::IChao follow.Return<::android::hardware::chao::V1_0::Status> setChao(::android::hardware::chao::V1_0::Type type, const ::android::hardware::chao::V1_0::ChaoState& state) override;// Methods from ::android::hidl::base::V1_0::IBase follow.};// FIXME: most likely delete, this is only for passthrough implementationsextern "C" IChao* HIDL_FETCH_IChao(const char* name);}  // namespace implementation
}  // namespace V1_0
}  // namespace chao
}  // namespace hardware
}  // namespace android

到此为止我们已经可以对这个目录进行简单的编译的,可以在源码目录下执行 mmma hardware/interfaces/chao/1.0/ -j8

执行结果如下:

此时可以看到我们的库已经生成了,具体目录如下图:

就像文章开头所说实现了解耦,这里的库默认也跑到了vendor目录下了,这样跟安卓framework完全脱离,system.img和vendor.img的分离,硬件适配全在vendor.img里,这也是后来Android10版本vendor.img不能随便替换的原因,否则容易不开机,因为跟硬件息息相关,反而system.img变的比较容易替换,因为已经脱离了基本的硬件依赖。

接下来我们来编写service.cpp,我们本例按照直通式的方式来实现,目录文件如下:

service.cpp内容如下:

#include <android/hardware/chao/1.0/IChao.h>
#include <hidl/LegacySupport.h>
#include <hwbinder/ProcessState.h>using android::hardware::chao::V1_0::IChao;
using android::hardware::defaultPassthroughServiceImplementation;int main() {
#ifdef ARCH_ARM_32android::hardware::ProcessState::initWithMmapSize((size_t)(32768));
#endifreturn defaultPassthroughServiceImplementation<IChao>();
}

这里的代码非常简单直通式的会调用系统预置的接口帮我们完成注册,我们直需把我们的接口名称传入defaultPassthroughServiceImplementation模板即可。

接下来还要自己创建一个android.hardware.chao@1.0-service.rc文件以便于开机的时候将我们的service.cpp驱动起来成为我们这个服务的守护进程,内容如下:

service vendor.chao-hal-1-0 /vendor/bin/hw/android.hardware.chao@1.0-serviceinterface android.hardware.chao@1.0::IChao defaultclass haluser systemgroup system

我们刚才的service.cpp以及rc文件都是后来自己创建的,我们需要把他们加到Android.bp当中参加系统的编译,所以在1.0目录下的Android.bp添加以下内容:

// FIXME: your file license if you have onecc_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: "android.hardware.chao@1.0-impl",relative_install_path: "hw",// FIXME: this should be 'vendor: true' for modules that will eventually be// on AOSP.proprietary: true,srcs: ["Chao.cpp",],shared_libs: ["libhidlbase","libhidltransport","libutils","android.hardware.chao@1.0",],
}cc_defaults {name: "chao_service_defaults",relative_install_path: "hw",defaults: ["hidl_defaults"],vendor: true,shared_libs: ["liblog","libbase","libdl","libutils","libhwbinder","libhardware","libhidlbase","libhidltransport","android.hardware.chao@1.0",],arch: {arm: {cflags: ["-DARCH_ARM_32"],},},
}cc_binary {name: "android.hardware.chao@1.0-service",defaults: ["chao_service_defaults"],init_rc: ["android.hardware.chao@1.0-service.rc"],srcs: ["service.cpp"],
}

这样我们就添加完成。

至此server端的实现基本完成,最后需要更新以下编译脚本,然后编译一下,执行如下命令:

felix@felix:~/workspace/SA800U_Android10.0_R03_r200$ ./hardware/interfaces/update-makefiles.sh

felix@felix:~/workspace/SA800U_Android10.0_R03_r200$ mmma hardware/interfaces/chao/1.0/ -j8

执行完命令以后会生成相应的bin文件以及rc文件到out目录

接下来我们还要把我们添加的服务注册到系统中去,这也是hidl区别于aidl的地方,使用一个manifest.xml来管理

我们在device/qcom/sdm845/manifest.xml中添加如下内容,这个路经不同平台为止不同需自行添加,有了这段内容才能够让客户端找到正确的版本进行绑定。
<hal format="hidl">
        <name>android.hardware.chao</name>
        <transport>hwbinder</transport>
        <version>1.0</version>
        <interface>
            <name>IChao</name>
            <instance>default</instance>
        </interface>
    </hal>

如果需要全局编译,需要添加兼容性适配将以上内容同样添加到hardware\interfaces\compatibility_matrices\compatibility_matrix.3.xml

暴力编译:

diff --git a/build/make/target/product/gsi/Android.mk b/build/make/target/product/gsi/Android.mk
index eaaa051..9418ace 100644
--- a/build/make/target/product/gsi/Android.mk
+++ b/build/make/target/product/gsi/Android.mk
@@ -62,7 +62,7 @@ $(check-vndk-list-timestamp): $(INTERNAL_VNDK_LIB_LIST) $(LATEST_VNDK_LIB_LIST)
          --new-line-format="Added %L" \
          --unchanged-line-format="" \
          $(LATEST_VNDK_LIB_LIST) $(INTERNAL_VNDK_LIB_LIST) \
-         || ( echo -e $(_vndk_check_failure_message); exit 1 ))
+         || ( echo -e $(_vndk_check_failure_message); ))
        $(hide) mkdir -p $(dir $@)
        $(hide) touch $@

温柔编译:

修改SA800U_Android10.0_R03_r200\build\target\product\gsi\current.txt

以及SA800U_Android10.0_R03_r200\build\target\product\gsi\29.txt

这几个文件跟版本有关,可以全部添加

如下内容:

如仍然遇到编译错误,将out目录生成的对比文件先删掉,然后重新编译

此时的服务还不能真正起来因为我们还没有配置seliunx权限,配置如下:

添加新的te文件hal_chao_default.te

内容如下:

type hal_chao_default, domain;
#hal_server_domain(hal_chao_default, hal_chao)

type hal_chao_default_exec, exec_type, vendor_file_type, file_type;
init_daemon_domain(hal_chao_default)

当我们刷入机器此时可以看到:

服务已经起来

生成hash值

felix@felix:~/workspace/SA800U_Android10.0_R03_r200$ hidl-gen -L hash -r androdi.harware:chao/1.0/default -r android.hardware:hardware/interfaces -r android.hidl:syste
m/libhidl/transport android.hardware.chao@1.0
d854281eec40e3f6adcdf50b2005d2b283839bb13b953a79d2641a2873cbd65a android.hardware.chao@1.0::types
518585848374af8138ca6de3d676db249eec5e055000647c1edab0e627cf4bd6 android.hardware.chao@1.0::IChao

至此我们服务端的建立算是完成,下篇文章再继续讲解客户端的调用。

客户端调用已完成见如下链接

Android HIDL 介绍学习之客户端调用_Super Jang的博客-CSDN博客

创作不易,各位看官想要支持请扫如下二维码:

Android HIDL 介绍学习和实战应用相关推荐

  1. Android HIDL 介绍学习之客户端调用

    应上一篇文章Android HIDL 介绍学习_Super Jang的博客-CSDN博客_安卓hidl读者的要求,本文更新客户端调用方法. hidl的客户端调用相比服务端的实现要简单很多,本次我们通过 ...

  2. Android HIDL机制学习

    前言: 在Android O之前,HAL是一个个的.so库,通过dlopen来进行打开,库和framework位于同一个进程.如下图所示: 在Android O之后,framework和hal运行于不 ...

  3. Android HIDL 学习

    链接:https://blog.csdn.net/lin20044140410/article/details/79578664 链接:https://blog.csdn.net/junwua/art ...

  4. WiFi基础学习到实战(一:802.11介绍)

    针对WLAN技术的前些课节分享,知识点比较独立,相互之间比较零散.以一个小功能或特性作为一次分享.通过一年多对WLAN技术的了解,为了彼此互相学习,在WLAN技术能有体系性的一起探讨进步.我们新起一个 ...

  5. Android HIDL学习 - HelloWord入门(整理1)

    Android HIDL学习(整理1) 概述 入手准备 实例应用 1.HIDL接口文件定义 2. 生成HAL相关文件 3. 实现HAL服务端的共享库 4. Hal server端启动注册程序 5.HI ...

  6. 2022 最新 Android 基础教程,从开发入门到项目实战【b站动脑学院】学习笔记——实战二:简易登录+找回密码

    在移动互联网时代,用户是每家IT企业最宝贵的资源,对于App而言,吸引用户注册并登录是万分紧要之事,因为用户登录之后才有机会产生商品交易.登录校验通常是用户名+密码组合,可是每天总有部分用户忘记密码, ...

  7. Android开发该学习哪些东西?

    开篇: 本人也是众多Android开发道路上行走的一员,听了不少大神的知乎live,自己也看了不少书,也和不少前辈交流过,所以在这里分享一下Android开发应该学习的书籍以及知识,当然,也包括一些方 ...

  8. Android HIDL 简介

    Android HIDL 简介 Qidi 2017.08.01 (Markdown & Haroopad) 注意:本文基于 Android 8.0 进行分析. 0.特别声明 本文是在 HIDL ...

  9. Android 2.3应用开发实战

    Android 2.3应用开发实战全面介绍Android OS操作环境,包括SDK 2.3版最新内容 丰富的范例程序详解Android应用开发,便于读者快速掌握 每一章的主题都安排进阶学习,满足不同读 ...

最新文章

  1. 输入法按照选字频率排序的C语言程序算法,算法与数据结构之选择排序(C语言)...
  2. 【CVPR2020-中科院计算所】多模态GNN:在视觉信息和场景文字上联合推理
  3. linux 重名名、删除文件操作
  4. 数学公式、可视化图齐齐上阵,神经网络如何一步步走向最优化「看得见」!...
  5. Elasticsearch 数据写入原理
  6. Python学习之路—初识Python
  7. 检测 USB 设备拨插的 C# 类库:USBClassLibrary
  8. Winform当移入控件时,鼠标变手的通用方法
  9. 求助!神舟笔记本BIOS进不去!
  10. 如何给PDF中的内容添加下划线
  11. Linux网卡应用程序测试
  12. python决策树分类案例_python实现决策树分类算法
  13. Chrome浏览器访问任何服务器资源,解决Origin 'null' is therefore not allowed access.错误
  14. history of program
  15. .Text blog的一点点安装心得
  16. java实现ftp跨服务器上传文件并用html5流媒体播放
  17. 从一个导出的DMP文件中,导入某一个表
  18. linux strip 命令,Linux中strip命令起什么作用呢?
  19. Word之邮件合并基础教程
  20. 2017人生总结(MECE分析法)

热门文章

  1. Excel技巧—瞬间吸引眼球的WIFI图表
  2. 时间复杂度以及NP问题详解
  3. 海尔、百度、华为盘踞的智能家居操作系统,谁拿下传统家电企业了?
  4. glibc 和 libc的区别
  5. IDEA_02 修改项目编码格式修改字体,大小
  6. js 动态插入html
  7. 解决mob网站短信验证SDK更新后,android studio下的mob短信验证接入问题
  8. PAT B1024/A1073
  9. buddy内存分配算法
  10. 计算机室在初中英语教学中的应用,信息技术在初中英语教学中的运用