Android IPC机制4-ServiceManager的addService与getService实现

普通client或者server在获得servicemanger的proxy对象后,肯定就要使用了。对于server来说,主要是调用addService,向serivceManager注册。而client则是通过serivcemanager查询所需server的信息,然后得到server的proxy对象。

注册服务-addService

以Native层的服务mediaservice为例,我们先来分析下server是如何向SerivceManager注册的吧

先来看入口 main_mediaserver.cpp的main函数中的与ServiceManager相关的代码:

int main(int argc __unused, char** argv)
{
    ...

    //获得ProcessState实例对象
    sp<ProcessState> proc(ProcessState::self());     
    //获取ServiceManager实例对象 【既BpServiceManager】
    sp<IServiceManager> sm = defaultServiceManager(); 
    AudioFlinger::instantiate(); 
    //多媒体服务            
    MediaPlayerService::instantiate();               
    ResourceManagerService::instantiate(); 
    CameraService::instantiate();         
    AudioPolicyService::instantiate();  
    SoundTriggerHwService::instantiate(); 
    RadioService::instantiate(); 
    registerExtensions();
    //创建Binder线程,并加入线程池
    ProcessState::self()->startThreadPool();  
    //当前线程加入到线程池    
    IPCThreadState::self()->joinThreadPool();    
 }

proc(ProcessState::self())

首先调用的函数是ProcessState::self(),获得ProcessState对象,ProcessState位置在framework\base\libs\binder\ProcessState.cpp,在上一篇文章里有过讲述。其内部有Binder驱动的一些配置,每个进程只有一个。

获取到ProcessState对象后赋值给了proc变量,程序运行完,proc会自动delete内部的内容,所以就自动释放了先前分配的资源。

MediaPlayerService:instantiate

void MediaPlayerService::instantiate() {
    defaultServiceManager()->addService(
           String16("media.player"), new MediaPlayerService()); 
}

MediaPlayerService的初始化其实就是调用defaultServiceManager的addserivce方法向servicemanager注册,而上一篇已经讲过了defaultServiceManager,此处不在赘述。defaultServiceManager最终得到的是BpServiceManager。
下面来看看BpServiceManager的addService方法:

virtual status_t addService(const String16& name, const sp<IBinder>& service,
        bool allowIsolated)
{
    Parcel data, reply; //Parcel是数据通信包
    data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());   //先把Interface名字写进去,也就是什么android.os.IServiceManager
    data.writeString16(name);        // name为 "media.player"
    data.writeStrongBinder(service); // MediaPlayerService对象
    data.writeInt32(allowIsolated ? 1 : 0); // allowIsolated= false
    status_t err = remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply); //
    return err == NO_ERROR ? reply.readExceptionCode() : 
}

上面函数的核心就是status_t err = remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply); ,而remote()就是创建bpservicemanager时的bpbinder(handle=0)

BpBinder::transact()

来看看BpBinder的Transact函数:

status_t BpBinder::transact(
    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
    if (mAlive) {
        status_t status = IPCThreadState::self()->transact(
            mHandle, code, data, reply, flags); 
        if (status == DEAD_OBJECT) mAlive = 0;
        return status;
    }
    return DEAD_OBJECT;
}

可以发现,transact函数还不是在这个执行的,而是调用的IPCThreadState的transact.

IPCThreadState::self

看到这个形式就知道肯定是单例模式了,确实也是这样

IPCThreadState* IPCThreadState::self()
{
    if (gHaveTLS) { 
restart:
        const pthread_key_t k = gTLS;
        IPCThreadState* st = (IPCThreadState*)pthread_getspecific(k);
        if (st) return st;
        return new IPCThreadState;  //初始IPCThreadState 
    }
    
    if (gShutdown) return NULL;
    
    pthread_mutex_lock(&gTLSMutex);
    if (!gHaveTLS) { //首次进入gHaveTLS为false
        if (pthread_key_create(&gTLS, threadDestructor) != 0) { //创建线程的TLS
            pthread_mutex_unlock(&gTLSMutex);
            return NULL;
        }
        gHaveTLS = true;
    }
    pthread_mutex_unlock(&gTLSMutex);
    goto restart;
}

IPCThreadState::IPCThreadState()
    : mProcess(ProcessState::self()),
      mMyThreadId(gettid()),    
      mStrictModePolicy(0),
      mLastTransactionBinderFlags(0)
{
    pthread_setspecific(gTLS, this);
    clearCaller();
    mIn.setDataCapacity(256);
    mOut.setDataCapacity(256);
}

