騰訊面試官:Binder的系統服務是如何獲取的?

本文首發於微信公衆號「後廠村碼農」html

關聯繫列
Android AOSP基礎系列
Android系統啓動系列
應用進程啓動系列
Android深刻四大組件系列
Android深刻理解Context系列
Android深刻理解JNI系列
Android解析WindowManager
Android解析WMS系列
Android解析AMS系列
Android包管理機制系列
Android輸入系統系列前端

前言

在本系列的此前文章中,以MediaPlayerService爲例,講解了系統服務是如何註冊的(addService),既然有註冊那確定也要有獲取,本篇文章仍舊以MediaPlayerService爲例,來說解系統服務的獲取過程(getService)。文章會分爲兩個部分進行講解,分別是客戶端MediaPlayerService請求獲取服務和服務端ServiceManager處理請求,先來學習第一部分。 此前的系列文章見:liuwangshu.cn/tags/Binder…程序員

1.客戶端MediaPlayerService請求獲取服務

要想獲取MediaPlayerService,須要先調用getMediaPlayerService函數,以下所示。 frameworks/av/media/libmedia/IMediaDeathNotifier.cpp微信

IMediaDeathNotifier::getMediaPlayerService()
{
    ALOGV("getMediaPlayerService");
    Mutex::Autolock _l(sServiceLock);
    if (sMediaPlayerService == 0) {
        sp<IServiceManager> sm = defaultServiceManager();//1
        sp<IBinder> binder;
        do {
            binder = sm->getService(String16("media.player"));//2
            if (binder != 0) {//3
                break;
            }
            ALOGW("Media player service not published, waiting...");
            usleep(500000); //4
        } while (true);

        if (sDeathNotifier == NULL) {
            sDeathNotifier = new DeathNotifier();
        }
        binder->linkToDeath(sDeathNotifier);
        sMediaPlayerService = interface_cast<IMediaPlayerService>(binder);//5
    }
    ALOGE_IF(sMediaPlayerService == 0, "no media player service!?");
    return sMediaPlayerService;
}
複製代碼

註釋1處的defaultServiceManager返回的是BpServiceManager,註釋2處獲取名爲」media.player」的系統服務(MediaPlayerService),返回的值爲BpBinder。因爲這個時候MediaPlayerService可能尚未向ServiceManager註冊,那麼就不能知足註釋3的條件,在註釋4處休眠0.5s後繼續調用getService函數,直到獲取服務對應的爲止。 註釋5處的interface_cast函數用於將BpBinder轉換成BpMediaPlayerService,其原理就是經過BpBinder的handle來找到對應的服務,即BpMediaPlayerService。app

註釋2處的獲取服務是本文的重點,BpServiceManager的getService函數以下所示。 frameworks/native/libs/binder/IServiceManager.cpp::BpServiceManager函數

virtual sp<IBinder> getService(const String16& name) const
    {
       ...
        int n = 0;
        while (uptimeMillis() < timeout) {
            n++;
            if (isVendorService) {
                ALOGI("Waiting for vendor service %s...", String8(name).string());
                CallStack stack(LOG_TAG);
            } else if (n%10 == 0) {
                ALOGI("Waiting for service %s...", String8(name).string());
            }
            usleep(1000*sleepTime);

            sp<IBinder> svc = checkService(name);//1
            if (svc != NULL) return svc;
        }
        ALOGW("Service %s didn't start. Returning NULL", String8(name).string());
        return NULL;
    }
複製代碼

getService函數中主要作的事就是循環的查詢服務是否存在,若是不存在就繼續查詢,查詢服務用到了註釋1處的checkService函數,代碼以下所示。
frameworks/native/libs/binder/IServiceManager.cpp::BpServiceManageroop

virtual sp<IBinder> checkService( const String16& name) const
    {
        Parcel data, reply;//1
        data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());
        data.writeString16(name);//2
        remote()->transact(CHECK_SERVICE_TRANSACTION, data, &reply);//3
        return reply.readStrongBinder();
    }
複製代碼

註釋1處的data,看過上一篇文章的同窗應該很熟悉,它出如今BpServiceManager的addService函數中,data是一個數據包,後面會不斷的將數據寫入到data中。註釋2處將字符串"media.player"寫入到data中。 註釋3處的remote()指的是mRemote,也就是BpBinder,BpBinder的transact函數以下所示。學習

frameworks/native/libs/binder/BpBinder.cppui

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;
}
複製代碼

BpBinder將邏輯處理交給IPCThreadState,後面的調用鏈在=Android Binder原理(三)系統服務的註冊過程中講過,這裏再次簡單的過一遍,IPCThreadState::self()會建立建立IPCThreadState,IPCThreadState的transact函數以下所示。
frameworks/native/libs/binder/IPCThreadState.cppspa

