Android Binder Design and Implementation (2)-design

Source: Internet
Author: User

5. Binder statement
During the entire binder communication process, we can find that the binder exists in the following parts of the system:

· Application processes: Server Processes and Client Processes

· Binder DRIVER: server and client have different expressions

· Data Transmission: Since the binder can be transferred across processes, it must be expressed in the transmitted data.

In different parts of the system, the functions implemented by the binder are different in different forms. Next we will discuss the roles and data structures used by the binder in each part one by one.

 

5.1 statement of the binder in the Application

Although the binder uses the object-oriented idea, it does not limit the application to use the object-oriented language. Both C and C ++ can easily use the binder for communication. For example, although Android mainly uses Java or C ++, an important process like smgr is implemented in C language. However, the object-oriented method is more convenient to express, so this article assumes that the application is implemented in object-oriented language.

The binder is essentially an underlying communication method and has nothing to do with a specific service. To provide specific services, the server must provide a set of interface functions for the client to remotely access various services. In this case, the proxy design mode is usually used: interface functions are defined in an abstract class (such as imediaplayerservice), and server and client implement all interface functions based on this abstract class, the difference is that the server side is the real function implementation, while the client side is the packaging of Remote Call requests for these functions (such as bpmediaplayerservice ). How to combine the binder and proxy design patterns is the fundamental problem for applications to implement object-oriented binder communication.

 

5.1.1 binder expression on the server-binder entity
As the basis of the proxy design pattern, first define an abstract interface class (for example, imediaplayerservice) to encapsulate all functions of the server, including a series of pure virtual functions to be implemented by the server and proxy respectively. Because these functions need to be called across processes, they must be numbered one by one, so that the server can decide which function to call based on the number received. Next, we need to introduce the binder. The server defines another binder abstract class to process the binder request data packets from the client. The most important member is the virtual function ontransact () (such as bnmediaplayerservice ). This function analyzes the received data packets and calls the corresponding interface function to process the request.

Next, we use the Inheritance Method to Construct the entity (for example, mediaplayerservice) of the binder in the server based on the Interface Class and the binder abstract class, and implement all the virtual functions in the base class, includes public interface functions and data packet processing functions: ontransact (). The input of this function is a data packet from the client's binder_transaction_data structure. As mentioned above, there is a member code in this structure that contains the interface function number of this request. Ontransact () parses the code value in case-by-case mode and extracts the function parameter from the data packet.
Number of public interface functions that have been implemented in the called interface class. After the function is executed, construct a binder_transaction_data package and enter the returned data packet.

When Will ontransact () of each binder object be called? This requires driver participation. As mentioned above, the binder object must be sent to other processes in the form of the Binde Transmission Structure flat_binder_object to establish the binder communication, and the binder Object Pointer is stored in the handle domain of the structure. The driver obtains the Transmission Structure of the binder from the transmitted data based on the binder location array, creates a binder node in the kernel, and records the binder object pointer in the node. If another process sends data to the binder, the driver will enter the binder object pointer in the binder_transaction_data
Target. PTR is returned to the receiving thread. The receiving thread retrieves the pointer from the data packet. reinterpret_cast is a binary abstract class and the ontransact () function is called. Because this is a virtual function, different binder entities have their own implementations, so that they can call ontransact () provided by different binder entities ().

 

5.1.2 binder Statement on the client-binder reference
As part of the proxy design pattern, the client-side binder must inherit the public interface classes provided by the server and implement public functions (such as bpmediaplayerservice ). But this is not a real implementation, but a packaging of remote function calls: Package function parameters, send an application to the server through the binder, and wait for the return value. Therefore, the client-side binder also needs to know the information about the binder entity, that is, the reference to the binder entity. This reference is either forwarded by smgr, a reference to the real-name binder, or directly sent by another process. It is anonymous.
Binder reference.

Because it inherits the same public interface class, the client binder provides the same function prototype as the server binder, making the user feel that the server is running locally or remotely. In client binder, the implementation of common interface functions is: Create a binder_transaction_data packet, enter the corresponding encoding in the code field, and enter the parameters required to call this function in data. buffer points to the cache and specifies the destination of the data packet, that is, the reference to the binder entity that has been obtained, and fill in the data packet
Target. Handle. Note the difference between this and server: in fact, the target domain is a consortium, including PTR and handle. The former is used as the server of the responder and points to the memory space corresponding to the binder object; the latter is used as the client of the requester to store the reference of the binder object and inform the driver of the object to which the data packet will be routed. After the data packet is ready, it is sent through the driver interface. Call the function remotely and obtain the return value after the bc_transaction/bc_reply round.

 

