(转载)Binder系列6—获取服务(getService)

原文链接:Binder系列6—获取服务(getService) - CSDN博客

一、 获取服务

在Native层的服务注册,我们选择以media为例来展开讲解,先来看看media的类关系图。

1.1 类图

mediaplayerservice类图

图解:

    蓝色: 代表获取MediaPlayerService服务相关的类;

    绿色: 代表Binder架构中与Binder驱动通信过程中的最为核心的两个类;

    紫色: 代表注册服务和获取服务的公共接口/父类;

二. 获取Media服务

2.1 getMediaPlayerService

[-> framework/av/media/libmedia/IMediaDeathNotifier.cpp]

sp<IMediaPlayerService>&

IMediaDeathNotifier::getMediaPlayerService()

{

    Mutex::Autolock _l(sServiceLock);

    if (sMediaPlayerService == 0) {

        sp<IServiceManager> sm = defaultServiceManager(); //获取ServiceManager

        sp<IBinder> binder;

        do {

            //获取名为"media.player"的服务 【见2.2】

            binder = sm->getService(String16("media.player"));

            if (binder != 0) {

                break;

            }

            usleep(500000); // 0.5s

        } while (true);

        if (sDeathNotifier == NULL) {

            sDeathNotifier = new DeathNotifier(); //创建死亡通知对象

        }

        //将死亡通知连接到binder 【见流程14】

        binder->linkToDeath(sDeathNotifier);

        sMediaPlayerService = interface_cast<IMediaPlayerService>(binder);

    }

    return sMediaPlayerService;

}

其中defaultServiceManager()过程在上一篇文章获取ServiceManager已讲过,返回BpServiceManager。

在请求获取名为”media.player”的服务过程中,采用不断循环获取的方法。由于MediaPlayerService服务可能还没向ServiceManager注册完成或者尚未启动完成等情况,故则binder返回为NULL,休眠0.5s后继续请求,直到获取服务为止。

2.2 BpSM.getService

[-> IServiceManager.cpp ::BpServiceManager]

virtual sp<IBinder> getService(const String16& name) const

    {

        unsigned n;

        for (n = 0; n < 5; n++){

            sp<IBinder> svc = checkService(name); //【见2.3】

            if (svc != NULL) return svc;

            sleep(1);

        }

        return NULL;

    }

通过BpServiceManager来获取MediaPlayer服务:检索服务是否存在,当服务存在则返回相应的服务,当服务不存在则休眠1s再继续检索服务。该循环进行5次。为什么是循环5次呢,这估计跟Android的ANR时间为5s相关。如果每次都无法获取服务,循环5次,每次循环休眠1s,忽略checkService()的时间,差不多就是5s的时间。

2.3 BpSM.checkService

[-> IServiceManager.cpp ::BpServiceManager]

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); //【见2.4】

    return reply.readStrongBinder(); //【见小节2.9】

}

检索指定服务是否存在, 其中remote()为BpBinder。

2.4 BpBinder::transact

[-> BpBinder.cpp]

status_t BpBinder::transact(

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

{

    if (mAlive) {

        //【见流程2.5】

        status_t status = IPCThreadState::self()->transact(

            mHandle, code, data, reply, flags);

        if (status == DEAD_OBJECT) mAlive = 0;

        return status;

    }

    return DEAD_OBJECT;

}

Binder代理类调用transact()方法,真正工作还是交给IPCThreadState来进行transact工作,

2.4.1 IPCThreadState::self

[-> IPCThreadState.cpp]

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 【见小节2.4.2】

    }

    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;

}

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

2.4.2 IPCThreadState初始化

[-> IPCThreadState.cpp]

IPCThreadState::IPCThreadState()

    : mProcess(ProcessState::self()),

      mMyThreadId(gettid()),

      mStrictModePolicy(0),

      mLastTransactionBinderFlags(0)

{

    pthread_setspecific(gTLS, this);

    clearCaller();

    mIn.setDataCapacity(256);

    mOut.setDataCapacity(256);

}

每个线程都有一个IPCThreadState,每个IPCThreadState中都有一个mIn、一个mOut。成员变量mProcess保存了ProcessState变量(每个进程只有一个)。

    mIn 用来接收来自Binder设备的数据,默认大小为256字节;

    mOut用来存储发往Binder设备的数据,默认大小为256字节。

2.5 IPC::transact

