Android Binder学习(四)之addService流程分析

Android Binder学习(四)之addService流程分析

备注:本文基于Android5.1分析
  该篇博文去年年底就基本整理完毕,年后几个月太忙,最近才有时间整理一下发表出来。博文是边看代码边整理的,本人能力有限,前后花了三个星期的苦逼夜晚整理。博文是按着代码的执行流程来分析的。以mediaServer添加camera服务为例来分析数据打包传递过程,中间穿插一些我自己理解画出来的图,如果你发现有问题,欢迎发表自己的看法,大家共同进步。
  binder通信的过程,好比我们在淘宝下单至商品发货的过程。我们是客户端、淘宝相当于binder内核驱动、店家代表service_manager。就像下面说的(一开始可能不太理解,但大概先了解它的工作情形吧)。如果想粗线条的理解,也可以直接跳到博文最后总结性的流程分析。

  • 1.服务请求:我们想买自己的商品,下单后,淘宝客户端会告诉我们,我们的商品已经订购成功,与此同时淘宝也会给店家发送一条消息(有人买你们的东西,且多了一个订单)
  • 2.服务处理:店家收到订单后,会进行商品的分类打包,并贴上响应的邮寄地址。
  • 3.服务反馈:店家打包完毕,发货后,会通过淘宝给我们发一条消息,说你的商品已经出库了。
    上面的处理过程和binder进程间通信类型。在开始看的时候,最好能看看前面的一些数据结构介绍,这样在看的时候,一些概念才能更清楚。下面介绍addservice过程时,是根据camera服务来分析的。下一篇博文我们先介绍camera framwork代码,在进行后面的客户端请求camera服务分析。

一、Media_Server发送addService请求

1.CameraService::instantiate();

int main()
{
    sp<ProcessState> proc(ProcessState::self());
    sp<IServiceManager> sm = defaultServiceManager();
    ALOGI("ServiceManager: %p", sm.get());
    AudioFlinger::instantiate();
    MediaPlayerService::instantiate();
    CameraService::instantiate(); //这个是我们重点关注对象
    AudioPolicyService::instantiate();
    SoundTriggerHwService::instantiate();
    //以上是服务实例注册流程
    ProcessState::self()->startThreadPool(); //启动线程池
    IPCThreadState::self()->joinThreadPool(); //主线程加入到线程池中
}

  在Android7.0以后,CameraService驻留在CameraServer,其它的android版本都是在mediaServer进程中。这里我分析的是Android5.1,所以CameraServie是在media_server进程中。上面的代码就是media_server进程的main函数。可以看到驻留在media_server进程中的服务有5个,这里我们只关注CameraService.首先我们来看看instantiate()方法。

template<typename SERVICE>
class BinderService
{
public:
    static status_t publish(bool allowIsolated = false) {
        sp<IServiceManager> sm(defaultServiceManager()); //这里获取serviceManager代理对象。
        return sm->addService(
                String16(SERVICE::getServiceName()), //找到服务对应的名字,这里是media.camera
                new SERVICE(), allowIsolated); //大家因该注意到前面那个new SERVICE(),看样子服务在这里注册,没错,服务就是在这里注册的。
    }

    static void publishAndJoinThreadPool(bool allowIsolated = false) {
        publish(allowIsolated);
        joinThreadPool();
    }

    static void instantiate() { publish(); } //可以看到instantiate后面有调用的publish()方法,请看上面对应的方法
......
}

  上面是一个模板类,调用那个service_manage代理对象addServic()时大家看看,新创建服务对象其实就是模板参数。这里我们知道这里创建的是一个cameraService。详情请看下面代码追踪,CameraService类继承了BinderService,传进去的模板参数就是CameraService。

2.CameraService类

class CameraService :
    public BinderService<CameraService>, //这里模板传入的是CameraSrvice,那么在上面的new出来的就是CameraService对象了。
    public BnCameraService,
    public IBinder::DeathRecipient,
    public camera_module_callbacks_t
{
    friend class BinderService<CameraService>;
public:
    class Client;
    class BasicClient;

    // Implementation of BinderService<T>
    static char const* getServiceName() { return "media.camera"; } //可以看到服务名字是media.camera
......
}

  具体CameraService服务的接口实现,我们放在后面其它博客中详细叙述,这里主要介绍Binder。可以看到该类继承了BinderService类,模板参数正是CameraService。现在我们需要做的就是下面一件事情

  • 1.这里在instantiate方法之后,会new出来一个CameraService对象。然后用sm代理对象,将服务对象添加到sm中了。下面看看它时如何添加的。

3.sm->addService()都干了什么

class BpServiceManager : public BpInterface<IServiceManager>
{
public:
......
    virtual status_t addService(const String16& name, const sp<IBinder>& service,
            bool allowIsolated)
    {
        Parcel data, reply;
        data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor()); //这里descriptor = “android.os.IServiceManager”
        data.writeString16(name);
        data.writeStrongBinder(service);
        data.writeInt32(allowIsolated ? 1 : 0); //由上面的publish方法定义的地方,可以看到这里allowIsolated变量为0.
        status_t err = remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply); //调用代理对象,为什么叫remote,就因为是一个ServiceManager远端代理。
        return err == NO_ERROR ? reply.readExceptionCode() : err;
    }
......
}
IMPLEMENT_META_INTERFACE(ServiceManager, "android.os.IServiceManager"); //

  代码中看是打包数据了,为了看清楚数据的包含关系,这里我画出打包数据的数据分布,方便大家一起跟踪一下。其中大家要住的是writeStrongBinder()方法了,该方法会将cameraService实例打包成一个flat_binder_object对象,具体如何打包的大家可以查看之前的博客。接下来看看serviceManager代理如何通信的。

4.remote()->transact()

status_t BpBinder::transact(
    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) //上面的flag默认为0,从上面一段代码看到,在调用时没有传入flag参数。
{
    // Once a binder has died, it will never come back to life.
    if (mAlive) {
        status_t status = IPCThreadState::self()->transact(
            mHandle, code, data, reply, flags);//注意这里的mHandle就是用来查找sm 进程的唯一标识。
        if (status == DEAD_OBJECT) mAlive = 0;
        return status;
    }

    return DEAD_OBJECT;
}

这里看到remote()就是远房的亲戚(代理对象),代码逻辑简单,这里只是做了一层封装,我们还是去IPCThreadState::transact()看看吧。

5.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) {
        LOG_ONEWAY(">>>> SEND from pid %d uid %d %s", getpid(), getuid(),
            (flags & TF_ONE_WAY) == 0 ? "READ REPLY" : "ONE WAY");
        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) { //flag参数默认是0,这里只有前面设置为TF_ACCEPT_FDS,所以这里为true.

        if (reply) {
            err = waitForResponse(reply); //走这一路
        } else {
            Parcel fakeReply;
            err = waitForResponse(&fakeReply);
        }
        //去除debug信息......
    } else {
        err = waitForResponse(NULL, NULL);
    }

    return err;
}

上面就是线程的事务处理函数,主要就下面2件事情

  • 1.写通信事务数据(由writeTransactionData()方法来完成),后面我们会画出打包之后的数据存储分布。
  • 2.读取事务数据(由waitForResponse()方法来完成),当service响应了这一请求,就会回一个消息。

6.IPCThreadState::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; /* Don't pass uninitialized stack data to a remote process */
    tr.target.handle = handle;//这里handle是serviceManage唯一标识,而且放到target中,而我们的目标就是sm。
    tr.code = code;//这里code就是ADD_SERVICE_TRANSACTION
    tr.flags = binderFlags;
    tr.cookie = 0;
    tr.sender_pid = 0;
    tr.sender_euid = 0;

    const status_t err = data.errorCheck();
    if (err == NO_ERROR) {
        //下面是记录binder对象的信息了。
        tr.data_size = data.ipcDataSize();//buffer大小
        tr.data.ptr.buffer = data.ipcData();//buffer地址
        tr.offsets_size = data.ipcObjectsCount()*sizeof(binder_size_t);//binder对象的个数。
        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);
    }

    mOut.writeInt32(cmd);
    mOut.write(&tr, sizeof(tr));

    return NO_ERROR;
}

  该函数的一开始就是填充binder_transaction_data对象的数据,最后填充完毕后,重要数据的分布如图所示。

  需要发送的binder_transaction_data数据填充好之后,在函数的最后会将所有数据,写入发送缓冲区中mOut中,最终mOut重要数据成员分布如下所示。

