[Android] 使用gbinder与Android的service进行native层的IPC通讯

本文详细介绍了在Android中使用BinderIPC与libmediaplayerservice通信的方法,包括RPC方式、基于IPC的通信以及如何通过libgbinder实现跨系统IPC。还强调了设置DeathRecipient以处理Service死亡事件的重要性。
摘要由CSDN通过智能技术生成

前言:

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



不要忘记linkToDeath

在使用 binder 的时候,我们在通过 ServiceManager 获取到 Service 的 IBinder 指针之后,第一件事就是要设置监听 Service 的 Death 事件。所有的 Service 在 die 的时候都会调用设置给自己的 DeathNotify-like 监听类 ,我们需要在监听类里对 Service 进行 unlink 。而且很多时候我们还需要向上层(app)抛出一个 Server Died / Death Object 的异常,从而防止与已经消失的 Service IBinder 对象进行通讯,否则可能出现程序崩溃等未知问题。

    class DeathNotifier : public IBinder::DeathRecipient {
      public:
        DeathNotifier(IBinder* service) {
           mService = service;
        }

        virtual ~DeathNotifier() {
        }
        
        //当service所在的进程终止(自然终止/被杀)时,此函数被调用。
        //注意,此函数不会阻塞service所在的进程终止,仅仅是一个notify机制。
        //这类似于一个野指针通知机制,service对应的IBinder指针指向了一个野指针。
        virtual void binderDied(const wp<IBinder>& who __unused) {
           mService ->unlinkToDeath(mDeathNotifier);
        }
       sp<IBinder> mService;
    };

    sp<IBinder> service = defaultServiceManager()->getService("xxx")
    DeathNotifier* deathnotify = new DeathNotifier(service );
    service->linkToDeath(deathnotify);
    

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值