Android advanced: Binder, IBinder, Parcel, AI of Android cross process communication mechanism

###Foreword
Binder mechanism is a cross process communication mechanism provided by Android system. This article will start with the introduction of Linux related basic concept knowledge, lead out binder mechanism from the basic concept knowledge, summarize the advantages and disadvantages of binder mechanism and Linux system cross process mechanism, and then analyze binder's communication model and principle. The best embodiment of binder mechanism is AIDL, so it will be divided later Analyze the implementation principle of AIDL, and finally simply put forward the binder system of AMS. The introduction of IBinder, binder and Parcel will be interspersed in the middle of the whole article. It is not difficult to read the whole article, and it will not involve the binder principle of framework layer. The AIDL part needs to have the use basis of AIDL

###Basic concepts
In the basic concept part, some mechanisms of Linux are introduced. The main purpose is to express the reason for the emergence of Binder driver. If you are familiar with Linux, you can skip this part directly. See the fifth point

1, Process isolation

For security reasons, one process cannot operate the data of another process, and then an operating system must have the feature of process isolation. In Linux system, the virtual memory mechanism allocates linear continuous memory space for each process. The operating system maps this virtual memory space to the physical memory space. Each process has its own virtual memory space, so it can't operate the memory space of other processes. Each process can only operate its own virtual memory space, and only the operating system has permission to operate things Manage memory space.

Process isolation ensures the memory security of each process, but in most cases, data communication between different processes is inevitable, so the operating system must provide a cross process communication mechanism

2, User space and kernel space

User space: indicates that the process is running in a specific mode of operation and does not have permission to contact physical memory or devices
Kernel space: it means that independent of ordinary applications, it can access protected memory space and all permissions of underlying hardware devices

3, System call / kernel state / user state

In abstract, the concept of security boundary in the operating system is like the concept of loop (provided that the system supports this feature). A specific permission group is held in a loop, Intel supports four layer loop, but Linux Only two of them are used (ring 0 has all permissions, ring 3 has the least permissions, ring 1 and ring 2 are not used), the system process runs in ring 1, and the user process runs in ring 3. If a user process needs other advanced permissions, it must transition from ring 3 to ring 0, and the transition needs to pass a gateway for security parameter check, which is called the system Call, there will be a certain amount of calculation cost in the execution process. So the only way user space can access kernel space is through system call

4, Kernel module / driver

Through system call, user space can access kernel space. How does it access kernel space? The dynamic loadable kernel module mechanism of Linux solves this problem. Modules are programs with independent functions, which can be compiled separately, but can not be run independently. In this way, Android system can run in the kernel space by adding a kernel module, through which the user processes can complete the communication. In Android system, the kernel module running in the kernel space, which is responsible for the communication of user processes through Binder, is called Binder driver

5, A simple summary

It is very easy to digest knowledge points by connecting and understanding all the previous concepts:

1. The virtual memory mechanism of Linux leads to the isolation of memory, which leads to the process isolation
2. The emergence of process isolation results in the division of memory operations into user space and kernel space
3. User space needs to access kernel space across permissions, which must be implemented by system call
4. System calls need to be completed by kernel module / driver

The first three steps determine that inter process communication needs to be realized by kernel module / driver, and Binder driver is used to realize inter process communication in kernel module / driver

###Why use Binder
Linux provides cross process methods such as pipeline, message queue, semaphore, memory sharing, socket, etc. why does Android choose Binder?

1, Good transmission performance

  • Socket: it is a general interface, which leads to low transmission efficiency and high cost
  • Shared memory: although it does not need to copy data during transmission, its control mechanism is complex
  • Binder: complex data type transfer can reuse memory and need to copy data once
  • Pipeline and message queue: adopt store and forward mode, at least copy data twice, low efficiency

2, High security

  • Traditional process: the communication mode does not strictly verify the identity of both sides of the communication, only on the upper layer protocol
  • Binder mechanism: from the protocol itself, it supports the identity verification of both sides of the communication, thus greatly improving the security
    ###Binder communication model
    First, familiarize yourself with these concepts before understanding the model:

  • Client process: cross process communication client (running in a process)
  • Server process: the server side of cross process communication (running in a process)
    Binder driver: a medium for cross process communication
  • ServiceManager: provides registration and query of services in cross process communication (running in System process)

Here is just a simple model, just understand the communication process of the model:

