For details about the Binder design idea and Driver layer implementation, refer to: Android Binder Design and Implementation-design. Here we will give a brief introduction.
Each Binder entity of the Service is located in the process type of the Service. The Binder entity is represented as binder_node in the driver, and all references to the Binder entity in the driver are directed to the refs member, the Binder reference is represented as binder_ref in the driver, and points to the referenced Binder entity through the member node.
Every process using the Binder will open the Binder device in its ProcessState constructor. When the Binder setting is enabled, the binder_open driver will be called. In binder_open, A binde_proc node will be created for the process that uses the Binder. The binder_proc member nodes indexes all the Binder entities created by the process, refs_by_desc and refs_by_node are the red and black trees built with the reference number of the Binder object referenced by this process and the internal address of the referenced object in the kernel respectively. In this way, each process can retrieve all the self-created Binder entities and all references to other Binder entities through its own binder_proc node.
Anonymous Binder must be passed through real-name Binder, while real-name Binder must be registered with ServiceManager. Therefore, a process must first call ioctl (bs-> fd, BINDER_SET_CONTEXT_MGR, 0) to become ServiceManager. When a process requests to become ServiceManager, the driver uses binder_new_node to establish the first binder_node node in the kernel.
Data is transmitted in the binder_transaction_date structure in the driver, and the member ptr of binder_transaction_data. buffer points to the memory address of the data to be sent. It can also transmit Binder entities or references between processes. For example, when sending anonymous Binder entities or references to data, you need a method to point the position of the Binder object or reference in the data, ptr. offsets points to the Binder offset array. offsets_size indicates the size of the Binder offset array. The two member drivers can find all the Binder entities or references in the transmitted data. The Binder object or reference is expressed as flat_binder_object during transmission. The type field of flat_binder_object indicates the TYPE of the transmitted Binder, and TYPE_BINDER _ (WEAK) _ type indicates that the transmitted Binder object is, TYPE_BINDER _ (WEAK) _ HANDLE indicates the Binder reference, and BINDER_TYPE_FD indicates the file Binder.
To send a request to the Service, the Client must obtain a reference to the Service's Binder entity. When the Client sends a request to the Service, it specifies the Service to which the request is sent with a reference number, 0 indicates to send a request to ServiceManager.
Generally, if the Client wants to make a request to a Service, it first sends a GET_SERVICE request to the Binder reference with the reference number 0 to obtain the reference of the desired Service, then, send a request to the reference Service.
The driver will forward all requests sent to ServiceManager with the reference number 0. When a process requests a Service from ServiceManager with the reference number 0, serviceManager checks whether the Service requested by the Client has been registered with itself. When the Binder entity registers with ServiceManager, ServiceManager stores the name and reference of the Binder object in a query table, if the Service has already been registered, the Service-registered Binder will be referenced and returned to the requesting process.
When ServiceManager sends a Service Binder reference requested by a process to this process, because the reference is sent, the type value of flat_binder_object is TYPE_BINDER _ (WEAK) _ HANDLE, the driver uses the ptr of binder_transaction_date. offsets and offsets_size know that the returned data contains the Binder entity or reference, and then find the Binder entity or reference in the data through the two members, the type member of flat_binder_object knows that the returned data contains a Binder reference, create a reference to the Service Binder object and save it to the refs Member of the binder_node node in the drive and binder_proc of the Client process.
After the Client obtains a Service reference, it can send a request to the Service using this reference. The data packet is a binder_transaction_date struct, and its member target is a union, target. handle indicates the reference number of the Client to the Service, target. ptr indicates the memory address of the Binder entity in the Service process. When the Client sends a request to the Service, it fills the target. handle field. The driver obtains the binder_proc object reference binder_ref of the Client to the Service based on the binder_proc node to which the Client belongs and the reference number handle, then, get the Binder object of the Service in the kernel node binder_node through the node member of binder_ref, and then add the Client request to the waiting queue of the Service process or the waiting queue of a certain thread of the Service process, the Service can process Client requests.
Next, let's take a look at the Native layer's use of Binder.
The Binder is implemented as a character device, and the application calls ioctl to communicate with the Binder driver. First, let's look at the class structure relationships involved in implementing a ServiceDemo.
RefBase is a class for Android to implement pointer management. All reference counts are inherited from this class, and then strong reference count and weak reference count are managed through sp and wp.
The Binder uses the Client-Server communication mode. To implement a Server, you must first define an interface. The Client and Server implement this interface at the same time, and the Server completes the actual functions, the Client only encapsulates Server-side function calls. Because this interface requires cross-process calls, all interfaces need to be numbered one by one. The Server determines which function to call based on the interface number. The interface is defined as IServiceDemo.
To implement inter-process communication, first define the communication protocol and then provide the communication interface to the application. The Binder Driver defines the communication protocol, IBinder, BpBinder, BBinder undertakes the communication interface work. IBinder defines the communication interface. BpBinder is the proxy object for the Client to access the server. It is responsible for opening the Binder device and communicating with the Binder device, BBinder serves as the interface for the server to communicate with the Binder device. The Client connects to the Binder Driver through BpBinder, and then the Binder Driver communicates with the Server through BBinder to complete inter-process communication.
IServiceDemo defines the interface for communication between the Client and the Server, which must be implemented by both the Client and the Server. We already know how the Client can obtain BpBinder by communicating with the Server's BBinder through BpBinder, on the Server side, how does one get BBinder? As you can see, IServiceDemo inherits from IInterface. In fact, IInterface defines a method asBinder and returns a pointer to an IBinder object. This method should be used to obtain BpBinder and BBinder objects. As you can see from the implementation of asBinder, asBinder directly calls onAsBinder. onAsBinder is a virtual method, so it is the concrete implementation of the called subclass. We found that IInterface has two sub-classes: BpInterface and BnInterface. onAsBinder is implemented in both classes. In BpInterface, onAsBinder returns remote (), remote () it actually returns a BpBinder object, which will be seen later. In BnInterface, onAsBinder directly returns this pointer, while BnInterface inherits from BBinder. Therefore, onAsBinder of BnInterface returns a BBinder object, which is available for both BpBinder and BBinder, the Client can communicate with the Server.
As mentioned above, remote () returns a BpBinder object. How does this object return? As you can see, BnInterface inherits from BBinder, but BpInterface does not inherit from BpBinder. However, we find that the BpInterface constructor receives an IBinder-type parameter, let's take a look at the BpInterface constructor:
template<typename INTERFACE><INTERFACE>::BpInterface( sp<IBinder>&
BpInterface is inherited from BpRefBase. In the initialization list of BpInterface, the constructor of the parent class BpRefBase is called to pass IBinder remote over. Let's look at the BpRefBase constructor:
BpRefBase::BpRefBase( sp<IBinder>&()), mRefs(NULL), mState(->incStrong(); mRefs = mRemote->createWeak();
The IBinder remote passed by BpInterface is saved to the mRemote member, and the remote () function directly returns the mRemote object.
The BpBinder object is saved through the BpInterface constructor. When is the BpInterface constructor called, and when is the BpBinder passed as the constructor parameter constructed? Taking ServiceManager as an example, real-name Binder needs to register with ServiceManager through addService, which is also inter-process communication. Then we need to obtain the BpBinder of ServiceManager, that is, the BpInterface subclass BpServiceManager object, let's take a look at how to obtain BpServiceManager:
sp<IServiceManager> (gDefaultServiceManager != NULL) (gDefaultServiceManager == } }
In Singleton mode, check the red part of the code above. ProcessState indicates the process object. Each process has only one. In ProcessState: self () in Singleton mode, the unique instance of the ProcessState of each process is returned. In the ProcessState function, the Binder device is opened through the open call and the memory ing is established through mmap. Open causes binder_open in binder driver to be called. binder_open creates binder_proc nodes and initializes todo and wait queues, the binder_proc node is saved in binder_open's second parameter struct file * flip-> private_data and binder_procs.
sp<IBinder> ProcessState::getContextObject( sp<IBinder>& getStrongProxyForHandle(<IBinder><IBinder>* e = (e != IBinder* b = e-> (b == NULL || !e->refs->attemptIncWeak( e->binder = (b) e->refs = b->=->refs->decWeak(
If handle is 0, the returned result of lookupHandleLocked is NULL. Therefore, the red part is executed to create a BpBinder. The red part of defaservicservicemanager can be simplified:
gDefaultServiceManager = interface_cast<IServiceManager>( BpBinder());
BpBinder is available. We know earlier that BpBinder will be passed to the BpInterface constructor as a parameter. When will the constructor of the BpInterface be called? From the code above, it should be interface_cast. Convert the BpBinder parameter to the BpInterface subclass BpServiceManager. Then let's look at the implementation of interface_cast.
template<typename INTERFACE><INTERFACE> interface_cast( sp<IBinder>&
INTERFACE is IServiceManager. Classes inherited from IInterface declare DELCARE_META_INTERFACE and IMPLEMENT_META_INTERFACE. Let's take a look at the implementation of IMPLEMENT_META_INTERFACE:
IMPLEMENT_META_INTERFACE(INTERFACE, NAME) \ android::String16&<I##INTERFACE> android::sp<android::IBinder>&<I##INTERFACE> (obj !== static_cast<I##INTERFACE*> \ (intr == \ } \ } \ ~I##INTERFACE() { }
AsInterface is implemented in the IMPLEMENT_META_INTERFACE macro. In the above red code, obj is the BpBinder (0) passed in. In the above figure, the queryLocalInterface of BpBinder returns NULL, therefore, the blue code is executed. The INTERFACE is Servicemanager, so a new BpServiceManager object is created. The BpServiceManager object is available, and the BpBinder object returned by its asBinder method can communicate with the Server.
If the Client has a proxy pair like BpInterface, how can it communicate with the Server through this proxy object? The standard method is as follows:
remote()->transact(SET_MASTER_VOLUME, data, &reply);
As mentioned above, the Client communicates with the Server through the Binder driver through BpBinder. From here, it is true that remote () returns the BpBinder object, call the BpBinder statement to communicate with the Server. The statement is defined in IBinder. Both BpBinder and BBinder implement this method.
In the implementation of BpBinder: transact, IPCThreadState: transact is called directly. ProcessState indicates the process object. Each process has one, the constructor in ProcessState calls the Binder device and performs mmap. The IPCThreadState indicates the Thread object, and each Thread has an IPCThreadState object using LTS (Local Thread Storage, binder communication is the communication between threads. Here we can communicate with the Server through IPCThreadState: transact.
IPCThreasState: The transact method first calls writeTransactionDate to encapsulate the request data into the binder_transaction_data structure and write it into Parcel mOut. Then call waitForResponse.
WaitForResponse calls talkWithDriver, and talkWithDriver communicates with the Binder driver through ioctl (driverFD, BINDER_WIRTE_READ, & binder_write_read). After the Server completes processing the request, talkWithDriver returns success, then, read the command returned by the Binder Driver in waitForResponse and execute the corresponding action.
In Server, the joinThreadPool of the binder thread calls taklWithDriver to wait for the Client request. When a request arrives, talkWithDriver returns, reads the command, and CALLS executeCommand to process the request. In the executeCommand, call BBinder's transact to process the request. BBinder: transact calls the virtual method onTransact to complete the specific function. The specific implementation is BnServiceManager: onTransact or BnServiceDemo: onTransact. Generally, there is a class inherited from BnXXXXX to complete the specific function. In onTransact of BnXXXXX, the interface to complete the corresponding function is called. Because it is a virtual method, it will call the specific implementation class.
Register the context Manager-ServiceManager
Ioctl (bs-> fd, BINDER_SET_CONTEXT_MGR, 0) is used. A process can be registered as the upper and lower file manager and the ioctl request is executed in ServiceManager.
The ioctl call will execute the binder_ioctl function of the Binder Driver. binder_ioctl will execute the corresponding same operation according to the second parameter cmd. See the corresponding processing of BINDER_SET_CONTEXT_MGR:
(binder_context_mgr_node !== - (binder_context_mgr_uid != - (binder_context_mgr_uid != current->cred-> ->cred->= -= current->cred->= (binder_context_mgr_node === -->local_weak_refs++->local_strong_refs++->has_strong_ref = ->has_weak_ref = ;
It is easy to get a binder_node from binder_new_node and save it to the global variable binder_context_mgr_node. The UID is saved at the same time and only one context_manager is available.