Android binder Mechanic (3)-How to register a service with the System

Source: Internet
Author: User

In this article, we will thoroughly analyze how to register a service with the system.

 

In the example in the first article, exampleservice registers a service with the system using the following statement.

// File: exampleservice. cpp <br/> int r = defaservicservicemanager ()-> addservice (string16 ("Byn. Example"), new exampleservice ());

In the previous article, we know that the reference of the servicemanager proxy object of the current process can be obtained by calling the defaultservicemanager () global function. Before in-depth analysis of the addservice () method, let's take a look at how servicemanager is started. First, let's take a look at its source code:

// File: Frameworks/base/cmds/servicemanager/service_manager.c <br/> int main (INT argc, char ** argv) <br/>{ <br/> struct binder_state * BS; <br/> void * svcmgr = binder_service_manager; </P> <p> BS = binder_open (128*1024 ); </P> <p> If (binder_become_context_manager (BS) {<br/> LogE ("cannot become context Manager (% s)/n", strerror (errno )); <br/> return-1; <br/>}</P> <p> svcmgr_handle = svcmgr; <br/> binder_loop (BS, svcmgr_handler ); <br/> return 0; <br/>}< br/>

Servicemanager is a system process and a core Android program. From its main function, it first calls the binder_open () function to open the binder device (/dev/binder), and then calls the binder_become_context_manager () function, change yourself to the "Administrator" of the system service ". Let's take a look at the source code of the binder_become_context_manager () function:

// File: Frameworks/base/cmds/servicemanager/service_manager.c <br/> int binder_become_context_manager (struct binder_state * BS) <br/>{< br/> return IOCTL (BS-> FD, binder_set_context_mgr, 0); <br/>}

You can see that the ioctl system is called with binder_set_context_mgr as the parameter, and you can set yourself as the "Administrator" of the system service, that is, servicemanager.

Finally, servicemanager calls binder_loop to enter the loop state, and provides a callback function svcmgr_handler (), waiting for the user's request.

 

Now let's analyze the process of registering a service with the system. First, call the defaservicservicemanager () global function to obtain a reference to the servicemanager proxy object. Therefore, the addservice method called here is the proxy object method, rather than the actual servicemanager addservice method. Let's take a look at its source code:

// File: Frameworks/base/libs/binder/iservicemanager. CPP <br/> class bpservicemanager: Public bpinterface <iservicemanager> <br/> {<br/> Public: <br/> ...... <br/> virtual status_t addservice (const string16 & name, const sp <ibinder> & Service) <br/>{< br/> parcel data, reply; <br/> data. writeinterfacetoken (iservicemanager: getinterfacedescriptor (); <br/> data. writestring16 (name); <br/> data. writestro Ngbinder (service); <br/> status_t err = remote ()-> transact (add_service_transaction, Data, & reply); <br/> return err = no_error? Reply. readint32 (): Err; <br/>}< br/> ...... <br/>}

Here is the transact () method called by the command code add_service_transaction. Because the servicemanager proxy object bpservicemanager inherits from bpbinder, bpbinder: transact () is called here. Let's take a look at its source code:

// File: Frameworks/base/libs/binder/bpbinder. CPP <br/> status_t bpbinder: transact (<br/> uint32_t code, const parcel & Data, parcel * reply, uint32_t flags) <br/>{< br/> // once a binder has died, it will never come back to life. <br/> If (malive) {<br/> status_t status = ipcthreadstate: Self ()-> transact (<br/> mhandle, code, Data, reply, flags); <br/> If (status = dead_object) malive = 0; <br/> return status; <br/>}</P> <p> return dead_object; <br/>}

We can see that this method continues to pass requests by calling the method of the same name in the ipcthreadstate class. Here we need to explain the role of the ipcthreadstate class.

Each process has only one ipcthreadstate object. It is used to maintain all I/O operations on the binder device (/dev/binder) in the current process, that is to say, if a process wants to communicate with another process through the Binder Mechanism, it will eventually be completed through the ipcthreadstate object. With the ipcthreadstate encapsulation, the application does not need to directly deal with the binder device through IOCTL. Let's take a look at the source code of the ipcthreadstate class's transact method:

// File: Frameworks/base/libs/binder/ipcthreadstate. CPP <br/> status_t ipcthreadstate: transact (int32_t handle, <br/> uint32_t code, const parcel & Data, <br/> parcel * reply, uint32_t flags) <br/> {<br/> status_t err = data. errorcheck (); </P> <p> flags | = tf_accept_fds; </P> <p> if_log_transactions () {<br/> textoutput :: bundle _ B (alog); <br/> alog <"bc_transaction thr" <(void *) pthread_self () <"/ Hand "<br/> <pandle <"/code "<typecode <": "<br/> <indent <data <dedent <Endl; <br/>}</P> <p> If (ERR = no_error) {<br/> log_oneway (">>> send from PID % d uid % d % s", getpid (), getuid (), <br/> (flags & tf_one_way) = 0? "Read reply": "One Way"); <br/> err = writetransactiondata (bc_transaction, flags, handle, code, Data, null ); <br/>}</P> <p> If (Err! = No_error) {<br/> If (reply) reply-> seterror (ERR); <br/> return (mlasterror = ERR ); <br/>}</P> <p> If (flags & tf_one_way) = 0) {<br/> If (reply) {<br/> err = waitforresponse (reply); <br/>}else {<br/> parcel fakereply; <br/> err = waitforresponse (& fakereply ); <br/>}</P> <p> if_log_transactions () {<br/> textoutput: bundle _ B (alog ); <br/> alog <"br_reply thr" <(void *) pthread_self () <"/hand" <br/> <pandle <":"; <br/> If (reply) alog <indent <* reply <dedent <Endl; <br/> else alog <"(None requested) "<Endl; <br/>}< br/>} else {<br/> err = waitforresponse (null, null ); <br/>}</P> <p> return err; <br/>}

The writetransactiondata () method is called to complete the request. Let's take a look at the definition of the writetransactiondata () method:

// File: Frameworks/base/libs/binder/ipcthreadstate. CPP <br/> status_t ipcthreadstate: Success (int32_t cmd, uint32_t binderflags, <br/> int32_t handle, uint32_t code, const parcel & Data, status_t * statusbuffer) <br/>{< br/> binder_transaction_data TR; </P> <p> tr.tar get. handle = handle; <br/> tr. code = Code; <br/> tr. flags = binderflags; </P> <p> const status_t err = data. errorcheck (); <br/> If (ERR = no_error) {<br/> tr. data_size = data. ipcdatasize (); <br/> tr. data. PTR. buffer = data. ipcdata (); <br/> tr. offsets_size = data. ipcobjectscount () * sizeof (size_t); <br/> tr. data. PTR. offsets = data. ipcobjects (); <br/>} else if (statusbuffer) {<br/> tr. flags | = tf_status_code; <br/> * statusbuffer = err; <br/> tr. data_size = sizeof (status_t); <br/> tr. data. PTR. buffer = statusbuffer; <br/> tr. offsets_size = 0; <br/> tr. data. PTR. offsets = NULL; <br/>}else {<br/> return (mlasterror = ERR); <br/>}</P> <p> MOUT. writeint32 (CMD); <br/> MOUT. write (& TR, sizeof (TR); </P> <p> return no_error; <br/>}

The command and data are encapsulated here and then written to the queue to be sent (MOUT. writeint32 (CMD), MOUT. Write (& TR, sizeof (TR ))). Return to the transact () method of the ipcthreadstate class. After the writetransactiondata () method is returned, the request is sent by calling the waitforresponse () method and waiting for the returned result. The waitforresponse () method calls the ipcthreadstate: talkwithdriver () method to send the request and retrieve the returned value. Let's take a look at the source code of the talkwithdriver () method:

// File: Frameworks/base/libs/binder/ipcthreadstate. CPP <br/> status_t ipcthreadstate: talkwithdriver (bool doreceive) <br/>{< br/> log_assert (mprocess-> mdriverfd> = 0, "binder driver is not opened"); </P> <p> binder_write_read BWR; </P> <p> // is the read buffer empty? <Br/> const bool needread = min. dataposition ()> = min. datasize (); </P> <p> // we don't want to write anything if we are still reading <br/> // from data left in the input buffer and the caller <br //> // has requested to read the next data. <br/> const size_t outavail = (! Doreceive | needread )? MOUT. datasize (): 0; </P> <p> BWR. write_size = outavail; <br/> BWR. write_buffer = (long unsigned INT) MOUT. data (); </P> <p> // This is what we'll read. <br/> If (doreceive & needread) {<br/> BWR. read_size = min. datacapacity (); <br/> BWR. read_buffer = (long unsigned INT) min. data (); <br/>}else {<br/> BWR. read_size = 0; <br/>}</P> <p> if_log_commands () {<br/> textoutput: bundle _ B (alog ); <br/> If (o Utavail! = 0) {<br/> alog <"sending commands to driver:" <indent; <br/> const void * cmds = (const void *) BWR. write_buffer; <br/> const void * end = (const uint8_t *) cmds) + BWR. write_size; <br/> alog <pexdump (cmds, BWR. write_size) <Endl; <br/> while (cmds <End) cmds = printcommand (alog, cmds); <br/> alog <dedent; <br/>}< br/> alog <"size of Receive Buffer:" <BWR. read_size <br/> <", needread:" <needread <", doreceive:" <doreceive <Endl; <br/>}</P> <p> // return immediately if there is nothing to do. <br/> If (BWR. write_size = 0) & (BWR. read_size = 0) return no_error; </P> <p> BWR. write_consumed = 0; <br/> BWR. read_consumed = 0; <br/> status_t err; <br/> do {<br/> if_log_commands () {<br/> alog <"about to read/write, write size = "<MOUT. datasize () <Endl; <br/>}< br/> # If defined (have_android_ OS) <br/> If (IOCTL (mprocess-> mdriverfd, binder_write_read, & BWR)> = 0) <br/> err = no_error; <br/> else <br/> err =-errno; <br/> # else <br/> err = invalid_operation; <br/> # endif <br/> if_log_commands () {<br/> alog <"finished read/write, write size =" <MOUT. datasize () <Endl; <br/>}< br/>}while (ERR =-eintr); </P> <p> if_log_commands () {<br/> alog <"Our err:" <(void *) Err <", write consumed:" <br/> <BWR. write_consumed <"(of" <MOUT. datasize () <br/> <"), read consumed:" <BWR. read_consumed <Endl; <br/>}</P> <p> If (ERR> = no_error) {<br/> If (BWR. write_consumed> 0) {<br/> If (BWR. write_consumed <(ssize_t) MOUT. datasize () <br/> MOUT. remove (0, BWR. write_consumed); <br/> else <br/> MOUT. setdatasize (0); <br/>}< br/> If (BWR. read_consumed> 0) {<br/> Min. setdatasize (BWR. read_consumed); <br/> Min. setdataposition (0); <br/>}< br/> if_log_commands () {<br/> textoutput: bundle _ B (alog ); <br/> alog <"remaining data size:" <MOUT. datasize () <Endl; <br/> alog <"stored ed commands from driver:" <indent; <br/> const void * cmds = min. data (); <br/> const void * end = min. data () + min. datasize (); <br/> alog <pexdump (cmds, Min. datasize () <Endl; <br/> while (cmds <End) cmds = printreturncommand (alog, cmds); <br/> alog <dedent; <br/>}< br/> return no_error; <br/>}</P> <p> return err; <br/>}

The talkwithdriver () method encapsulates the requests to be sent (previously stored in the mout object) into a binder_write_read struct BWR, and calls ioctl (mprocess-> mdriverfd, binder_write_read, & BWR) to send the request. The following work is completed by the kernel space. This process will be blocked in IOCTL until a return value is returned. It can be seen that the IPC communication through the Binder Mechanism is a synchronization process. This is very important.

The client program is analyzed here for the time being. Now let's see what the server will do after receiving this request.

As mentioned above, the main function of servicemanager registers the callback function svcmgr_handler (). Therefore, this function is called when the above request is sent to the servicemanager end through the binder device. Let's take a look at its source code:

// File: Frameworks/base/cmds/servicemanager/service_manager.c <br/> int svcmgr_handler (struct binder_state * BS, <br/> struct binder_txn * txn, <br/> struct binder_io * MSG, <br/> struct binder_io * reply) <br/>{< br/> struct svcinfo * si; <br/> uint16_t * s; <br/> unsigned Len; <br/> void * PTR; </P> <p> Logi ("[Byn] target = % P code = % d pid = % d uid = % d/N ", <br/> txn-> Target, txn-> code, txn-> sender_pid, txn-> send Er_euid); </P> <p> If (txn-> target! = Svcmgr_handle) <br/> return-1; </P> <p> S = bio_get_string16 (MSG, & Len); </P> <p> If (Len! = (Sizeof (svcmgr_id)/2) | <br/> memcmp (svcmgr_id, S, sizeof (svcmgr_id) {<br/> fprintf (stderr, "invalid ID % s/n", str8 (s); <br/> return-1; <br/>}</P> <p> switch (txn-> code) {<br/> case svc_mgr_get_service: <br/> case svc_mgr_check_service: <br/> S = bio_get_string16 (MSG, & Len); <br/> PTR = do_find_service (BS, S, Len); <br/> If (! PTR) <br/> break; <br/> bio_put_ref (reply, PTR); <br/> return 0; </P> <p> case svc_mgr_add_service: <br/> S = bio_get_string16 (MSG, & Len); <br/> PTR = bio_get_ref (MSG); <br/> If (do_add_service (BS, S, Len, PTR, txn-> sender_euid) <br/> return-1; <br/> break; </P> <p> case svc_mgr_list_services: {<br/> unsigned n = bio_get_uint32 (MSG); </P> <p> SI = svclist; <br/> while (n --> 0) & Si) <br/> SI = Si-> next; <br/> If (SI) {<br/> bio_put_string16 (reply, Si-> name ); <br/> return 0; <br/>}< br/> return-1; <br/>}< br/> default: <br/> LogE ("unknown code % d/N", txn-> Code); <br/> return-1; <br/>}</P> <p> bio_put_uint32 (reply, 0); <br/> return 0; <br/>}

Because the addservice service is called, it will go to the svc_mgr_add_service branch and call the do_add_service () function. Let's take a look at its source code:

// File: Frameworks/base/cmds/servicemanager/service_manager.c <br/> int do_add_service (struct binder_state * BS, <br/> uint16_t * s, unsigned Len, <br/> void * PTR, unsigned UID) <br/>{< br/> struct svcinfo * si; <br/> // Logi ("add_service ('% s', % P) uid = % d/N", str8 (s), PTR, UID ); </P> <p> If (! PTR | (LEN = 0) | (LEN> 127) <br/> return-1; </P> <p> If (! Svc_can_register (UID, S) {<br/> LogE ("add_service ('% s', % P) uid = % d-Permission denied/N ", <br/> str8 (s), PTR, UID); <br/> return-1; <br/>}</P> <p> SI = find_svc (S, len); <br/> If (SI) {<br/> If (Si-> PTR) {<br/> LogE ("add_service ('% s ', % P) uid = % d-already registered/N ", <br/> str8 (s), PTR, UID); <br/> return-1; <br/>}< br/> Si-> PTR = PTR; <br/>}else {<br/> SI = malloc (sizeof (* si) + (LEN + 1) * sizeof (uint16_t); <br/> If (! Si) {<br/> LogE ("add_service ('% s', % P) uid = % d-out of memory/N ", <br/> str8 (s), PTR, UID); <br/> return-1; <br/>}< br/> Si-> PTR = PTR; <br/> Si-> Len = Len; <br/> memcpy (Si-> name, S, (LEN + 1) * sizeof (uint16_t )); <br/> Si-> name [Len] = '/0'; <br/> Si-> death. func = svcinfo_death; <br/> Si-> death. PTR = SI; <br/> Si-> next = svclist; <br/> svclist = SI; <br/>}</P> <p> binder_acquire (BS, PTR); <br/> binder_link_to_death (BS, PTR, & Si-> death); <br/> return 0; <br/>} 

Check whether you have the permission to register the Service (svc_can_register (UID, S). If you can, check whether the service has been registered. If not, construct a svcinfo object and add it to the svclist linked list. Finally, inform the binder device that a new service is registered.

After the server returns, the client is blocked (called by the ioctl system) and then returned layer by layer, thus completing the service registration request.

 

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.