1.Server side registers in ServiceManager through Binder driver
2. The client obtains the Server registered in ServiceManager through Binder driver
3.Client side communicates with Server side through Binder driver
###Binder communication principle

After understanding the model process, start to understand the communication principle of the model:

1. The service side registers the add method of the Object object Object in the lookup table of the ServiceManager through the Binder driver
2. The client finds the add method of the Object object in the lookup table of ServiceManager through Binder driver, and returns the add method of the proxy Object. The add method is an empty implementation. The proxy Object is not a real Object object, but an add method of the proxy class encapsulated by Binder driver
3. when the Client side invokes the add method, the Client will invoke the add method of the proxy object, request ServiceManager through Binder drive to find the real object of the Service end, then call the add method of the Service side.

###Binder objects and binder drivers

  • Binder object: the object for inter process communication in binder mechanism. It is binder local object for Service end and binder proxy object for Client end
  • Binder driver: the medium for interprocess communication in binder mechanism. Binder driver will do special processing for objects with cross process delivery capability, and automatically complete the conversion of proxy objects and local objects

Because Binder driver will do special processing for the object with cross process delivery ability, and automatically complete the conversion of proxy object and local object, it saves the relevant information of each Binder object across the process in the driver, the Binder local object (or Binder entity) is saved in the data structure of Binder u node, and the Binder proxy object (or Binder reference / handle) is saved in the Binder_ Data structure of ref

###Binder for Java layer

  • Binder class: is a binder local object
  • Binder proxy class: an internal class of the BinderProxy class, which represents the local proxy of the BinderProxy object of a remote process
  • Parcel class: a container, which is mainly used to store serialized data, which can then be passed between processes through Binder
  • IBinder interface: it represents the ability of cross process transmission. By implementing this interface, you can transfer this object across processes
  • IInterface interface: the calling contract between client and server. Implementing this interface represents the ability of remote server object. Because the implementation of asBinder method of IInterface interface can return Binder local object or proxy object

The binder class and BinderProxy class are inherited from IBinder, so they have the ability to transfer across processes. When crossing processes, the binder driver will automatically complete the conversion of these two objects. IBinder is the basic interface of remote object and the core part of lightweight remote call mechanism designed for high performance, but it is not only used for remote call, but also for in-process call. The IBinder interface defines the protocol for interacting with remote objects. It is recommended not to implement this interface directly, but to derive from binder. Binder implements the IBinder interface, but it doesn't need to directly implement this kind of interface. It is generated by a tool in the development package according to your needs. This tool is called aidi. You define the methods of remote objects through aidi language, then generate the derived classes of binder with aidi tool, and then use it

###AIDL
Since the compiler will generate a static inner class of Stub, this class inherits Binder, indicating that it is a Binder local object, and it implements the IInterface interface, indicating that it has the ability that the remote Server promises to the Client

1, Server

In the server side, we just need to implement Stub abstract class and its methods

private IBinder myS = new IMyAidlInterface.Stub() {  
    @Override  
    public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {  

    }  

    @Override  
    public int add(int num1, int num2) throws RemoteException {  
        Log.i("Hensen", "From the client AIDL request:num1->" + num1 + "::num2->" + num2);  
        return num1 + num2;  
    }  
}; 

2, Client

In the client, the AIDL interface can be obtained through the callback of bindService

private ServiceConnection conn = new ServiceConnection() {  
    @Override  
    public void onServiceConnected(ComponentName name, IBinder service) {  
        iMyAidlInterface = IMyAidlInterface.Stub.asInterface(service);  
    }  

    @Override  
    public void onServiceDisconnected(ComponentName name) {  
        iMyAidlInterface = null;  
    }  
};

public void add(View view) {  
    try {  
        int res = iMyAidlInterface.add(1, 2);  
        Log.i("Hensen", "Successful result of calling from the server:" + res);  
    } catch (RemoteException e) {  
        e.printStackTrace();
    }
}   

Sort out the calling process of the client:

1. Call Stub.asInterface to get BinderProxy object
2. Call the add method of BinderProxy object

3, Principle of analysis

1,Stub

The Stub class inherits from Binder, which means that the Stub is actually a Binder local object, and then implements the IMyAidlInterface interface. The IMyAidlInterface itself is an IInterface, so it carries some capabilities required by the client (here is the method add). This class has an internal class Proxy, which is the Binder Proxy object

/* 
 * This file is auto-generated.  DO NOT MODIFY. 
 * Original file: D:\\workspace5\\Boke\\app\\src\\main\\aidl\\com\\handsome\\boke\\IMyAidlInterface.aidl 
 */  
