公司动态
android treble项目HIDL学习总结
文章目录概念介绍Treble项目---HIDL产生背景独立升级framework需求HIDL概念---HAL 接口定义语言目标framework和HAL解耦HIDL的两种模式---Passthrough直通 / Binderized绑定HIDL设计分析HAL模块与package软件包包含.hal文件.hal文件 语法简介.hal文件 hidl-gen工具编译转换.h/.cpp文件.hal文件hash值与多版本匹配HIDL设计举例---hwc composer的分析composer相关源码分析composer相关权限配置分析composer相关编译文件分析composer启动流程分析新增基于HIDL的HAL模块流程1. 建立模块包目录、编写.hal文件创建HIDL接口2. 执行构建脚本生成对应makefile和default目录与文件3. 在xml文件添加接口声明、在current.txt添加版本hash值4. 实现service服务端hal注册与功能实现代码5. 实现client客户端调用hal模块代码6. 配置新增hal模块Selinux配置权限7. 在mk文件添加编译关系参考概念介绍Treble项目—HIDL产生背景独立升级framework需求Android 8.0 重新设计了 Android 操作系统框架在一个名为Treble的项目中以便让制造商能够以更低的成本更轻松、更快速地将设备更新到新版 Android 系统。在这种新架构中HAL 接口定义语言HIDL指定了 HAL 和其用户之间的接口让用户能够替换 android framework而无需重新编译 HAL。HIDL概念—HAL 接口定义语言目标framework和HAL解耦HIDL HAL 接口定义语言HAL interface definition language。在 Android 8.0 中被全面使用用于在可以独立编译的代码库之间进行通信的系统定义Android Framework与Android HAL实现之间的接口。把framework和hal隔离目的是使 Android 可以在不重新编译 HAL 的情况下对 Framework 进行 OTA 升级OTAOver The Air一种远程无线升级技术在线升级系统。HIDL与AIDL比较类似底层也是基于binder机制。但是也有稍微不一样的地方。为了支持HIDLAndroid 对BInder做了一定程度的修改/dev/hwbinder。HIDL的两种模式—Passthrough直通 / Binderized绑定为了将以往设备的 Android 版本更新到 Android O开发者需要将传统的 HAL 封装到新的 HIDL 接口中这个接口为 HAL 提供了 Binder 模式以及 Passthrough 模式。1Passthrough直通模式对原先HAL的包装老版本HAL的兼容最终的binder service 跟binder client都是活在同一个进程当中仅对 C 客户端与实现适用。2Binderized绑定模式Binder化的HALHAL都被写成了binder serviceAndroid framework是binder client两者再不同的上下文。推出时即搭载了 Android 8.0 或后续版本的设备都必须只支持绑定式 HAL。如下图HIDL设计分析基于HIDL接口的HAL模块整体流程如下调用者需要包含Client端对应so通过getService获取服务端实例通过对client的操作对服务端Service进程发出申请。而Serive端是开机时init进程 通过rc启动脚本执行service对应可执行文件 创建进程并在启动过程中向HwServiceManager注册服务来响应client的访问。而实际项目过程往往service可执行文件并不会包含过多的业务实现而是通过链接impl.so共享库来实现impl.so中为真正的业务实现代码并调用Vender底层SDK涉及硬件驱动就通过kernel中内核驱动模块提供支持。整个设计android的HIDL机制主要就是client与service之间的访问接口设计。HIDL的原理大致如下android提供了一种特定编写规则的.hal文件类似c和java然后通过特定的hidl-gen工具和一些构建脚本 把.hal转换为可以编译的.cpp/.h文件、android.bp构建文件这些生成的文件已经实现了client对应soservice可执行文件包含了注册service过程。而接下来只需要在对应.cpp实现内部细节然后添加对应权限配置加入编译过程等就完成了hal模块新增。HAL模块与package软件包包含.hal文件每个HAL模块设计一个接口软件包package可以具有子级如 package.subpackage。已发布的 HIDL 软件包的根目录为 hardware/interfaces 或 vendor/vendorName。软件包名称在根目录下形成一个或多个子目录定义同一个软件包的所有文件都位于同一目录下。软件包目扩展名为 .hal 的文件。每个文件均必须包含一个指定文件所属的软件包和版本的 package 语句。types.hal 文件如果存在并不定义接口而是定义软件包中每个接口可以访问的数据类型。.hal文件 语法简介https://source.android.google.cn/devices/architecture/hidl/code-styleHIDL 语言与 C 语言类似。注释/** */表示文档注释只用于类型、方法、字段和枚举值声明;/* */表示多行注释;// 表示注释一直持续到行尾;[empty]表示该字词可能为空。?跟在文本或字词后表示它是可选的。...表示包含零个或多个项、使用指定的分隔符号分隔的序列。HIDL 中不含可变参数。 逗号用于分隔序列元素。 分号用于终止各个元素包括最后的元素。 大写字母是非终止符。 italics 是一个令牌系列如 integer 或 identifier标准 C 解析规则。constexpr是 C 样式的常量表达式例如11和1L3。 import_name 是软件包或接口名称按 HIDL 版本控制中所述的方式加以限定。 小写 words 是文本令牌。.hal文件 hidl-gen工具编译转换.h/.cpp文件hidl-gen 编译器会将 .hal 文件编译成一组 .h 和 .cpp 文件。这些自动生成的文件可用来构建客户端/服务器实现链接到的共享库。用于构建此共享库的 Android.bp 文件由 hardware/interfaces/update-makefiles.sh 脚本自动生成。IFoo.h描述 C 类中的纯 IFoo 接口它包含 IFoo.hal 文件中的 IFoo 接口中所定义的方法和类型类的命名空间包含软件包名称和版本号客户端和服务器都包含此标头客户端用它来调用方法服务器用它来实现这些方法。IHwFoo.h其中包含用于对接口中使用的数据类型进行序列化的函数的声明。开发者不得直接包含其标头它不包含任何类。BpFoo.h 从 IFoo 继承的类可描述接口的 HwBinder 代理客户端实现。开发者不得直接引用此类。BnFoo.h 保存对 IFoo 实现的引用的类可描述接口的 HwBinder 服务器端实现。开发者不得直接引用此类。FooAll.cpp包含 HwBinder 客户端和 HwBinder 服务器端的实现的类。当客户端调用接口方法时代理会自动从客户端封送参数并将事务发送到绑定内核驱动程序该内核驱动程序会将事务传送到另一端的服务器端实现。.hal文件hash值与多版本匹配HIDL的设计目标就是 把 framework 与 vender之间 的接口固定下来HIDL用的方法就是给每个hal文件生成一个hash值记录到current.txt文件中类似如下composer模块的hash值在编译的时候就可以校验。当需要新增接口的时候就新增一个版本继承上一个版本的hal接口进行扩展同时生成新版本对应的hash值。HIDL设计举例—hwc composer的分析hwc composer是android里面对叠加器的加速相关概念参考https://blog.csdn.net/runafterhit/article/details/118884165代码http://androidxref.com/9.0.0_r3/xref/hardware/interfaces/graphics/composer/2.2/composer相关源码分析/hardware/interfaces/graphics/composer/2.2/IComposer.hal定义了composer模块的hal接口。package android.hardware.graphics.composer2.2;;// 指定文件所属的软件包和版本import2.1::IComposer;// 导入2.1的类interface IComposer extends 2.1::IComposer{// 完全继承2.1包定义的IComposer}// 2.1包定义的IComposer 如下interface IComposer{getCapabilities()generates(vecCapabilitycapabilities);// 获取能力级别dumpDebugInfo()generates(string debugInfo);// 打印debug信息createClient()generates(Error error,IComposerClient client);// 创建clenit实例}IComposerClient.hal定义了composer的client的hal接口package android.hardware.graphics.composer2.1;// 指定文件所属的软件包和版本interface IComposerClient extends 2.2::IComposerClient{// 继承2.1包定义的IComposerClient 同时专门新增了2.2相关功能接口部分如下getReadbackBufferFence(Display display)generates(Error error,handle acquireFence);// 获取buf的fence}// 2.1包定义的IComposerClient 如下interface IComposerClient{// 重点相关函数如下registerCallback(IComposerCallback callback);// 回调注册接口createVirtualDisplay(/* 省略*/);// 创建虚拟display通道createLayer(Display display,uint32_tbufferSlotCount)generates(Error error,Layer layer);// 创建layerdestroyLayer(Display display,Layer layer)generates(Error error);// 销毁layersetVsyncEnabled(Display display,Vsync enabled)generates(Error error);// 使能通道vsyncsetInputCommandQueue(fmq_syncuint32_tdescriptor)generates(Error error);// 设置输入命令到queueexecuteCommands(/* 省略*/);// 直行命令queue中命令// 还有各种设置属性接口}IComposerCallback.hal定义client注册的回调类型定义在2.1包中package android.hardware.graphics.composer2.1;interface IComposerCallback{onHotplug(Display display,Connection connected);// display通路的拔插 回调onewayonRefresh(Display display);// display的刷新回调强制刷新onewayonVsync(Display display,int64_ttimestamp);// display的vsync信号回调}composer相关权限配置分析Selinux相关概念可以参考: https://blog.csdn.net/runafterhit/article/details/119920733composer要能被访问或者要访问其他资源需要进行selinux相关权限配置。这里完全是更具具体产品项目的权限设置往往需要设置1、对应服务service进程的权限管理可以访问那些资源包括binder2、service可执行文件的安全上下文谁能访问它也需要添加allow权限3、涉及硬件设备节点访问需要在service追加相关权限。举例如下/device/google/marlin/sepolicy/hal_graphics_composer_default.te 主要设置service的文件访问、binder访问等userdebug_or_eng( allow hal_graphics_composer_default diag_device:chr_file rw_file_perms;)dontaudit hal_graphics_composer_default diag_device:chr_file rw_file_perms;#misctypeattribute hal_graphics_composer_default data_between_core_and_vendor_violators;allow hal_graphics_composer_default display_data_file:dir create_dir_perms;allow hal_graphics_composer_default display_data_file:file create_file_perms;#persistallow hal_graphics_composer_default persist_file:dir search;#persist/displayallow hal_graphics_composer_default persist_display_file:dir r_dir_perms;allow hal_graphics_composer_default persist_display_file:file create_file_perms;vndbinder_use(hal_graphics_composer_default);add_service(hal_graphics_composer_default,qdisplay_service)#HWC_UeventThreadallow hal_graphics_composer_default self:netlink_kobject_uevent_socket create_socket_perms_no_ioctl;#Access/sys/devices/virtual/graphics/fb0/mdp/capsandmaybe othersr_dir_file(hal_graphics_composer_default,sysfs_type)composer相关编译文件分析在/hardware/interfaces/graphics/composer/目录下mm编译会生成1、android.hardware.graphics.composer2.-service. rc: 启动配置脚本直接从copy到vendor/etc/init。2、android.hardware.graphics.composer2.*. so客户端client使用时需要链接的客户端so。3、android.hardware.graphics.composer2.2-serviceservice的可执行文件生成在vendor/bin/hw/见2.2/default/Android.mk中编译规则service 服务的作用就是向 hwservicemanager 注册 HAL并链接impl .so调用实现。4、android.hardware.graphics.composer2.1-impl .soservice编译时链接的共享库在service进程加载时会链接进去。composer启动流程分析1、开机init进程会读取vendor/etc/init下的xxx_service. rc初始脚本文件执行vendor/bin/hw/xx_service可执行文件从而启动service进程// 脚本内容servicevendor.hwcomposer-2-2 /vendor/bin/hw/android.hardware.graphics.composer2.2-service class hal animation user system group graphics drmrpc capabilities SYS_NICE onrestart restart surfaceflinger进程状态1|HWRVL:/vendor/etc/init $ps-A|grepcomposersystem61012248748282400S android.hardware.graphics.composer2.2-service2、进程启动后servie执行main函数建立Binder IPC 通信。/hardware/interfaces/graphics/composer/2.2/default/service.cppHIDL 框架为了支持供应商 HAL 进程提供了 libbinder 用户空间库用于操作 Binder 设备节点hwbinder vndbinder。然后调用HwcLoader::load()进行hwc模块加载。intmain(){// the conventional HAL might start binder servicesandroid::ProcessState::initWithDriver(/dev/vndbinder);android::ProcessState::self()-setThreadPoolMaxThreadCount(4);android::ProcessState::self()-startThreadPool();// same as SF main threadstructsched_paramparam{0};param.sched_priority2;if(sched_setscheduler(0,SCHED_FIFO|SCHED_RESET_ON_FORK,param)!0){ALOGE(Couldnt set SCHED_FIFO: %d,errno);}android::hardware::configureRpcThreadpool(4,true/* will join */);android::spIComposercomposerHwcLoader::load();if(composernullptr){return1;}if(composer-registerAsService()!android::NO_ERROR){ALOGE(failed to register service);return1;}return1;}3、HWC模块load这里最终调用到createComposer其中主要实现调用hal::Composer::create(std::move(hal)) 来调用到ComposerHal 类中的composer/2.2/utils/passthrough/include/composer-passthrough/2.2/HwcLoader.hclass HwcLoader:public V2_1::passthrough::HwcLoader{public: static IComposer*load(){const hw_module_t* moduleloadModule();if(!module){returnnullptr;}auto halcreateHalWithAdapter(module);if(!hal){returnnullptr;}returncreateComposer(std::move(hal)).release();}/* 省略 */ static std::unique_ptrIComposercreateComposer(std::unique_ptrhal::ComposerHalhal){returnhal::Composer::create(std::move(hal));}}新增基于HIDL的HAL模块流程以下梳理添加一个基于hidl的hal模块test的大致流程作为开发参考。具体例子可以看参考中的实例。整个流程大致为写.hal文件通过工具和配置文件生成hidl相关编译文件实现内部细节添加xml的接口配置和current接口hash版本按使用需求编写client访问设置selinux配置权限添加包编译构建关系。1. 建立模块包目录、编写.hal文件创建HIDL接口在接口目录添加对应模块目录如hardware/interfaces/test/1.0/表示新增test模块默认版本号1.0。创建文件ITest.hal参考内容如下如果有必要还可以types.hal定义复杂结构体或者拆解为多个.hal文件package android.hardware.test1.0;// 声明包的名称test版本1.0interface ITest{init(TestID id);//无返回值helloWorld(string name)generates(string result);};2. 执行构建脚本生成对应makefile和default目录与文件使用hidl-gen生成default目录 里的C文件PACKAGEandroid.hardware.test1.0LOChardware/interfaces/test/1.0/default hidl-gen-o$LOC-Lc-impl -randroid.hardware:hardware/interfaces -randroid.hidl:system/libhidl/transport$PACKAGE根目录使用update-makefiles.sh生成1.0目录下的Android.bp./hardware/interfaces/update-makefiles.sh生成文件如下├──1.0│ ├── default │ │ ├── Android.bp │ │ ├── Test.cpp // 需要实现service的实现代码 │ │ ├── Test.h │ ├── Android.bp │ ├── ITest.hal再新增android.hardware.test1.0-service. rc启动脚本文件servicetest_service /vendor/bin/hw/android.hardware.test1.0-service class hal user system group system3. 在xml文件添加接口声明、在current.txt添加版本hash值在manifest.xml文件中添加新增hal模块的接口ITest声明。halformathidlnameandroid.hardware.test/nametransporthwbinder/transportversion1.0/versioninterfacenameITest/nameinstancedefault/instance/interface/hal在current.txt中添加对应软件版本的hash值。用hidl-gen -L hash命令生成。// 参考命令xxx为供应商vender名称和产品使用具体版本相关 hidl-gen-Lhash-rvendor.xxx.harware:vendor/xxx/hardware-randroid.hardware:hardware/interfaces-randroid.hidl:system/libhidl/transport vendor.xxx.harware.test1.0生成后写入current.txt类似如下composer模块的hash值4. 实现service服务端hal注册与功能实现代码在工具生成的Test.cpp中实现service的内部实现。实际项目中经常再单独实现一个impl .soservice本身是一个轻量级的可执行文件service编译时链接的共享库impl .so包含真正的实现在service进程加载时会链接进去。// 部分代码如下ReturnvoidTest::init(const::android::hardware::test::V1_0::TestIDid){mExitfalse;mNameid.name;mIDid.id;ALOGD(init:);returnVoid();}ReturnvoidTest::helloWorld(consthidl_stringname,helloWorld_cb _hidl_cb){ALOGD(helloWorld:);charbuf[100];::memset(buf,0x00,100);::snprintf(buf,100,Hello World,%s,name.c_str());hidl_stringresult(buf);_hidl_cb(result);returnVoid();}5. 实现client客户端调用hal模块代码从framework层 当需要调用的对应hal接口Android.bp添加test编译时链接对应.so的模块。然后通过getService获取client实例。参考类似如下privateITest halService;halServiceITest.getService();//获取service对应实例halService.helloWorld(str);// 调用对应client方法向service发出申请6. 配置新增hal模块Selinux配置权限Selinux相关概念可以参考: https://blog.csdn.net/runafterhit/article/details/119920733一般新增一个模块往往需要添加已下几处典型selinux配置1新增一个hal_xxx安全策略文件定义服务进行追加服务对binder使用等权限等若涉及property写需要自己添加2在file_contexts中新增service可执行文件的安全上下文3在hwservice_context中新增服务相关安全上下文及其关联type类型4如果涉及设备节点的访问还需要对应hal_xxx安全策略文件添加响应访问权限7. 在mk文件添加编译关系在对应的包项目不同配置文件有差异编译文件添加新增hal模块相关包举例/build/target/product/emulator.mkPRODUCT_PACKAGES\/* 省略 */ android.hardware.test1.0-impl\android.hardware.test1.0-service\参考官方文档https://source.android.google.cn/devices/architecture/hidlhttps://source.android.google.cn/devices/architecture/hidl/code-styleHIDL概述https://blog.csdn.net/u013357557/article/details/84561457AndroidO Treble架构下Hal进程启动及HIDL服务注册过程https://blog.csdn.net/yangwen123/article/details/79854267Android P HAL层添加HIDL实例: https://blog.csdn.net/sinat_18179367/article/details/95940030https://blog.csdn.net/qq_19923217/article/details/88398660https://blog.csdn.net/qq_19923217/article/details/89173162https://blog.csdn.net/kuang_tian_you/article/details/86599869