Binder, AIDL, remote service combined with source code learning records


This article is a summary of knowledge and answers to doubts in the process of learning binder for myself. This article does not explain the specific usage methods, only the source code. Please criticize and correct any mistakes and make mutual progress.

What is binder

The main function of Binder is inter-process communication. Several other inter-process communication methods include file system, socket, pipe, Intents, ContentProviders, Messenger, and Binder.

The Android system is divided into four layers. From top to bottom, they are the application layer, the Framework layer, the native layer, and the driver layer. The picture below is the stolen picture:

Insert picture description here
The application layer and the service layer in Android are not in the same process, the system services are in separate processes, and different applications belong to different processes, to ensure that each process can run separately, and realize the isolation between the application layer and the system layer.

Binder is implemented based on memory mapping (mmap). The user area in the process cannot directly interact with the physical device. Therefore, to read the data on the disk to the user space, two copies are required:

The first time: from disk space to kernel space

Second time: from kernel space to user control.

Therefore, the use of memory mapping mmap to establish a mapping between physical media and user space can reduce the number of copies and improve efficiency

Binder is not a physical medium, so the Binder driver uses mmap () mapping relationship, not between the physical medium and the user control, but to establish the mapping between the kernel space and the data cache space.

The complete Binder communication process:
1. The Binder driver creates a data receiving buffer in the kernel space.
2. The kernel space opens up a kernel buffer to establish the mapping between the kernel cache area and the data reception buffer area in the kernel, and at the same time, the kernel data is established The mapping relationship between the receive buffer and the address of the user process space.
3. The sender process copies the data to the kernel buffer area through a system call. The mapping between the kernel buffer area and the receiving process sends the data to the user control of the receiving process, thus completing an inter-process communication.
(This picture is also a direct stolen picture)
Insert picture description here

The difference between transact and onTransact in binder

The basis for Binder to achieve remote communication is IBinder, this interface, and transact is a method in this interface

/**
 * Perform a generic operation with the object.
 * 
 * @param code The action to perform.  This should
 * be a number between {@link #FIRST_CALL_TRANSACTION} and
 * {@link #LAST_CALL_TRANSACTION}.
 * @param data Marshalled data to send to the target.  Must not be null.
 * If you are not sending any data, you must create an empty Parcel
 * that is given here.
 * @param reply Marshalled data to be received from the target.  May be
 * null if you are not interested in the return value.
 * @param flags Additional operation flags.  Either 0 for a normal
 * RPC, or {@link #FLAG_ONEWAY} for a one-way RPC.
 */
public boolean transact(int code, Parcel data, Parcel reply, int flags)
    throws RemoteException;

And onTransact is a method in Binder.java that inherits this interface

/**
 * Default implementation is a stub that returns false.  You will want
 * to override this to do the appropriate unmarshalling of transactions.
 *
 * <p>If you want to call this, call transact().
 */
protected boolean onTransact(int code, Parcel data, Parcel reply,
        int flags) throws RemoteException {

The role of transact is to allow you to make calls to remote IBinder objects. The second method allows your remote objects to receive calls accordingly. The APIs in IBinder are executed synchronously. The source code is as follows

/**
 * Default implementation rewinds the parcels and calls onTransact.  On
 * the remote side, transact calls into the binder to do the IPC.
 */
public final boolean transact(int code, Parcel data, Parcel reply,
        int flags) throws RemoteException {
    if (false) Log.v("Binder", "Transact: " + code + " to " + this);

    if (data != null) {
        data.setDataPosition(0);
    }
    boolean r = onTransact(code, data, reply, flags);
    if (reply != null) {
        reply.setDataPosition(0);
    }
    return r;
}

Transact does not return until the other party's Binder.onTransact method is called. The data passed by transact is Parcel, which is a general buffer. In addition to data, there are some metadata describing the content. Metadata is used to manage the IBinder object. References, so that these references can be saved in the buffer from one process to another, it can be guaranteed that when an IBinder is written to Parcel and sent to another process, if another process uses the same IBinder reference To the original process, the original process can receive the reference of the IBinder issued. This mechanism makes the IBinder and Binder manage between processes like a unique identifier.

Binder支持进程间的递归调用,进程执行自己的IBinder的transact()调用进程B 的Binder,进程B在其Binder.onTransact()中又用transact()向进程A 发起调用,那么进程A 在等待它发出调用返回的同时,还会用Binder.onTransact()响应进程B的transact。
Binder的作用让我们跨进程间的调用变成如同调用接口一般。

Parameter analysis in transact:

code: int type, this represents the requestID of this communication, so that the corresponding code can be called on the Binder side according to the requestID, which can be understood as the method name
data: it can be understood as the parameter in the
method reply: the return value of the method
flag: This flag represents whether the Binder communication is synchronous or asynchronous, which can be ignored for the time being. We focus on three.

What is AIDL

aidl is essentially a encapsulation of Binder

The following content is suitable for having successfully established AIDL communication, and then watching the java file generated by the AIDL file.

Stub and proxy in AIDL

First look at the source code of asInterface. It is a static method in the abstract static class Stub. The meaning in the source code is that if this is a call between the same process, then return to Stub, if it is a remote call, then return to Stub. proxy object

/**
* Cast an IBinder object into ancs.dn.remote.AIDLRemoteInterface interface,
 * generating a proxy if needed.
 */
public static cs.dn.remote.AIDLRemoteInterface asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}