package com.handsome.boke;  
// Declare any non-default types here with import statements  

public interface IMyAidlInterface extends android.os.IInterface {  
    /** 
     * Local-side IPC implementation stub class. 
     */  
    public static abstract class Stub extends android.os.Binder implements com.handsome.boke.IMyAidlInterface {  
        private static final java.lang.String DESCRIPTOR = "com.handsome.boke.IMyAidlInterface";  

        /** 
         * Construct the stub at attach it to the interface. 
         */  
        public Stub() {  
            this.attachInterface(this, DESCRIPTOR);  
        }  

        /** 
         * Cast an IBinder object into an com.handsome.boke.IMyAidlInterface interface, 
         * generating a proxy if needed. 
         */  
        public static com.handsome.boke.IMyAidlInterface asInterface(android.os.IBinder obj) {  
            if ((obj == null)) {  
                return null;  
            }  
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);  
            if (((iin != null) && (iin instanceof com.handsome.boke.IMyAidlInterface))) {  
                return ((com.handsome.boke.IMyAidlInterface) iin);  
            }  
            return new com.handsome.boke.IMyAidlInterface.Stub.Proxy(obj);  
        }  

        @Override  
        public android.os.IBinder asBinder() {  
            return this;  
        }  

        @Override  
        public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {  
            switch (code) {  
                case INTERFACE_TRANSACTION: {  
                    reply.writeString(DESCRIPTOR);  
                    return true;  
                }  
                case TRANSACTION_basicTypes: {  
                    data.enforceInterface(DESCRIPTOR);  
                    int _arg0;  
                    _arg0 = data.readInt();  
                    long _arg1;  
                    _arg1 = data.readLong();  
                    boolean _arg2;  
                    _arg2 = (0 != data.readInt());  
                    float _arg3;  
                    _arg3 = data.readFloat();  
                    double _arg4;  
                    _arg4 = data.readDouble();  
                    java.lang.String _arg5;  
                    _arg5 = data.readString();  
                    this.basicTypes(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5);  
                    reply.writeNoException();  
                    return true;  
                }  
                case TRANSACTION_add: {  
                    data.enforceInterface(DESCRIPTOR);  
                    int _arg0;  
                    _arg0 = data.readInt();  
                    int _arg1;  
                    _arg1 = data.readInt();  
                    int _result = this.add(_arg0, _arg1);  
                    reply.writeNoException();  
                    reply.writeInt(_result);  
                    return true;  
                }  
            }  
            return super.onTransact(code, data, reply, flags);  
        }  

        private static class Proxy implements com.handsome.boke.IMyAidlInterface {  
            private android.os.IBinder mRemote;  

            Proxy(android.os.IBinder remote) {  
                mRemote = remote;  
            }  

            @Override  
            public android.os.IBinder asBinder() {  
                return mRemote;  
            }  

            public java.lang.String getInterfaceDescriptor() {  
                return DESCRIPTOR;  
            }  

            /** 
             * Demonstrates some basic types that you can use as parameters 
             * and return values in AIDL. 
             */  
            @Override  
            public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException {  
                android.os.Parcel _data = android.os.Parcel.obtain();  
                android.os.Parcel _reply = android.os.Parcel.obtain();  
                try {  
                    _data.writeInterfaceToken(DESCRIPTOR);  
                    _data.writeInt(anInt);  
                    _data.writeLong(aLong);  
                    _data.writeInt(((aBoolean) ? (1) : (0)));  
                    _data.writeFloat(aFloat);  
                    _data.writeDouble(aDouble);  
                    _data.writeString(aString);  
                    mRemote.transact(Stub.TRANSACTION_basicTypes, _data, _reply, 0);  
                    _reply.readException();  
                } finally {  
                    _reply.recycle();  
                    _data.recycle();  
                }  
            }  

            @Override  
            public int add(int num1, int num2) throws android.os.RemoteException {  
                android.os.Parcel _data = android.os.Parcel.obtain();  
                android.os.Parcel _reply = android.os.Parcel.obtain();  
                int _result;  
                try {  
                    _data.writeInterfaceToken(DESCRIPTOR);  
                    _data.writeInt(num1);  
                    _data.writeInt(num2);  
                    mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);  
                    _reply.readException();  
                    _result = _reply.readInt();  
                } finally {  
                    _reply.recycle();  
                    _data.recycle();  
                }  
                return _result;  
            }  
        }  

        static final int TRANSACTION_basicTypes = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);  
        static final int TRANSACTION_add = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);  
    }  

    /** 
     * Demonstrates some basic types that you can use as parameters 
     * and return values in AIDL. 
     */  
    public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException;  

    public int add(int num1, int num2) throws android.os.RemoteException;  
}  

