欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android 8.1 从零开始写 HAL -- (3) 实现 Bp、Bn 端

程序员文章站 2022-06-17 22:43:38
既然 Binder 化的 HAL 依赖于 Binder 机制进行实现,那么我们自然必须按照 Binder 框架,相应编写 demoComponent HAL 的 Bp 端和 Bn 端。只有这样,才能打通客户端进程调用到服务端进程 —— 我们的 demoService —— 的通路。...

Android 8.1 从零开始写 HAL – (3) 实现 Bp、Bn 端

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


【前言】

既然 Binder 化的 HAL 依赖于 Binder 机制进行实现,那么我们自然必须按照 Binder 框架,相应编写 demoComponent HAL 的 Bp 端和 Bn 端。只有这样,才能打通客户端进程调用到服务端进程 —— 我们的 demoService —— 的通路。


一、定义 demoService 接口类

要将 demoService 接入 Binder,就必须定义一个我们自己的接口类,继承 Binder 的接口基类 IInterface,并实现所有 Binder 通信过程中要用到的方法。不过我们并不需要事无巨细地完成这项繁杂的工作,因为 Binder 框架提供了数个模板类和宏,大大方便了我们实现。

还记得在上一篇文章《Android 8.1 从零开始写 HAL – (2) 实现 HAL 主体》里我们创建了 default/ 目录。在该目录下新建头文件 DemoServiceBinderInterface.h,并在这个头文件里定义接口类 IDemoService。如下:

/*****************************************************************************
 * Copyright (C) 2020 Qidi.Huang
 *
 * Brief:
 *    Common data type definitions of binder transactions.
 *
 * Author: huang_qi_di@hotmail.com
 *****************************************************************************/

#pragma once

#include <binder/IInterface.h>
#include <vendor/harman/hardware/demoComponent/demoService/1.0/IDemoCallback.h>

#define DEMOSERVICE_NAME "com.qidi.demoService"

using namespace vendor::harman::hardware::demoComponent::demoService::V1_0;
using ::vendor::harman::hardware::demoComponent::demoService::V1_0::IDemoCallback;
using ::vendor::harman::hardware::demoComponent::demoService::V1_0::DemoData;

namespace android {

    // command codes for binder transactions
    enum eDemoServiceTransactionID
    {
        SET_STATUS = android::IBinder::FIRST_CALL_TRANSACTION,
        REGISTER_CALLBACK,
        UNREGISTER_CALLBACK
    };


    class IDemoService: public IInterface {
    public:
        DECLARE_META_INTERFACE(DemoService);

        virtual int32_t setStatus(const DemoData& sta) = 0;
        virtual int32_t registerCallback(const ::android::sp<IDemoCallback>& cb) = 0;
        virtual int32_t unregisterCallback(const ::android::sp<IDemoCallback>& cb) = 0;
    };
}

除了定义类 IDemoService,我们还在头文件 DemoServiceBinderInterface.h 里以枚举数据的形式定义了和 demoService Binder 调用相关的 Command code。 三个 Command code SET_STATUSREGISTER_CALLBACKUNREGISTER_CALLBACK 分别对应 demoService 的三个接口。第一个 Command code 必须赋值为 android::IBinder::FIRST_CALL_TRANSACTION

DECLARE_META_INTERFACE() 主要用来声明用于 Binder 通信的成员变量 descriptor 和通用接口 asInterface()getInterfaceDescriptor()。 相关代码位于 IInterface.h 中:

#define DECLARE_META_INTERFACE(INTERFACE)                               \
    static const ::android::String16 descriptor;                        \
    static ::android::sp<I##INTERFACE> asInterface(                     \
            const ::android::sp<::android::IBinder>& obj);              \
    virtual const ::android::String16& getInterfaceDescriptor() const;  \
    I##INTERFACE();                                                     \
    virtual ~I##INTERFACE();                                            \

至此,demoService 的接口类就定义好了。


二、声明 Bp、Bn 端

有了接口类之后,就可以正式着手 Bp、Bn 端的工作了。依照先声明后实现的顺序,我们继续在 default/ 目录下新建头文件 BpDemoService.hBnDemoService.hBinder 框架提供了模板类 BpInterface<>BnInterface<> 来简化这一过程。

  • BpDemoService.h 代码如下:
    /*****************************************************************************
     * Copyright (C) 2020 Qidi.Huang
     *
     * Brief:
     *    Declaration of demo service Bp side interfaces.
     *
     * Author: huang_qi_di@hotmail.com
     *****************************************************************************/
    #pragma once
    
    #include "DemoServiceBinderInterface.h"
    
    using namespace vendor::harman::hardware::demoComponent::demoService::V1_0;
    
    namespace android {
    
        class BpDemoService: public BpInterface<android::IDemoService> {
        public:
            BpDemoService(const sp<IBinder>& impl);
    
            virtual int32_t setStatus(const DemoData& sta);
            virtual int32_t registerCallback(const ::android::sp<IDemoCallback>& cb);
            virtual int32_t unregisterCallback(const ::android::sp<IDemoCallback>& cb);
        };
    }
    

