android 系统核心机制binder(08)匿名Service

112 篇文章 86 订阅

该系列文章总纲链接:专题分纲目录 android 系统核心机制 binder


本章关键点总结 & 说明:

这里主要关注 其他部分 匿名service,之类关注的主要是匿名service的 三方面what、why、how即可。

1 匿名Service说明

    这里对匿名service(没有在service_manager中注册,但还是基于binder通信的C/S架构)进行特殊说明

    (所谓匿名,就是不获取服务,直接通过C/S两端BpXXX和BnXXX来实现,而本质依赖于特定binder)    

先查看一个匿名Service的实例,这里以creat方法为例,先看BpMediaPlayerService,代码如下:

virtual sp<IMediaPlayer> create(const sp<IMediaPlayerClient>& client, int audioSessionId) {  
        Parcel data, reply;  
        data.writeInterfaceToken(IMediaPlayerService::getInterfaceDescriptor());  
        data.writeStrongBinder(client->asBinder());  
        data.writeInt32(audioSessionId);  
      
        remote()->transact(CREATE, data, &reply);//将要调用的BnXXX端的方法这一意图发送给binder驱动  
        return interface_cast<IMediaPlayer>(reply.readStrongBinder());//等待BnXXX端方法的返回值
    }

与非匿名service不同的是,可以直接通过binder跳转到BnMediaPlayerService中,代码如下:

status_t BnMediaPlayerService::onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
    switch (code) {
        case CREATE: {
            CHECK_INTERFACE(IMediaPlayerService, data, reply);
            sp<IMediaPlayerClient> client = interface_cast<IMediaPlayerClient>(data.readStrongBinder());
            int audioSessionId = data.readInt32();
            /* IMeidaPlayer类型对象,对于后面的分析很重要 */
            sp<IMediaPlayer> player = create(client, audioSessionId);
            /* 与驱动交互,特殊binder交互的关键点,将binder类型作为一种特殊类型处理 */
            reply->writeStrongBinder(player->asBinder());
            return NO_ERROR;
        } break;
        ...
        default:
            return BBinder::onTransact(code, data, reply, flags);
    }
}

    接下来可以继续分析继承了BnMediaPlayerService的MediaPlayerService,代码如下:

sp<IMediaPlayer> MediaPlayerService::create(const sp<IMediaPlayerClient>& client,int audioSessionId)
{
    pid_t pid = IPCThreadState::self()->getCallingPid();
    int32_t connId = android_atomic_inc(&mNextConnId);
 
    sp<Client> c = new Client(
            this, pid, connId, client, audioSessionId,
            IPCThreadState::self()->getCallingUid());
 
    ALOGV("Create new client(%d) from pid %d, uid %d, ", connId, pid,
         IPCThreadState::self()->getCallingUid());
 
    wp<Client> w = c;
    {
        Mutex::Autolock lock(mLock);
        mClients.add(w);
    }
    return c;
}

这里的creat方法返回的是一个client对象,是sp<IMediaPlayer>类型,内部类是在BnMediaPlayerService中定义的,代码如下:

class MediaPlayerService : public BnMediaPlayerService
{
    class Client;
    ...
private:
 
    class Client : public BnMediaPlayer {
        // IMediaPlayer interface
        virtual void            disconnect();
	    ...
        sp<MediaPlayerBase>     createPlayer(player_type playerType);
        virtual status_t        setDataSource(const sp<IMediaHTTPService> &httpService,const char *url,
                        const KeyedVector<String8, String8> *headers);
	    ...
    private:
        friend class MediaPlayerService;
                                Client( const sp<MediaPlayerService>& service,pid_t pid,int32_t connId,
                                        const sp<IMediaPlayerClient>& client,int audioSessionId,uid_t uid);
                                Client();
        virtual                 ~Client();
 
                void            deletePlayer();
	...
    }; // Client
    ...
}

当客户端MeidaPlayerClient调用create方法时,服务端直接返回IMediaPlayer类型对象(该对象是BnMediaPlayerService的内部类),然后将相关信息写入到binder驱动中,之后客户端MeidaPlayerClient就可以直接使用该对象来直接进行跨进程之间的通信了

说明:

  1.         这里也实现了binder架构的两端:BpMediaPlayer和BnMediaPlayer。
  2.         在SM中没有IMediaPlayer信息,即无法通过SM获取匹配对应服务的BpBinder进而获取到对应的BBinder的handle值。
  3.         当reply写到binder驱动中时,驱动为其建立一个全新的handle,等价于在驱动中注册了一项服务。

注意:

  1. 匿名service的本质就是匿名binder,而匿名binder就是没有向servicemanager提交注册的binder。
  2. 对于已经建立好Binder通信的Client和server,server可以将一个Binder引用传递给client,client通过引用来访问server。
  3. 在第一次返回Binder引用时,binder驱动保存了这个Binder实体的各种数据,创建了节点;所以才可以使用匿名binder。

为什么要这样做?

  1. 匿名Binder一般都是在这种情景中出现的:service有多个实体,在需要使用时,现场创建Binder实体。
  2. 而一般实名Binder,即是去service manager中注册的service,都只会有一个service实体,即只会new一次。

2 针对reply

这里继续分析其reply的writeStrongBinder的内部实现,代码如下所示:

status_t Parcel::writeStrongBinder(const sp<IBinder>& val)
{
    return flatten_binder(ProcessState::self(), val, this);
}

    这里实际就是往reply中存入了一个BBinder实体。

   继续分析flatten_binder,代码如下所示:

status_t flatten_binder(const sp<ProcessState>& proc,
    const sp<IBinder>& binder, Parcel* out)
{
    flat_binder_object obj;
    
    obj.flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;
    if (binder != NULL) {
        IBinder *local = binder->localBinder();//这里我们是由Client强转得到的,所以这里不是空,返回BBinder 
        if (!local) {
            BpBinder *proxy = binder->remoteBinder();
            if (proxy == NULL) {
                ALOGE("null proxy");
            }
            const int32_t handle = proxy ? proxy->handle() : 0;
            obj.type = BINDER_TYPE_HANDLE;
            obj.handle = handle;
            obj.cookie = NULL;
        } else {
            obj.type = BINDER_TYPE_BINDER;//关键,表示当前传入的类型,并将对象本身作为cookie传入。
            obj.binder = local->getWeakRefs();
            obj.cookie = local;
        }
    } else {
        obj.type = BINDER_TYPE_BINDER;
        obj.binder = NULL;
        obj.cookie = NULL;
    }    
    return finish_flatten_binder(binder, obj, out);
}

       server可以传递Binder对象的引用给client,流程如下:

  1.             server将Binder对象写入parcel,
  2.             通过ioctrl和binder驱动通信,binder驱动会对传入的对象进行检查,
  3.             如果发现传入的type是binder,则会在当前进程(server对应的proc)查找是否有这个节点,
  4.             如果没有就用传入的Binder实体在user空间指针和cookie(通常是BBinder本身)构造一个新的node,
  5.             接着得到Binder实体的引用,存放在transaction_data中等待client去获取。
  6.             client拿到Binder的引用后,就可以通过这个BpBinder和server的BBinder进行通信了(和实名注册的Binder是一样的)
  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

图王大胜

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值