Android Binder inter-process communication --- Register Service component --- Server processes BC_TRANSACTION

Source: Internet
Author: User

This article is based on the source code Scenario Analysis of the Android system, by Luo shengyang

I. Test code:

~ /Android/external/binder/server

---- FregServer. cpp

~ /Android/external/binder/common

---- IFregService. cpp

---- IFregService. h

~ /Android/external/binder/client

---- FregClient. cpp


Binder Library (libbinder) code:

~ /Android/frameworks/base/libs/binder

---- BpBinder. cpp

---- Parcel. cpp

---- ProcessState. cpp

---- Binder. cpp

---- IInterface. cpp

---- IPCThreadState. cpp

---- IServiceManager. cpp

---- Static. cpp

~ /Android/frameworks/base/include/binder

---- Binder. h

---- BpBinder. h

---- IInterface. h

---- IPCThreadState. h

---- IServiceManager. h

---- IBinder. h

---- Parcel. h

---- ProcessState. h


Driver Layer Code:

~ /Android // kernel/goldfish/drivers/staging/android

---- Binder. c

---- Binder. h


Ii. Source Code Analysis in the previous article, Android Binder processes communicate --- register the Service component --- Client sends BC_TRANSACTIONhttp: // blog.csdn.net/jltxgcy/article/details/26076149, list_add_tail (& t-> work. entry, target_list); added to the todo list of the target process. Wake_up_interruptible (target_wait); wakes up the target process. Remember to refer to the following article in "communication between Andorid Binder processes-start ServiceManager. The implementation is as follows:

~ /Android // kernel/goldfish/drivers/staging/android

---- Binder. c

