当前位置:网站首页>Tencent Interviewer: How did binder get its system services?

Tencent Interviewer: How did binder get its system services?

2022-06-26 04:02:00 Liuwangshu

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

Related series
Android AOSP Foundation Series
Android System startup series
Application process startup series
Android Go deep into the four major component series
Android In depth understanding of Context series
Android In depth understanding of JNI series
Android analysis WindowManager
Android analysis WMS series
Android analysis AMS series
Android Package management mechanism series
Android Input system series

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 .
For previous articles, see :https://liuwangshu.cn/tags/Binder%E5%8E%9F%E7%90%86/

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 .

For more information, please pay attention to the knowledge system of my independent blog :
http://liuwangshu.cn/system/


Share cutting edge technologies 、 Technical information 、 Industry Secrets , technical management , help 10 ten thousand + Programmers grow into technologists and architects .
原网站

版权声明
本文为[Liuwangshu]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/02/202202180544386920.html