7.waitForResponse()-焦急的等待

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

    while (1) {
        if ((err=talkWithDriver()) < NO_ERROR) break;//需要和kernel谈谈了,目的就是把上面打包的mOut数据给你^o^,我们先把目光移到下面的taklWithDriver();
        err = mIn.errorCheck();
        if (err < NO_ERROR) break;
        if (mIn.dataAvail() == 0) continue;

        cmd = mIn.readInt32();

        IF_LOG_COMMANDS() {
            alog << "Processing waitForResponse Command: "
                << getReturnString(cmd) << endl;
        }

        switch (cmd) {
        case BR_TRANSACTION_COMPLETE: .... break;

        case BR_DEAD_REPLY:
            err = DEAD_OBJECT;
            goto finish;

        case BR_FAILED_REPLY:
            err = FAILED_TRANSACTION;
            goto finish;

        case BR_ACQUIRE_RESULT: ....
            goto finish;

        case BR_REPLY:
            ......
            goto finish;

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

finish:
    .....
    return err;
}

该函数是与serviceManager通信的主要函数,首先会调用talkWithDriver()方法,将之前的打包在mOut中的数据打包成struct binder_write_read 对象,并通过ioctrl发送给kernel。虽然之前的博文已经介绍了IPCThreadState::talkWithDriver()方法,这里为了分析连贯,再贴一次出来。

8.IPCThreadState::talkWithDriver

status_t IPCThreadState::talkWithDriver(bool doReceive)
{
    if (mProcess->mDriverFD <= 0) {
        return -EBADF;
    }

    binder_write_read bwr;

    // Is the read buffer empty?
    const bool needRead = mIn.dataPosition() >= mIn.dataSize();

    const size_t outAvail = (!doReceive || needRead) ? mOut.dataSize() : 0;
   //doReceive参数,默认是为true,上面我们看到没有传参数,那么doReceive = 1; 
    bwr.write_size = outAvail;
    bwr.write_buffer = (uintptr_t)mOut.data(); //将mOut数据指针存放到这里,这就是我们上面打包的数据。

    // This is what we'll read.
    if (doReceive && needRead) {
        bwr.read_size = mIn.dataCapacity(); //注意这里数据的大小,在我们new IPCThreadState对象时,已经初始化为256.
        bwr.read_buffer = (uintptr_t)mIn.data(); //mIn数据指针,放到这里
    } else {
        bwr.read_size = 0;
        bwr.read_buffer = 0;
    }
    //......
    bwr.write_consumed = 0;
    bwr.read_consumed = 0;
    status_t err;
    do {
#if defined(HAVE_ANDROID_OS)
        if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0) //这里通过ioctl将数据写给kernel
            err = NO_ERROR;
        else
            err = -errno;
#else
        err = INVALID_OPERATION;
#endif
      .....
    } while (err == -EINTR);

    IF_LOG_COMMANDS() {
        alog << "Our err: " << (void*)(intptr_t)err << ", write consumed: "
            << bwr.write_consumed << " (of " << mOut.dataSize()
                        << "), read consumed: " << bwr.read_consumed << endl;
    }

    return err;
}

  该函数的作用就是将之前打包的数据通过系统调用ioctl发送给kernel,最终发送给kernel的数据是struct binder_write_read对象。该对象已经被打包了3次,它们的包含关系如下所示。这里说明一下

  • 1.在调用serviceManager代理对象时,打包在Parcel对象中的服务的描述符号、服务的名字、服务对象的地址(其实是一个打包了服务实例的struct flat_binder_object对象地址,其中真正的服务对象的地址保存在flat_binder_object对象的cookie域中)
  • 2.这里会将上一步中打包的Parcel对象数据域的指针,保存到一个struct binder_transaction_data对象里面。说是打包进去,倒不如说是保存数据指针,并保存记录信息数据。最后在将数据直接吸入mOut对象中
  • 3.将第二步打包好的Parcel对象数据域的指针,赋给一个struct binder_write_read对象的write_buffer域,同时将mIn对象的数据域指针赋给read_buffer域。紧接着就会将这个数据发送给kernel。

9.ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr)

static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
.....
    switch (cmd) {
    case BINDER_WRITE_READ: {
.....
        if (bwr.write_size > 0) {//这里>0,往下看binder_thread_write()实现。
            ret = binder_thread_write(proc, thread, bwr.write_buffer, bwr.write_size, &bwr.write_consumed);
        }
        if (bwr.read_size > 0) {
            ret = binder_thread_read(proc, thread, bwr.read_buffer, bwr.read_size, &bwr.read_consumed, filp->f_flags & O_NONBLOCK);
        }
               .......
        break;
    }
    //.....
}
------------------------------------------------------------------------

int binder_thread_write()
{
 .....
        case BC_TRANSACTION://我们上次传下来的就是这个命令:BC_TRANSACTION.
        case BC_REPLY: {
            struct binder_transaction_data tr;

            if (copy_from_user(&tr, ptr, sizeof(tr)))//取出事务数据
                return -EFAULT;
            ptr += sizeof(tr);//数据游标指针移动
            binder_transaction(proc, thread, &tr, cmd == BC_REPLY);//事务处理函数,请进入看看,如下
            break;
        }
.....
}
-------------------------------------------------------------------------------
static void binder_transaction(){
.....
    if (tr->target.handle) {//这里我们目的就是和serviceManage通信,handle = 0 走else分支;
.....
        } else {
            target_node = binder_context_mgr_node;//直接拿到service_manage全局的binder_node对象。
            if (target_node == NULL) {
                return_error = BR_DEAD_REPLY;
                goto err_no_context_mgr_node;
            }
        }
        e->to_node = target_node->debug_id;
        target_proc = target_node->proc; //注意这里target_proc就是Service_Manager进程的的binder_proc。
.....
    t = kzalloc(sizeof(*t), GFP_KERNEL); //为该事务分配内存空间。
    t->sender_euid = proc->tsk->cred->euid; //发送者就是media_server进程
    t->to_proc = target_proc;     //目的进程是Service_Manager进程,更详细可从源码中了解.
    t->to_thread = target_thread; //Service_Manager主线程
    t->code = tr->code;           //这里等于 ADD_SERVICE_TRANSACTION
    t->flags = tr->flags;         //由上面的数据包可知是TF_ACCEPT_FDS
    t->priority = task_nice(current);
    t->buffer->allow_user_free = 0;
    t->buffer->debug_id = t->debug_id;
    t->buffer->transaction = t; //当前binder_buffer属于哪个事务。
    t->buffer->target_node = target_node; //service_manager的binder_node对象
    if (target_node)
        binder_inc_node(target_node, 1, 0, NULL);//引用计数+1

    offp = (binder_size_t *)(t->buffer->data +
                 ALIGN(tr->data_size, sizeof(void *)));

    if (copy_from_user(t->buffer->data, (const void __user *)(uintptr_t) //请查看上面最近的一个图,这里会将上图中的1过程打包的数据拷贝到刚刚申请的binder_buffer中。
               tr->data.ptr.buffer, tr->data_size)) {
        binder_user_error("%d:%d got transaction with invalid data ptr\n",
                proc->pid, thread->pid);
        return_error = BR_FAILED_REPLY;
        goto err_copy_data_failed;
    }

    if (copy_from_user(offp, (const void __user *)(uintptr_t)
               tr->data.ptr.offsets, tr->offsets_size)) {              //这里会拷贝记录指针,即记录事务数据中,binder对象的偏移地址,以及个数。
        binder_user_error("%d:%d got transaction with invalid offsets ptr\n",
                proc->pid, thread->pid);
        return_error = BR_FAILED_REPLY;
        goto err_copy_data_failed;
    }
    for (; offp < off_end; offp++) {
        struct flat_binder_object *fp;
        ......
    fp = (struct flat_binder_object *)(t->buffer->data + *offp);
        switch (fp->type) {
        case BINDER_TYPE_WEAK_BINDER: {
            struct binder_ref *ref;
            struct binder_node *node = binder_get_node(proc, fp->binder); //这里fp->binder指向的是cameraService服务对象的引用计数对象指针。
            if (node == NULL) { //这里由于CameraService是第一次注册,所以是找不到这个binder_node的。
                node = binder_new_node(proc, fp->binder, fp->cookie);//这里会创建一个新的binder_node对象,注意参数中fp->cookie是cameraService服务在media_server进程地址空间的地址,后面找到cameraService服务对象就靠它了。
                if (node == NULL) {                                  //这里创建的binder_node会添加到media_server进程的binder_proc结构中的,binder_proc->nodes
                    return_error = BR_FAILED_REPLY;
                    goto err_binder_new_node_failed;
                }
                node->min_priority = fp->flags & FLAT_BINDER_FLAG_PRIORITY_MASK;
                node->accept_fds = !!(fp->flags & FLAT_BINDER_FLAG_ACCEPTS_FDS);
            }
            if (fp->cookie != node->cookie) {//显然刚创建的binder对象,这里是一样的。
                binder_user_error("%d:%d sending u%016llx node %d, cookie mismatch %016llx != %016llx\n",
                    proc->pid, thread->pid,
                    (u64)fp->binder, node->debug_id,
                    (u64)fp->cookie, (u64)node->cookie);
                goto err_binder_get_ref_for_node_failed;
            }
            if (security_binder_transfer_binder(proc->tsk, target_proc->tsk)) {
                return_error = BR_FAILED_REPLY;
                goto err_binder_get_ref_for_node_failed;
            }
            ref = binder_get_ref_for_node(target_proc, node); //这里会在在service_manager的binder_proc->refs_by_node域查找该binder_node对象的binder_ref引用对象,如果没有就会新创建一个,并分别添加到refs_by_node和refs_by_desc域中。这里我们是第一次添加CameraService所以这里会重新创建。
            if (ref == NULL) {                                
                return_error = BR_FAILED_REPLY;
                goto err_binder_get_ref_for_node_failed;
            }
            if (fp->type == BINDER_TYPE_BINDER)//在查找服务时,会走到这里,我们在getService那一章继续分析。
                fp->type = BINDER_TYPE_HANDLE;
            else
                fp->type = BINDER_TYPE_WEAK_HANDLE;
            fp->handle = ref->desc; //这里是非常关键的一步了,将该binder引用对象的desc,保存在flat_binder_object->handle中,方便后面查找该引用对象。 binder_inc_ref(ref, fp->type == BINDER_TYPE_HANDLE, //增加引用计数
                       &thread->todo);

            trace_binder_transaction_node_to_ref(t, node, ref);
            binder_debug(BINDER_DEBUG_TRANSACTION,
                     " node %d u%016llx -> ref %d desc %d\n",
                     node->debug_id, (u64)node->ptr,
                     ref->debug_id, ref->desc);
        } break;
    ......
   }
    if (reply) { //这里为false
        BUG_ON(t->buffer->async_transaction != 0);
        binder_pop_transaction(target_thread, in_reply_to);
    } else if (!(t->flags & TF_ONE_WAY)) { //这里flags = TF_ACCEPT_FDS,显然这里为true。
        BUG_ON(t->buffer->async_transaction != 0);
        t->need_reply = 1; //需要service_manager进程回复给media_server进程。
        t->from_parent = thread->transaction_stack; //该事务依赖的前一个事务
        thread->transaction_stack = t;
    } else {
        BUG_ON(target_node == NULL);
        BUG_ON(t->buffer->async_transaction != 1);
        if (target_node->has_async_transaction) {
            target_list = &target_node->async_todo;
            target_wait = NULL;
        } else
            target_node->has_async_transaction = 1;
    }
    t->work.type = BINDER_WORK_TRANSACTION;     //注意这个工作项的类型。
    list_add_tail(&t->work.entry, target_list); //将该事务加入到service_manager进程事务队列中。
    tcomplete->type = BINDER_WORK_TRANSACTION_COMPLETE;
    list_add_tail(&tcomplete->entry, &thread->todo);
    if (target_wait) //如果service_manager进程在等待,下面就会唤醒service_manager对进程。
        wake_up_interruptible(target_wait); 
    return;
}

