Android: simple understanding and use of Binder mechanism of Android learning notes

Binder mechanism

1. Background

Before we explain Binder, let's learn some basic knowledge of Linux

1.1. Process space division




IPC is inter process communication. Android is based on Linux. For security reasons, different processes cannot operate on each other's data, which is called "process isolation".

1.2. Process isolation & cross process communication (IPC)

Process isolation

  • In order to ensure security independence, one process cannot directly operate or access another process, that is, Android processes are independent and isolated from each other

Cross process communication (IPC)

  • That is, data interaction and communication are required between processes

Basic principles of cross process communication

Android applications and system services run in different processes for security, stability and memory management, but applications and system services need to communicate and share data.

advantage

  • Security: each process runs separately, which can ensure the isolation of the application layer from the system layer.
  • Stability: if one process crashes, it will not cause other processes to crash.
  • Memory allocation: if a process is not needed, it can be removed from memory and the corresponding memory can be recycled.

2. What the hell is Binder?


Take the activity as an example. It can be seen from the above figure that the activity is controlled by the ActivityManager. In fact, the ActivityManager controls the activity by obtaining the ActivityManagerService service through Binder. Moreover, the ActivityManager is the FrameWork layer of the Android system and is not the same process as the activity in the application.

  • A process space is divided into user space Kernel space (Kernel), that is to isolate the user Kernel in the process;
  • Data in user space cannot be shared, but data in kernel space can be shared. One process cannot directly operate or access another process, that is, Android processes are independent and isolated from each other
  • The cross process communication mechanism model is based on Client - Server mode. client\server\serverManager belongs to different processes. If you want to realize cross process communication, you need Binder and other inter process communication methods.

3. Binder cross process communication mechanism model

3.1 model schematic diagram

Binder cross process communication mechanism model is based on Client - Server mode

3.1.1 model expansion

A process cannot directly operate another process, such as reading the data of another process or writing data to the memory space of another process. The communication between processes can only be through the kernel process. Therefore, the process communication tool Binder is used here, as shown in the figure below:

However, there is another problem above, that is, the client and service need to deal directly with the binder driver, but in fact, the client and service do not want to know the relevant protocols of the binder, so the client further handles the interaction with the binder by adding a proxy and the service by adding a stub.


The advantage is that both the client and the service do not have to deal with the binder directly. The above figure seems to be perfect, but the Android system further encapsulates and does not let the client know the existence of binder. The Android system provides a Manager to manage the client. As shown below:

In this way, the client only needs to be managed by the manager, and there is no need to care about process communication. In fact, the manager is very familiar. For example, the activity is controlled by the ActivityManager, which controls the activity through the Binder to obtain the ActivityManagerService. In this way, we don't need to use Binder to communicate with ActivityManagerService.

3.2 model composition and role description


3.3 Binder drive


Core principles of cross process communication

For the core principle: memory mapping, please refer to the article: Operating system: Graphic detailed memory mapping

3.4 description of model principle steps



3.5 additional instructions

Note 1: the interaction between Client process, Server process & service manager process must be driven by Binder (using open and ioctl file operation functions), rather than direct interaction


reason:

  • Client process, Server process & service manager process belong to the user space of process space, and inter process interaction is not allowed
  • Binder driver belongs to the kernel space of process space and can carry out inter process & intra process interaction

Note 2: Binder driver & service manager process belongs to Android infrastructure (i.e. the system has been implemented); The Client process and Server process belong to the Android application layer (which needs to be implemented by the developer)

Description 3: thread management of Binder request

4. Specific implementation principle of Binder mechanism

The implementation of Binder mechanism in Android mainly depends on Binder class, which implements IBinder interface

4.1 example description

The Client process needs to call the addition function of the Server process (adding integers a and b)

  • The Client process needs to pass two integers to the Server process
  • The Server process needs to return the added results to the Client process