android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof cs.dn.remote.AIDLRemoteInterface))) {
return ((cs.dn.remote.AIDLRemoteInterface)iin);
}
return new cs.dn.service.remote.AIDLRemoteInterface.Stub.Proxy(obj);
}

Proxy is a static internal class in Stub. The onconnect below is an implementation class in my AIDL file. The implementation in proxy is as follows. The onConnect method shows that Parcel data is used to write DESCRIPTOR into the interface, and then call The transact method, as mentioned above when talking about the binder, the role of transact is to initiate a call to the remote process, and it will not return until the other party's Binder.onTransact method call is completed. This process is performed synchronously.

private static class Proxy implements cn.kuwo.service.remote.AIDLRemoteInterface
{
@Override public void onConnect() throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_onConnect, _data, _reply, 0);
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
}

Remote service and local service

AIDL is generally used in conjunction with service, first look at what is a remote service

android:exported="true"

The function of this sentence indicates that this service can be suspended by other apps, and service can be started by other apps just like Activity. (I have been confused for a long time before distinguishing remote and being called up by other processes is not the same thing)

android:process=":remote"

This sentence means that the service is running in a separate process. For the service control, there is another magical place. It can return a Binder object in serviceConnect. It has to be said that the service space was designed with this problem in mind at the beginning of the design. Please see the example below.

    <service
        android:name=".RemoteService"
        android:enabled="true"
        android:exported="true"
        android:process=":remote">
        <intent-filter android:priority="1000">
            <action android:name="cs.dn..RemoteService"/>
        </intent-filter>
    </service>
    <service
        android:name=".MainService"
        android:enabled="true"
        android:exported="true">
        <intent-filter android:priority="1000">
            <action android:name="cs.dn.MainService"/>
        </intent-filter>
    </service>

Take a look at the source code of bindService in service:

public abstract boolean bindService(@RequiresPermission Intent service,
        @NonNull ServiceConnection conn, @BindServiceFlags int flags);

The first parameter in this method is an intent, and the second is a ServiceConnection. Let's take a look at the source code of ServiceConnection.

/**
 * Interface for monitoring the state of an application service.  See
 * {@link android.app.Service} and
 * {@link Context#bindService Context.bindService()} for more information.
 * <p>Like many callbacks from the system, the methods on this class are called
 * from the main thread of your process.
 */
public interface ServiceConnection {
/**
 * Called when a connection to the Service has been established, with
 * the {@link android.os.IBinder} of the communication channel to the
 * Service.
 *
 * @param name The concrete component name of the service that has
 * been connected.
 *
 * @param service The IBinder of the Service's communication channel,
 * which you can now make calls on.
 */
void onServiceConnected(ComponentName name, IBinder service);

/**
 * Called when a connection to the Service has been lost.  This typically
 * happens when the process hosting the service has crashed or been killed.
 * This does <em>not</em> remove the ServiceConnection itself -- this
 * binding to the service will remain active, and you will receive a call
 * to {@link #onServiceConnected} when the Service is next running.
 *
 * @param name The concrete component name of the service whose
 * connection has been lost.
 */
void onServiceDisconnected(ComponentName name);

/**
 * Called when the binding to this connection is dead.  This means the
 * interface will never receive another connection.  The application will
 * need to unbind and rebind the connection to activate it again.  This may
 * happen, for example, if the application hosting the service it is bound to
 * has been updated.
 *
 * @param name The concrete component name of the service whose
 * connection is dead.
 */
default void onBindingDied(ComponentName name) {
}
}

This is an interface class that contains three callbacks to monitor the connection status of the service.

void onServiceConnected(ComponentName name, IBinder service);

This is successfully called in the server connection, pay attention to his second parameter, it is an IBinder, and this binder is also our top priority for cross-process calls.

Remote service establishes connection with AIDL

@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
    LogUtils.log(TAG,"onServiceConnected","componentName: " + componentName.getClassName());
    remoteInterface = AIDLRemoteInterface.Stub.asInterface(iBinder);
    try {
        remoteInterface.connect(PlayDelegateImp.getInstance());
    }catch (RemoteException e){
        LogMgr.e(TAG,e);
    }

。。。。。。
    super.onServiceConnected(componentName, iBinder);

}

The above is a piece of pseudo-code in my own code. We call the AIDLRemoteInterface.Stub.asInterface(iBinder) method to obtain the AIDL interface. From this point on, all the calls in the interface can be obtained directly through remoteInterface. Please pay attention to this. The binder is the IBinder object of the remote service defined by ourselves.

So far, the combination of binder, AIDL and remote services has been briefly introduced
(reference:
https://blog.csdn.net/u013309870/article/details/105328743
https://blog.csdn.net/sergeycao/article/ details/52585411
https://www.jianshu.com/p/2228c6c67144
)

Guess you like

Origin blog.csdn.net/u011976443/article/details/114898888