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

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: [email protected]
 *****************************************************************************/

#pragma once

#include 
#include 

#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 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: [email protected]
     *****************************************************************************/
    #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: [email protected]
     *****************************************************************************/
    #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: [email protected]
     *****************************************************************************/
    
    #include 
    #include 
    #include 
    
    #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::asInterface(              \
            const ::android::sp<::android::IBinder>& obj)               \
    {                                                                   \
        ::android::sp intr;                               \
        if (obj != NULL) {                                              \
            intr = static_cast(                          \
                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: [email protected]
     *****************************************************************************/
    
    #include 
    #include 
    #include 
    
    #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) 编译与整合》进行说明。

你可能感兴趣的:(嵌入式,Android,C++,HAL,HIDL,Android,8.1,Bp,Bn)