Static intbinder_thread_read (struct binder_proc * proc, struct binder_thread * thread, void _ user * buffer, int size, signed long * consumed, int non_block) {void _ user * ptr = buffer + * consumed; void _ user * end = buffer + size; int ret = 0 ;......... while (1) {uint32_t cmd; struct binder_transaction_data tr; struct binder_work * w; struct binder_transaction * t = NULL; if (! List_empty (& thread-> todo) w = list_first_entry (& thread-> todo, struct binder_work, entry); else if (! List_empty (& proc-> todo) & wait_for_proc_work) w = list_first_entry (& proc-> todo, struct binder_work, entry ); // Save the work item to be processed in the binder_work struct w else {if (ptr-buffer = 4 &&! (Thread-> logoff & BINDER_LOOPER_STATE_NEED_RETURN)/* no data added */goto retry; break ;}........ switch (w-> type) {case BINDER_WORK_TRANSACTION: {t = container_of (w, struct binder_transaction, work); // The type of binder_work struct w is BINDER_WORK_TRANSACTION, that is, it is a work item embedded in a binder_transaction struct, so it can be safely converted to a binder_transaction struct t} break ;.........} if (! T) continue; BUG_ON (t-> buffer = NULL); if (t-> buffer-> target_node) {struct binder_node * target_node = t-> buffer-> target_node1_tr.tar get. ptr = target_node-> ptr; // the ptr of the Binder object is NULLtr. cookie = target_node-> cookie; // The Binder object cookie is NULLt-> saved_priority = task_nice (current); if (t-> priority <target_node-> min_priority &&! (T-> flags & TF_ONE_WAY) binder_set_nice (t-> priority); else if (! (T-> flags & TF_ONE_WAY) | t-> saved_priority> target_node-> min_priority) binder_set_nice (target_node-> min_priority); cmd = BR_TRANSACTION; // cmd set BR_TRANSACTION} else {.....} tr. code = t-> code; // ADD_SERVICE_TRANCATIONtr.flags = t-> flags; // TF_ACCEPTS_FDStr.sender_euid = t-> sender_euid; if (t-> from) {struct task_struct * sender = t-> from-> proc-> tsk; tr. sender_pid = task_tgid_nr_ns (sender, current-> nsproxy-> Pid_ns);} else {.......} tr. data_size = t-> buffer-> data_size; // data buffer size tr. offsets_size = t-> buffer-> offsets_size; // offset array size tr. data. ptr. buffer = (void *) t-> buffer-> data + proc-> user_buffer_offset; // The kernel space address of the kernel buffer differs from the user space address by a fixed value, and saved in its member variable user_buffer_offset tr. data. ptr. offsets = tr. data. ptr. buffer + ALIGN (t-> buffer-> data_size, sizeof (void *); // if (put_user (cmd, (uint32_t _ user *) ptr) // run the command Return-EFAULT; ptr + = sizeof (uint32_t); if (copy_to_user (ptr, & tr, sizeof (tr) // return the return-efault to the binder_transaction_data struct tr; ptr + = sizeof (tr );....... list_del (& t-> work. entry); // Delete this task item t-> buffer-> allow_user_free = 1; // release if (cmd = BR_TRANSACTION &&! (T-> flags & TF_ONE_WAY) {t-> to_parent = thread-> transaction_stack; t-> to_thread = thread; thread-> transaction_stack = t ;} else {t-> buffer-> transaction = NULL; kfree (t );........} break;} done: * consumed = ptr-buffer; // sum of the tr size of the cmd and binder_transaction_data struct ........ return 0 ;}
The if statement first checks whether the thread's own todo queue has a work item to be processed. If no, the if statement in line 1 checks whether there are work items to be processed in the todo queue of the proc process to which it belongs. As long as one of the todo queues has work items to be processed, the binder_thread_read function retrieves them for processing and stores them in the binder_work struct w. The binder_work struct w is of the BINDER_WORK_TRANSACTION type, that is, it is a work item embedded in a binder_transaction struct, so it can be safely converted to a binder_transaction struct t. Use the binder_transaction struct t to set the parameters of the binder_transaction_data struct tr. Return the cmd and binder_transaction_data struct tr to binder_ioctl, and then return to binder_loop :~ /Android/frameworks/base/cmd/servicemanager ---- binder. c
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; // write the BC_ENTER_LOOPER protocol to binder_write (bs, readbuf, sizeof (unsigned) in the buffer zone first )); // call binder_write to send it to the Binder driver for (;) {bwr. read_size = sizeof (readbuf); bwr. read_consumed = 0; B Wr. read_buffer = (unsigned) readbuf; res = ioctl (bs-> fd, BINDER_WRITE_READ, & bwr); // bwr. write_size is 0, bwr. read_size is not 0 if (res <0) {LOGE ("binder_loop: ioctl failed (% s) \ n", strerror (errno); break ;} res = binder_parse (bs, 0, readbuf, bwr. read_consumed, func); // at this time, readbuf is the cmd and binder_transaction_data struct tr, bwr. read_consumed is the sum of the tr values of cmd and binder_transaction_data struct. if (res = 0) {LOGE ("binder_loop: unex Pected reply ?! \ N "); break;} if (res <0) {LOGE (" binder_loop: io error % d % s \ n ", res, strerror (errno )); break ;}}}
Start to run binder_parse. The implementation is as follows:
Int binder_parse (struct binder_state * bs, struct binder_io * bio, uint32_t * ptr, uint32_t size, binder_handler func) {int r = 1; uint32_t * end = ptr + (size/4); while (ptr <end) {uint32_t cmd = * ptr ++ ;....... switch (cmd) {// cmd is BR_TRANSACTION ...... case BR_TRANSACTION: {struct binder_txn * txn = (void *) ptr; // The binder_transaction_data struct tr is retrieved and put into the binder_txt struct ........ if (func) {// svcmgr_handler function pointer 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); // The svcmgr_handler function pointer binder_send_reply (bs, & reply, txn-> data, res);} ptr + = sizeof (* txn)/sizeof (uint32_t); break ;}......} return r ;}
Before introducing binder_parse, we should first look at several structs. ~ /Android/frameworks/base/cmd/servicemanager ---- binder. h
Struct binder_object {struct type; uint32_t flags; void * pointer; void * cookie;}; struct binder_txn {void * target; void * cookie; uint32_t code; uint32_t flags; sender_pid; uint32_t sender_euid; uint32_t data_size; uint32_t offs_size; void * data; void * offs;}; struct binder_io // For more information, see {char * data; /* pointer to read/write from */uint32_t * offs;/* array of offsets */uint32_t data_avail;/* bytes available in data buffer */uint32_t offs_avail; /* entries available in offsets array */char * data0;/* start of data buffer */uint32_t * offs0;/* start of offsets buffer */uint32_t flags; uint32_t unused ;};
The struct binder_txn is used to describe inter-process communication data. It is equivalent to the aforementioned binder_transaction_data struct.
The struct binder_io is used to parse inter-process communication data. It is similar to the Parcel class in the Binder library. The struct binder_object is used to describe a Binder object in inter-process communication data. It is equivalent to the struct flat_binder_object. Execute binder_parse, first retrieve cmd, and then retrieve the binder_transaction_data struct tr and save it to the binder_txn struct txn. Then, call the bio_init function to implement the following:
~ /Android/frameworks/base/cmd/servicemanager ---- binder. c
Void bio_init (struct binder_io * bio, void * data, uint32_t maxdata, uint32_t maxoffs) {uint32_t n = maxoffs * sizeof (uint32_t ); // The size of the Offset array if (n> maxdata) {// the size of the Offset array cannot be greater than the maximum allocable size bio-> flags = BIO_F_OVERFLOW; bio-> data_avail = 0; bio-> offs_avail = 0; return;} bio-> data = bio-> data0 = data + n; // The offset array is followed by the data buffer bio-> offs = bio-> offs0 = data; // the start is the offset array bio-> data_avail = maxdata-n; // data buffer size bio-> offs_avail = maxoffs; // offset array size bio-> flags = 0 ;}
Bio_init initializes the binder_io structure reply. Return binder_parse and execute the bio_init_from_txn function. The implementation is as follows :~ /Android/frameworks/base/cmd/servicemanager ---- binder. c
void bio_init_from_txn(struct binder_io *bio, struct binder_txn *txn){    bio->data = bio->data0 = txn->data;    bio->offs = bio->offs0 = txn->offs;    bio->data_avail = txn->data_size;    bio->offs_avail = txn->offs_size / 4;    bio->flags = BIO_F_SHARED;}
Bio_init_from_txn initializes the binder_io struct msg. Return binder_parse and execute the svcmgr_handler function. The implementation is as follows :~ /Android/frameworks/base/cmd/servicemanager ---- service_manager.c
Int struct (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 ;...... if (txn-> target! = Svcmgr_handle) // txn-> target is NULL, svcmgr_handle is NULL (void * (0) return-1; // Equivalent to Parcel: enforceInterface (), reading the RPC // header with the strict mode policy mask and the interface 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); // strict_policy is STRICT_MODE_PENALTY_GATHER s = Bio_get_string16 (msg, & len); // The value is android. OS. IServiceManager if (len! = (Sizeof (svcmgr_id)/2) | memcmp (svcmgr_id, s, sizeof (svcmgr_id) {// check whether the comparison is consistent. If not, error fprintf (stderr, "invalid % s \ n", str8 (s); return-1 ;}switch (txn-> code) {// ADD_SERVICE_TRANSACTION, that is, SVC_MGR_ADD_SERVICE ........ case SVC_MGR_ADD_SERVICE: s = bio_get_string16 (msg, & len); // s is shy. luo. fregService, len is its length ptr = bio_get_ref (msg); // returns the handle value of the Binder referenced object if (do_add_service (bs, s, len, ptr, txn-> sender_euid) return-1; break ;....... bio_put_uint32 (reply, 0); return 0 ;}
The svcmgr_id [] is implemented as follows :~ /Android/frameworks/base/cmd/servicemanager ---- service_manager.c
uint16_t svcmgr_id[] = {     'a','n','d','r','o','i','d','.','o','s','.',    'I','S','e','r','v','i','c','e','M','a','n','a','g','e','r' };
The program obtains information about three strings from the binder_io struct msg, and then calls the bio_get_ref function to return the handle value of the Binder referenced object. The implementation is as follows :~ /Android/frameworks/base/cmd/servicemanager ---- binder. c
void *bio_get_ref(struct binder_io *bio){    struct binder_object *obj;    obj = _bio_get_obj(bio);    if (!obj)        return 0;    if (obj->type == BINDER_TYPE_HANDLE)        return obj->pointer;    return 0;}
_ Bio_get_obj implementation :~ /Android/frameworks/base/cmd/servicemanager ---- binder. c
Static struct binder_object * _ bio_get_obj (struct binder_io * bio) {unsigned n; unsigned off = bio-> data-bio-> data0; // flat_binder_object offset, since the obtained string moved data/* TODO: be smarter about this? */For (n = 0; n <bio-> offs_avail; n ++) {// offs_avail equals 1 if (bio-> offs [n] = off) return bio_get (bio, sizeof (struct binder_object);} bio-> data_avail = 0; bio-> flags | = BIO_F_OVERFLOW; return 0 ;}
_ Bio_get_obj first calculates the flat_binder_object offset, and then checks whether the offset is consistent with bio-> offs [0]. If the offset is consistent, the bio_get function is called to implement the following. ~ /Android/frameworks/base/cmd/servicemanager ---- binder. c
Static void * bio_get (struct binder_io * bio, uint32_t size) {size = (size + 3 )&(~ 3); if (bio-> data_avail <size ){.......} else {void * ptr = bio-> data; bio-> data + = size; // increase the data Pointer bio-> data_avail-= size; // reduce the available space return ptr; // The flat_binder_object struct is returned }}
The function returns the flat_binder_object struct and returns it to the bio_get_ref function to convert it to the binder_object struct pointer. Because type is equal to BINDER_TYPE_HANDLE, the handle value of the referenced object of the Binder is returned.

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.