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

68 篇文章 14 订阅
12 篇文章 0 订阅

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 端的实现方式。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值