Android 8.1 从零开始写 HAL – (2) 实现 HAL 主体

注意:本文基于 Android 8.1 进行分析
Qidi 2020.07.18 (Markdown & Haroopad)


【前言】

通过上一篇文章《Android 8.1 从零开始写 HAL – (1) 定义接口》的努力,我们定义好了 demoComponent HAL 的接口和参数,也了解到编译时会自动产生 Binder 框架代码。通过 Binder 机制,经过 demoComponent HAL 的 Bp 和 Bn 端,用户进程就可以调用到我们的 demoService 了。

在打通 Bp/Bn 通路前,我们有必要让 demoComponent HAL 的主体 —— demoService —— 先鲜活起来。


一、配置 HAL

因为不同的产品可能使用不同的外设,所以每个产品都有自己的资源清单 manifest.xml,位于目录 /device/<CompanyName>/<PlatformName>/<ProductName>/ 下。清单中会列出该款产品支持的所有外设、服务和它们的 HAL 类型。

我们也应该把正在创建的 demoComponent HAL 添加进这个清单里。以我用的 Intel 平台为例,产品代号就不说了,manifest.xml 位于 /device/harman/broxton/XXXX/ 目录下。添加完成后看起来类似下面这个样子:

<manifest version="1.0" type="device">......<hal format="hidl"><name>vendor.harman.hardware.demoComponent.demoService</name><transport>hwbinder</transport><version>1.0</version><interface><name>IDemoServiceDef</name><instance>default</instance></interface></hal>......<sepolicy><version>27.0</version></sepolicy>
</manifest>

其中 <hal format="hidl"> 表示 HAL 使用 HIDL 描述; <name> 的值可以看出来就是接口描述文件所在的位置,但中间少了个 “.interfaces”<transport> 表示 HAL 实现所依赖的 binder 类型; <version> 表示 HAL 版本,回忆上一篇文章,我们在编写接口描述文件时,文件存放路径里也有个 HAL 版本号,这两个版本号要一致; <interface> 节点用来标明 HAL 接口,下属的 <name> 指定了 HAL 的接口描述文件为 IDemoServiceDef.hal<instance> 指定了 HAL 的实现位于 default/ 目录下。

default/ 目录需要我们自己创建,位于 /vendor/<CompanyName>/hardware/interfaces/<ComponentName>/<SubComponentName>/<VersionCode>/ 还是以我用的平台为例,完整路径为 /vendor/harman/hardware/interfaces/demoComponent/demoService/1.0/default


二、实现 HAL 主体

接下来我们要实现 demoComponent HAL 的主体。 因为 Binder 化后的 HAL 是以服务进程的形式运行在 Android native 层的,所以我给这个主体取名为 demoService。在 default/ 目录下新建 DemoServiceImpl.hDemoServiceImpl.cpp

这之后要做的事大家就很熟悉了 —— 在 DemoServiceImpl.h 里引用必要的头文件、声明类和方法:

/****************************************************************************** Copyright (C) 2020 Qidi.Huang** Brief:*    Declaration of demo service interfaces.** Author: huang_qi_di@hotmail.com*****************************************************************************/
#pragma once#include <hardware/hardware.h>
#include <binder/IServiceManager.h>
#include <binder/IPCThreadState.h>#include <vendor/harman/hardware/demoComponent/demoService/1.0/IDemoServiceDef.h>
#include <vendor/harman/hardware/demoComponent/demoService/1.0/IDemoCallback.h>#include "DemoServiceBinderInterface.h"using namespace android;
using namespace vendor::harman::hardware::demoComponent::demoService::V1_0;class DemoServiceImpl : public IDemoServiceDef {public:DemoServiceImpl();~DemoServiceImpl() {}virtual ::android::hardware::Return<int32_t> setStatus(const DemoData& sta) override;virtual ::android::hardware::Return<int32_t> registerCallback(const ::android::sp<IDemoCallback>& cb) override;virtual ::android::hardware::Return<int32_t> unregisterCallback(const ::android::sp<IDemoCallback>& cb) override;private:android::sp<IDemoCallback> callback = nullptr;
};