[-> IPCThreadState.cpp]

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) {

        // 传输数据 【见流程2.6】

        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) { //flags=0进入该分支

        if (reply) {

            //等待响应 【见流程2.7】

            err = waitForResponse(reply);

        } else {

            Parcel fakeReply;

            err = waitForResponse(&fakeReply);

        }

    } else {

        //不需要响应消息的binder则进入该分支

        err = waitForResponse(NULL, NULL);

    }

    return err;

}

2.6 IPC.writeTransactionData

[-> IPCThreadState.cpp]

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;            // code = CHECK_SERVICE_TRANSACTION

    tr.flags = binderFlags;    // binderFlags = 0

    tr.cookie = 0;

    tr.sender_pid = 0;

    tr.sender_euid = 0;

    // data为记录Media服务信息的Parcel对象

    const status_t err = data.errorCheck();

    if (err == NO_ERROR) {

        tr.data_size = data.ipcDataSize();  // mDataSize

        tr.data.ptr.buffer = data.ipcData(); //mData

        tr.offsets_size = data.ipcObjectsCount()*sizeof(binder_size_t); //mObjectsSize

        tr.data.ptr.offsets = data.ipcObjects(); //mObjects

    } else if (statusBuffer) {

        ...

    } else {

        return (mLastError = err);

    }

    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。

2.7 IPC.waitForResponse

[-> IPCThreadState.cpp]

status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult)

{

    int32_t cmd;

    int32_t err;

    while (1) {

        if ((err=talkWithDriver()) < NO_ERROR) break; // 【见流程2.8】

        err = mIn.errorCheck();

        if (err < NO_ERROR) break;

        if (mIn.dataAvail() == 0) continue;

        cmd = mIn.readInt32();

        switch (cmd) {

            case BR_TRANSACTION_COMPLETE: ...

            case BR_DEAD_REPLY: ...

            case BR_FAILED_REPLY: ...

            case BR_ACQUIRE_RESULT: ...

            case BR_REPLY:

            {

              binder_transaction_data tr;

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

              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 {

                      ...

                  }

              }

            }

            goto finish;

            default:

                err = executeCommand(cmd);

                if (err != NO_ERROR) goto finish;

                break;

        }

    }

    ...

    return err;

}

2.8 IPC.talkWithDriver

[-> IPCThreadState.cpp]

status_t IPCThreadState::talkWithDriver(bool doReceive)

{

    ...

    binder_write_read bwr;

    const bool needRead = mIn.dataPosition() >= mIn.dataSize();

    const size_t outAvail = (!doReceive || needRead) ? mOut.dataSize() : 0;

    bwr.write_size = outAvail;

    bwr.write_buffer = (uintptr_t)mOut.data();

    if (doReceive && needRead) {

        //接收数据缓冲区信息的填充。如果以后收到数据,就直接填在mIn中了。

        bwr.read_size = mIn.dataCapacity();

        bwr.read_buffer = (uintptr_t)mIn.data();

    } else {

        bwr.read_size = 0;

        bwr.read_buffer = 0;

    }

    //当读缓冲和写缓冲都为空,则直接返回

    if ((bwr.write_size == 0) && (bwr.read_size == 0)) return NO_ERROR;

    bwr.write_consumed = 0;

    bwr.read_consumed = 0;

    status_t err;

    do {

        //通过ioctl不停的读写操作,跟Binder Driver进行通信【2.8.1】

        if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0)

            err = NO_ERROR;

        ...

    } while (err == -EINTR); //当被中断,则继续执行

    ...

    return err;

}

binder_write_read结构体用来与Binder设备交换数据的结构, 通过ioctl与mDriverFD通信,是真正与Binder驱动进行数据读写交互的过程。 先向service manager进程发送查询服务的请求(BR_TRANSACTION),见Binder系列3—启动ServiceManager。当service manager进程收到该命令后,会执行do_find_service() 查询服务所对应的handle,然后再binder_send_reply()应答 发起者,发送BC_REPLY协议,然后调用binder_transaction(),再向服务请求者的Todo队列 插入事务。

接下来,再看看binder_transaction过程。

2.8.1 binder_transaction