5.2 Statement of binder in Data Transmission
The binder can be inserted into valid data packets and passed from one process to another through the process boundary. The binder in the transmission is represented by the structure flat_binder_object, as shown in the following table:

Table 6 binder Transmission Structure: flat_binder_object

Member Description
Unsigned long type Indicates the type of the binder, including the following:

Binder_type_binder: It indicates that the binder object is passed and the references to this object are strongly typed;

Binder_type_weak_binder: It indicates that the binder object is passed, and references to this object are weak types;

Binder_type_handle: indicates that a reference with a strong binder type is passed.

Binder_type_weak_handle: indicates that references of weak binder types are passed.

Binder_type_fd: the file-format binder is passed. For details, see the following section.

Unsigned long flags This field is only valid when the binder object is passed for the first time, because at this moment, the driver needs to create the corresponding entity node in the kernel, and some parameters need to be retrieved from this field:

0-7 bits: Use flat_binder_flag_priority_mask in the code to obtain the lowest priority of the thread that processes the request data packets of this entity. When an application provides multiple entities, you can adjust the processing capabilities allocated to each entity through this parameter.

8th bits: the code is obtained using flat_binder_flag_accepts_fds. If this parameter is set to 1, the entity can receive binder files sent by other processes. Because the received file-form binder will automatically open the file in this process, some servers can use this flag to disable this function to prevent too many files from being opened.

Union {

Void * binder;

Signed long handle;

};

When the binder object is passed, the binder domain is used to point to the address of the binder object in the application.

The handle field is used to store the reference number of the binder in the process when the binder is referenced.

Void * cookie; This field is only valid for the binder object and stores additional information related to this binder.

Both the binder object and the reference to the object are subordinate to a process. Therefore, the structure cannot be transparently transmitted between processes and must be driven. For example, when the server passes the binder object to the client, the type in flat_binder_object in the sent data is binder_type_binder, and the binder points to the server process user space address.

If passthrough is useless to the receiver, the driver must modify the binder in the data stream to binder_type_handle; create a reference in the kernel for this binder in the receiving process and enter the reference number in handle. The binder of the reference type in the data stream must also be converted. After processing, the binder reference obtained by the receiving process from the data stream is valid. You can fill in the target. Handle field of the data packet binder_transaction_data to send a request to the binder entity.

This is also for security considerations: the application cannot randomly guess a reference number and fill in the target. handle can request services from the server, because the driver does not create this reference for you in the kernel, it will be rejected by the driver. Only the binder granted to you by The 'authorization' can be used after the authentication is confirmed legal, because the driver has already created a reference for you in the kernel, the quote number given to you is legal.

 

The following table summarizes the operations performed by the driver when the flat_binder_object structure passes through the driver:

Table 7 driver operations on flat_binder_object

Binder type (Type field) Actions on the sender Operations on the receiver
Binder_type_binder

Binder_type_weak_binder

Only the process where the object is located can send this type of binder. If this is the first time the driver is sent, it will create an entity node in the kernel and save the binder, cookie, and Flag Fields. If this is the first time you receive the binder, create an object reference in the kernel. Replace the handle field with the new reference number. Replace the type field with binder_type _ (weak _) handle.
Binder_type_handle

Binder_type_weak_handle

All processes referenced by the binder can send this type of binder. The driver searches for references established in the kernel based on the reference number provided by the handle domain. If the reference number is valid, the request is rejected. If the received binder object is in the receiving process: Replace the PTR domain with the binder value in the existing node, the cookie with the cookie value in the existing node, and the type with binder_type _ (weak _) binder.

If the received binder object is not in the receiving process: if it is received for the first time, a reference of the object is created in the kernel; replace the handle field with the new reference number.

Binder_type_fd Verify whether the opened file number provided in the handle domain is valid. If the value is invalid, the sending request is rejected. Create a new open file number on the receiver and bind it with the provided open file description structure.

5.2.1 file format Binder
In addition to the binder used for communication, there is also a special binder: file binder. The basic idea of this kind of binder is to regard the file as the binder entity, and the file number opened by the process as the reference of the binder. A process can pass the file number of the opened file to another process, and the other process opens the same file, just as the reference of the binder is transmitted between processes.

When a process opens a file, it obtains the number of open files bound to the file. From the perspective of the binder, the description structure of the Open File Created by Linux in the kernel, struct file is the entity of the binder, and the open file number is the reference of the process to the entity. Since it is a binder, it can be transferred between processes. Therefore, you can also use the flat_binder_object structure to send the file binder to other processes through data packets. The value of the type field in the structure is binder_type_fd, indicates that the binder is a file binder. The handle field in the structure stores the open file number of the file in the sender's process. We know that opening a file number is a value limited to a specific process.
The process is meaningless. This is the same as the binder Object User pointer or binder reference number. If you want to cross-process conversion, the driver must also perform the conversion. The driver creates a new open file number in the process space that receives the binder and hooks it with the existing open file description structure struct file. Therefore, this binder entity has another reference. The new open file number overwrites the original file number in flat_binder_object and submits it to the receiving process. The receiving process can be used to perform file operations such as read () and write.

