Android Binder IPC Analysis

Source: Internet
Author: User
Tags unpack

1. binder communication Overview
 
Binder communication is a client-server communication structure,
1. On the surface, the client directly calls the server by obtaining a proxy interface of the server;
2. In fact, the methods defined in the proxy interface correspond one to one with those defined in the server;
3. When the client calls a method in a proxy interface, the proxy interface method packs the parameters passed by the client into a Parcel object;
4. The proxy interface sends the Parcel to the binder driver in the kernel.
5. The server will read the request data in the binder driver. If it is sent to itself, unpack the Parcel object, process it, and return the result;
6. The entire call process is a synchronization process. During server processing, the client will block it.
 

 
2. service manager
 
Service Manager is a linux-level process. As its name implies, it is the service Manager. What is the concept of service here? The concept of service and init. the services in rc are different. init. the services in rc are all linux processes, but here the service is not necessarily a process, that is, one or more services may belong to the same linux Process. Unless otherwise specified in this Article, the android native service is used.
Before any service is used, it must be registered with SM (Service Manager). When the client needs to access a service, it should first query SM for whether the service exists. If SM has this service, the handle of this service is returned to the client, which is the unique identifier of each service.

The SM entry function is in service_manager.c. The following is the SM Code section.
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 )){
LOGE ("cannot become context manager (% s)/n", strerror (errno ));
Return-1;
}
 
Svcmgr_handle = svcmgr;
Binder_loop (bs, svcmgr_handler );
Return 0;
}
The main work of this process is as follows:
1. initialize the binder, enable the/dev/binder device, and map 128 kb of space to the binder in the memory;
2. specify the handle of the proxy binder corresponding to sm as 0. When the client tries to communicate with SM, it needs to create a proxy binder whose handle is 0, the proxy binder here is actually the proxy interface described in section 1;
3. Notify binder driver (BD) to make SM the context manager of BD;
4. maintain an endless loop. In this endless loop, the binder driver in the kernel is constantly read to check whether there is readable content; that is, whether there are service operation requirements. If yes, then the svcmgr_handler callback is called to process the request operation.
5. SM maintains a svclist list to store service information.
 

 
Here, we need to declare that when a service is registered with SM, the service is a client, and SM acts as the server. When a process needs to communicate with the service, the process is client and the service serves as the server. Therefore, the service is not necessarily a server, and sometimes it also exists as a client.
 
The following sections describe several concepts related to the binder communication, so we will introduce the SM function in the following sections.
 
The communication between applications and services involves two binder communications.
 
1. The application queries SM for whether the service exists. If a proxy binder obtains the service, this is a binder communication;
2. The application calls the service method through the proxy binder. This is the second binder communication.
 
3. ProcessState
 
ProcessState is designed in singleton mode. Each process needs to maintain a ProcessState instance to describe the binder status of the current process when using the binder Mechanism for communication.
ProcessState has the following two main functions:
1. Create a thread to communicate with the binder module in the kernel. This thread is called a Pool thread;
2. Create a BpBinder object for the specified handle and manage all BpBinder objects in the process.
 
3.1 Pool thread
 
In Binder IPC, all processes start a thread to directly communicate with BD, that is, to read and write BD without stopping. The implementation of this thread is an IPCThreadState object, this type is described below.
The following describes how to start a Pool thread:
ProcessState: self ()-> startThreadPool ();
3.2 obtain BpBinder
 
The main function of BpBinder is to send the data of the call request from the client to BD. It is the core object of client-side binder communication. It sends the data of the call request to BD by calling the transact function. Its constructor is as follows:
BpBinder (int32_t handle );
Through the BpBinder constructor, BpBinder records the handle of the server in the current communication. when data is sent, it notifies the BD data sending target.
ProcessState obtains the BpBinder object in the following way:
ProcessState: self ()-> getContextObject (handle );
In this process, ProcessState will maintain a vector mHandleToObject of BpBinder. Whenever ProcessState creates a BpBinder instance, it will go back and query mHandleToObject. If the corresponding handle already has a binder pointer, it will not be created, otherwise, create a binder and insert it to mHandleToObject.
Generally, a client-side proxy interface is built as a parameter for the BpBinder instance created by ProcessState. This proxy interface is in the form of BpINTERFACE. For example, when communicating with SM, the client creates a proxy interface BpServiceManager.


4. IPCThreadState
 
IPCThreadState is also designed in singleton mode. Because each process only maintains one ProcessState instance and ProcessState only starts one Pool thread, that is, each process only starts one Pool thread. Therefore, each process only needs one IPCThreadState.
The actual content of Pool thread is:
IPCThreadState: self ()-> joinThreadPool ();
 
ProcessState has two Parcel members, mIn and mOut. The Pool thread constantly queries whether data in BD is readable. If data is read and saved to mIn, at the same time, the mOut constantly checks whether there is data to be sent to BD. If so, the content is written to BD. In short, the data read from BD is saved to mIn, data to be written to BD is stored in mOut.
The BpBinder instance generated in ProcessState writes data to mOut by calling the transact function of IPCThreadState. In this way, the sending process of the client call request in the binder IPC process is clear.
 