status_t IPCThreadState::transact(int32_t handle,
                                  uint32_t code, const Parcel& data,
                                  Parcel* reply, uint32_t flags)
{
    status_t err;

    flags |= TF_ACCEPT_FDS;
    ...
    err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, NULL);//1

    if (err != NO_ERROR) {
        if (reply) reply->setError(err);
        return (mLastError = err);
    }

    if ((flags & TF_ONE_WAY) == 0) {
       ...
        if (reply) {
            err = waitForResponse(reply);//2
        } else {
            Parcel fakeReply;
            err = waitForResponse(&fakeReply);
        }
       ...
    } else {
       //不須要等待reply的分支
        err = waitForResponse(NULL, NULL);
    }

    return err;
}
複製代碼

調用BpBinder的transact函數實際上就是調用IPCThreadState的transact函數。註釋1處的writeTransactionData函數用於傳輸數據,其中第一個參數BC_TRANSACTION表明向Binder驅動發送命令協議。 註釋1處的writeTransactionData用於準備發送的數據,其內部會將BC_TRANSACTION和binder_transaction_data結構體寫入到mOut中。 接着查看waitForResponse函數作了什麼,代碼以下所示。
frameworks/native/libs/binder/IPCThreadState.cpp

status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult)
{
    uint32_t cmd;
    int32_t err;
    while (1) {
        if ((err=talkWithDriver()) < NO_ERROR) break;//1
        err = mIn.errorCheck();
        if (err < NO_ERROR) break;
        if (mIn.dataAvail() == 0) continue;
        cmd = (uint32_t)mIn.readInt32();
        IF_LOG_COMMANDS() {
            alog << "Processing waitForResponse Command: "
                << getReturnString(cmd) << endl;
        }
        switch (cmd) {
        case BR_TRANSACTION_COMPLETE:
            if (!reply && !acquireResult) goto finish;
            break;
       ...
        default:
            //處理各類命令協議
            err = executeCommand(cmd);
            if (err != NO_ERROR) goto finish;
            break;
        }
}
finish:
    ...
    return err;
}
複製代碼

註釋1處的talkWithDriver函數的內部經過ioctl與Binder驅動進行通訊,代碼以下所示。 frameworks/native/libs/binder/IPCThreadState.cpp

status_t IPCThreadState::talkWithDriver(bool doReceive)
{
    if (mProcess->mDriverFD <= 0) {
        return -EBADF;
    }
    //和Binder驅動通訊的結構體
    binder_write_read bwr; //1
    //mIn是否有可讀的數據,接收的數據存儲在mIn
    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();//2
    //這時doReceive的值爲true
    if (doReceive && needRead) {
        bwr.read_size = mIn.dataCapacity();
        bwr.read_buffer = (uintptr_t)mIn.data();//3
    } 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 {
        IF_LOG_COMMANDS() {
            alog << "About to read/write, write size = " << mOut.dataSize() << endl;
        }
#if defined(__ANDROID__)
        if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0)//4
            err = NO_ERROR;
        else
            err = -errno;
#else
        err = INVALID_OPERATION;
#endif
     ...
    } while (err == -EINTR);
    ...
    return err;
}
複製代碼

註釋1處的 binder_write_read是和Binder驅動通訊的結構體,在註釋2和3處將mOut、mIn賦值給binder_write_read的相應字段,最終經過註釋4處的ioctl函數和Binder驅動進行通訊。這一過程的時序圖以下所示。

MUr7w9.md.png

這時咱們須要再次查看Android Binder原理(三)系統服務的註冊過程這篇文章第2小節給出的圖。

Ka0Dx0.png

從這張簡化的流程圖能夠看出,咱們當前分析的是客戶端進程的流程,當MediaPlayerService向Binder驅動發送BC_TRANSACTION命令後,Binder驅動會向ServiceManager發送BR_TRANSACTION命令,接下來咱們來查看服務端ServiceManager是如何處理獲取服務這一請求的。

2.服務端ServiceManager處理請求

說到服務端ServiceManager處理請求,不得不說到ServiceManager的啓動過程,具體的請看Android Binder原理(四)ServiceManager的啓動過程 這篇文章。 這裏簡單回顧servicemanager的入口函數,以下所示。

frameworks/native/cmds/servicemanager/service_manager.c

int main(int argc, char** argv) {
   ...
    bs = binder_open(driver, 128*1024);
    ...
    if (binder_become_context_manager(bs)) {
        ALOGE("cannot become context manager (%s)\n", strerror(errno));
        return -1;
    }
    ...
    if (getcon(&service_manager_context) != 0) {
        ALOGE("SELinux: Failed to acquire service_manager context. Aborting.\n");
        abort();
    }
    binder_loop(bs, svcmgr_handler);//1
    return 0;
}
複製代碼

main函數主要作了三件事,其中最後一件事就是調用binder_loop函數,這裏須要注意,它的第二個參數爲svcmgr_handler,後面會再次提到svcmgr_handler。 binder_loop函數以下所示。 frameworks/native/cmds/servicemanager/binder.c