Why is it so troublesome to upload a file? You can directly upload the file name to the receiver using the binder. Is it okay if the receiver uses open () to open the file? In fact, there is a difference. First, there are different levels of Open File Sharing: files opened by using the file binder share the struct file, struct dentry, and struct inode structure in Linux VFS, which means that a process uses read () /write ()/seek () changes the file pointer of another process. If the two processes use the file name to open the same file, they have their own struct.
File structure to maintain file pointers independently without interfering with each other. Second, some special device files must be shared at the struct file level. For example, ashmem, another android driver, is also a misc device like the binder to implement shared memory between processes. Only when the ashmem file opened by a process is sent to another process through the file binder can the memory be shared, which greatly improves the security of Memory Sharing, the same principle is true for enhanced IPC security as for binder.

The display buffer shared between surfacecomposerclient and surfaceflinger is implemented using the file binder. The FD is first transmitted, and then MMAP is obtained.

 

5.3 Statement of binder in Driver
Driver is the core of binder communication. All binder entities in the system and references of each entity in each process are registered in the driver; the driver must record the many-to-one relationship between the binder references-> entities; find the corresponding entities for the references; create or find the corresponding references for the entities in a process; record the binder's locations (in which process); Create/destroy the binder entity by managing the strong/weak references of the binder.

When did the binder in the driver be created? As mentioned above, in order to implement real-name binder registration, the system must create the first chicken-created for smgr, and register the binder entity dedicated to real-name binder, responsible for inter-process communication during real-name binder registration. Since the entity has been created, it must also have a reference: The driver reserves the 0 reference in all processes to the binder entity, that is, the reference 0 of all processes at the beginning indicates that the binder dedicated to real-name binder is registered, and any process can register real-name binder through reference 0 without special operations. Next, as the application continuously registers the real-name binder, it constantly requests the reference of the binder from smgr
Processes are passed to another process. More and more binder are traversing the driver in the form of Transmission Structure-flat_binder_object for cross-Process Migration. Because the data. offset array exists in binder_transaction_data, all the binder that flows through the driver cannot escape the driver's eyes. The binder will perform the following operations on the binder that crosses the process boundary: Check the type field of the transmission structure. If it is binder_type_binder or binder_type_weak_binder, the binder entity will be created; if binder_type_handle or binder_type_weak_handle is used, the binder reference is created.
Binder_type_handle opens the file for the process without creating any data structure. For detailed procedures, see table 7. As more and more binder entities or references pass through the driver in the process, the driver will create more and more nodes or references in the kernel. Of course, this process is transparent to users.

 

5.3.1 statement of the binder entity in the driver
The binder object in the driver is also called a 'node'. It belongs to the process that provides the entity, represented by the struct binder_node structure:

Table 8 binder node description structure: binder_node

Member Description
Int debug_id; For debugging
Struct binder_work work; When the reference count of this node changes, you need to notify the process to which it belongs, and then use this member to mount the to-do queue of the process to wake up the process to execute the modification of the binder object reference count.
Union {

Struct rb_node;

Struct hlist_node dead_node;

};

Each process maintains a red/black tree, and stores all the binder entities of the process using the pointer of the binder object in the user space, that is, the PTR Member of the structure as the index. In this way, the driver can quickly find the node in the kernel based on the pointer of the binder object in the user space. Rb_node is used to link the current node to the red/black tree.

When destroying a node, you must remove the rb_node from the red/black tree. However, if the node still has references that are not cut off, use dead_node to isolate the node from another linked list, the node may not be destroyed until all processes are notified to be disconnected from the reference of the node.

Struct binder_proc * proc; This member points to the process to which the node belongs, that is, the process that provides the node.
Struct hlist_head refs; This member is the queue header, and all references pointing to this node are linked to this queue. These references may belong to different processes. This queue can be used to traverse all references pointing to this node.
Int internal_strong_refs; To implement a counter with a strong pointer: generate a strong reference pointing to the current node and Add 1 to the counter.
Int local_weak_refs; The weak reference count set by the driver for the binder in the transmission. If a binder is packaged in a data packet and sent from one process to another, the driver increases the reference count for the binder until the receiving process notifies the driver through bc_free_buffer to release the data zone of the data packet.
Int local_strong_refs; The strong reference count set by the driver for the binder in the transmission. Same as above.
Void _ User * PTR; Pointer to the binder object of the user space, from the binder member of flat_binder_object
Void _ User * cookie; Additional pointer to the user space, from the cookie member of flat_binder_object
Unsigned has_strong_ref;

