Android binder mechanism (ServiceManager)

Source: Internet
Author: User
Tags goto mremote

As an IPC communication mechanism, the binder mechanism plays a very important role in the Android system, so I also spent some time to study it, according to my understanding, I will from 4 aspects of the binder, if there is a wrong place, but also hope that we can advise. The following examples will be in mediaserver terms.

First, ServiceManager

ServiceManager in the binder system quite the same as the Dns,server will register here first, and then the client will query the service here to obtain the path to establish communication with the server process where the service resides.

In the communication with ServiceManager, the book is based on AddService, for example, I will take getservice as an example. directly on the code.

/*static*/ Const Sp<imediaplayerservice>&imediadeathnotifier::getmediaplayerservice () {ALOGV ("    Getmediaplayerservice ");    Mutex::autolock _l (Sservicelock);        if (Smediaplayerservice = = 0) {sp<iservicemanager> sm = Defaultservicemanager ();        Sp<ibinder> Binder;            do {binder = Sm->getservice (String16 ("Media.player"));            if (binder! = 0) {break;            } ALOGW ("Media player service not published, waiting ..."); Usleep (500000);        0.5 S} while (true);        if (Sdeathnotifier = = NULL) {sdeathnotifier = new deathnotifier ();        } binder->linktodeath (Sdeathnotifier);    Smediaplayerservice = interface_cast<imediaplayerservice> (binder);    } aloge_if (Smediaplayerservice = = 0, "No Media Player service!?"); return smediaplayerservice;} 

First we look at Defaultservicemanager (), which is a singleton pattern, implemented as follows:

Sp<iservicemanager> Defaultservicemanager () {    if (gdefaultservicemanager! = NULL) return Gdefaultservicemanager;        {        Automutex _l (gdefaultservicemanagerlock);        while (Gdefaultservicemanager = = NULL) {            Gdefaultservicemanager = interface_cast<iservicemanager> (                Processstate::self ()->getcontextobject (NULL));            if (Gdefaultservicemanager = = NULL)                sleep (1);}    }        return Gdefaultservicemanager;}

where Processstate::self ()->getcontextobject (NULL) returns a Bpbinder (0), then there are:

Gdefaultservicemanager = interface_cast<iservicemanager> (bpbinder (0));

According to the definition of interface_cast, it becomes:

Gdefaultservicemanager =bpservicemanager (bpbinder (0));

Next look at the following statement:

Binder = Sm->getservice (String16 ("Media.player"));

From the previous analysis, SM is an example of Bpservicemanager, We directly to IserviceManager.cpp inside find Bpservicemanager implementation, and find GetService method, its core implementation called the Checkservice method, implemented as follows:


Virtual sp<ibinder> checkservice (const string16& name) const{     Parcel data, reply;     Data.writeinterfacetoken (Iservicemanager::getinterfacedescriptor ());     DATA.WRITESTRING16 (name);     Remote ()->transact (check_service_transaction, data, &reply);     return Reply.readstrongbinder ();}

Here, we have a question: what the remote () returns.

First look at the definition of Bpservicemanager:

Classbpservicemanager:public bpinterface<iservicemanager>

Template<typenameinterface>

Classbpinterface:public INTERFACE, Public bprefbase

Replace the template and turn it into

Classbpinterface:public Iservicemanager, Public bprefbase

OK, the definition of remote () was found in Bprefbase:

Inline ibinder* remote () {return mremote;}

When did the Mremote be assigned? Let's look at the Bpservicemanager constructor:

Bpservicemanager (const sp<ibinder>& impl)        : bpinterface<iservicemanager> (impl) {}
Inline bpinterface<interface>::bpinterface (const sp<ibinder>& remote)    : bprefbase (remote) {}
Bprefbase::bprefbase (const sp<ibinder>& o)    : Mremote (O.get ()), Mrefs (NULL), mstate (0) {    Extendobjectlifetime (object_lifetime_weak);    if (mremote) {        Mremote->incstrong (this);           Removed on first Incstrong ().        Mrefs = Mremote->createweak (this);  Held for our entire lifetime.    }}
At this point, we can tell that remote () returned the previously created Bpbinder object Bpbinder (0). Then remote ()->transact () is actually called the Transact method of Bpbinder. Let's jump inside the Bpbinder and take a look at the implementation of Transact:
status_t bpbinder::transact (    uint32_t Code, const parcel& data, parcel* reply, uint32_t flags) {    //Once a bind Er has died, it'll never come back to life.    if (malive) {        status_t status = Ipcthreadstate::self ()->transact (            mhandle, code, data, reply, flags);        if (status = = dead_object) malive = 0;        return status;    }    return dead_object;}
It gave the work to Ipcthreadstate to do. What is Ipcthreadstate? It's the binder that really works in the transfer data, each thread has a ipcthreadstate, each ipcthreadstate has a min, a mout, where min is used to receive data from the binder device, The mout is used to store data destined for binder devices. OK, we continue to jump into the ipcthreadstate.

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;......err = Writetransactiondata (bc_transaction, flags, handle, code, data, NULL); err = Waitforrespo NSE (reply); ... return err;} status_t ipcthreadstate::writetransactiondata (int32_t cmd, uint32_t binderflags, int32_t handle, uint32_t code, const P    arcel& data, status_t* statusbuffer) {Binder_transaction_data tr;    Tr.target.handle = handle;    Tr.code = code;    Tr.flags = Binderflags;    Tr.cookie = 0;    tr.sender_pid = 0;        Tr.sender_euid = 0;    Const status_t Err = Data.errorcheck ();        if (err = = no_error) {tr.data_size = Data.ipcdatasize ();        Tr.data.ptr.buffer = Data.ipcdata ();        Tr.offsets_size = Data.ipcobjectscount () *sizeof (size_t);    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 = Statusbuffer;        tr.offsets_size = 0;    Tr.data.ptr.offsets = NULL;    } else {return (Mlasterror = err);    } mout.writeint32 (cmd);        Mout.write (&tr, sizeof (TR)); return no_error;}
Writetransactiondata just writes the data to Mout and waits to be sent to binder, and then waitForResponse.

