Android(安卓)8.1 从零开始写 HAL -- (2) 实现 HAL 主体
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/
下。清单中会列出该款产品支持的所有外设、服务和它们的 HAL 类型。
我们也应该把正在创建的 demoComponent HAL 添加进这个清单里。以我用的 Intel 平台为例,产品代号就不说了,manifest.xml
位于 /device/harman/broxton/XXXX/
目录下。添加完成后看起来类似下面这个样子:
<manifest version="1.0" type="device"> ...... <hal format="hidl"> <name>vendor.harman.hardware.demoComponent.demoServicename> <transport>hwbindertransport> <version>1.0version> <interface> <name>IDemoServiceDefname> <instance>defaultinstance> interface> hal> ...... <sepolicy> <version>27.0version> sepolicy>manifest>
其中
表示 HAL 使用 HIDL 描述;
的值可以看出来就是接口描述文件所在的位置,但中间少了个 “.interfaces” ;
表示 HAL 实现所依赖的 binder 类型;
表示 HAL 版本,回忆上一篇文章,我们在编写接口描述文件时,文件存放路径里也有个 HAL 版本号,这两个版本号要一致;
节点用来标明 HAL 接口,下属的
指定了 HAL 的接口描述文件为 IDemoServiceDef.hal
,
指定了 HAL 的实现位于 default/
目录下。
default/
目录需要我们自己创建,位于 /vendor/
。 还是以我用的平台为例,完整路径为 /vendor/harman/hardware/interfaces/demoComponent/demoService/1.0/default
。
二、实现 HAL 主体
接下来我们要实现 demoComponent HAL 的主体。 因为 Binder 化后的 HAL 是以服务进程的形式运行在 Android native 层的,所以我给这个主体取名为 demoService。在 default/
目录下新建 DemoServiceImpl.h
和 DemoServiceImpl.cpp
。
这之后要做的事大家就很熟悉了 —— 在 DemoServiceImpl.h
里引用必要的头文件、声明类和方法:
/***************************************************************************** * Copyright (C) 2020 Qidi.Huang * * Brief: * Declaration of demo service interfaces. * * Author: huang_qi_di@hotmail.com *****************************************************************************/#pragma once#include #include #include #include #include #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++_headers
和 vendor.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 #include #include #include #include #include #include #include #include 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 #include #include #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 端的实现方式。
更多相关文章
- C语言的函数递归(下)
- Android系统板子上电启动流程
- android hook方法收集及选择优化
- Android中通过GPS或NetWork获取当前位置的经纬度
- 通过google接口在Android中实现天气预报效果
- Android(安卓)IPC机制(Android开发艺术探索)
- AndroidManifest中original-package标签
- android实体类的Parcelable
- android service 通过broadcast通知Myreceiver 启动activity