void binder_loop(struct binder_state *bs, binder_handler func) {
...
    for (;;) {
        bwr.read_size = sizeof(readbuf);
        bwr.read_consumed = 0;
        bwr.read_buffer = (uintptr_t) readbuf;

        res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);

        if (res < 0) {
            ALOGE("binder_loop: ioctl failed (%s)\n", strerror(errno));
            break;
        }
        res = binder_parse(bs, 0, (uintptr_t) readbuf, bwr.read_consumed, func);
        if (res == 0) {
            ALOGE("binder_loop: unexpected reply?!\n");
            break;
        }
        if (res < 0) {
            ALOGE("binder_loop: io error %d %s\n", res, strerror(errno));
            break;
        }
    }
}
複製代碼

在無限循環中不斷的調用ioctl函數,它不斷的使用BINDER_WRITE_READ指令查詢Binder驅動中是否有新的請求,若是有就交給binder_parse函數處理。若是沒有,當前線程就會在Binder驅動中睡眠,等待新的進程間通訊請求。 binder_parse函數以下所示。
frameworks/native/cmds/servicemanager/binder.c

int binder_parse(struct binder_state *bs, struct binder_io *bio, uintptr_t ptr, size_t size, binder_handler func) {
    int r = 1;
    uintptr_t end = ptr + (uintptr_t) size;

    while (ptr < end) {
        uint32_t cmd = *(uint32_t *) ptr;
        ptr += sizeof(uint32_t);
#if TRACE
        fprintf(stderr,"%s:\n", cmd_name(cmd));
#endif
        switch(cmd) {
        ...
        case BR_TRANSACTION: {
            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;
                int res;

                bio_init(&reply, rdata, sizeof(rdata), 4);
                bio_init_from_txn(&msg, txn);
                res = func(bs, txn, &msg, &reply);//1
                if (txn->flags & TF_ONE_WAY) {
                    binder_free_buffer(bs, txn->data.ptr.buffer);
                } else {
                    binder_send_reply(bs, &reply, txn->data.ptr.buffer, res);
                }
            }
            ptr += sizeof(*txn);
            break;
        }
        ...
    }

    return r;
}
複製代碼

這裏截取了BR_TRANSACTION命令的處理部分,註釋1出的func經過一路傳遞指向的是svcmgr_handler,svcmgr_handler函數以下所示。
frameworks/native/cmds/servicemanager/service_manager.c

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_GET_SERVICE:
    case SVC_MGR_CHECK_SERVICE:
        s = bio_get_string16(msg, &len);
        if (s == NULL) {
            return -1;
        }
        handle = do_find_service(s, len, txn->sender_euid, txn->sender_pid);
        if (!handle)
            break;
        bio_put_ref(reply, handle);
        return 0;

   ...
    default:
        ALOGE("unknown code %d\n", txn->code);
        return -1;
    }

    bio_put_uint32(reply, 0);
    return 0;
}
複製代碼

當要獲取服務時,會調用do_find_service函數,代碼以下所示。 frameworks/native/cmds/servicemanager/service_manager.c

uint32_t do_find_service(const uint16_t *s, size_t len, uid_t uid, pid_t spid)
{
    struct svcinfo *si = find_svc(s, len);//1

    if (!si || !si->handle) {
        return 0;
    }

    if (!si->allow_isolated) {
        uid_t appid = uid % AID_USER;
        if (appid >= AID_ISOLATED_START && appid <= AID_ISOLATED_END) {
            return 0;
        }
    }
    if (!svc_can_find(s, len, spid, uid)) {
        return 0;
    }

    return si->handle;
}
複製代碼

註釋1處的find_svc函數用於查詢服務,返回的svcinfo是一個結構體,其內部包含了服務的handle值,最終會返回服務的handle值。接着來看find_svc函數:
frameworks/native/cmds/servicemanager/service_manager.c

struct svcinfo *find_svc(const uint16_t *s16, size_t len) {
    struct svcinfo *si;

    for (si = svclist; si; si = si->next) {
        if ((len == si->len) &&
            !memcmp(s16, si->name, len * sizeof(uint16_t))) {
            return si;
        }
    }
    return NULL;
}
複製代碼

系統服務的註冊流程中,在Kernel Binder中會調用do_add_service函數,其內部會將包含服務名和handle值的svcinfo保存到svclist列表中。一樣的,在獲取服務的流程中,find_svc函數中會遍歷svclist列表,根據服務名查找對應服務是否已經註冊,若是已經註冊就會返回對應的svcinfo,若是沒有註冊就返回NULL。

總結

這篇文章將系統服務的獲取過程分爲兩個部分,代碼涉及到了Native Binder和Kernel Binder。在下一篇文章中會繼續學習Java Binder相關的內容。

更多的內容請關注個人獨立博客的知識體系:
liuwangshu.cn/system/


這裏不只分享大前端、Android、Java等技術,還有程序員成長類文章。

相關文章
相關標籤/搜索