前言:

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;
}