status_t Ipcthreadstate::waitforresponse (Parcel *reply, status_t *acquireresult) {int32_t cmd;    int32_t err;        while (1) {if ((Err=talkwithdriver ()) < no_error) break;        Err = Min.errorcheck ();        if (Err < no_error) break;                if (min.dataavail () = = 0) continue;                cmd = Min.readint32 ();                Switch (cmd) {... case br_reply: {binder_transaction_data tr;                Err = Min.read (&tr, sizeof (TR));                if (err! = no_error) goto finish; if (reply) {if ((Tr.flags & tf_status_code) = = 0) {Reply->ipcsetdataref                            Erence (Reinterpret_cast<const uint8_t*> (Tr.data.ptr.buffer),                            Tr.data_size, Reinterpret_cast<const size_t*> (tr.data.ptr.offsets), Tr.offsets_size/sizeof (size_t), FREebuffer, this);                        } else {err = *static_cast<const status_t*> (tr.data.ptr.buffer);                            FreeBuffer (NULL, Reinterpret_cast<const uint8_t*> (Tr.data.ptr.buffer),                            Tr.data_size, Reinterpret_cast<const size_t*> (tr.data.ptr.offsets),                    Tr.offsets_size/sizeof (size_t), this); }} else {FreeBuffer (NULL, Reinterpret_cast<const uint8_t*&gt ;(tr.data.ptr.buffer), Tr.data_size, Reinterpret_cast<const size_t*> (t                    R.data.ptr.offsets), tr.offsets_size/sizeof (size_t), this);                Continue        }} goto finish;            ... default:err = ExecuteCommand (cmd);           if (err! = no_error) goto finish; Break        }}finish:if (Err! = No_error) {if (acquireresult) *acquireresult = err;        if (reply) reply->seterror (err);    Mlasterror = err; } return err;}
Look, it is constantly talkwithdriver, look at the literal meaning, should be in this function inside operation of Binder Drive, let us a probe.

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 (); We don ' t want to write anything if we is still reading//from data left in the input buffer and the caller//h    As requested to read the next data. Const size_t Outavail = (!doreceive | | needread)?        Mout.datasize (): 0;    Bwr.write_size = Outavail;    Bwr.write_buffer = (long unsigned int) mout.data ();    This is the what we ll read.        if (doreceive && needread) {bwr.read_size = Min.datacapacity ();    Bwr.read_buffer = (long unsigned int) min.data ();        } else {bwr.read_size = 0;    Bwr.read_buffer = 0;    }//Return immediately if there is nothing.    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 (IOCTL (MPROCESS-&GT;MDRIVERFD, Binder_write_read, &AMP;BWR) >= 0) err = no_error;        else err =-errno;        if (mprocess->mdriverfd <= 0) {err =-EBADF;    }} while (err = =-EINTR); if (Err >= no_error) {if (bwr.write_consumed > 0) {if (Bwr.write_consumed < (ssize_t) MOut.dat            Asize ()) mout.remove (0, bwr.write_consumed);        else Mout.setdatasize (0);            } if (bwr.read_consumed > 0) {min.setdatasize (bwr.read_consumed);        Min.setdataposition (0);    } return no_error; } return err;}

In Talkwithdriver, Ipcthreadstate constantly writes and reads the binder driver, so first the data prepared in Mout in Writetransactiondata is written to the binder drive, after which began to wait for new data to appear in the binder, who would write the data in it? It should be the target process, and let's take a look at how this part is done.

In the binder IPC communication process, inter-process communication must first send the BC_XXX command to the binder driver, and then the binder driver is slightly processed by the corresponding br_xxx to pass the command to the target process.

If there is a return value, the process first sends the returned results to the binder driver in the form of bc_reply, and then forwards the br_reply command via the driver.