上面的代码很长,为了看起来精简一些,去掉了一些数据拷贝、错误检查的代码、以及一些其它的和今天议题暂时没有关系的代码。

  • 1.由于tr->target.handle是0,所以这里由事务数据可以确定,这里数据是要发送给service_manager进程的。所以就有了target_node = binder_context_mgr_node,然后就定位到binder_proc对应的就是service_manager进程了。接着就会申请一个struct binder_transaction对象,并从service_manager buffer缓冲区中申请内存,并将对应的media_server进程的数据拷贝到binder_buffer中,最后将该binder_buffer对象保存binder_proc的buffers链表中,具体过程如下所示。

  • 2.根据上层传下来的服务的引用计数指针,在当前binder_proc(meida_server)中查找该计数对象对应的binder_node对象,如果该bidner_node对象不存在,就会根据服务(这里是CameraService)的引用计数指针和服务对象的地址重新创建一个binder_node对象,紧接着会在service_manager 进程的binder_proc对象的refs_by_node上查找是否存在该binder_node的binder_ref对象,如果有会直接返回该binder_ref对象,反之就会创建一个该binder_node对应的binder_ref对象,并将该binder_ref对象插入到service_manager进程的binder_proc对象的refs_by_desc链表上。

小总结:之前没有看这一部分代码,老是认为binder_node都是链接到全局的binder_node对象 binder_context_mgr_node上的,现在发现是这样的。

  • 1.各个server中注册的服务对应的binder_node对象都是链接在对应的server进程binder_proc对象上的。
  • 2.所有binder_ref都是链接在service_manager进程的binder_proc对象中的。

10.事务数据读取(binder_thread_read)

service_manager进程从kernel醒来之后,就会读取自己是否有需要处理的事务,恰巧我们上面media_server发给它一条。

