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//hardware/interfaces//// 还是以我用的平台为例,完整路径为 /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 #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++_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 #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 端的实现方式。

更多相关文章

  1. C语言的函数递归(下)
  2. Android系统板子上电启动流程
  3. android hook方法收集及选择优化
  4. Android中通过GPS或NetWork获取当前位置的经纬度
  5. 通过google接口在Android中实现天气预报效果
  6. Android(安卓)IPC机制(Android开发艺术探索)
  7. AndroidManifest中original-package标签
  8. android实体类的Parcelable
  9. android service 通过broadcast通知Myreceiver 启动activity

随机推荐

  1. Android WIFI DEMO(扫描附近WIFI强度)
  2. 【Android】广播大全(二)
  3. Android Studio编译问题:ProcessException
  4. Android版本检测\自动更新
  5. 关于Android中是否可以使用全局变量的问
  6. android类似于Jfreechart的图表
  7. android PreferenceScreen使用笔记
  8. android 中spinner 三级联动
  9. Android 菜单资源
  10. Android 四种Http协议详解