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: 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_STATUS
、REGISTER_CALLBACK
和 UNREGISTER_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.h
和 BnDemoService.h
。Binder 框架提供了模板类 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.cpp
和 BnDemoService.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