IPCThreadState has two important functions: the talkWithDriver function reads and writes data from BD, And the executeCommand function parses and executes data in mIn.
 
5. Main base classes
 
5.1 basic class IInterface
Provides interfaces for the server. Its subclass declares all the methods that the service can implement;
 
5.2 base class IBinder
Both BBinder and BpBinder are subclasses of IBinder. Therefore, we can see that IBinder defines the communication protocol of binder IPC, and the receiving and sending operations performed by BBinder and BpBinder within the framework of this Protocol, built a basic binder IPC Mechanism.
5.3 base class BpRefBase
After the client queries SM to obtain the required BpBinder, BpRefBase is responsible for managing the currently obtained BpBinder instance.
 
 
6. Two interface classes
 
6.1 BpINTERFACE
 
If the client wants to use binder IPC to communicate with each other, it first queries and obtains the BpBinder of the server service from the SM. on the client side, this object is considered as a remote proxy of the server. To enable the client to call a remote server like a local call, the server must provide an interface to the client. The client creates a BpINTERFACE Based on the interface and uses this object, client Applications can directly call the server method just like local calls. Instead of worrying about the specific binder IPC implementation.
Let's take a look at the prototype of BpINTERFACE:
Class BpINTERFACE: public BpInterface <IINTERFACE>
 
Follow the inheritance relationship and look up
Template <typename INTERFACE>
Class BpInterface: public INTERFACE, public BpRefBase
 
Bpinterfaces are inherited from interfaces and BpRefBase;
● BpINTERFACE implements local operations on each method in the service, and sends parameters of each method to BD in the form of Parcel.
For example
Virtual status_t addService (const String16 & name, const sp <IBinder> & service)
{
Parcel data, reply;
Data. writeInterfaceToken (IServiceManager: getInterfaceDescriptor ());
Data. writeString16 (name );
Data. writeStrongBinder (service );
Status_t err = remote ()-> transact (ADD_SERVICE_TRANSACTION, data, & reply );
Return err = NO_ERROR? Reply. readExceptionCode (): err;
}
● BpBinder is managed as a member and stored in mRemote. BpServiceManager calls BpRefBase remote () to obtain the BpBinder pointer.
 
6.2 BnINTERFACE
 
When defining the service of android native, each service inherits from BnINTERFACE (the INTERFACE is the service name ). The BnINTERFACE type defines an onTransact function, which is used to unpack the received Parcel and execute the client-side request method.
 
Follow the inheritance relationship of BnINTERFACE and look up again,
Class BnINTERFACE: public BnInterface <IINTERFACE>
 
IINTERFACE is the common interface class of the client-side proxy interface BpINTERFACE and the server-side BnINTERFACE. The purpose of this common interface class is to ensure the consistency of the service method at both ends of the C-S.
 
Look up
Class BnInterface: public INTERFACE, public BBinder
 
At the same time, we found the BBinder type. What is this type used? Since each service can be regarded as a binder, the operations and status maintenance of the true server-side binder are implemented by inheriting from BBinder. It can be seen that BBinder is the essence of service as a binder.
 
So what is the difference between BBinder and BpBinder?
 
In fact, their difference is very simple. BpBinder is a proxy created by the client for message sending, while BBinder is a channel used by the server to receive messages. View the code and you will find that although there are two types of transact methods, they have different functions. The BpBinder's transact method is to send messages to the IPCThreadState instance, notifying them that they want to send messages to BD; the BBinder is used to pass the message of bd to the IPCThreadState instance through the transact method of BBinder to its subclass BnSERVICE to perform server-side operations.
 
7. Parcel
 
Parcel is the most basic communication unit in binder IPC, which stores the parameters of function calls between C-S. however, Parcel can only store basic data types. For complex data types, you need to split them into basic data types for storage.
 
The simple Parcel read/write is not introduced. The following describes two functions.
 
7.1 writeStrongBinder
 
This function can be called when the client needs to send a binder to the server. For example
Virtual status_t addService (const String16 & name, const sp <IBinder> & service)
{
Parcel data, reply;
Data. writeInterfaceToken (IServiceManager: getInterfaceDescriptor ());
Data. writeString16 (name );
Data. writeStrongBinder (service );
Status_t err = remote ()-> transact (ADD_SERVICE_TRANSACTION, data, & reply );
Return err = NO_ERROR? Reply. readExceptionCode (): err;
}
 
 
Let's take a look at the writeStrongBinder entity.
Status_t Parcel: writeStrongBinder (const sp <IBinder> & val)
{
Return flatten_binder (ProcessState: self (), val, this );
}
 