TLS是指Thread local storage(线程本地储存空间),每个线程都拥有自己的TLS,并且是私有空间,线程之间不会共享,,和java中的ThreadLocal是差不多的概念。通过pthread_getspecific/pthread_setspecific函数可以获取/设置这些空间中的内容。从线程本地存储空间中获得保存在其中的IPCThreadState对象。

IPCThreadState的构造函数中则是对所属进程,现成id,优先级等做了一些设置。需要注意的是mInmOut,后面会用到。

IPCThreadState::transact

回到IPCThreadState::transact这个方法,看看是怎么实现的。

status_t IPCThreadState::transact(int32_t handle,
                                  uint32_t code, const Parcel& data,
                                  Parcel* reply, uint32_t flags)
{
    status_t err = data.errorCheck(); //数据错误检查      
    flags |= TF_ACCEPT_FDS;     
    ....
    if (err == NO_ERROR) {
           //调用writeTransactionData 发送数据
        err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, NULL);
    }
    
    if (err != NO_ERROR) {
        if (reply) reply->setError(err);
        return (mLastError = err);
    }
    
    if ((flags & TF_ONE_WAY) == 0) { //flgs=0进入该分支
        if (reply) {
            //等待响应  
            err = waitForResponse(reply);
        } else {
            Parcel fakeReply;
            err = waitForResponse(&fakeReply);
        }

    } else {
        //不需要响应消息的binder则进入该分支
        err = waitForResponse(NULL, NULL); 
    }
    return err;
}

上面代码的核心就是writeTransactionData 发送数据,waitForResponse等待响应,来看看这两个函数
writeTransactionData:

status_t IPCThreadState::writeTransactionData(int32_t cmd, uint32_t binderFlags,
    int32_t handle, uint32_t code, const Parcel& data, status_t* statusBuffer)
{
    binder_transaction_data tr;

    tr.target.ptr = 0;
    tr.target.handle = handle; // handle=0
    tr.code = code;            // ADD_SERVICE_TRANSACTION
    tr.flags = binderFlags;    // 0 
    tr.cookie = 0;
    tr.sender_pid = 0;
    tr.sender_euid = 0;
    
    const status_t err = data.errorCheck();
    if (err == NO_ERROR) {
        tr.data_size = data.ipcDataSize();  
        tr.data.ptr.buffer = data.ipcData();
        tr.offsets_size = data.ipcObjectsCount()*sizeof(binder_size_t);
        tr.data.ptr.offsets = data.ipcObjects();
    } else if (statusBuffer) {
        tr.flags |= TF_STATUS_CODE;
        *statusBuffer = err;
        tr.data_size = sizeof(status_t);
        tr.data.ptr.buffer = reinterpret_cast<uintptr_t>(statusBuffer);
        tr.offsets_size = 0;
        tr.data.ptr.offsets = 0;
    } else {
        return (mLastError = err);
    }
//上面把命令数据封装成binder_transaction_data,然后
//写到mOut中,mOut是命令的缓冲区,也是一个Parcel
    
    mOut.writeInt32(cmd);         //cmd = BC_TRANSACTION
    mOut.write(&tr, sizeof(tr));  //写入binder_transaction_data数据
    
    return NO_ERROR;
}

其中handle的值用来标识目的端,注册服务过程的目的端为service manager,此处handle=0所对应的是binder_context_mgr_node对象,正是service manager所对应的binder实体对象。binder_transaction_data结构体是binder驱动通信的数据结构,该过程最终是把Binder请求码BC_TRANSACTION和binder_transaction_data结构体写入到mOut。

再来看看waitForResponse函数