Bp 端头文件中将 IDemoService 作为参数传入 BpInterface<> 模板,所以需要引用 DemoServiceBinderInterface.h。由于模板类的设计,文件中声明的 demoService 的 3 个接口将被整合到 Binder 框架代码中。我们即便不去关注其中的细节也没关系,只要负责填入接口类和接口声明,剩下的交给 Binder 框架就好。当然,如果你充满好奇且时间充裕,也可以一头扎进去理一理思路。 BpInterface<> 模板类的定义如下:

template<typename INTERFACE>
class BpInterface : public INTERFACE, public BpRefBase
{
public:
    explicit                    BpInterface(const sp<IBinder>& remote);

protected:
    virtual IBinder*            onAsBinder();
};
  • BnDemoService.h 代码如下:
    /*****************************************************************************
     * Copyright (C) 2020 Qidi.Huang
     *
     * Brief:
     *    Declaration of demo service Bn side interface.
     *
     * Author: huang_qi_di@hotmail.com
     *****************************************************************************/
    #pragma once
    
    #include "DemoServiceBinderInterface.h"
    
    namespace android {
    
        /**
         * @brief:
         *    Binder service hook.
         * @param[in]: code: The action to perform.
         * @param[in]: data: Marshalled data to receive from client.
         * @param[out]: reply: Marshalled data to return result.
         * @param[in]: flags: Additional operation flags
         * @return:
         *    -1: failed
         *     0: success
         */
        class BnDemoService: public BnInterface<IDemoService> {
            virtual status_t onTransact( uint32_t code, const Parcel& data,
                    Parcel* reply, uint32_t flags = 0);
        };
    }
    

Bn 端头文件的内容与 Bp 端类似,但需要注意,由于 Binder 框架的设计原因,这里的方法名 onTransact() 及参数都是固定的,我们不能写成其它形式。 BnInterface<> 模板类在 IInterface.h 中的定义如下:

template<typename INTERFACE>
class BnInterface : public INTERFACE, public BBinder
{
public:
    virtual sp<IInterface>      queryLocalInterface(const String16& _descriptor);
    virtual const String16&     getInterfaceDescriptor() const;

protected:
    virtual IBinder*            onAsBinder();
};

三、实现 Bp、Bn 端

声明之后,该进行实现了。继续在 default/ 目录下新建源文件 BpDemoService.cppBnDemoService.cpp

实现过程只是顺水推舟而已,直接看代码吧。

  • BpDemoService.cpp:
    /*****************************************************************************
     * Copyright (C) 2020 Qidi.Huang
     *
     * Brief:
     *    Implementation of demo service Bp side interfaces.
     *
     * Author: huang_qi_di@hotmail.com
     *****************************************************************************/
    
    #include <utils/Log.h>
    #include <binder/IServiceManager.h>
    #include <binder/Parcel.h>
    
    #include "BpDemoService.h"
    #include "DemoServiceBinderInterface.h"
    
    #define LOG_D ALOGD
    #define LOG_E ALOGE
    
    using namespace android;
    using namespace vendor::harman::hardware::demoComponent::demoService::V1_0;
    
    int32_t BpDemoService::setStatus(const DemoData& sta) {
        Parcel data, reply;
    
        data.writeInterfaceToken(IDemoService::getInterfaceDescriptor());
        data.write(&sta, sizeof(DemoData));
    
        status_t status = remote()->transact(SET_STATUS, data, &reply);
        if (status != NO_ERROR) {
            LOG_E("BpDemoService::setStatus transact failed");
        }
    
        return reply.readInt32();
    }
    
    int32_t BpDemoService::registerCallback(const ::android::sp<IDemoCallback>& cb) {
        Parcel data, reply;
    
        data.writeInterfaceToken(IDemoService::getInterfaceDescriptor());
        data.write(&cb, sizeof(::android::sp<IDemoCallback>));
    
        status_t status = remote()->transact(REGISTER_CALLBACK, data, &reply);
        if (status != NO_ERROR) {
            LOG_E("BpDemoService::registerCallback transact failed");
        }
    
        return reply.readInt32();
    }
    
    int32_t BpDemoService::unregisterCallback(const ::android::sp<IDemoCallback>& cb) {
        Parcel data, reply;
    
        data.writeInterfaceToken(IDemoService::getInterfaceDescriptor());
        data.write(&cb, sizeof(::android::sp<IDemoCallback>));
    
        status_t status = remote()->transact(UNREGISTER_CALLBACK, data, &reply);
        if (status == NO_ERROR) {
            LOG_E("BpDemoService::unregisterCallback transact failed");
        }
    
        return reply.readInt32();
    }
    
    BpDemoService::BpDemoService(const sp<IBinder> &impl) : BpInterface<IDemoService>(impl) {}
    IMPLEMENT_META_INTERFACE(DemoService, DEMOSERVICE_NAME);
    

