编程知识 cdmana.com

Android binder principle (5) system service acquisition process

  • Binder principle
  • Android Frame layer

This article was first published on WeChat public 「 Post Factory Technical Officer 」


Preface

In previous articles in this series , With MediaPlayerService For example , Explains how system services are registered (addService), Since there is registration, there must be acquisition , This article still uses MediaPlayerService For example , To explain the acquisition process of system services (getService). The article will be divided into two parts to explain , They are clients MediaPlayerService Request to get service and server ServiceManager Processing requests , Let's learn the first part .

1. client MediaPlayerService Request for service

To get MediaPlayerService, You need to call getMediaPlayerService function , As shown below .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;}

notes 1 Situated defaultServiceManager The return is BpServiceManager, notes 2 It was named ”media.player” System services (MediaPlayerService), The returned value is BpBinder. Because of this time MediaPlayerService Maybe not to ServiceManager register , Then it can't satisfy the comment 3 Conditions , In the comments 4 Sleep at 0.5s Then continue to call getService function , Until the corresponding service is obtained . notes 5 Situated interface_cast The function is used to BpBinder convert to BpMediaPlayerService, The principle is through BpBinder Of handle To find the corresponding service , namely BpMediaPlayerService.

notes 2 The key point of this paper is to obtain services at ,BpServiceManager Of getService The function is shown below .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 The main thing to do in the function is to query whether the circular query service exists , If it doesn't exist, continue to query , The query service uses comments 1 Situated checkService function , The code is as follows . frameworks/native/libs/binder/IServiceManager.cpp::BpServiceManager

    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();    }

notes 1 Situated data, The students who read the last article should be familiar with , It appears in BpServiceManager Of addService Function ,data It's a packet , The data will be written to data in . notes 2 Place string "media.player" Write to data in . notes 3 Situated remote() refer to mRemote, That is to say BpBinder,BpBinder Of transact The function is shown below .

frameworks/native/libs/binder/BpBinder.cpp

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 Give logic to IPCThreadState, The subsequent call chain is Android Binder principle ( 3、 ... and ) Registration process of system services In the said , Let's go through it again ,IPCThreadState::self() Will create create IPCThreadState,IPCThreadState Of transact The function is shown below .frameworks/native/libs/binder/IPCThreadState.cpp

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 {       // No need to wait reply The branch of         err = waitForResponse(NULL, NULL);    }    return err;}

call BpBinder Of transact A function is actually a call to IPCThreadState Of transact function . notes 1 Situated writeTransactionData Function to transfer data , The first parameter BC_TRANSACTION Represents to Binder Drive send command protocol . notes 1 Situated writeTransactionData Data to be sent , Inside it will be BC_TRANSACTION and binder_transaction_data Structure write to mOut in . Then check waitForResponse What does the function do , The code is as follows .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:            // Deal with all kinds of command protocols             err = executeCommand(cmd);            if (err != NO_ERROR) goto finish;            break;        }}finish:    ...    return err;}

notes 1 Situated talkWithDriver The function passes through ioctl And Binder Drive to communicate , The code is as follows .frameworks/native/libs/binder/IPCThreadState.cpp

status_t IPCThreadState::talkWithDriver(bool doReceive){    if (mProcess->mDriverFD <= 0) {        return -EBADF;    }    // and Binder The structure that drives communication     binder_write_read bwr; //1    //mIn Is there any readable data , The received data is stored in 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    // At this time doReceive The value of is 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;}

notes 1 Situated binder_write_read Is and Binder The structure that drives communication , In the comments 2 and 3 General mOut、mIn Assign a value to binder_write_read The corresponding fields of , Finally, through notes 4 Situated ioctl Functions and Binder Drive to communicate . The sequence diagram of this process is as follows .MUr7w9.md.png

Now we need to check again Android Binder principle ( 3、 ... and ) Registration process of system services This article is No 2 The graph given in Section .Ka0Dx0.png

As can be seen from this simplified flow chart , We are currently analyzing the process of the client process , When MediaPlayerService towards Binder Drive send BC_TRANSACTION After the command ,Binder The drive will go to ServiceManager send out BR_TRANSACTION command , Next let's look at the server ServiceManager How to deal with the request of getting service .

2. Server side ServiceManager Processing requests

When it comes to servers ServiceManager Processing requests , I have to say ServiceManager Start up process of , For details, please see Android Binder principle ( Four )ServiceManager Start up process of This article . Here is a brief review servicemanager The entry function of , As shown below .

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 Function does three main things , The last thing is to call binder_loop function , Here we need to pay attention to , Its second parameter is svcmgr_handler, It will be mentioned again later svcmgr_handler.binder_loop The function is shown below .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;        }    }}

Call continuously in infinite loop ioctl function , It's constantly used BINDER_WRITE_READ Command query Binder Is there a new request in the driver , If there is one, give it to binder_parse Function processing . without , The current thread will be in Binder Driving sleep , Waiting for new interprocess communication requests .binder_parse The function is shown below .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;}

Here we intercept BR_TRANSACTION The processing part of the order , notes 1 Out of func All the way to the point is svcmgr_handler,svcmgr_handler The function is shown below .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;}

When it comes to getting Services , Would call do_find_service function , The code is as follows .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;}

notes 1 Situated find_svc Function to query services , Back to svcinfo It's a structure , It contains the handle value , It will eventually return to the service handle value . Then look find_svc function :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;}

In the registration process of system services , stay Kernel Binder Will call do_add_service function , It will contain the service name and handle It's worth it svcinfo Save to svclist In the list . alike , In the process of getting Services ,find_svc Function will traverse svclist list , Find whether the corresponding service has been registered according to the service name , If you have already registered, you will return the corresponding svcinfo, If you don't register, return NULL.

summary

This article divides the acquisition process of system services into two parts , The code involves Native Binder and Kernel Binder. I will continue to learn in the next article Java Binder Related content .

 Preview

版权声明
本文为[Liu Wangshu]所创,转载请带上原文链接,感谢
https://cdmana.com/2021/04/20210421235129517T.html

Scroll to Top