static int binder_thread_read(){
......
            ret = wait_event_freezable_exclusive(proc->wait, binder_has_proc_work(proc, thread)); //service_manager进程从这里唤醒
......
    while (1) {
        uint32_t cmd;
        struct binder_transaction_data tr;
        struct binder_work *w;
        struct binder_transaction *t = NULL;
        if (!list_empty(&thread->todo))
            w = list_first_entry(&thread->todo, struct binder_work, entry);
        else if (!list_empty(&proc->todo) && wait_for_proc_work)
            w = list_first_entry(&proc->todo, struct binder_work, entry);
......
        switch (w->type) {
        case BINDER_WORK_TRANSACTION: {
            t = container_of(w, struct binder_transaction, work);
        } break;
.....
        if (t->buffer->target_node) {
            struct binder_node *target_node = t->buffer->target_node;
            tr.target.ptr = target_node->ptr;
            tr.cookie =  target_node->cookie;
            //......
            cmd = BR_TRANSACTION;
        }
......
        tr.code = t->code;
        tr.flags = t->flags;
......
        tr.data_size = t->buffer->data_size;
        tr.offsets_size = t->buffer->offsets_size;
        tr.data.ptr.buffer = (binder_uintptr_t)(
                    (uintptr_t)t->buffer->data +
                    proc->user_buffer_offset);
        tr.data.ptr.offsets = tr.data.ptr.buffer +
                    ALIGN(t->buffer->data_size,
                        sizeof(void *));
        if (put_user(cmd, (uint32_t __user *)ptr))
            return -EFAULT;
        ptr += sizeof(uint32_t);
        if (copy_to_user(ptr, &tr, sizeof(tr)))
            return -EFAULT;
        ptr += sizeof(tr);

}

  刚才添加cameraService的工作项是发送给service_manager进程的,而且service_manage进程在刚开机时,事情不是很多,一会就在kernel中睡眠了。所以我们接着它醒来的代码行分析。由于media_server发送过来的是一个类型为BINDER_WORK_TRANSACTION的工作项。这里先做个关于通信数据对象的转变过程分析。

上面的对象转来转去,最终会传到servie_manager进程中,传到service_manager进程中的也是一个binder_transaction_data,不过此时binder_transaction_data->buffer指向的是一个binder_buffer。而之前在media_server的binder_transaction_data->buffer指向的是一个Parcel对象。传到service_manager进程中的binder_transaction_data对象的分布如下(之前做了memcp):

二、ServiceManager处理addService请求

1.binder_parse()

binder_parse(){
    int r = 1;
    uintptr_t end = ptr + (uintptr_t) size;

    while (ptr < end) {
        uint32_t cmd = *(uint32_t *) ptr; //上面得知cmd = BR_TRANSACTION
        ptr += sizeof(uint32_t);
......
        switch(cmd) {
        case BR_TRANSACTION: { //media_server发送的就是这个消息。
            struct binder_transaction_data *txn = (struct binder_transaction_data *) ptr;
            if ((end - ptr) < sizeof(*txn)) {
                ALOGE("parse: txn too small!\n");
                return -1;
            }
            binder_dump_txn(txn);
            if (func) {
                unsigned rdata[256/4]; //请看下面图片分析
                struct binder_io msg;   
                struct binder_io reply; //要发送给media_server的消息
                int res;

                bio_init(&reply, rdata, sizeof(rdata), 4); //初始化reply数据,后面返回处理结果时,会通过reply对象的。
                bio_init_from_txn(&msg, txn); //这里会将binder_transaction_data数据传递给一个struct binder_io类型的数据msg,后面解析事务数据就通过msg了。
                res = func(bs, txn, &msg, &reply); //从上面传递的参数可以知道func = svcmgr_handler,请看下面源码
                binder_send_reply(bs, &reply, txn->data.ptr.buffer, res);
            }
            ptr += sizeof(*txn);
            break;
         ......
        }

}

  我们知道ServiceManager进程在没有工作项的时候,会在kernel中睡眠等待。但是前面我们知道media_server进程已经唤醒了serviceManager进程,而且还给它发了一个事务工作项。这时候ServiceManager进程被唤醒之后,继续从kernel中读取消息来处理。此时的确有一条Media_Server进程发送给它的事务工作项。数据打包的数据如上图所示。

2.struct binder_io-结构分析

void bio_init(struct binder_io *bio, void *data,
              size_t maxdata, size_t maxoffs)
{
    size_t n = maxoffs * sizeof(size_t); //maxoffs = 4,所以这里一般为16个字节

    if (n > maxdata) { //16 > 256 ?显然不成立
        bio->flags = BIO_F_OVERFLOW;
        bio->data_avail = 0;
        bio->offs_avail = 0;
        return;
    }

    bio->data = bio->data0 = (char *) data + n; //初始化数据其实位置,故意跳过前面binder对象偏移数组。
    bio->offs = bio->offs0 = data; //binder对象偏移数组位置。
    bio->data_avail = maxdata - n; //实际可用的buffer大小等于总的buffer大小减去偏移数组大小。
    bio->offs_avail = maxoffs; //binder对象为4个,这个是固定的。
    bio->flags = 0; //这个要留意了,后面在kernel中要用到,先留个心眼。
}

void bio_init_from_txn(struct binder_io *bio, struct binder_transaction_data *txn)
{
    bio->data = bio->data0 = (char *)(intptr_t)txn->data.ptr.buffer; //可以看到这是记录了buffer的地址。
    bio->offs = bio->offs0 = (binder_size_t *)(intptr_t)txn->data.ptr.offsets; //偏移数组的位置
    bio->data_avail = txn->data_size; //可用数据的大小
    bio->offs_avail = txn->offsets_size  // sizeof(size_t); //binder对象个数
    bio->flags = BIO_F_SHARED;
}
struct binder_io //下面的
{
    char *data;            /* pointer to read/write from */
    binder_size_t *offs;   /* array of offsets */偏移数组
    size_t data_avail;     /* bytes available in data buffer */
    size_t offs_avail;     /* entries available in offsets array */

    char *data0;           /* start of data buffer */
    binder_size_t *offs0;  /* start of offsets buffer */
    uint32_t flags;
    uint32_t unused;
};

下图只是在初始化rdata[256/4]数据时才有的分布,只是定义一个struct binder_io 对象来管理rdata数据缓冲区。

3.svcmgr_handler()-消息处理中心

int svcmgr_handler(struct binder_state *bs,
                   struct binder_transaction_data *txn,
                   struct binder_io *msg,
                   struct binder_io *reply)
{
    struct svcinfo *si;
    uint16_t *s;
    size_t len;
    uint32_t handle;
    uint32_t strict_policy;
    int allow_isolated;

    //ALOGI("target=%x code=%d pid=%d uid=%d\n",
    // txn->target.handle, txn->code, txn->sender_pid, txn->sender_euid);

    if (txn->target.handle != svcmgr_handle)
        return -1;

    if (txn->code == PING_TRANSACTION)
        return 0;

    // Equivalent to Parcel::enforceInterface(), reading the RPC
    // header with the strict mode policy mask and the interface name.
    // Note that we ignore the strict_policy and don't propagate it
    // further (since we do no outbound RPCs anyway).
    strict_policy = bio_get_uint32(msg);
    s = bio_get_string16(msg, &len);
    if (s == NULL) {
        return -1;
    }

    if ((len != (sizeof(svcmgr_id) / 2)) || //全局变量svcmgr_id = “android.os.IServiceManager”
        memcmp(svcmgr_id, s, sizeof(svcmgr_id))) {   //这里为了确定消息的准确性,会将发送给它数据的开始处的字符串数据对比。没有就直接返回错误。
        fprintf(stderr,"invalid id %s\n", str8(s, len)); //上面我画的发送给service_manager user层的数据第一个就是“android.os.IServiceManager”,这里就不会进去了。

        return -1;
    }

   //......selinux相关的

    switch(txn->code) {
    case SVC_MGR_GET_SERVICE:
    case SVC_MGR_CHECK_SERVICE:
        s = bio_get_string16(msg, &len);
        if (s == NULL) {
            return -1;
        }
        handle = do_find_service(bs, s, len, txn->sender_euid, txn->sender_pid);
        if (!handle)
            break;
        bio_put_ref(reply, handle);
        return 0;

    case SVC_MGR_ADD_SERVICE:    // 由上面返回给srviceManager进程的数据得知.code = ADD_SERVICE_TRANSACTION,就是这个case项
        s = bio_get_string16(msg, &len); //这里获取到服务的名字“media.camera"
        if (s == NULL) {
            return -1;
        }
        handle = bio_get_ref(msg); //这里获取到media.camera binder_ref引用对象的desc成员数据,该数据标识了该服务对象。
        allow_isolated = bio_get_uint32(msg) ? 1 : 0;
        if (do_add_service(bs, s, len, handle, txn->sender_euid,
            allow_isolated, txn->sender_pid))
            return -1;
        break;

    case SVC_MGR_LIST_SERVICES: {
        uint32_t n = bio_get_uint32(msg);

        if (!svc_can_list(txn->sender_pid)) {
            ALOGE("list_service() uid=%d - PERMISSION DENIED\n",
                    txn->sender_euid);
            return -1;
        }
        si = svclist;
        while ((n-- > 0) && si)
            si = si->next;
        if (si) {
            bio_put_string16(reply, si->name);
            return 0;
        }
        return -1;
    }
    default:
        ALOGE("unknown code %d\n", txn->code);
        return -1;
    }

    bio_put_uint32(reply, 0);
    return 0;
}

这个才是最核心的处理函数,这里又出现了一个新的结构体struct svcinfo,我们先来分析一下它的成员变量。

struct svcinfo
{
    struct svcinfo *next; //链表指针,可见每一个服务都是手拉手的。
    uint32_t handle; //这个非常重要,读应该binder_ref引用对象中的desc数据,也就是通过这个来区分binder_ref引用对象了,以及后续的服务查找也是通过这个handle查找的。
    struct binder_death death; //服务的死亡通知对象,
    int allow_isolated; 
    size_t len;
    uint16_t name[0]; //服务的名字
};

  上面就是一个服务注册到service_manager进程空间的存在形态,其中最最重要的要属handle成员变量了,该成员变量目前我理解是唯一标示一个服务。通过该handle就可以找到或者创建该服务在kernel中的binder_ref引用对象,接着就会把对应的binder_ref挂接到对应客户端进程的binder_ref 链表中了。
  说完了上面的结构体,我们来分析一下svcmgr_handler()方法,该方法承载了service_manager的大部分工作。这里我们只关注了ADD_SERVICE的case项。该case项大概就做了下面几个动作。

  • 1.首先通过bio_get_string16()方法,从数据包中解析出我们注册服务名字是”media.camera”.具体如何查找的细节,感兴趣的可以自己进去瞅瞅。

  • 2.紧接着调用bio_get_ref()方法,找到该服务的binder_ref引用对象在kernel的一个handle(可以理解该handle唯一标示该引用对象),后面创建服务代理对象时,拿到的就是这个handle。

  • 3.然后调用do_add_service()方法将该服务打包成一个struct svcinfo 对象并插入到svclist全局链表中。当然这个添加过程还夹杂一些其它的处理,下面我们看看它的源代码。

4.do_add_service()核心工作

int do_add_service(struct binder_state *bs,
                   const uint16_t *s, size_t len,
                   uint32_t handle, uid_t uid, int allow_isolated,
                   pid_t spid)
{
    struct svcinfo *si;

    //ALOGI("add_service('%s',%x,%s) uid=%d\n", str8(s, len), handle,
    // allow_isolated ? "allow_isolated" : "!allow_isolated", uid);

    if (!handle || (len == 0) || (len > 127))
        return -1;

    if (!svc_can_register(s, len, spid)) {
        ALOGE("add_service('%s',%x) uid=%d - PERMISSION DENIED\n",
             str8(s, len), handle, uid);
        return -1;
    }

    si = find_svc(s, len); //根据名字“media.camera"查找之前是否已经在serviceManager注册过了camera服务。如果存在说明服务挂掉了
    if (si) {
        if (si->handle) { //如果第一次注册camera服务的话,这里肯定不为真,否则系统已经出问题了。
            ALOGE("add_service('%s',%x) uid=%d - ALREADY REGISTERED, OVERRIDE\n",
                 str8(s, len), handle, uid);
            svcinfo_death(bs, si);
        }
        si->handle = handle;//记录服务引用对象handle,很重要
    } else { //首次注册
        si = malloc(sizeof(*si) + (len + 1) * sizeof(uint16_t)); //申请struct svcinfo 对象
        if (!si) {
            ALOGE("add_service('%s',%x) uid=%d - OUT OF MEMORY\n",
                 str8(s, len), handle, uid);
            return -1;
        }
        si->handle = handle; //保存camera服务的binder_ref引用对象的desc成员,这个成员非常重要
        si->len = len; //名字的长度
        memcpy(si->name, s, (len + 1) * sizeof(uint16_t));
        si->name[len] = '\0';
        si->death.func = (void*) svcinfo_death;
        si->death.ptr = si;
        si->allow_isolated = allow_isolated;
        si->next = svclist; //这里采用头插发,插入该服务对应的struct svcinfo 对象数据。
        svclist = si; //其中 svclist是一个全局变量。
    }

    binder_acquire(bs, handle); //这里会增加该服务binder_ref引用对象的的引用计数,下面我们进kernel看看
    binder_link_to_death(bs, handle, &si->death); //注册一个当服务出现异常时,处理函数。
    return 0;
}

这里是addService()工作处理的狠心,看到这里可能大家都已经明白了,服务在serviceManage中存在形态,总的说来该函数就干了下面几件事。

  • 1.根据要注册服务的名字,在服务链表中查找该服务是否已经注册过了。
  • 2.根据前面检测结果,如果已经注册过了而且描述符号handle为0时,就更改该服务的描述符,反之就申请一个新struct svcinfo对象。
  • 3.上面都成功执行后,通过binder_acquire()增加camera服务的引用计数(已经注册到service_manage中了,引用计数要+1),请看下面源码。
  • 4.注册死亡处理函数,当服务死亡时要回收一些东东。

5.binder_acquire()

void binder_acquire(struct binder_state *bs, uint32_t target)
{
    uint32_t cmd[2];
    cmd[0] = BC_ACQUIRE; //命令
    cmd[1] = target; //该参数就是上面的handle,(即binder_ref->desc)
    binder_write(bs, cmd, sizeof(cmd)); //这个函数在之前我们就分析过了,它的作用只是向kernel写入命令和数据,不会读取消息,我们直接进到kernel分析。
}

上面就是打包了一下通信数据。即将包含了命令和处理对象的handle下发给kernle,下面我们直接看处理消息的地方。

6.binder_thread_write()

int binder_thread_write(struct binder_proc *proc, struct binder_thread *thread,
            void __user *buffer, int size, signed long *consumed)
{
    uint32_t cmd;
    void __user *ptr = buffer + *consumed;
    void __user *end = buffer + size;

    while (ptr < end && thread->return_error == BR_OK) {
        if (get_user(cmd, (uint32_t __user *)ptr))  //第一个获取的先是命令参数BC_ACQUIRE
            return -EFAULT;
        ptr += sizeof(uint32_t);
        trace_binder_command(cmd);
        if (_IOC_NR(cmd) < ARRAY_SIZE(binder_stats.bc)) {
            binder_stats.bc[_IOC_NR(cmd)]++;
            proc->stats.bc[_IOC_NR(cmd)]++;
            thread->stats.bc[_IOC_NR(cmd)]++;
        }
        switch (cmd) {
        case BC_INCREFS:
        case BC_ACQUIRE: //需处理的是这一条命令
        case BC_RELEASE:
        case BC_DECREFS: {
            uint32_t target;
            struct binder_ref *ref;
            const char *debug_string;

            if (get_user(target, (uint32_t __user *)ptr)) //这是获取到handle参数(camera服务对象的handle),而且是非0
                return -EFAULT;
            ptr += sizeof(uint32_t); //buffer数据已经取出,位置指针移动
            if (target == 0 && binder_context_mgr_node && //这里target肯定不是0,因为只有service_manager的binder_ref->desc才是0,所以这里是假
                (cmd == BC_INCREFS || cmd == BC_ACQUIRE)) {
                ref = binder_get_ref_for_node(proc,
                           binder_context_mgr_node);
                if (ref->desc != target) {
                    binder_user_error("binder: %d:"
                        "%d tried to acquire "
                        "reference to desc 0, "
                        "got %d instead\n",
                        proc->pid, thread->pid,
                        ref->desc);
                }
            } else
                ref = binder_get_ref(proc, target); //会执行这里,明显是在service_manager的binder_proc查找该handle对应的binder_ref对象
            if (ref == NULL) {  //之前已经创建该binder_ref对象而且已经插入到service_manager的binder_proc中,所以这里会找到相应的引用对象。 
                binder_user_error("binder: %d:%d refcou"
                    "nt change on invalid ref %d\n",
                    proc->pid, thread->pid, target);
                break;
            }
            switch (cmd) {
            case BC_INCREFS:
                debug_string = "IncRefs";
                binder_inc_ref(ref, 0, NULL);
                break;
            case BC_ACQUIRE: //下面就要增加binder_ref的强引用计数了。
                 debug_string = "Acquire";
                binder_inc_ref(ref, 1, NULL); //这里就是为了+1.请看下面是怎么+1的。
                break;
...
}
//---------------------增加强引用代码------------------------
static int binder_inc_ref(struct binder_ref *ref, int strong,
              struct list_head *target_list)
{
    int ret;
    if (strong) { //上面可以看到strong传进来的是1,
        if (ref->strong == 0) { //第一次,所以为0
            ret = binder_inc_node(ref->node, 1, 1, target_list); //增加该binder_ref对应的binder_node强引用计数,这里就不贴代码了。感兴趣的自己查阅源码
            if (ret)
                return ret;
        }
        ref->strong++; //这里增加强引用计数
    } else {
        if (ref->weak == 0) {
            ret = binder_inc_node(ref->node, 0, 1, target_list);
            if (ret)
                return ret;
        }
        ref->weak++; //反之增加弱引用计数
    }
    return 0;
}

上面的这条BC_ACQUIRE消息意义只有下面这两条处理过程。

  • 1.根据handle增加挂在service_manger进程binder_proc对象下面响应binder_ref强引用计数。标示已经有人开始引用你了,确实我们已经在service_manager user空间引用了。
  • 2.增加上一步binder_ref对应binder_node响应的引用计数。

7.binder_link_to_death()

void binder_link_to_death(struct binder_state *bs, uint32_t target, struct binder_death *death)
{
    struct {
        uint32_t cmd;
        struct binder_handle_cookie payload;
    } __attribute__((packed)) data;

    data.cmd = BC_REQUEST_DEATH_NOTIFICATION; //注意这个命令
    data.payload.handle = target; //唯一标示一个binder_ref对象的handle,(即binder_ref->desc)
    data.payload.cookie = (uintptr_t) death; //死亡通知对象
    binder_write(bs, &data, sizeof(data));
}

注意上面的target参数就是camera服务的handle(binder_ref->desc,通过这个找到camera引用对象,进而找到camera服务)。此函数也是打包注册服务死亡处理函数。然后将命令发给kernel。具体请看下面操作。

int binder_thread_write(struct binder_proc *proc, struct binder_thread *thread,
            binder_uintptr_t binder_buffer, size_t size,
            binder_size_t *consumed)
{
...
        case BC_REQUEST_DEATH_NOTIFICATION:
        case BC_CLEAR_DEATH_NOTIFICATION: {
...
            ref = binder_get_ref(proc, target); //找到camera服务在service_manger中的binder_ref对象。
            if (ref == NULL) {
...
                break;
            }

            if (cmd == BC_REQUEST_DEATH_NOTIFICATION) {
                if (ref->death) {//第一次注册死亡通知函数,这里为NULL
                    binder_user_error("%d:%d BC_REQUEST_DEATH_NOTIFICATION death notification already set\n",
                        proc->pid, thread->pid);
                    break;
                }
                death = kzalloc(sizeof(*death), GFP_KERNEL);//首先new一个死亡通知引用对象(struct binder_ref_death)
                if (death == NULL) {
                    thread->return_error = BR_ERROR;
                    binder_debug(BINDER_DEBUG_FAILED_TRANSACTION,
                             "%d:%d BC_REQUEST_DEATH_NOTIFICATION failed\n",
                             proc->pid, thread->pid);
                    break;
                }
                binder_stats_created(BINDER_STAT_DEATH);
                INIT_LIST_HEAD(&death->work.entry);
                death->cookie = cookie;//将死亡处理函数,绑定对应的地方。
                ref->death = death;//当前camera的binder_ref,绑定死亡处理函数。
                if (ref->node->proc == NULL) {
//......此处省略很多代码,都是再次确认一些参数是否是有效的,我们知道死亡通知注册过来就行了。
        } break;

当service注册一个服务之后,service_manager也会注册死亡通知对象,用于在camera出现异常时,给其它代理对象一个交代。希望通知是在new camera服务对象时,一起传过来的。

三、ServiceManager返回处理结果

1.binder_parse()后处理

int binder_parse(struct binder_state *bs, struct binder_io *bio,
                 uintptr_t ptr, size_t size, binder_handler func)
{
 ......
        case BR_TRANSACTION: {
            struct binder_transaction_data *txn = (struct binder_transaction_data *) ptr; //注意kernel传过来的是binder_transaction_data类型的数据。
            if ((end - ptr) < sizeof(*txn)) {
                ALOGE("parse: txn too small!\n");
                return -1;
            }
            binder_dump_txn(txn);
            if (func) {
......
                res = func(bs, txn, &msg, &reply); //这里会往reply数据缓冲区中添数据。我们进去看看,func = svcmgr_handler
                binder_send_reply(bs, &reply, txn->data.ptr.buffer, res); //这个才是最后发送返回数据。res = 0
            }
            ptr += sizeof(*txn);
            break;
        }
......
}
-----------------------------------------------------------------
//上面的func 函数指着就是下面的svcmgr_handler。
int svcmgr_handler(struct binder_state *bs,
                   struct binder_transaction_data *txn,
                   struct binder_io *msg,
                   struct binder_io *reply)
{
......
    switch(txn->code) {
    case SVC_MGR_ADD_SERVICE:
        s = bio_get_string16(msg, &len);
        if (s == NULL) {
            return -1;
        }
        handle = bio_get_ref(msg);
        allow_isolated = bio_get_uint32(msg) ? 1 : 0;
        if (do_add_service(bs, s, len, handle, txn->sender_euid,
            allow_isolated, txn->sender_pid))
            return -1;
        break;
......}
    bio_put_uint32(reply, 0); //在上面的addservice操作中,没有对reply对象操作。
}

  上面的代码上面已经介绍过,这里只是为了分析连贯性,再贴出一遍。add_service()操作处理完之后,就会将处理结果返回给media_server进程了。跳出switch case后,就是bio_put_uint32(reply, 0);

void bio_put_uint32(struct binder_io *bio, uint32_t n)//注意n = 0;
{
    uint32_t *ptr = bio_alloc(bio, sizeof(n)); //字面是数据申请,其实是数据记录指针偏移,这里申请4个字节。
    if (ptr)
        *ptr = n; //这里将0保存到数据的开始位置,如下图所示。
}
static void *bio_alloc(struct binder_io *bio, size_t size)//紧接着上面的size = 4;
{
    size = (size + 3) & (~3); //size = 4 byte,而且4字节对齐。
    if (size > bio->data_avail) {
        bio->flags |= BIO_F_OVERFLOW;
        return NULL;
    } else {
        void *ptr = bio->data; //得到可用数据其实地址
        bio->data += size; //可用数据记录指针移动4个字节,即data+4
        bio->data_avail -= size; //可用数据大小要减少4个字节
        return ptr; //返回可用buffer首地址
    }
}

上面的操作只是在缓冲区中添加了一个0,且相应的移动记录指针也移动+4了,目前还没有binder对象,偏移数组都是0。然后我们就回到binder_parse()函数中的binder_send_reply()

2.binder_send_reply()-回复数据打包

void binder_send_reply(struct binder_state *bs,
                       struct binder_io *reply,
                       binder_uintptr_t buffer_to_free,
                       int status) //status 是上一次操作的返回值,这里=0;
{
    struct {
        uint32_t cmd_free;
        binder_uintptr_t buffer;
        uint32_t cmd_reply;
        struct binder_transaction_data txn;
    } __attribute__((packed)) data; //重新打包一个临时一个结构体

    data.cmd_free = BC_FREE_BUFFER; //释放命令
    data.buffer = buffer_to_free;   //要释放的binder_buffer地址
    data.cmd_reply = BC_REPLY;      //回复命令,这里是media_Server
    data.txn.target.ptr = 0; //注意 ptr = 0 ,即sm
    data.txn.cookie = 0;     //注意 cookie = 0 ,即sm
    data.txn.code = 0;
    if (status) { //由上面的调用结果可知,这里status = 0
        data.txn.flags = TF_STATUS_CODE;
        data.txn.data_size = sizeof(int);
        data.txn.offsets_size = 0;
        data.txn.data.ptr.buffer = (uintptr_t)&status;
        data.txn.data.ptr.offsets = 0;
    } else {
        data.txn.flags = 0;
        data.txn.data_size = reply->data - reply->data0; //结合上面的图,这里数据大小是4,即之前存入的那个0
        data.txn.offsets_size = ((char*) reply->offs) - ((char*) reply->offs0); //这里没有binder对象,所以binder对象数量= 0
        data.txn.data.ptr.buffer = (uintptr_t)reply->data0; //buffer的起始位置
        data.txn.data.ptr.offsets = (uintptr_t)reply->offs0;//偏移数组的起始位置
    }
    binder_write(bs, &data, sizeof(data)); //又到了我们熟悉的写数据函数,
}

上面的打包的binder_transaction_data 数据内容很简单,这里我们就不罗列出数据结构图了。这里主要做的就是打包二个BC_FREE_BUFFER,BC_REPLY,以及记录当前发送数据的格式。我们可以不用态关心这里,只需要知道是那回事就好了。下面直接进kernel看看。

域名
data.cmd_free BC_FREE_BUFFER
data.buffer buffer_to_free(要释放binder_buffer地址)
data.cmd_reply BC_REPLY(回复命令)
data.txn.target.ptr 0
data.txn.cookie 0
data.txn.flags 0
data.txn.data_size reply->data - reply->data0即0,结合上图分析
data.txn.offsets_size 这里没有binder对象,为0,在getService时会有值
data.txn.data.ptr.buffer 事务数据,这里是一个256字节的局部变量
data.txn.data.ptr.offsets 偏移数组的起始位置,这里就是开始处为0

这里为了直观看得清晰,上面就列成表格的形式,来记录下数据的保存形式。

3.binder_thread_write()-释放binder_buffer和写回复数据

int binder_thread_write(struct binder_proc *proc, struct binder_thread *thread,
            binder_uintptr_t binder_buffer, size_t size,
            binder_size_t *consumed)
{
......
case BC_FREE_BUFFER: {
            binder_uintptr_t data_ptr;
            struct binder_buffer *buffer;

            if (get_user(data_ptr, (binder_uintptr_t __user *)ptr)) //获取上面打包的事务数据中要释放的binder_buffer用户空间地址
                return -EFAULT;
            ptr += sizeof(binder_uintptr_t);//数据指针移动

            buffer = binder_buffer_lookup(proc, data_ptr);//获取到binder_buffer地址 //这里根据binder_buffer用户空间地址,找到kernel对应的地址,紧接着就会释放当前binder_buffer.
            if (buffer == NULL) {
                binder_user_error("%d:%d BC_FREE_BUFFER u%016llx no match\n",
                    proc->pid, thread->pid, (u64)data_ptr);
                break;
            }
            if (!buffer->allow_user_free) { //如果不允许用户空间释放的话,这里就直接就break。
                binder_user_error("%d:%d BC_FREE_BUFFER u%016llx matched unreturned buffer\n",
                    proc->pid, thread->pid, (u64)data_ptr);
                break;
            }
            binder_debug(BINDER_DEBUG_FREE_BUFFER,
                     "%d:%d BC_FREE_BUFFER u%016llx found buffer %d for %s transaction\n",
                     proc->pid, thread->pid, (u64)data_ptr, buffer->debug_id,
                     buffer->transaction ? "active" : "finished");

            if (buffer->transaction) {
                buffer->transaction->buffer = NULL;
                buffer->transaction = NULL;
            }
            if (buffer->async_transaction && buffer->target_node) {
                BUG_ON(!buffer->target_node->has_async_transaction);
                if (list_empty(&buffer->target_node->async_todo))
                    buffer->target_node->has_async_transaction = 0;
                else
                    list_move_tail(buffer->target_node->async_todo.next, &thread->todo);
            }
            trace_binder_transaction_buffer_release(buffer);
            binder_transaction_buffer_release(proc, buffer, NULL);
            binder_free_buf(proc, buffer);
            break;
        }

        case BC_TRANSACTION:
        case BC_REPLY: {//第二次循环走到这里
            struct binder_transaction_data tr;

            if (copy_from_user(&tr, ptr, sizeof(tr))) //拷贝出sm用户空间传下来的事务数据。
                return -EFAULT;
            ptr += sizeof(tr); //移动记录指针
            binder_transaction(proc, thread, &tr, cmd == BC_REPLY); //注意当前cmd = BC_REPLY,大家要注意了,此时此刻传进去的thread = service_Manager主线程。
            break;
        }
......
}

上面我们知道这里处理2个命令。

  • 1.首先先处理的是BC_FREE_BUFFER,在释放binder_buffer时,因为传下来的是用户空间的地方,所以需要将地址转换成kernel空间的虚拟地址。由于前面我们已经说过,用户空间的地方和kernel空间的虚拟地址,相差一个固定的值,所以在binder_buffer_lookup中直接根据地址减去一个固定值就得到了kernel空间的虚拟地址。
  • 2.主要关注最后一组参数binder_transaction(proc, thread, &tr, cmd == BC_REPLY);

这里上面就做了释放binder_buffer,和给media_server回复

4.binder_transaction()-回复事务数据处理

static void binder_transaction(struct binder_proc *proc,
                   struct binder_thread *thread,
                   struct binder_transaction_data *tr, int reply)
{
......
if (reply) { //replay = 1;
        in_reply_to = thread->transaction_stack; 
        //大家一定要转变过来,此时的thread = service_manager主线程。还记得在上面的media_server发送给
        //service_manager的事务吗,目前该事务仍在service_manager事务栈的顶端,所以根据这个事务,
        //service_manager主线程就可以找到发送事务的进程和线程,这样才能发送回复消息给它们。
        if (in_reply_to == NULL) {
            binder_user_error("%d:%d got reply transaction with no transaction stack\n",
                      proc->pid, thread->pid);
            return_error = BR_FAILED_REPLY;
            goto err_empty_call_stack;
        }
        binder_set_nice(in_reply_to->saved_priority);
        if (in_reply_to->to_thread != thread) { //显然上一个事务是发送给service_manager进程主线程,而thread 就是sm主线程,这里为假。
            binder_user_error("%d:%d got reply transaction with bad transaction stack, transaction %d has target %d:%d\n",
                proc->pid, thread->pid, in_reply_to->debug_id,
                in_reply_to->to_proc ?
                in_reply_to->to_proc->pid : 0,
                in_reply_to->to_thread ?
                in_reply_to->to_thread->pid : 0);
            return_error = BR_FAILED_REPLY;
            in_reply_to = NULL;
            goto err_bad_call_stack;
        }
        thread->transaction_stack = in_reply_to->to_parent; //将之前sm主线程中记录的事务,重新安排到事务栈栈顶。
        target_thread = in_reply_to->from; //这里由media_server发送给sm的事务,找到发送事务的源端是media_server主线程
        if (target_thread == NULL) {
            return_error = BR_DEAD_REPLY;
            goto err_dead_binder;
        }
        if (target_thread->transaction_stack != in_reply_to) { //之前在media_server进程给sm进程发送事务时,事务也会保存到media_server事务栈栈顶,这里是一样的为假。
            binder_user_error("%d:%d got reply transaction with bad target transaction stack %d, expected %d\n",
                proc->pid, thread->pid,
                target_thread->transaction_stack ?
                target_thread->transaction_stack->debug_id : 0,
                in_reply_to->debug_id);
            return_error = BR_FAILED_REPLY;
            in_reply_to = NULL;
            target_thread = NULL;
            goto err_dead_binder;
        }
        target_proc = target_thread->proc; //这里找到了media_server进程的binder_proc对象
    } else {
.......
    }
    if (target_thread) {//media_server进程肯定不为NULL
        e->to_thread = target_thread->pid;  //media_server主线程pid
        target_list = &target_thread->todo; //media_server主线程任务队列
        target_wait = &target_thread->wait; //media_server主线程等待队列
    } else {
        target_list = &target_proc->todo;
        target_wait = &target_proc->wait;
    }
    /* TODO: reuse incoming transaction for reply */
    t = kzalloc(sizeof(*t), GFP_KERNEL); //分配事务数据buffer
    tcomplete = kzalloc(sizeof(*tcomplete), GFP_KERNEL);
......
    t->sender_euid = proc->tsk->cred->euid;
    t->to_proc = target_proc;  //记录media_server进程binder_proc对象
    t->to_thread = target_thread; //记录media_server进程主线程对象
    t->code = tr->code; //这里=0,组要要根据上面的表格对比。
    t->flags = tr->flags; //这里= 0
    t->priority = task_nice(current);
    t->buffer = binder_alloc_buf(target_proc, tr->data_size, //在media_server进程的buffer空间中申请binder_buffer,由此可见,事务是发给哪一个进程的,就在哪一个进程的buffer中申请binder_buffer.
        tr->offsets_size, !reply && (t->flags & TF_ONE_WAY));
    if (t->buffer == NULL) {
        return_error = BR_FAILED_REPLY;
        goto err_binder_alloc_buf_failed;
    }
    t->buffer->allow_user_free = 0; //不允许用户空间释放buffer
    t->buffer->debug_id = t->debug_id;
    t->buffer->transaction = t;
    t->buffer->target_node = target_node; //回复消息时,target_node 始终为NULL.
   //......这里有一些查找binder对象的操作,由于从上面了sm发送下来的数据知道,目前我们没有binder对象,这里就省略了,同时也为了代码的简洁性。
    if (reply) { //为真
        BUG_ON(t->buffer->async_transaction != 0);
        binder_pop_transaction(target_thread, in_reply_to); //这里会根据in_reply_to 事务数据找到media_server主线程之前在栈顶的事务。
    } else if (!(t->flags & TF_ONE_WAY)) {
        BUG_ON(t->buffer->async_transaction != 0);
        t->need_reply = 1;
        t->from_parent = thread->transaction_stack;
        thread->transaction_stack = t;
    } else {
        BUG_ON(target_node == NULL);
        BUG_ON(t->buffer->async_transaction != 1);
        if (target_node->has_async_transaction) {
            target_list = &target_node->async_todo;
            target_wait = NULL;
        } else
            target_node->has_async_transaction = 1;
    }
    t->work.type = BINDER_WORK_TRANSACTION; //事务工作项
    list_add_tail(&t->work.entry, target_list);//将工作项添加到media_server进程的工作队列中
    tcomplete->type = BINDER_WORK_TRANSACTION_COMPLETE; //返回给service_manager进程BINDER_WORK_TRANSACTION_COMPLETE消息。
    list_add_tail(&tcomplete->entry, &thread->todo); //将该工作项BINDER_WORK_TRANSACTION_COMPLETE添加到service_manager进程的工作队列中。
    if (target_wait) //media_server的等待队列不为空。
        wake_up_interruptible(target_wait); //这里就唤醒了media_server进程了。
    return;

//上面的代码虽然我们之前也介绍过,但是这里的目的端已经切换成media_server进程了。
static void binder_pop_transaction(struct binder_thread *target_thread,
                   struct binder_transaction *t)
{
    if (target_thread) {
        BUG_ON(target_thread->transaction_stack != t);
        BUG_ON(target_thread->transaction_stack->from != target_thread);
        target_thread->transaction_stack =
            target_thread->transaction_stack->from_parent; //这里根据事务t,找到media_server进程之前事务栈中的事务。因此我们可以对事务栈做个总结了。
        t->from = NULL;
    }
    t->need_reply = 0;
    if (t->buffer)
        t->buffer->transaction = NULL;
    kfree(t); //释放该事务。
    binder_stats_deleted(BINDER_STAT_TRANSACTION);
}

说到这里,不得不讲一下transaction_statck的用处。
我们知道事务处理是有先后顺序的,这里在事务的数据结构中加了一个from_parent和to_parent结构就是来记录这个先后顺序的。如下所示,有了下面两个域就可以在这个事务处理完毕之后,还原发送和接收端进程的事务堆栈,以让他们继续处理。

  • 1.from_parent:记录来自发送事务消息的进程,事务堆栈中的事务消息(这里发送发送的事务就是in_reply_to)。
  • 2.to_parent:记录来自事务接受端进程事务堆栈中的消息。

这里介绍一下上图中的流程信息(这里假设中间事务为A)。

  • 1.在media_server 发送事务A给service_manager时,media_server的事务堆栈信息B,就会保存在事务A的from_parent域。
  • 2.同时当media_server唤醒service_manager进程时,sm进程读取事务信息时,会将自己的transaction_stack中的数据保存在事务A的to_parent域。
  • 3&4. media_server发送事务给service_manager进程时,media_server和service_manager进程的事务堆栈都会保存当前即将要处理的事务A
  • 5&6. service_manager进程处理完add_service请求之后,将处理结果返回给media_server进程时,由sm之前处理的事务A中,找到media_server和service_manager事务堆栈中要处理的事务,并分别赋给他们的transaction_statck域。
    (表达能力有限,不知道写明白了没)

5.binder_thread_read(来到media_server进程的waitForResponse())

static int binder_thread_read(struct binder_proc *proc,
                  struct binder_thread *thread,
                  void  __user *buffer, int size,
                  signed long *consumed, int non_block)
{
    void __user *ptr = buffer + *consumed;
    void __user *end = buffer + size;

    int ret = 0;
    int wait_for_proc_work;

    if (*consumed == 0) { //media_server刚开始的时候*consumed = 0.
              if (put_user(BR_NOOP, (uint32_t __user *)ptr)) //所以这里会将BR_NOOP命令保存到缓冲区中
            return -EFAULT;
        ptr += sizeof(uint32_t);
    }

    if (wait_for_proc_work) {
......
        if (non_block) {
            if (!binder_has_proc_work(proc, thread))
                ret = -EAGAIN;
        } else
            ret = wait_event_freezable_exclusive(proc->wait, binder_has_proc_work(proc, thread)); //media_server就是这被睡眠了,与此同时醒来时也是在这里站起来的。
    }
......
    while (1) {
        switch (w->type) {
        case BINDER_WORK_TRANSACTION: { //还记得上面唤醒media_server发的就是这个命令吧。
            t = container_of(w, struct binder_transaction, work); //找到事务的数据
        } break;
    ......
    }
        if (!t)
            continue;

        BUG_ON(t->buffer == NULL);
        if (t->buffer->target_node) { //之前已经说过了,在反馈binder进程间通信时,target_node都是为NULL。
       //.......
        } else {
            tr.target.ptr = NULL;
            tr.cookie = NULL;
            cmd = BR_REPLY; //返回回去的是BR_REPLY
        }
        tr.code = t->code;  //这里为0 
        tr.flags = t->flags; //为0
        tr.sender_euid = t->sender_euid;
        tr.data_size = t->buffer->data_size; //有效数据的大小
        tr.offsets_size = t->buffer->offsets_size; //biner对象的个数,这里为0.
        tr.data.ptr.buffer = (void *)t->buffer->data + //这里buffer的初始地址,已经是用户空间的了。
                    proc->user_buffer_offset;
        tr.data.ptr.offsets = tr.data.ptr.buffer +  //binder偏移数组的起始位置。
                    ALIGN(t->buffer->data_size,
                        sizeof(void *));

        if (put_user(cmd, (uint32_t __user *)ptr)) //将BR_REPLY命令保存到缓冲区中
            return -EFAULT;
        ptr += sizeof(uint32_t);
        if (copy_to_user(ptr, &tr, sizeof(tr))) //将事务数据都打包到缓冲区中。
            return -EFAULT;
        ptr += sizeof(tr); //缓冲区记录指针移动

        list_del(&t->work.entry); //删除servie_manager发送给media_server的事务
        t->buffer->allow_user_free = 1; //允许用户释放binder_buffer.
        if (cmd == BR_TRANSACTION && !(t->flags & TF_ONE_WAY)) { //cmd = BR_RELY
            t->to_parent = thread->transaction_stack;
            t->to_thread = thread;
            thread->transaction_stack = t;
        } else {
            t->buffer->transaction = NULL;  
            kfree(t); //释放已经处理过的事务数据。
            binder_stats_deleted(BINDER_STAT_TRANSACTION);//增加统计信息
        }

上面主要是数据的打包过程,这个时候返回给media_server进程用户空间的数据分布如下。

四、Media_Server梦开始的地方main()

在上面我们已经画出mediaServer即将要处理的消息内容了,但是为了查看方便,有些代码还是在贴一遍吧。这一小结主要分析mediaServer拿到通信数据后,做了什么。

1.MediaServer-main()

void main(){
        sp<ProcessState> proc(ProcessState::self()); 
        MediaLogService::instantiate();
        ProcessState::self()->startThreadPool(); //启动线程池。

.....
        CameraService::instantiate(); //注册camera服务
.....
        ProcessState::self()->startThreadPool(); //启动线程池,前面已经启动过了,这里多此一举
        IPCThreadState::self()->joinThreadPool();//当前主线程加入线程池
}

  还记着开始的时候,CameraService::instantiate()就是调用的IPCThreadState::transact()方法,向service_manager进程注册camera服务的。我们接着这个函数,看看它如何处理service_manager进程反馈回来的消息。如果不明白为什么走这里,请看前面一开始ADD_SERVICE请求发出的地方。

status_t IPCThreadState::transact(int32_t handle,
                                  uint32_t code, const Parcel& data,
                                  Parcel* reply, uint32_t flags)
{
........
    if (err == NO_ERROR) {
        LOG_ONEWAY(">>>> SEND from pid %d uid %d %s", getpid(), getuid(),
            (flags & TF_ONE_WAY) == 0 ? "READ REPLY" : "ONE WAY");
        err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, NULL); //这里是请求service_manager添加camera服务数据。
    }
......
        if (reply) {
            err = waitForResponse(reply);//这里会等待service_manager反馈回来的数据。即这里是一个while(1)循环。
        } else {
            Parcel fakeReply;
            err = waitForResponse(&fakeReply);
        }
}

后面注册服务都是走的上面的流程,先发出请求,然后等待service_manger进程反馈处理结果。

2.media_server的等待waitForResponse()

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

    while (1) {
        if ((err=talkWithDriver()) < NO_ERROR) break; //talkWithDriver()意义就是读写binder通信数据,这里是读取的操作。
        err = mIn.errorCheck();
        if (err < NO_ERROR) break;
        if (mIn.dataAvail() == 0) continue;

        cmd = mIn.readInt32();//根据之前我画的service_manager反馈过来的数据分布图,可以知道,这里第一个是BR_NOOP.

        switch (cmd) {
        case BR_REPLY:
            {
                binder_transaction_data tr;
                err = mIn.read(&tr, sizeof(tr)); //从min数据对象中读出事务数据。
                ALOG_ASSERT(err == NO_ERROR, "Not enough command data for brREPLY");
                if (err != NO_ERROR) goto finish;

                if (reply) {
                    if ((tr.flags & TF_STATUS_CODE) == 0) { //还记着我们之前画的数据map吗,这里flags = 0,TF_STATUS_CODE = 0x08,所以这里为真,标示之前的请求已经成功处理掉了。
                        reply->ipcSetDataReference( //这个方法,就是把返回到用户空间的事务数据打包到reply对象中。
                            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 {
                    //.......
                    }
                } else {
                 //.......
                }
            }
            goto finish; //最后会走到这里跳出while(1)循环。
        default:
            err = executeCommand(cmd); //BR_NOOP命令就是在这里处理的。
            if (err != NO_ERROR) goto finish;
            break;
    return err;
}

该方法就像它的名字那样,它会等待在那里,其实主线程在kernel中休眠了,不等也没办法了。它的处理简单就下面两步。

  • 1.BR_NOOP:该消息走的default项,什么也没做直接返回的。(不信你可以进去看看)
  • 2.BR_REPLY:这是真正的sm发送过来处理结果数据。如果tr.flags 不等于TF_STATUS_CODE说明请求成功了。

3.void Parcel::ipcSetDataReference()

void Parcel::ipcSetDataReference(const uint8_t* data, size_t dataSize,
    const binder_size_t* objects, size_t objectsCount, release_func relFunc, void* relCookie)
{
    binder_size_t minOffset = 0;
    freeDataNoInit();
    mError = NO_ERROR;
    mData = const_cast<uint8_t*>(data); //这是有4个字节的buffer。且存放的数据是0
    mDataSize = mDataCapacity = dataSize; //之前申请的大小就是4个字节。
    //ALOGI("setDataReference Setting data size of %p to %lu (pid=%d)", this, mDataSize, getpid());
    mDataPos = 0;
    ALOGV("setDataReference Setting data pos of %p to %zu", this, mDataPos);
    mObjects = const_cast<binder_size_t*>(objects); //binder对象其实地址
    mObjectsSize = mObjectsCapacity = objectsCount; //binder对象的个数。
    mNextObjectHint = 0;
    mOwner = relFunc; //释放内存的函数,后面我们就不进行了。
    mOwnerCookie = relCookie;
    for (size_t i = 0; i < mObjectsSize; i++) {
        binder_size_t offset = mObjects[i];
        if (offset < minOffset) {
            ALOGE("%s: bad object offset %"PRIu64" < %"PRIu64"\n",
                  __func__, (uint64_t)offset, (uint64_t)minOffset);
            mObjectsSize = 0;
            break;
        }
        minOffset = offset + sizeof(flat_binder_object);
    }
    scanForFds();
}

上面做的工作只是将事务数据分别安放到当前Parcel对象的相应位置。其中scanForFds()是为了查找返回来的数据中是否有binder对象,这个在获取代理对象时有用。下面我们回到刚开始addService()函数中。

4.返回到addService()

    virtual status_t addService(const String16& name, const sp<IBinder>& service,
            bool allowIsolated)
    {
        Parcel data, reply;
        data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());
        data.writeString16(name);
        data.writeStrongBinder(service);
        data.writeInt32(allowIsolated ? 1 : 0);
        status_t err = remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply);//这个处理过程就是在IPCThreadState.cpp中的transact方法中处理。
        return err == NO_ERROR ? reply.readExceptionCode() : err;//返回NO_ERROR成功执行了。
    }

到这里我们addService总算分析完了,下面做个简要总结。

五、总结

  下面以一个简单流程图,来结束本篇博文。博文中有些细节方面东西,如果不是做binder驱动的,只需要了解就行了,不要太深入研究。在实际开发中只要会用就行了。

你可能感兴趣的:(android)