Next, let's look at flatten_binder.
Status_t flatten_binder (const sp <ProcessState> & proc,
Const sp <IBinder> & binder, Parcel * out)
{
Flat_binder_object obj;

Obj. flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;
If (binder! = NULL ){
IBinder * local = binder-> localBinder ();
If (! Local ){
BpBinder * proxy = binder-> remoteBinder ();
If (proxy = NULL ){
LOGE ("null proxy ");
}
Const int32_t handle = proxy? Proxy-> handle (): 0;
Obj. type = BINDER_TYPE_HANDLE;
Obj. handle = handle;
Obj. cookie = NULL;
} Else {
Obj. type = BINDER_TYPE_BINDER;
Obj. binder = local-> getWeakRefs ();
Obj. cookie = local;
}
} Else {
Obj. type = BINDER_TYPE_BINDER;
Obj. binder = NULL;
Obj. cookie = NULL;
}

Return finish_flatten_binder (binder, obj, out );
}
 
Take addService as an example. Its parameter is a BnINTERFACE type pointer, and BnINTERFACE inherits from BBinder,
BBinder * BBinder: localBinder ()
{
Return this;
}
Therefore, the binder type written to Parcel is BINDER_TYPE_BINDER. When you read the SM Code, you will find that if the binder type of the service received by SM is not BINDER_TYPE_HANDLE, SM will not add this service to svclist, but it is clear that each service is successfully added. The binder type passed by addService at the beginning is BINDER_TYPE_BINDER, and the binder type received by SM is BINDER_TYPE_HANDLE, so what happened in this process?
In order to understand this problem, I spent a lot of events and finally found the problem. The following operations were previously performed in BD (drivers/staging/android/Binder. c ):
 
Static void binder_transaction (struct binder_proc * proc,
Struct binder_thread * thread,
Struct binder_transaction_data * tr, int reply)
{
........................................ ..
 
If (fp-> type = BINDER_TYPE_BINDER)
Fp-> type = BINDER_TYPE_HANDLE;
Else
Fp-> type = BINDER_TYPE_WEAK_HANDLE;
Fp-> handle = ref-> desc;
........................................ ..
}
 
 
 
After reading the addService code, you will find that SM only saves the handle and service name of the service binder. So when the client needs to communicate with a service, how can we obtain the service binder? Look at the next Function
7.2 readStrongBinder
 
After the server receives a client call request, if a binder needs to be returned, it can send the binder to BD. When the IPCThreadState instance receives the returned Parcel, the client can use this function to read the binder returned by the server.
 
Sp <IBinder> Parcel: readStrongBinder () const
{
Sp <IBinder> val;
Unflatten_binder (ProcessState: self (), * this, & val );
Return val;
}
 
View unflatten_binder in
 
Status_t unflatten_binder (const sp <ProcessState> & proc,
Const Parcel & in, sp <IBinder> * out)
{
Const flat_binder_object * flat = in. readObject (false );

If (flat ){
Switch (flat-> type ){
Case BINDER_TYPE_BINDER:
* Out = static_cast <IBinder *> (flat-> cookie );
Return finish_unflatten_binder (NULL, * flat, in );
Case BINDER_TYPE_HANDLE:
* Out = proc-> getStrongProxyForHandle (flat-> handle );
Return finish_unflatten_binder (
Static_cast <BpBinder *> (out-> get (), * flat, in );
}
}
Return BAD_TYPE;
}
 
If the binder type returned by the server is BINDER_TYPE_BINDER, that is, if a binder reference is returned, the binder is obtained directly. If the binder type returned by the server is BINDER_TYPE_HANDLE, that is, if the server returns only the binder handle, you need to recreate a BpBinder and return it to the client.
 
 
The code above shows that the binder of the service saved by SM is only a handle, while the client obtains the handle from SM to re-build the proxy binder to communicate with the server.
 
 
Here, we mention a special situation where both parties of the binder communication can act as the client or server. That is to say, the binder communication is a half-duplex communication. In this case, the operation process is more complex than the work order, but the basic principle is the same. If you are interested, you can analyze the MediaPlayer and MediaPlayerService examples.
 
8. Classic bridge section analysis
 
Main _ mediaserver. cpp
Int main (int argc, char ** argv)
{
// Create a ProcessState instance of the mediaserver Process
Sp <ProcessState> proc (ProcessState: self ());
// Obtain SM BpServiceManager
Sp <IServiceManager> sm = defaultServiceManager ();
LOGI ("ServiceManager: % p", sm. get ());
// Add the services supported by mediaserver.
AudioFlinger: instantiate ();
MediaPlayerService: instantiate ();
CameraService: instantiate ();
AudioPolicyService: instantiate ();
// Start the pool thread of ProcessState
ProcessState: self ()-> startThreadPool ();
// This step is too repetitive. Adding or not is irrelevant.
IPCThreadState: self ()-> joinThreadPool ();
}
 
9. Java-layer binder Mechanism
 
After learning about the native communication mechanism, we can analyze the binder Mechanism at the JAVA layer, which will be very understandable. It only encapsulates the native binder. This part is basically not a complicated process. I will not go into details here.
 
From du Wentao's column

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.