status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult)
{
    int32_t cmd;
    int32_t err;

    while (1) {
        if ((err=talkWithDriver()) < NO_ERROR) break; // 
        err = mIn.errorCheck();
        if (err < NO_ERROR) break;
        if (mIn.dataAvail() == 0) continue;
  //这里开始操作mIn了,看来talkWithDriver中
//把mOut发出去,然后从driver中读到数据放到mIn中了。
        cmd = mIn.readInt32();

        switch (cmd) {
        case BR_TRANSACTION_COMPLETE:
            if (!reply && !acquireResult) goto finish;
            break;
        
        case BR_DEAD_REPLY:
            err = DEAD_OBJECT;
            goto finish;

        case BR_FAILED_REPLY:
            err = FAILED_TRANSACTION;
            goto finish;
        
        case BR_ACQUIRE_RESULT:
            {
                const int32_t result = mIn.readInt32();
                if (!acquireResult) continue;
                *acquireResult = result ? NO_ERROR : INVALID_OPERATION;
            }
            goto finish;
        
        case BR_REPLY:
            {
                binder_transaction_data tr;
                err = mIn.read(&tr, sizeof(tr));
                if (err != NO_ERROR) goto finish;

                if (reply) {
                    if ((tr.flags & TF_STATUS_CODE) == 0) {
                        reply->ipcSetDataReference(
                            reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),
                            tr.data_size,
                            reinterpret_cast<const binder_size_t*>(tr.data.ptr.offsets),
                            tr.offsets_size/sizeof(binder_size_t),
                            freeBuffer, this);
                    } else {
                        err = *reinterpret_cast<const status_t*>(tr.data.ptr.buffer);
                        freeBuffer(NULL,
                            reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),
                            tr.data_size,
                            reinterpret_cast<const binder_size_t*>(tr.data.ptr.offsets),
                            tr.offsets_size/sizeof(binder_size_t), this);
                    }
                } else {
                    freeBuffer(NULL,
                        reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),
                        tr.data_size,
                        reinterpret_cast<const binder_size_t*>(tr.data.ptr.offsets),
                        tr.offsets_size/sizeof(binder_size_t), this);
                    continue;
                }
            }
            goto finish;

        default:
            err = executeCommand(cmd);  //
            if (err != NO_ERROR) goto finish;
            break;
        }
    }

finish:
    if (err != NO_ERROR) {
        if (acquireResult) *acquireResult = err;
        if (reply) reply->setError(err);
        mLastError = err;
    }
    
   

上面代码的细节就不深究了,到这里,我们发送addService的流程就彻底走完了。
其实就是BpServiceManager发送了一个addService命令到BnServiceManager,然后收到回复。

回到开始的mediaservice的main函数,在将MediaPlayerService、CameraService、SoundTriggerHwService等服务都初始化后,就会走到下面的语句:

ProcessState::self()->startThreadPool();  
    //当前线程加入到线程池    
IPCThreadState::self()->joinThreadPool(); 

进入两个函数内部看看

...

看看startThreadPool吧

void ProcessState::startThreadPool()

{

  ...

    spawnPooledThread(true);

}

void ProcessState::spawnPooledThread(bool isMain)

{

    sp<Thread> t = new PoolThread(isMain);isMain是TRUE

//创建线程池,然后run起来,和java的Threadf非常像

    t->run(buf);

 }

//PoolThread从Thread类中派生,那么此时会产生一个线程吗?看看PoolThread和Thread的构造吧

PoolThread::PoolThread(bool isMain)

        : mIsMain(isMain)

    {

    }
//再来看看父类
Thread::Thread(bool canCallJava)//canCallJava默认值是true

    :   mCanCallJava(canCallJava),

        mThread(thread_id_t(-1)),

        mLock("Thread::mLock"),

        mStatus(NO_ERROR),

        mExitPending(false), mRunning(false)

{

}

//喔,这个时候还没有创建线程呢。然后调用PoolThread::run,实际调用了基类的run。

status_t Thread::run(const char* name, int32_t priority, size_t stack)