DemoServiceImpl 类继承自 IDemoServiceDef 类,并对接口进行覆写,同时声明了一个 IDemoCallback 指针用来保存回调函数。

大家可能会困惑IDemoServiceDef 类和接口声明是哪里来的?我们可以通过名字推测它是由 IDemoServiceDef.hal 生成的。实际上和推测一样,编译阶段自动生成的文件会被放在 /out/soong/.intermediates/vendor/harman/hardware/interfaces/demoComponent/demoService/1.0/ 目录,头文件和源文件可以分别在 vendor.harman.hardware.demoComponent.demoService@1.0_genc++_headersvendor.harman.hardware.demoComponent.demoService@1.0_genc++ 中找到。 DemoServiceImpl.h 里需要 #include 引用这些自动生成的头文件。

下方展示的是自动生成的头文件 IDemoServiceDef.h 的部分代码:

#ifndef HIDL_GENERATED_VENDOR_HARMAN_HARDWARE_DEMOCOMPONENT_DEMOSERVICE_V1_0_IDEMOSERVICEDEF_H
#define HIDL_GENERATED_VENDOR_HARMAN_HARDWARE_DEMOCOMPONENT_DEMOSERVICE_V1_0_IDEMOSERVICEDEF_H#include <android/hidl/base/1.0/IBase.h>
#include <vendor/harman/hardware/demoComponent/demoService/1.0/IDemoCallback.h>
#include <vendor/harman/hardware/demoComponent/demoService/1.0/types.h>#include <android/hidl/manager/1.0/IServiceNotification.h>#include <hidl/HidlSupport.h>
#include <hidl/MQDescriptor.h>
#include <hidl/Status.h>
#include <utils/NativeHandle.h>
#include <utils/misc.h>namespace vendor {namespace harman {namespace hardware {namespace demoComponent {namespace demoService {namespace V1_0 {struct IDemoServiceDef : public ::android::hidl::base::V1_0::IBase {virtual bool isRemote() const override { return false; }virtual ::android::hardware::Return<int32_t> setStatus(const DemoData& data) = 0;virtual ::android::hardware::Return<int32_t> registerCallback(const ::android::sp<IDemoCallback>& cb) = 0;virtual ::android::hardware::Return<int32_t> unregisterCallback(const ::android::sp<IDemoCallback>& cb) = 0;// ....省略
};std::string toString(const ::android::sp<IDemoServiceDef>&);}  // namespace V1_0
}  // namespace demoService
}  // namespace demoComponent
}  // namespace hardware
}  // namespace harman
}  // namespace vendor#endif  // HIDL_GENERATED_VENDOR_HARMAN_HARDWARE_DEMOCOMPONENT_DEMOSERVICE_V1_0_IDEMOSERVICEDEF_H

然后在 DemoServiceImpl.cpp 中实现各方法:

