A quote.
I’ve been working with A lot of the Framework recently, and I’ve had to deal with Some Binder, but I was a little nervous about writing this article. Moreover, it is not easy to describe the complexity of the Binder mechanism in a few words, and I am afraid that my understanding of Binder will mislead some friends (PS: no one reads…. anyway Pierce heart) so also referenced a lot of information.
This article is mainly from the perspective of Android development to roughly analyze some knowledge principles of Binder in the Java layer, to form a complete concept in your mind, such as the implementation principle of AIDL, how Binder communication and so on
If you are familiar with it, please check out the next section, which will introduce the process of launching the Activity and the Hook technology in Android:
Shock!!! Activity does not register? Hook you hand in hand
2. Summary of Binder
2.1 Why Android chooses Binder
Android is based on the Linux kernel, so to achieve inter-process communication, Android can actually use some original means of Linux, such as pipes, shared memory, socket, etc., but Android still uses Binder as the main mechanism. Binder has incomparable advantages.
Is really two aspects factors, process communication about performance, transmission efficiency, the traditional pipe adopt the way of memory buffer queue model, data from the sender first buffer copy into the buffer zone of kernel open up, and then from the kernel buffer copies to the receiver buffer area, there are at least two copy process, and socket know transmission efficiency is low, Expensive, used for cross-network process interaction is more, although shared memory does not need to copy.
As an open platform with a large number of developers, Android has a wide range of application sources, so it is very important to ensure the terminal security. The traditional IPC communication method does not have any measures and basically relies on the upper-layer protocol. On the one hand, it cannot confirm the reliable identity of the other party. Android assigns its own UID to each installed application, so the PROCESS UID is an important symbol to identify the process identity, traditional IPC to send similar UID can only be placed in the data packet, but also easy to intercept, malicious attack, socket need to expose their IP and port. Knowing these malicious programs can make any access.
To sum up, Android needs a highly efficient and secure process communication mode, namely Binder. Binder only needs one copy, and its performance is second only to shared memory. Moreover, Binder uses the traditional C/S structure, and its stability is needless to say.
2.2 Binder Implementation Mechanism
2.2.1 Process isolation
We know that is not directly interact between processes, each process with its own data, and the stability of the operating system in order to ensure their own safety, split system Kernel space and user space, ensure the collapse process of the user program will not affect the whole system, that is simple, the Kernel space (Kernel) are operating system Kernel space, UserSpace is the space where user programs run. For security purposes, they are isolated, so user-space processes interact through kernel space to drive the process.
2.2.2 C/S structure
Binder is based on the C/S mechanism. To implement this mechanism, a server must have a specific node to receive requests from a client, that is, the entry address. For example, a url is entered and the corresponding IP is resolved by DNS for access. As for Binder, different from traditional C/S, Binder itself serves as a node provided by the Server, and the client obtains the address corresponding to the Binder entity object to access the Server. For the client, how to get the address and establish the whole channel is the key to the entire interaction. Moreover, a Binder is an entity in the Server. The object provides a series of methods to implement the request between the Server and the client. As long as the client obtains the reference or a reference with the method proxy object, the communication can be carried out.
Android Bander Design and Implementation – Design in one paragraph:
The introduction of object-oriented thinking transformed the interprocess communication into invoking the methods of a Binder object by referring to it. The unique feature of a Binder object is that a Binder object is an object that can be referenced across processes, and its entities are located in one process, while its references are distributed throughout various processes in the system. Most tantalizing of all, this reference can be strongly typed or weakly typed, just like in Java, and can be passed from one process to another, giving everyone access to the same Server, just like assigning an object or reference to another. Binder blurs process boundaries and weakens interprocess communication, so that the entire system seems to run in the same object-oriented program. The various Binder objects and references are the glue that binds applications together.
2.2.3 Binder communication Model
Binder defines four roles based on the C/S structure: Server, Client, ServerManager, and Binder drivers. The first three drivers are in user space, that is, they cannot directly interact with each other. Binder drivers belong to kernel space, which belongs to the core of the entire communication. However, the implementation is similar to the driver. The driver is responsible for the establishment of Binder communication between processes, the transmission of Binder between processes, the management of Binder reference count, the transmission and interaction of packets between processes and a series of low-level support.
What does the ServerManager do?
We know that ServerManager is also a process in user space that serves as a bridge between servers and clients. Clients can get references to Binder entities in the Server from ServerManager. This may be a little vague, but for a simple example, When we visit www.baidu.com, the home page of Baidu is displayed. First of all, we know that this page must be published on a server of Baidu. DNS resolves the corresponding IP address through your address, and then visits the corresponding page, and then returns the data to the client to complete interaction. This is very similar to Binder’s C/S, where DNS is the corresponding ServerManager. First, Binder entity objects in the Server register their references (i.e., IP addresses) with the ServerManager. The Client uses a specific key (i.e., baidu) to bind to this reference. The ServerManager maintains a map-like table for one-to-one mapping. In the case of Android development, we know that many system services interact with AMS with Binder, such as access to volume services:
AudioManager am = (AudioManager)context.getSystemService(Context.AUDIO_SERVICE);
Careful friends should find ServerManager and Server are two different processes ah, Server to ServerManager to register is not also involved in the communication between the process, the current implementation of the communication between the process and use the communication between the process, you this is not nonsense…. Now, the beauty of a Binder is that when the ServerManager serves as the Serve side, it provides a special Binder that has no name and no registration. When a process registers itself as SMgr using the BINDER_SET_CONTEXT_MGR command, the Binder driver automatically creates a Binder entity for it. The reference to this Binder is fixed to 0 in all clients and does not need to be obtained by any other means. In other words, a Server that registers with the Binder of the ServerManager must communicate with the Binder of the ServerManager through the reference number 0. How can a client get an object from a server
From the figure above, we can clearly see the entire interaction process. The binder reference from SM is processed by the binder driver layer and then returned to the client as a proxy object. In fact, if the client and server are in the same process, the current binder object is returned. If the client and server are not in the same process, a proxy object is returned to the client.
2.2.4 Binder Role Positioning
Binder essentially provides a means of communication, regardless of what we implement. In order to implement a service, we need to define interfaces that allow clients to remotely invoke the service, which is cross-process. In this case, we need to design proxy mode. The client and server implement the interface function. The server is the actual implementation of the service, and the client acts as a remote call.
- From the point of view of the Server process, the Binder is an existing entity object, and the client is driven by the Binder through the transact() function, and finally calls back to the onTransact() function of the Binder entity.
- From the perspective of a Client process, a Binder refers to a Binder proxy object, which is a remote proxy of a Binder entity object and interacts through a Binder driver
3. Handwritten process communication
After r all that has been said above, everyone is tired of seeing it. Let’s deepen our understanding of Binder in the form of code. In daily development, when interprocess communication is involved, we may first think of AIDL, but I don’t know if there are any friends who feel the same with me. When writing AIDL for the first time, it was blind. After passing the. AIDL file, the compiler automatically generated code and generated a Java file, which also had Stub class and Proxy class. We did not understand the mechanism inside, which was really inconvenient for us to understand and learn.
First we need to define an interface service, that is, the above server to have the capabilities to provide the client, define an interface inherited from the IInterface, representing the capabilities of the server
public interface PersonManger extends IInterface {
void addPerson(Person mPerson);
List<Person> getPersonList();
}
Copy the code
Next we need to define a Binder entity object in the Server, which must inherit the Binder and implement the service interface defined above
public abstract class BinderObj extends Binder implements PersonManger {
public static final String DESCRIPTOR = "com.example.taolin.hellobinder";
public static final int TRANSAVTION_getPerson = IBinder.FIRST_CALL_TRANSACTION;
public static final int TRANSAVTION_addPerson = IBinder.FIRST_CALL_TRANSACTION + 1;
public static PersonManger asInterface(IBinder mIBinder){
IInterface iInterface = mIBinder.queryLocalInterface(DESCRIPTOR);
if(null! =iInterface&&iInterface instanceof PersonManger){return (PersonManger)iInterface;
}
return new Proxy(mIBinder);
}
@Override
protected boolean onTransact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags) throws RemoteException {
switch (code){
case INTERFACE_TRANSACTION:
reply.writeString(DESCRIPTOR);
return true;
case TRANSAVTION_getPerson:
data.enforceInterface(DESCRIPTOR);
List<Person> result = this.getPersonList();
reply.writeNoException();
reply.writeTypedList(result);
return true;
case TRANSAVTION_addPerson:
data.enforceInterface(DESCRIPTOR);
Person arg0 = null;
if(data.readInt() ! = 0) { arg0 = Person.CREATOR.createFromParcel(data); } this.addPerson(arg0); reply.writeNoException();return true;
}
return super.onTransact(code, data, reply, flags);
}
@Override
public IBinder asBinder() {
returnthis; }}Copy the code
First we look at the asInterface method, which drives the IBinder object passed by the Binder. We look for the local Binder object through the queryLocalInterface method. If the return is PersonManger, then the client and server are in the same process. Returns directly or, if not, to a proxy object.
Of course, as a proxy object, you also need to implement the service interface
public class Proxy implements PersonManger {
private IBinder mIBinder;
public Proxy(IBinder mIBinder) {
this.mIBinder =mIBinder;
}
@Override
public void addPerson(Person mPerson) {
Parcel data = Parcel.obtain();
Parcel replay = Parcel.obtain();
try {
data.writeInterfaceToken(DESCRIPTOR);
if(mPerson ! = null) { data.writeInt(1); mPerson.writeToParcel(data, 0); }else {
data.writeInt(0);
}
mIBinder.transact(BinderObj.TRANSAVTION_addPerson, data, replay, 0);
replay.readException();
} catch (RemoteException e){
e.printStackTrace();
} finally {
replay.recycle();
data.recycle();
}
}
@Override
public List<Person> getPersonList() {
Parcel data = Parcel.obtain();
Parcel replay = Parcel.obtain();
List<Person> result = null;
try {
data.writeInterfaceToken(DESCRIPTOR);
mIBinder.transact(BinderObj.TRANSAVTION_getPerson, data, replay, 0);
replay.readException();
result = replay.createTypedArrayList(Person.CREATOR);
}catch (RemoteException e){
e.printStackTrace();
} finally{
replay.recycle();
data.recycle();
}
return result;
}
@Override
public IBinder asBinder() {
returnnull; }}Copy the code
The proxy object is the service that the client finally gets to communicate with the Server by serializing the data through the Parcel, then calling remote.transact() to transfer the method code and data to the Server. The corresponding callback is in onTransact() on the Server
Then there is our Server process. The onBind method returns the mStub object, which is the Binder entity object in the Server
public class ServerSevice extends Service {
private static final String TAG = "ServerSevice";
private List<Person> mPeople = new ArrayList<>();
@Override
public void onCreate() {
mPeople.add(new Person());
super.onCreate();
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return mStub;
}
private BinderObj mStub = new BinderObj() {
@Override
public void addPerson(Person mPerson) {
if (mPerson==null){
mPerson = new Person();
Log.e(TAG,"null obj");
}
mPeople.add(mPerson);
Log.e(TAG,mPeople.size()+"");
}
@Override
public List<Person> getPersonList() {
returnmPeople; }}; }Copy the code
Finally, in the client process, bindService passes in a ServiceConnection object, and upon establishing a connection with the server, returns a proxy object via our defined BinderObj asInterface method, which we then invoke to interact
public class MainActivity extends AppCompatActivity {
private boolean isConnect = false;
private static final String TAG = "MainActivity";
private PersonManger personManger;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
start();
findViewById(R.id.textView).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (personManger==null){
Log.e(TAG,"connect error");
return;
}
personManger.addPerson(new Person());
Log.e(TAG,personManger.getPersonList().size()+""); }}); } private voidstart() {
Intent intent = new Intent(this, ServerSevice.class);
bindService(intent,mServiceConnection,Context.BIND_AUTO_CREATE);
}
private ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.e(TAG,"connect success");
isConnect = true;
personManger = BinderObj.asInterface(service);
List<Person> personList = personManger.getPersonList();
Log.e(TAG,personList.size()+"");
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.e(TAG,"connect failed");
isConnect = false; }}; }Copy the code
In this way, the communication between the Client and Server processes can be completed at one time. Finally, it is recommended that you implement the communication between the Client and Server processes by hand without AIDL to better understand the Binder communication process.
This article in the process of writing a reference to a lot of articles and source code, thanks to the selfless dedication of the big guys, slip away to slip away ~
Refer to the article
- Binder for interprocess communication in Android
- Android’s Binder mechanism is simple
- Android Bander Design and implementation – Design