{

  bool res;

    if (mCanCallJava) {

        res = createThreadEtc(_threadLoop,//线程函数是_threadLoop

                this, name, priority, stack, &mThread);

    }

//终于,在run函数中,创建线程了。从此

//主线程执行IPCThreadState::self()->joinThreadPool();新开的线程执行_threadLoop

//我们先看看_threadLoop

int Thread::_threadLoop(void* user)

{

    Thread* const self = static_cast<Thread*>(user);

    sp<Thread> strong(self->mHoldSelf);

    wp<Thread> weak(strong);

    self->mHoldSelf.clear();

 

    do {

 ...

        if (result && !self->mExitPending) {

                result = self->threadLoop();哇塞,调用自己的threadLoop

            }

        }

//我们是PoolThread对象,所以调用PoolThread的threadLoop函数

virtual bool PoolThread ::threadLoop()

    {

//mIsMain为true。

//而且注意,这是一个新的线程,所以必然会创建一个

//新的IPCThreadState对象(记得线程本地存储吗?TLS),然后      

IPCThreadState::self()->joinThreadPool(mIsMain);

        return false;

    }

//主线程和工作线程都调用了joinThreadPool,看看这个干嘛了!

void IPCThreadState::joinThreadPool(bool isMain)

{

     mOut.writeInt32(isMain ? BC_ENTER_LOOPER : BC_REGISTER_LOOPER);

     status_t result;

    do {

        int32_t cmd;

         result = talkWithDriver();

         result = executeCommand(cmd);

        }

       } while (result != -ECONNREFUSED && result != -EBADF);

 

    mOut.writeInt32(BC_EXIT_LOOPER);

    talkWithDriver(false);

}

//看到没?有loop了,但是好像是有两个线程都执行了这个啊!这里有两个消息循环?

//下面看看executeCommand

status_t IPCThreadState::executeCommand(int32_t cmd)

{

BBinder* obj;

    RefBase::weakref_type* refs;

    status_t result = NO_ERROR;

case BR_TRANSACTION:

        {

            binder_transaction_data tr;

            result = mIn.read(&tr, sizeof(tr));

//来了一个命令,解析成BR_TRANSACTION,然后读取后续的信息

       Parcel reply;

             if (tr.target.ptr) {

//这里用的是BBinder。(因为自己是做server端)

                sp<BBinder> b((BBinder*)tr.cookie);

                const status_t error = b->transact(tr.code, buffer, &reply, 0);

}

//让我们看看BBinder的transact函数干嘛了

status_t BBinder::transact(

    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)

{

//就是调用自己的onTransact函数嘛      

err = onTransact(code, data, reply, flags);

    return err;

}

BnMediaPlayerService从BBinder派生,所以会调用到它的onTransact函数
终于水落石出了,让我们看看BnMediaPlayerServcice的onTransact函数。

status_t BnMediaPlayerService::onTransact(

    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)

{

// BnMediaPlayerService从BBinder和IMediaPlayerService派生,所有IMediaPlayerService

//看到下面的switch没?所有IMediaPlayerService提供的函数都通过命令类型来区分

//

    switch(code) {

        case CREATE_URL: {

            CHECK_INTERFACE(IMediaPlayerService, data, reply);

            create是一个虚函数,由MediaPlayerService来实现!!

sp<IMediaPlayer> player = create(

                    pid, client, url, numHeaders > 0 ? &headers : NULL);

 

            reply->writeStrongBinder(player->asBinder());

            return NO_ERROR;

        } break;

其实,到这里,我们就明白了。BnXXX的onTransact函数收取命令,然后派发到派生类的函数,由他们完成实际的工作。
说明:
这里有点特殊,startThreadPool和joinThreadPool完后确实有两个线程,主线程和工作线程,而且都在做消息循环。

小节

过程分析:

  1. MediaPlayerService进程获得bpservicemanager,通过bpservicemanager对象的bpbinder成员(handl=0)中转到IPCThreadState执行transact

  2. IPCThreadState调用writeTransactionData函数向mout缓冲区写入数据,然后调用waitForResponse等待响应。

  3. mediaservice开启一个工作线程,和主线程一起开始做消息循环,不断的与binder驱动通信,故后续更加需求Binder驱动会增加binder线程个数。

获取服务-getService

再来看看client如何通过servicemanager获得server的bpbinder的。

getService

首先来看看IServiceManager的getService函数:

virtual sp<IBinder> getService(const String16& name) const
    {
        unsigned n;
        for (n = 0; n < 5; n++){
            sp<IBinder> svc = checkService(name);
            if (svc != NULL) return svc;
            sleep(1);
        }
        return NULL;
    }

核心是checkService(name),进入代码看看

virtual sp<IBinder> checkService( const String16& name) const
{
    Parcel data, reply;
    //写入RPC头
    data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());
    //写入服务名
    data.writeString16(name); 
    remote()->transact(CHECK_SERVICE_TRANSACTION, data, &reply); 
    return reply.readStrongBinder();
}

remote()->transact(CHECK_SERVICE_TRANSACTION, data, &reply);,这句和之前addService的非常类似,肯定又是通过bpservicemanager最终流转到IPCThreadState的transact,只是传递的参数有所不同罢了。 这里就不再讲了。

总结

本片文章说是讲ServiceManager的addServicegetService,其实主要还是在分析transact这个函数的执行流转过程,上面有些代码细节我也没有完全弄懂,但我觉得也够了。简单的说ServiceMnager通信的核心有以下几个:

  1. IPCProcessState和IPCThreadState这两个类,他们分别记录了进程和线程中IPC通信需要的相关要素。这两个类都是单例模式,IPCThreadState是最终通信的执行者。
  2. BpBinder:通过handl=0可以直接与ServiceManager的实体Binder通信
  3. Server端会在主线程和一个工作线程开始消息循环,来处理和binder驱动的交互,如有需求再开启新的线程。
  4. 函数调用的流转过程:通过一次次的流转,每个部分都做了需要自己做的事,然后由一个最终的执行者去执行。

文章里很多部分都来自与gityuaninnost,在此表示感谢!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,839评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,543评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,116评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,371评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,384评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,111评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,416评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,053评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,558评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,007评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,117评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,756评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,324评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,315评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,539评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,578评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,877评论 2 345

推荐阅读更多精彩内容