/****************************************************************************** Copyright (C) 2020 Qidi.Huang** Brief:*    Implementation of demo service interfaces.** Author: huang_qi_di@hotmail.com*****************************************************************************/#include "DemoServiceImpl.h"using namespace vendor::harman::hardware::demoComponent::demoService::V1_0;DemoServiceImpl::DemoServiceImpl() {}::android::hardware::Return<int32_t> DemoServiceImpl::setStatus(const DemoData& sta) {ALOGI("DemoServiceImpl setStatus");// 省略设置状态的代码ALOGI("DemoService invokes callback");// 在最后执行回调函数发送通知callback->onCallbackEvent(sta);return 0;
}::android::hardware::Return<int32_t> DemoServiceImpl::registerCallback(const ::android::sp<IDemoCallback>& cb) {ALOGI("DemoServiceImpl registerCallback");// 保存回调函数callback = cb;ALOGI("DemoService callback function saved");return 0;
}::android::hardware::Return<int32_t> DemoServiceImpl::unregisterCallback(const ::android::sp<IDemoCallback>& cb) {ALOGI("DemoServiceImpl unregisterCallback");if (callback == cb) {ALOGI("DemoService callback function cleared.");// 清空回调函数callback = nullptr;} else {ALOGI("DemoService callback function mismatch, uncleared.");}return 0;
}

这 3 个接口的实现写得很简单,直接看代码注释就可以,无需赘言。


三、将 HAL 注册为 Binder 服务

因为 Binder 化的 HAL 以独立本地进程的形式运行,所以必定需要 main() 函数作为进程启动入口。我们当然可以把 main() 写在 DemoServiceImpl.cpp 中,但为了与接口实现进行区分,我在 default/ 目录下新建源文件 DemoService.cpp,在该文件中实现且仅实现 main() 函数。如下:

/****************************************************************************** Copyright (C) 2020 Qidi.Huang** Brief:*    Entry of demo service.** Author: huang_qi_di@hotmail.com*****************************************************************************/#define LOG_TAG "vendor.harman.demoComponent.demoService@1.0-service"#include <android/log.h>
#include <binder/ProcessState.h>
#include <hidl/LegacySupport.h>#include "DemoServiceImpl.h"int main(int /* argc */, char* /* argv */ []) {android::ProcessState::initWithDriver("/dev/hwbinder");  // 初始化 Binder 驱动auto service = std::make_unique<DemoServiceImpl>();      // 构造 DemoServiceImpl 实例android::hardware::configureRpcThreadpool(4, true /* callerWillJoin */);ALOGI("DemoService registerAsService");android::status_t status = service->registerAsService();  // 注册为 Binder 服务if (status != android::OK) {ALOGE("Unable to register DemoService (%d)", status);return 1;}android::hardware::joinRpcThreadpool();return 1;
}

main() 函数中主要干了些事:使用设备节点 /dev/hwbinder 初始化 Binder 驱动,并以单例(Singleton)的方式将 DemoServiceImpl 类的一个实例注册为 Binder 服务。

如果你希望 HAL 进程也能与其它本地进程交互,那么在初始化驱动的时候应该使用 /dev/vndbinder


【结语】

如果说用户端进程相当于一条公交路线的始发站,那么 demoComponent 的 demoService 就相当于这条公交路线的终点站。汽车要从始发站开到终点站,还需要一条完好的公路,这条公路就是 demoComponent Bp/Bn 端实现。

下一篇文章 《Android 8.1 从零开始写 HAL – (3) 实现 Bp、Bn 端》要说明的就是 demoComponent BpBn 端的实现方式。