After Binder1 writes data to the driver, the binder driver first determines whether the current command receiver is a service manager or a normal server side, judging by the Tr->target.handle.if (tr-> Target.handle = = 0) indicates that the command is sending a special node, i.e. service Manager, and else for the general case, we need to determine if there are any corresponding node references in the binder driver, Normally it should be able to find the handle corresponding to the Binder node reference. With a node reference, we can navigate to the Binder node (entity node) that handles the command.

In the above writetransactiondata, Tr->target.handle = = 0, the Service Manager process receives the Br_transaction command, and service Manager finishes processing the command, The result is written back to the binder driver via the bc_reply message, so that the above waitforresponse (which exists in the client process) can be br_reply response to complete an interaction.

Next, let's jump to service_manager.c and see how the service Manager process handles the br_transaction command.

int main (int argc, char **argv) {struct binder_state *bs;    void *svcmgr = Binder_service_manager;    BS = Binder_open (128*1024);        if (Binder_become_context_manager (BS)) {Aloge ("Cannot become context Manager (%s) \ n", Strerror (errno));    return-1;    } svcmgr_handle = Svcmgr;    Binder_loop (BS, Svcmgr_handler); return 0;}    void Binder_loop (struct binder_state *bs, binder_handler func) {int res;    struct Binder_write_read BWR;    unsigned readbuf[32];    bwr.write_size = 0;    bwr.write_consumed = 0;        Bwr.write_buffer = 0;    Readbuf[0] = Bc_enter_looper;    Binder_write (BS, readbuf, sizeof (unsigned)); for (;;)        {bwr.read_size = sizeof (READBUF);        bwr.read_consumed = 0;        Bwr.read_buffer = (unsigned) readbuf;        res = IOCTL (BS-&GT;FD, Binder_write_read, &AMP;BWR);            if (Res < 0) {Aloge ("Binder_loop:ioctl failed (%s) \ n", Strerror (errno));        Break } res = Binder_parse (BS, 0, 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 }    }}

In the main loop, the Service manager process continues to manipulate the binder driver, and after reading the data, it calls Binder_parse to process it.

int binder_parse (struct binder_state *bs, struct binder_io *bio, uint32_t *ptr, uint32_t size, Binder_hand    Ler func) {int r = 1;    uint32_t *end = ptr + (SIZE/4);        while (PTR < end) {uint32_t cmd = *ptr++; #if TRACE fprintf (stderr, "%s:\n", Cmd_name (cmd)); #endif            Switch (cmd) {... case br_transaction: {struct BINDER_TXN *txn = (void *) ptr;                if ((end-ptr) * sizeof (uint32_t) < sizeof (struct BINDER_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);//Writes the result back to Binder Drive binder_send_reply (BS, &reply, Txn->data, RES);            } ptr + = sizeof (*TXN)/sizeof (uint32_t);        Break            } ... default:aloge ("Parse:oops%d\n", cmd);        return-1; }} return R;}

Func Here is the Svcmgr,svcmgr function in main to handle a variety of commands, including Add_service and Get_service, after processing, call binder_send_reply will reply write back to the binder driver , which returns to its client process. Let's take a look at the implementation of Svcmgr.

int Svcmgr_handler (struct binder_state *bs, struct binder_txn *txn, struct binder_io *    msg, struct binder_io *reply) {struct Svcinfo *si;    uint16_t *s;    unsigned len;    void *ptr;    uint32_t Strict_policy; int allow_isolated;//alogi ("target=%p code=%d pid=%d uid=%d\n",//Txn->target, Txn->code, Txn->sender    _pid, Txn->sender_euid);    if (txn->target! = svcmgr_handle) return-1; Equivalent to Parcel::enforceinterface (), reading the RPC/headers with the strict mode policy mask and the Interfa    Ce 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 (len! = (sizeof (svcmgr_id)/2)) | |        MEMCMP (svcmgr_id, S, sizeof (SVCMGR_ID))) {fprintf (stderr, "Invalid ID%s\n", str8 (s));    return-1; } switch (Txn->coDE) {case Svc_mgr_get_service:case svc_mgr_check_service:s = Bio_get_string16 (msg, &len);        ptr = Do_find_service (BS, S, Len, Txn->sender_euid);        if (!ptr) break;        Bio_put_ref (reply, PTR);    return 0;        Case svc_mgr_add_service:s = Bio_get_string16 (msg, &len);        ptr = Bio_get_ref (msg); allow_isolated = Bio_get_uint32 (msg)?        1:0;        if (Do_add_service (BS, S, Len, PTR, Txn->sender_euid, allow_isolated)) return-1;    Break        Case svc_mgr_list_services: {unsigned n = bio_get_uint32 (msg);        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;}

Yes, we saw the Svc_mgr_check_service,svc_mgr_add_service and so on, and the orders were finally handled properly here.

OK, here, the entire process of getting the service is done.
Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.