注意:本文基于 Android 8.1 进行分析
Qidi 2020.07.18 (Markdown & Haroopad)
既然 Binder 化的 HAL 依赖于 Binder 机制进行实现,那么我们自然必须按照 Binder 框架,相应编写 demoComponent HAL 的 Bp 端和 Bn 端。只有这样,才能打通客户端进程调用到服务端进程 —— 我们的 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_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 asInterface( \
const ::android::sp<::android::IBinder>& obj); \
virtual const ::android::String16& getInterfaceDescriptor() const; \
I##INTERFACE(); \
virtual ~I##INTERFACE(); \
至此,demoService 的接口类就定义好了。
有了接口类之后,就可以正式着手 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: [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();
};
声明之后,该进行实现了。继续在 default/
目录下新建源文件 BpDemoService.cpp
和 BnDemoService.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() { } \
/*****************************************************************************
* 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) 编译与整合》进行说明。