Android 8.1 从零开始写 HAL -- (2) 实现 HAL 主体相关推荐

  1. 【Android项目实战 | 从零开始写app(十二)】实现app首页智慧服务热门推荐热门主题、新闻

    说在前面,由于各种adapter,xml布局,bean实体类,Activity,也为了让看懂,代码基本都是"简单粗暴直接不好看",没啥okhttp和util工具类之类的封装,本篇幅 ...

  2. 【Android项目实战 | 从零开始写app (六) 】用TabLayout+ViewPager搭建App 框架主页面底部导航栏

    本篇实现效果: 搭建app框架的方式有很多,本节主要用TabLayout+ViewPager搭建App框架,这种方式简单易实现,在主页中加载Fragment碎片,实现不同功能页面的切换效果图如下: 文 ...

  3. 【Android项目实战 | 从零开始写app(十三)】实现用户中心模块清除token退出登录信息修改等功能

    五一后,被ji金伤了,哇呜呜,还是得苦逼老老实实打工写代码,看下面吧 本篇实现效果: 实现登录用户名展示到用户中心页面上,并且页面有个人信息,订单列表,修改密码,意见反馈发送到服务端,前面登录后,通过 ...

  4. 【Android项目实战 | 从零开始写app一一智慧服务】完结篇系列导航篇、源代码

    目录 文章介绍 涉及知识 系列汇总 项目源代码 文章介绍 本系列小文是一个简单的Android app项目实战,对于刚入门Android 的初学者来说,基础学完了,但是怎么综合的去写一个小app,可能 ...

  5. 《Android深度探索(卷1):HAL与驱动开发》——6.4节使用多种方式测试Linux驱动...

    本节书摘来自异步社区<Android深度探索(卷1):HAL与驱动开发>一书中的第6章,第6.4节使用多种方式测试Linux驱动,作者李宁,更多章节内容可以访问云栖社区"异步社区 ...

  6. 新书出版:《Android深度探索(卷1):HAL与驱动开发》

    <Android深度探索(卷1):HAL与驱动开发> [1]亚马逊 [2]当当网 [3]京东商城 [4]互动网 [5]淘宝网 [6]豆瓣网 < Android深度探索(卷1):HAL ...

  7. python 模拟浏览器selenium_从零开始写Python爬虫 --- 3.1 Selenium模拟浏览器

    本来说好要写Mechanize模拟浏览器的,结果一看居然只支持2.x版本的Python,而且已经长久没有更新维护了,本着学新不学旧的原则,我决定跳过他,直接开学Selenium(其实就是我懒,逃 Se ...

  8. dotnet 从零开始写一个人工智能 从一个神经元开始

    现在小伙伴说的人工智能都是弱智能,可以基于神经网络来做.而神经网络是有多层网络,每一层网络都有多个神经元.那么最简单的神经网络就是只有一层,而这一层只有一个神经元,也就是整个神经网络只是有一个神经元. ...

  9. 从零开始写STM32平衡小车代码,从0到1

    从零开始写STM32平衡小车代码,从0到1 教你从零开始写STM32平衡小车代码 前言: 本人也是学生,只是分享一下自己的设计思路与代码教学. 这次STM32平衡小车是基于STM32CubeMX软件生 ...

最新文章

  1. python3基础知识点总结_python基础知识点总结
  2. onSaveInstanceState与onRestoreInstance
  3. 关于idea中新建web项目 webapp文件夹没有小蓝点 ,启动服务,访问不到解决方案
  4. pyquery获取不到网页完整源代码_PyQuery 详解
  5. 康托展开式---我排第几+逆康托展开
  6. usb转ttl模块与matlab,图文详解USB转TTL设备与电路板的连接
  7. Parameter '**' not found. Available parameters are [0, 1, param1, param2]解决办法
  8. feignclient对象找不到_领导同事争相介绍对象,相亲N次,我找不到一条结婚的理由...
  9. 2016 版 Laravel 系列入门教程(三)【最适合中国人的 Laravel 教程】
  10. python爬取斗鱼弹幕_Python爬取斗鱼的弹幕,看看奇葩网友都说了些什么
  11. KND数控系统PLC开发环境
  12. 从日文输入法项目看心理模型和实现模型
  13. vs 2010旗舰版问题
  14. 2013MDCC 参观有感
  15. java编写一个可切换的界面_java web 项目实现手动中英文切换
  16. 使用 In-Trangle Test 检测极点
  17. Matlab逆向归纳法,6.完全信息动态博弈—逆向归纳法和子博弈完美均衡.ppt
  18. MySQL 数据库备份和还原数据库 mysqldump、source
  19. Oracle的视图和索引
  20. 华为防火墙配置HTTP服务器的负载均衡

热门文章

  1. 华信短信视频开源平台搭建攻略(之三完结易错篇)
  2. guava实现MD5加密
  3. iMeta高被引论文|西农焦硕/韦革宏等-干旱生态系统中土壤真菌与细菌群落构建的关系...
  4. springSecurity 登陆失败前台显示账号密码登录错误
  5. yolo3.cfg相关配置
  6. 密码学基础算法(二)中国剩余定理
  7. 带你领略3D转换的魅力~
  8. 【JAVA】Java8对时间的一些常用操作记录。例如:LocalDateTime、ZoneId等。
  9. Linux 3.10内核锁瓶颈描述以及解决-IPv6路由cache的性能缺陷
  10. matlab示波器有功功率,巧用示波器计算功率