前言:
Android 框架是以 Binder 为基础搭建起来的,处处都离不开 Binder IPC, Android的native binder库为libbinder。其他第三方的binder库有libgbinder。
预备知识:
假设我们需要与libmediaplayerservice里 "media.player" service 通讯,那么我们在需要知道 service name的同时,还需要知道 interface name,即 IMediaPlayerService.cpp.里 IMPLEMENT_META_INTERFACE 的第二个参数 "android.media.IMediaPlayerService" 。
IMPLEMENT_META_INTERFACE(MediaPlayerService, "android.media.IMediaPlayerService");
目前media.player对外暴露的接口有如下几个:
enum {
CREATE = IBinder::FIRST_CALL_TRANSACTION,
CREATE_MEDIA_RECORDER,
CREATE_METADATA_RETRIEVER,
ADD_BATTERY_DATA,
PULL_BATTERY_DATA,
LISTEN_FOR_REMOTE_DISPLAY,
GET_CODEC_LIST,
};
使用RPC进行通讯:
我们有两种方式可以与Binder Native端通讯,一是使用RPC的方式,这种方式需要我们获取IMediaPlayerService 对象,然后调用 BpMediaPlayerService 中的相关方法即可。比如
const sp<IMediaPlayerService> IMediaDeathNotifier::getMediaPlayerService()
{
ALOGV("getMediaPlayerService");
Mutex::Autolock _l(sServiceLock);
if (sMediaPlayerService == 0) {
sp<IServiceManager> sm = defaultServiceManager();
sp<IBinder> binder;
do {
binder = sm->getService(String16("media.player"));
if (binder != 0) {
break;
}
ALOGW("Media player service not published, waiting...");
usleep(500000); // 0.5 s
} while (true);
if (sDeathNotifier == NULL) {
sDeathNotifier = new DeathNotifier();
}
binder->linkToDeath(sDeathNotifier);
sMediaPlayerService = interface_cast<IMediaPlayerService>(binder);
}
ALOGE_IF(sMediaPlayerService == 0, "no media player service!?");
return sMediaPlayerService;
}
//USE
const sp<IMediaPlayerService> service(getMediaPlayerService());
//1. use Bp RPC
service->getCodecList();
这种方法的局限性在与Binder Proxy端必须在Android系统中,如果跨系统则失效。因为他依赖于Android的IMediaPlauerService数据类型。
使用IPC进行通讯
另外一种方法是IPC,这种方法是不需要依赖于Android的数据类型,跳过BpMediaPlayerService直接进入BnMediaPlayerService的onTransact函数。
status_t BnMediaPlayerService::onTransact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
switch (code) {
...
case GET_CODEC_LIST: {
CHECK_INTERFACE(IMediaPlayerService, data, reply);
sp<IMediaCodecList> mcl = getCodecList();
reply->writeStrongBinder(IInterface::asBinder(mcl));
return NO_ERROR;
} break;
...
}
}
Binder Proxy端的写法如下:
const sp<IMediaPlayerService> IMediaDeathNotifier::getMediaPlayerService()
{
ALOGV("getMediaPlayerService");
Mutex::Autolock _l(sServiceLock);
if (sMediaPlayerService == 0) {
sp<IServiceManager> sm = defaultServiceManager();
sp<IBinder> binder;
do {
binder = sm->getService(String16("media.player"));
if (binder != 0) {
break;
}
ALOGW("Media player service not published, waiting...");
usleep(500000); // 0.5 s
} while (true);
if (sDeathNotifier == NULL) {
sDeathNotifier = new DeathNotifier();
}
binder->linkToDeath(sDeathNotifier);
sMediaPlayerService = interface_cast<IMediaPlayerService>(binder);
}
ALOGE_IF(sMediaPlayerService == 0, "no media player service!?");
return sMediaPlayerService;
}
//USE
const sp<IMediaPlayerService> service(getMediaPlayerService());
//2. use IPC
Parcel data, reply;
const String16 interface("android.media.IMediaPlayerService");
data.writeInterfaceToken(interface);
IInterface::asBinder(service)->transact(7, data, &reply);
从上述代码中可见,只需要通过 IBinder类的 transact 方法即可发送出 IPC 消息,注意第一个参数对应 Binder Native 端具体的 enum 业务的值,比如这里的 7 就是 GET_CODEC_LIST,请参考预备知识里的enum代码。
使用libgbinder进行跨系统的IPC通讯
如果希望进行跨系统的 Binder 通讯,那么可以使用 libgbinder 来进行,但是只能进行 IPC, 不能进行 RPC ,因为数据结构缺失。
demo代码如下:
#define DEVICE_NAME "/dev/binderfs/binder"
#define SERVICE_NAME "media.player"
#define INTERFACE_NAME "android.media.IMediaPlayerService"
bool DoIPC() {
// 1. get binder
serviceManager = gbinder_servicemanager_new(DEVICE_NAME);
// 2. get service
meidaPlayerService = gbinder_servicemanager_get_service_sync(serviceManager, SERVICE_NAME, NULL);
// 3. create client
gbinder_remote_object_ref(meidaPlayerService);
client = gbinder_client_new(meidaPlayerService, INTERFACE_NAME);
// 4. send IPC Request
GBinderLocalRequest* req = gbinder_client_new_request(mClient);
//add string value if needed, here for demo
//gbinder_local_request_append_string16(req, "xxx");
int status = 0;
gbinder_client_transact_sync_reply(client, 7, req, &status);
gbinder_local_request_unref(req);
return true;
}