2,asInterface

In the callback of the onserviceconnection of the Client bindService, obtain the remote service through the asInterface method. Its function parameter is obj of type IBinder. This object is driven to us. If it is a Binder local object, it is of type Binder. If it is a Binder proxy object, it is of type BinderProxy. In the asInterface method, queryLocalInterface will be called to find the Binder local object. If it is found, it means that both the Client and Server are in the same process. This parameter is directly the local object, which is directly cast and then returned. If it is not found, it means that it is a remote object, then it is necessary to create the Binder proxy object, so that the Binder proxy object can be used for the remote object Access to

/** 
 * Cast an IBinder object into an com.handsome.boke.IMyAidlInterface interface, 
 * generating a proxy if needed. 
 */  
public static com.handsome.boke.IMyAidlInterface asInterface(android.os.IBinder obj) {  
    if ((obj == null)) {  
        return null;  
    }  
    android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);  
    if (((iin != null) && (iin instanceof com.handsome.boke.IMyAidlInterface))) {  
        return ((com.handsome.boke.IMyAidlInterface) iin);  
    }  
    return new com.handsome.boke.IMyAidlInterface.Stub.Proxy(obj);  
}

3,add

When the client calls the add method, first serialize the data with Parcel, then call the mRemote.transact method, and mRemote is new Stub.Proxy(obj), which is BinderProxy object.

@Override  
public int add(int num1, int num2) throws android.os.RemoteException {  
    android.os.Parcel _data = android.os.Parcel.obtain();  
    android.os.Parcel _reply = android.os.Parcel.obtain();  
    int _result;  
    try {  
        _data.writeInterfaceToken(DESCRIPTOR);  
        _data.writeInt(num1);  
        _data.writeInt(num2);  
        mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);  
        _reply.readException();  
        _result = _reply.readInt();  
    } finally {  
        _reply.recycle();  
        _data.recycle();  
    }  
    return _result;  
}  

4,transact

The transact method of BinderProxy is the native method. Its implementation is in the native layer. It will use the Binder driver to complete the data transmission. When the data transmission is completed, it will wake up the Server side and call the onTransact function of the local object on the Server side

public native boolean transact(int code, Parcel data, Parcel reply,
            int flags) throws RemoteException;

5,onTransact
In the Server process, onTransact will call relevant functions according to the calling code, then write the result to reply and return it to the driver through super.onTransact. The driver wakes up the thread in the suspended Client process and returns the result

@Override  
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {  
    switch (code) {  
        case INTERFACE_TRANSACTION: {  
            reply.writeString(DESCRIPTOR);  
            return true;  
        }  
        case TRANSACTION_add: {  
            data.enforceInterface(DESCRIPTOR);  
            int _arg0;  
            _arg0 = data.readInt();  
            int _arg1;  
            _arg1 = data.readInt();  
            int _result = this.add(_arg0, _arg1);  
            reply.writeNoException();  
            reply.writeInt(_result);  
            return true;  
        }  
    }  
    return super.onTransact(code, data, reply, flags);  
}  

6. Digression

Why the generated file is not directly divided into 1 interface and 2 classes, which is clear. Android's design makes sense. When there are multiple AIDL files, the Stub and Proxy classes will have the same name. Put them in their respective AIDL interfaces, and they will be distinguished

###Binder system of AMS

AMS is the most core service in Android, which is mainly responsible for the start-up, switching, scheduling of the four components in the system, as well as the management and scheduling of the application process. It can be seen from the figure:

###Conclusion
Here is just a simple understanding of the basic principle of Binder mechanism. Later, I will study the knowledge of framework layer. If interested students can complete cross process communication without relying on AIDL tools and handwritten remote Service, so as to deepen the understanding of AIDL and Binder. One thinks that this is the best way to remember. Let's write it together

The article is not easy. If you like this article, or it can help you, I hope you like it a lot, forward it and pay attention to it. The article will be updated continuously. Absolutely dry!!!

Tags: Android Linux Java socket

Posted on Wed, 22 Apr 2020 11:18:27 -0700 by boneXXX