Unsigned pending_strong_ref;

Unsigned has_weak_ref;

Unsigned pending_weak_ref

This group of flag is used to control the Interactive Modification reference count of the process where the driver and the binder entity are located.
Unsigned has_async_transaction; This member indicates that the node has not completed Asynchronous interaction in the to-do queue. The driver saves all data packets sent to the receiving end in the to-do queue opened by the receiving process or thread. For Asynchronous interaction, the driver implements proper traffic control: if there is Asynchronous interaction in the to-do queue to be processed, the Member is set to 1, this will cause the new Asynchronous interaction to be stored in the structure member asynch_todo queue, rather than directly sent to the to-do queue. The purpose is to make way for Synchronous interaction and avoid blocking the sending end for a long time.
Unsigned accept_fds Indicates whether the node agrees to accept the file-based binder, from the flat_binder_flag_accepts_fds bit of the flags member in flat_binder_object. Because the received file binder automatically opens a file for the process and occupies a limited file descriptor, the node can set this bit to reject this behavior.
Int min_priority Set the minimum priority of the thread that processes the binder request. When the sending thread submits the data to the receiving thread for processing, the driver also gives the priority of the sending thread to the receiving thread, so that the data can be processed with the same priority even if it is beyond the process. However, if the priority of the sending thread is too low, the receiving thread runs at the predefined minimum.

The value of this field is from the flags member in flat_binder_object.

Struct list_head async_todo Asynchronous interaction waiting queue; used to distribute Asynchronous interaction packets sent to the current node

Each process has a red/black tree used to store the created nodes, and the binder pointer in the user space is used as the index. Every time a flat_binder_object representing the binder entity is detected in the transmitted data, the system first searches for the red and black trees using the binder pointer of the structure as the index. If not found, a new node is created and added to the tree. Because the memory address is unique for the same process, it will not cause confusion due to repeated construction.

5.3.2 description of binder reference in Driver
Like an object, the binder reference is also created by the Driver Based on flat_binder_object In the transmitted data. It belongs to the process that obtains the reference and is represented by the struct binder_ref struct:

Table 9 binder reference description structure: binder_ref

Member Description
Int debug_id; Debugging
Struct rb_node rb_node_desc; Each process has a red/black tree. All references of the process are added to the tree by reference signs (that is, the DESC domain of the local structure. This member is used as a node to link to the tree.
Struct rb_node rb_node_node; Each process has a red/black tree. All the references of the process are introduced by the memory address of the node entity in the drive (that is, the node domain of the current structure. This member is used as a node to link to the tree.
Struct hlist_node node_entry; This field uses this reference as the refs queue in the binder object structure binder_node to which the node links
Struct binder_proc * proc; Process to which this reference belongs
Struct binder_node * node; This reference points to the node (Binder entity)
Uint32_t DESC; Reference Number of this structure
Int strong; Strong reference count
Int weak; Weak reference count
Struct binder_ref_death * death; The application sends the bc_request_death_notification or bc_clear_death_notification command to the driver, and receives a notification from the driver when the binder object is destroyed. If this field is not empty, it indicates that the user has subscribed to the 'bad ged' of object destruction '.

 

Just as an object has many pointers, the same binder object may have many references. The difference is that these references may be distributed in different processes. Like an object, each process uses a red/black tree to store all references that the process is using. However, binder references can be indexed by two key values:

· The address of the corresponding object in the kernel. Note that this refers to the address of the binder_node structure created by the driver in the kernel, rather than the address of the binder entity in the user process. The addresses of entities in the kernel are unique, and the use of indexes does not produce ambiguity. However, entities may come from different user processes, and the addresses of entities in different user processes may overlap, it cannot be used for indexing. The driver uses the red/black tree to quickly search for references corresponding to a binder object in a process (one entity creates only one reference in one process ).

· Reference number. The reference number is a 32-bit identifier allocated by the driver to the reference. It is unique within a process and may have the same value in different processes, this is similar to the process's file opening number. The reference number is returned to the application as a handle referenced by the binder in the user process. Except that the 0 reference is retained to smgr in all processes, other values are dynamically allocated by the driver when the reference is created. When a data packet is sent to the binder, the application indicates the purpose of the data packet by entering the reference number in the target. Handle field of the binder_transaction_data structure.
Binder.

The driver finds the referenced binder_ref structure in the red/black tree based on the reference number, and then learns the process and other related information of the target binder entity through its node domain to route data packets.

 

 

 

 

 

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.