4.1.1 step 1: register service

    
    Binder binder = new Stub();
    // Step 1: create Binder object - > > analysis 1

    // Step 2: create an anonymous class of IInterface interface class
    // Before creation, you need to define the interface -- > analysis 3 that inherits the IInterface interface in advance
    IInterface plus = new IPlus(){

          // Determine the method that the Client process needs to call
          public int add(int a,int b) {
               return a+b;
         }

          // Implement the only method in the IInterface interface
          public IBinder asBinder(){ 
                return null ;
           }
};
          // Step 3
          binder.attachInterface(plus,"add two int");
         // 1. Store (add two, int, plus) as a (key,value) pair into a map < string, iiinterface > object in the Binder object
         // 2. After that, the Binder object can obtain the reference of the corresponding IInterface object (i.e. plus) through querylocalinterface() according to add two int, and can rely on this reference to complete the call to the request method
        // After analysis, jump out


<-- Analysis 1: Stub class -->
    public class Stub extends Binder {
    // Inherited from Binder class - > > analysis 2

          // Replication onTransact()
          @Override
          boolean onTransact(int code, Parcel data, Parcel reply, int flags){
          // Wait until step 3 to explain the specific logic. Skip here first
          switch (code) { 
                case Stub.add:  { 

                       data.enforceInterface("add two int"); 

                       int  arg0  = data.readInt();
                       int  arg1  = data.readInt();

                       int  result = this.queryLocalIInterface("add two int") .add( arg0,  arg1); 

                        reply.writeInt(result); 

                        return true; 
                  }
           } 
      return super.onTransact(code, data, reply, flags); 

}
// Go back to step 1 above and continue with step 2

<-- Analysis 2: Binder class -->
 public class Binder implement IBinder{
    // The implementation of Binder mechanism in Android mainly depends on Binder class, which implements IBinder interface
    // IBinder interface: it defines the basic interface of remote operation object and represents a cross process transmission capability
    // The system will provide cross process transmission capability for each object that implements the IBinder interface
    // That is, Binder class objects have the ability of cross process transmission

        void attachInterface(IInterface plus, String descriptor);
        // effect:
          // 1. Store (descriptor, plus) as a (key,value) pair into a map < string, iinterface > object in the Binder object
          // 2. After that, the Binder object can obtain the reference of the corresponding IInterface object (i.e. plus) through querylocalinterface() according to the descriptor, and can rely on this reference to complete the call to the request method

        IInterface queryLocalInterface(Stringdescriptor) ;
        // Function: find the corresponding IInterface object (i.e. plus reference) according to the parameter descriptor

        boolean onTransact(int code, Parcel data, Parcel reply, int flags);
        // Definition: inherited from IBinder interface
        // Function: execute the target method requested by the Client process (subclass needs replication)
        // Parameter Description:
        // code: Client process request method identifier. That is, the Server process determines the requested target method according to the identity
        // data: parameters of the target method. (passed in by the Client process, here are integers a and b)
        // reply: the result after the target method is executed (returned to the Client process)
         // Note: run in Binder thread pool of Server process; When the Client process initiates a remote request, the remote request will require the system bottom layer to execute the callback method

        final class BinderProxy implements IBinder {
         // That is, the proxy object class of the Binder object created by the Server process
         // This class belongs to Binder's internal class
        }
        // Go back to analysis 1
}

<-- Analysis 3: IInterface Interface implementation class -->

 public interface IPlus extends IInterface {
          // Inherited from IInterface interface - > > analysis 4
          // Define the interface method to be implemented, that is, the method to be called by the Client process
         public int add(int a,int b);
// Return to step 2
}

<-- Analysis 4: IInterface Interface class -->
// Common interface defined by interprocess communication
// Cross process communication can be realized by defining the interface, and then implementing the interface on the server and calling the interface on the client.
public interface IInterface
{
    // There is only one method: return the Binder object associated with the current interface.
    public IBinder asBinder();
}
  // Go back to analysis 3

After registering the service, the Binder driver holds the Binder entity created by the Server process

4.1.2 step 2: obtain service

Before the Client process uses a Service (here is the addition function), it must obtain the corresponding Service information from the ServiceManager process through the Binder driver

4.1.3 step 3: use the service

Step 1: the Client process sends the parameters (integers a and b) to the Server process

// 1. The client process writes the data to be transferred to the Parcel object
// Data = data = parameters of the target method (passed in by the Client process, here are integers a and b) + identifier descriptor of IInterface interface object
  android.os.Parcel data = android.os.Parcel.obtain();
  data.writeInt(a); 
  data.writeInt(b); 

  data.writeInterfaceToken("add two int");;
  // The method object identifier allows the Server process to find the corresponding IInterface object (i.e. the plus created by the Server) in the Binder object through querylocalinterface() according to "add two int". The addition method that the Client process needs to call is in this object

  android.os.Parcel reply = android.os.Parcel.obtain();
  // reply: the result after the target method is executed (here is the result after addition)

// 2. Send the above data to the Binder driver by calling transact() of the proxy object
  binderproxy.transact(Stub.add, data, reply, 0)
  // Parameter Description:
    // 1. Stub.add: identifier of the target method (agreed between the Client process and the Server process, which can be any)
    // 2. data: the above Parcel object
    // 3. reply: returns the result
    // 0: it doesn't matter

// Note: after sending data, the thread of the Client process will be suspended temporarily
// Therefore, if the Server process performs time-consuming operations, please do not use the main thread to prevent ANR


// 3. Binder driver finds the Server process of the corresponding real binder object according to the proxy object (automatically executed by the system)
// 4. Binder driver sends data to the Server process and notifies the Server process to unpack (automatically executed by the system)

Step 2: the Server process calls the target method (i.e. addition function) according to the Client's request

// 1. After receiving the Binder driver notification, the Server process unpacks the data by calling the Binder object onTransact() & calling the target method
  public class Stub extends Binder {

          // Replication onTransact()
          @Override
          boolean onTransact(int code, Parcel data, Parcel reply, int flags){
          // code is the identifier of the target method agreed in transact()

          switch (code) { 
                case Stub.add:  { 
                  // a. Unpack data in Parcel
                       data.enforceInterface("add two int"); 
                        // a1.  Resolve the identifier of the target method object

                       int  arg0  = data.readInt();
                       int  arg1  = data.readInt();
                       // a2.  Get the parameters of the target method
                      
                       // b. Obtain the reference of the corresponding IInterface object (i.e. the plug created by the Server) through querylocalinterface() according to "add two int", and call the method through the object reference
                       int  result = this.queryLocalIInterface("add two int") .add( arg0,  arg1); 
                      
                        // c. Write calculation results to reply
                        reply.writeInt(result); 
                        
                        return true; 
                  }
           } 
      return super.onTransact(code, data, reply, flags); 
      // 2. Return the settlement result to Binder driver

Step 3: the Server process returns the result of the target method (i.e. the added result) to the Client process

  // 1. The binder driver returns the result along the original path according to the proxy object and notifies the Client process to obtain the returned result
  // 2. Receive the result through the proxy object (the previously suspended thread is awakened)

    binderproxy.transact(Stub.ADD, data, reply, 0);
    reply.readException();;
    result = reply.readInt();
          }
}

4.1.4 summary


advantage

Binder is more suitable for Android system than traditional IPC. The specific reasons include the following three points:

  • Binder itself is based on C/S architecture, which is more in line with the architecture of Android system
  • More advantages in performance: the communication of pipeline, message queue and Socket requires two copies of data, while Binder only needs one. You should know that for the IPC form at the bottom of the system, less than one data copy will have a great impact on the overall performance
  • Better security: in the traditional form of IPC, the identity (UID/GID) of the other party cannot be obtained, but Binder IPC is used
    These identifiers are passed automatically following the calling process. The Server side can easily know the identity of the Client side, which is very convenient for security inspection

5. Interview questions

When asked about the Android development interview, Binder doesn't know yet. It's enough to collect this one (with illustrations)

reference resources

1,Illustrate Binder mechanism in Android
2,Android Binder (perhaps the easiest to understand)
3,Graphic explanation of Binder cross process communication principle
4,Android interface definition language (AIDL)
5,[learning notes] exploration of Android development Art: IPC mechanism

Keywords: Java Android

Added by rbrown on Mon, 10 Jan 2022 01:53:48 +0200