Bp 端的源文件里使用了宏 IMPLEMENT_META_INTERFACE(),扩展之后即是宏 DECLARE_META_INTERFACE() 所声明的接口的实现。

这个宏同样定义在 IInterface.h 中,如下:

#define IMPLEMENT_META_INTERFACE(INTERFACE, NAME)                       \
    const ::android::String16 I##INTERFACE::descriptor(NAME);           \
    const ::android::String16&                                          \
            I##INTERFACE::getInterfaceDescriptor() const {              \
        return I##INTERFACE::descriptor;                                \
    }                                                                   \
    ::android::sp<I##INTERFACE> I##INTERFACE::asInterface(              \
            const ::android::sp<::android::IBinder>& obj)               \
    {                                                                   \
        ::android::sp<I##INTERFACE> intr;                               \
        if (obj != NULL) {                                              \
            intr = static_cast<I##INTERFACE*>(                          \
                obj->queryLocalInterface(                               \
                        I##INTERFACE::descriptor).get());               \
            if (intr == NULL) {                                         \
                intr = new Bp##INTERFACE(obj);                          \
            }                                                           \
        }                                                               \
        return intr;                                                    \
    }                                                                   \
    I##INTERFACE::I##INTERFACE() { }                                    \
    I##INTERFACE::~I##INTERFACE() { }                                   \
  • BnDemoService.cpp:
    /*****************************************************************************
     * Copyright (C) 2020 Qidi.Huang
     *
     * Brief:
     *    Implementation of demo service Bn side interface.
     *
     * Author: huang_qi_di@hotmail.com
     *****************************************************************************/
    
    #include <utils/Log.h>
    #include <utils/Errors.h>
    #include <binder/Parcel.h>
    
    #include "BnDemoService.h"
    #include "DemoServiceBinderInterface.h"
    
    #define LOG_D ALOGD
    #define LOG_E ALOGE
    
    using namespace android;
    using namespace vendor::harman::hardware::demoComponent::demoService::V1_0;
    using ::vendor::harman::hardware::demoComponent::demoService::V1_0::DemoData;
    
    status_t BnDemoService::onTransact(uint32_t code, const Parcel &data, Parcel *reply, uint32_t flags) {
        status_t retCode = NO_ERROR;
        switch (code) {
            case SET_STATUS:{
                LOG_D("BnDemoService SET_STATUS()");
                CHECK_INTERFACE(IDemoService, data, reply);
                DemoData sta;
                data.read(&sta, sizeof(DemoData));
                reply->writeInt32(setStatus(sta));
            }
            break;
    
            case REGISTER_CALLBACK:{
                LOG_D("BnDemoService REGISTER_CALLBACK()");
                CHECK_INTERFACE(IDemoService, data, reply);
                ::android::sp<IDemoCallback> rcb;
                data.read(&rcb, sizeof(::android::sp<IDemoCallback>));
                reply->writeInt32(registerCallback(rcb));
            }
            break;
    
            case UNREGISTER_CALLBACK: {
                LOG_D("BnDemoService UNREGISTER_CALLBACK()");
                CHECK_INTERFACE(IDemoService, data, reply);
                sp<IDemoCallback> urcb;
                data.read(&urcb, sizeof(::android::sp<IDemoCallback>));
                reply->writeInt32(unregisterCallback(urcb));
            }
            break;
    
            default: {
                LOG_E("BnDemoService::onTransact(0x%x,0x%x)", code, flags);
                retCode = BBinder::onTransact(code, data, reply, flags);
            }
            break;
        }
    
        return retCode;
    }
    

Bn 端的源文件里对 onTransact() 进行实现,使用 switch...case... 对 Command code 进行处理。在每个 case 的末尾可以看到对 demoService 里各个方法的调用,并将调用的返回值写入 reply。


【结语】

如此,我们已经准备好了 demoService 需要的所有源码。下一步工作估计你已经猜到了 —— 准备编译。为了编译出可执行文件,我们要编写 Makefile;为了让 demoComponent HAL 自启动,我们要编写 *.rc 文件。

这些内容留待下一篇文章《Android 8.1 从零开始写 HAL – (4) 编译与整合》进行说明。

本文地址:https://blog.csdn.net/Qidi_Huang/article/details/107428286