static void binder_transaction(struct binder_proc *proc,

              struct binder_thread *thread,

              struct binder_transaction_data *tr, int reply){

    //根据各种判定,获取以下信息:

    struct binder_thread *target_thread; //目标线程

    struct binder_proc *target_proc;    //目标进程

    struct binder_node *target_node;    //目标binder节点

    struct list_head *target_list;      //目标TODO队列

    wait_queue_head_t *target_wait;    //目标等待队列

    ...

    //分配两个结构体内存

    struct binder_transaction *t = kzalloc(sizeof(*t), GFP_KERNEL);

    struct binder_work *tcomplete = kzalloc(sizeof(*tcomplete), GFP_KERNEL);

    //从target_proc分配一块buffer

    t->buffer = binder_alloc_buf(target_proc, tr->data_size,

    for (; offp < off_end; offp++) {

        switch (fp->type) {

        case BINDER_TYPE_BINDER: ...

        case BINDER_TYPE_WEAK_BINDER: ...

        case BINDER_TYPE_HANDLE:

        case BINDER_TYPE_WEAK_HANDLE: {

          struct binder_ref *ref = binder_get_ref(proc, fp->handle,

                fp->type == BINDER_TYPE_HANDLE);

          ...

          //此时运行在servicemanager进程,故ref->node是指向服务所在进程的binder实体,

          //而target_proc为请求服务所在的进程,此时并不相等。

          if (ref->node->proc == target_proc) {

            if (fp->type == BINDER_TYPE_HANDLE)

              fp->type = BINDER_TYPE_BINDER;

            else

              fp->type = BINDER_TYPE_WEAK_BINDER;

            fp->binder = ref->node->ptr;

            fp->cookie = ref->node->cookie; //BBinder服务的地址

            binder_inc_node(ref->node, fp->type == BINDER_TYPE_BINDER, 0, NULL);

          } else {

            struct binder_ref *new_ref;

            //请求服务所在进程并非服务所在进程,则为请求服务所在进程创建binder_ref

            new_ref = binder_get_ref_for_node(target_proc, ref->node);

            fp->binder = 0;

            fp->handle = new_ref->desc; //重新赋予handle值

            fp->cookie = 0;

            binder_inc_ref(new_ref, fp->type == BINDER_TYPE_HANDLE, NULL);

          }

        } break;

        case BINDER_TYPE_FD: ...

        }

    }

    //分别target_list和当前线程TODO队列插入事务

    t->work.type = BINDER_WORK_TRANSACTION;

    list_add_tail(&t->work.entry, target_list);

    tcomplete->type = BINDER_WORK_TRANSACTION_COMPLETE;

    list_add_tail(&tcomplete->entry, &thread->todo);

    if (target_wait)

        wake_up_interruptible(target_wait);

    return;

}

这个过程非常重要,分两种情况来说:

    当请求服务的进程与服务属于不同进程,则为请求服务所在进程创建binder_ref对象,指向服务进程中的binder_node;

    当请求服务的进程与服务属于同一进程,则不再创建新对象,只是引用计数加1,并且修改type为BINDER_TYPE_BINDER或BINDER_TYPE_WEAK_BINDER。

2.8.2 binder_thread_read

binder_thread_read(...){

    ...

    //当线程todo队列有数据则执行往下执行;当线程todo队列没有数据,则进入休眠等待状态

    ret = wait_event_freezable(thread->wait, binder_has_thread_work(thread));

    ...

    while (1) {

        uint32_t cmd;

        struct binder_transaction_data tr;

        struct binder_work *w;

        struct binder_transaction *t = NULL;

        //先从线程todo队列获取事务数据

        if (!list_empty(&thread->todo)) {

            w = list_first_entry(&thread->todo, struct binder_work, entry);

        // 线程todo队列没有数据, 则从进程todo对获取事务数据

        } else if (!list_empty(&proc->todo) && wait_for_proc_work) {

            ...

        }

        switch (w->type) {

            case BINDER_WORK_TRANSACTION:

                //获取transaction数据

                t = container_of(w, struct binder_transaction, work);

                break;

            case : ... 

        }

        //只有BINDER_WORK_TRANSACTION命令才能继续往下执行

        if (!t) continue;

        if (t->buffer->target_node) {

            ...

        } else {

            tr.target.ptr = NULL;

            tr.cookie = NULL;

            cmd = BR_REPLY; //设置命令为BR_REPLY

        }

        tr.code = t->code;

        tr.flags = t->flags;

        tr.sender_euid = t->sender_euid;

        if (t->from) {

            struct task_struct *sender = t->from->proc->tsk;

            //当非oneway的情况下,将调用者进程的pid保存到sender_pid

            tr.sender_pid = task_tgid_nr_ns(sender, current->nsproxy->pid_ns);

        } else {

            ...

        }

        tr.data_size = t->buffer->data_size;

        tr.offsets_size = t->buffer->offsets_size;

        tr.data.ptr.buffer = (void *)t->buffer->data +

                    proc->user_buffer_offset;

        tr.data.ptr.offsets = tr.data.ptr.buffer +

                    ALIGN(t->buffer->data_size,

                        sizeof(void *));

        //将cmd和数据写回用户空间

        put_user(cmd, (uint32_t __user *)ptr);

        ptr += sizeof(uint32_t);

        copy_to_user(ptr, &tr, sizeof(tr));

        ptr += sizeof(tr);

        list_del(&t->work.entry);

        t->buffer->allow_user_free = 1;

        if (cmd == BR_TRANSACTION && !(t->flags & TF_ONE_WAY)) {

            ...

        } else {

            t->buffer->transaction = NULL;

            kfree(t); //通信完成则运行释放

        }

        break;

    }

done:

    *consumed = ptr - buffer;

    if (proc->requested_threads + proc->ready_threads == 0 &&

        proc->requested_threads_started < proc->max_threads &&

        (thread->looper & (BINDER_LOOPER_STATE_REGISTERED |

        BINDER_LOOPER_STATE_ENTERED))) {

        proc->requested_threads++;

        // 生成BR_SPAWN_LOOPER命令,用于创建新的线程

        put_user(BR_SPAWN_LOOPER, (uint32_t __user *)buffer);

    }

    return 0;

}

2.9 readStrongBinder

[-> Parcel.cpp]

sp<IBinder> Parcel::readStrongBinder() const

{

    sp<IBinder> val;

    //【见小节2.9.1】

    unflatten_binder(ProcessState::self(), *this, &val);

    return val;

}

2.9.1 unflatten_binder

[-> Parcel.cpp]

status_t unflatten_binder(const sp<ProcessState>& proc, const Parcel& in, sp<IBinder>* out) {

    const flat_binder_object* flat = in.readObject(false);

    if (flat) {

        switch (flat->type) {

            case BINDER_TYPE_BINDER:

                // 当请求服务的进程与服务属于同一进程

                *out = reinterpret_cast<IBinder*>(flat->cookie);

                return finish_unflatten_binder(NULL, *flat, in);

            case BINDER_TYPE_HANDLE:

                //请求服务的进程与服务属于不同进程【见2.9.2】

                *out = proc->getStrongProxyForHandle(flat->handle);

                //创建BpBinder对象

                return finish_unflatten_binder(

                    static_cast<BpBinder*>(out->get()), *flat, in);

        }

    }

    return BAD_TYPE;

}

2.9.2 getStrongProxyForHandle

[-> ProcessState.cpp]

sp<IBinder> ProcessState::getStrongProxyForHandle(int32_t handle)

{

    sp<IBinder> result;

    AutoMutex _l(mLock);

    //查找handle对应的资源项[2.9.3]

    handle_entry* e = lookupHandleLocked(handle);

    if (e != NULL) {

        IBinder* b = e->binder;

        if (b == NULL || !e->refs->attemptIncWeak(this)) {

            ...

            //当handle值所对应的IBinder不存在或弱引用无效时,则创建BpBinder对象

            b = new BpBinder(handle);

            e->binder = b;

            if (b) e->refs = b->getWeakRefs();

            result = b;

        } else {

            result.force_set(b);

            e->refs->decWeak(this);

        }

    }

    return result;

}

readStrongBinder的功能是flat_binder_object解析并创建BpBinder对象。

2.9.3 lookupHandleLocked

ProcessState::handle_entry* ProcessState::lookupHandleLocked(int32_t handle)

{

    const size_t N=mHandleToObject.size();

    //当handle大于mHandleToObject的长度时,进入该分支

    if (N <= (size_t)handle) {

        handle_entry e;

        e.binder = NULL;

        e.refs = NULL;

        //从mHandleToObject的第N个位置开始,插入(handle+1-N)个e到队列中

        status_t err = mHandleToObject.insertAt(e, N, handle+1-N);

        if (err < NO_ERROR) return NULL;

    }

    return &mHandleToObject.editItemAt(handle);

}

根据handle值来查找对应的handle_entry。

二. 总结

请求服务(getService)过程,就是向servicemanager进程查询指定服务,当执行binder_transaction()时,会区分请求服务所属进程情况。

    1. 当请求服务的进程与服务属于不同进程,则为请求服务所在进程创建binder_ref对象,指向服务进程中的binder_node;

        1. 最终readStrongBinder(),返回的是BpBinder对象;

    2. 当请求服务的进程与服务属于同一进程,则不再创建新对象,只是引用计数加1,并且修改type为BINDER_TYPE_BINDER或BINDER_TYPE_WEAK_BINDER。

         1. 最终readStrongBinder(),返回的是BBinder对象的真实子类;

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

推荐阅读更多精彩内容