Proposition
When a service is often called remotely, we often use aidl to define an interface for the service and client to use. This is actually the IPC communication using the Binder Mechanism. After the client bind service is successful, the system am will call the callback function onserviceconnected to pass the service ibinder to the client. The client then calls the asinterface () method generated by aidl to obtain the service calling interface, at this point, the BIND process is over. We can remotely call the service method on the client side. For example
public void onServiceConnected(ComponentName className, IBinder service) { mSecondaryService = ISecondary.Stub.asInterface(service); }
Let's look at the definition of asinterface () generated by aidl.
public static com.example.android.apis.app.ISecondary asInterface(android.os.IBinder obj){if ((obj==null)) {return null;}android.os.IInterface iin = (android.os.IInterface)obj.queryLocalInterface(DESCRIPTOR);if (((iin!=null)&&(iin instanceof com.example.android.apis.app.ISecondary))) {return ((com.example.android.apis.app.ISecondary)iin);}return new com.example.android.apis.app.ISecondary.Stub.Proxy(obj);}
First, asinterface () will query whether the imported ibinder object has a localinterface. The localinterface here refers to whether the imported ibinder is a reference of the service or a proxy.
1. when the input ibinder of asinterface is a server reference (Binder type), this reference is directly returned. In this case, the method for invoking server is not IPC communication, but a direct function call;
2. When the input ibinder of asinterface is the proxy of the server (binderproxy type), you need to create the proxy of the server and return it. At this time, the method of calling the server is IPC communication.
So what are the conditions for the above two situations? Here we give the answer first, and then go deep into the code to study two different situations.
1. When the client and service are in the same process, the ibinder input by asinterface is a reference of the server;
2. When the client and service are in different processes, the ibinder input by asinterface is the proxy of the server.
Before studying the above implementation code, let's first introduce the status changes when ibinder is used as a parameter for inter-process communication using IPC. In fact, this is the core content of this article and we understand this mechanism, we can easily understand the principles of the proposition above.
Model
Passing ibinder as a parameter in IPC communication may confuse some people. Isn't ibinder a medium for IPC communication? How can it be passed as a parameter? In this case, it is a little narrow. For native-layer IPC, the client obtains the service interface from the SM (Service Manager, this interface is also an ibinder type. When both ends of C/S need duplex communication, that is, when the so-called service end needs to call the client method in turn, the client needs to pass the client-side ibinder to the service through the aforementioned interface.
This is especially true for the service at the Java application layer. For example, we will analyze the proposition in this article. First, we will introduce the principle knowledge.
In binder IPC communication, binder is the communication medium, and parcel is the communication content. During the method remote call, the parameters are packaged into parcel format for transmission. The ibinder object is no exception. Let's take a look at writestrongbinder () in the parcel class (because the Java layer and native Layer Methods correspond to each other, the Java layer is only the native encapsulation, so we only need to look at native ),
status_t Parcel::writeStrongBinder(const sp<IBinder>& val){ return flatten_binder(ProcessState::self(), val, this);}
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);}
The above code is divided into two situations:
1. If the passed ibinder is the local ibinder object of the service, then the ibinder object is of the bbinder type, so the local above is not null, so the binder type is binder_type_binder.
2. If the passed ibinder object proxy ibinder object, the binder type is binder_type_handle.
The client package the method call parameters into parcel and sends them to the binder module of the kernel. Therefore, we will analyze the processing of the binder module of the kernel.
Binder_transaction () in kernel/Drivers/staging/Android/binder. c ()
switch (fp->type) { case BINDER_TYPE_BINDER: case BINDER_TYPE_WEAK_BINDER: { struct binder_ref *ref; struct binder_node *node = binder_get_node(proc, fp->binder); if (node == NULL) { node = binder_new_node(proc, fp->binder, fp->cookie); if (node == NULL) { return_error = BR_FAILED_REPLY; goto err_binder_new_node_failed; } node->min_priority = fp->flags & FLAT_BINDER_FLAG_PRIORITY_MASK; node->accept_fds = !!(fp->flags & FLAT_BINDER_FLAG_ACCEPTS_FDS); } if (fp->cookie != node->cookie) { binder_user_error("binder: %d:%d sending u%p " "node %d, cookie mismatch %p != %p\n", proc->pid, thread->pid, fp->binder, node->debug_id, fp->cookie, node->cookie); goto err_binder_get_ref_for_node_failed; } ref = binder_get_ref_for_node(target_proc, node); if (ref == NULL) { return_error = BR_FAILED_REPLY; goto err_binder_get_ref_for_node_failed; } if (fp->type == BINDER_TYPE_BINDER) fp->type = BINDER_TYPE_HANDLE; else fp->type = BINDER_TYPE_WEAK_HANDLE; fp->handle = ref->desc; binder_inc_ref(ref, fp->type == BINDER_TYPE_HANDLE, &thread->todo); binder_debug(BINDER_DEBUG_TRANSACTION, " node %d u%p -> ref %d desc %d\n", node->debug_id, node->ptr, ref->debug_id, ref->desc); } break; case BINDER_TYPE_HANDLE: case BINDER_TYPE_WEAK_HANDLE: { struct binder_ref *ref = binder_get_ref(proc, fp->handle); if (ref == NULL) { binder_user_error("binder: %d:%d got " "transaction with invalid " "handle, %ld\n", proc->pid, thread->pid, fp->handle); return_error = BR_FAILED_REPLY; goto err_binder_get_ref_failed; } if (ref->node->proc == target_proc) { if (fp->type == BINDER_TYPE_HANDLE) fp->type = BINDER_TYPE_BINDER; else fp->type = BINDER_TYPE_WEAK_BINDER; fp->binder = ref->node->ptr; fp->cookie = ref->node->cookie; binder_inc_node(ref->node, fp->type == BINDER_TYPE_BINDER, 0, NULL); binder_debug(BINDER_DEBUG_TRANSACTION, " ref %d desc %d -> node %d u%p\n", ref->debug_id, ref->desc, ref->node->debug_id, ref->node->ptr); } else { struct binder_ref *new_ref; new_ref = binder_get_ref_for_node(target_proc, ref->node); if (new_ref == NULL) { return_error = BR_FAILED_REPLY; goto err_binder_get_ref_for_node_failed; } fp->handle = new_ref->desc; binder_inc_ref(new_ref, fp->type == BINDER_TYPE_HANDLE, NULL); binder_debug(BINDER_DEBUG_TRANSACTION, " ref %d desc %d -> ref %d desc %d (node %d)\n", ref->debug_id, ref->desc, new_ref->debug_id, new_ref->desc, ref->node->debug_id); } } break;
The above code is also divided into two different branches:
1. When the incoming ibinder type is binder_type_binder, the binder type value is binder_type_handle;
2. when the ibinder type is binder_type_handle, the process defined by the entity of the ibinder is determined (that is, the process defined by the server represented by the ibinder) it is the same as the target process (that is, the target process passed by ibinder). If it is the same, the ibinder type is converted to binder_type_binder and changed to a reference of the local ibinder object.
Through the above processing, we can draw the following conclusion:
1. the local ibinder object reference (binder_type_binder type) passed between different processes will be converted to the proxy (binder_type_handle type, but only change its type at present, the ibinder receiver converts the data type to the proxy );
2. because ibinder is sent to the binder module only between different processes, the following two possible processes are a --> process B --> process, process a --> process B --> Process C; the ibinder type is binder_type_binder --> binder_type_handle --> binder_type_binder, binder_type_binder --> binder_type_handle.
According to the above conclusion, we will understand that the local ibinder objects of the same process in the binder IPC communication process will not be transmitted to the binder module of the kernel without passing through different processes, therefore, it is always the local object of ibinder. If it is passed between processes, even if it is passed between multiple processes, as long as the final target is the component of the same process, then, the ibinder object is a local object.
Apply Model
After understanding the model above, we can go back and look at the conclusion of the initial proposition.
1. When the client and service are in the same process, the ibinder input by asinterface is a reference of the server;
2. When the client and service are in different processes, the ibinder input by asinterface is the proxy of the server.
If a component (such as an acitient) is in process a, it needs bind a service, and the service is in process B, we will briefly introduce the BIND process.
1. process a sends a BIND request to Am (process system_server), and provides the callback serviceconnection to Am (the interface that is passed to Am (iserviceconnection), because the communication between AM and A is duplex, therefore, a needs to provide ibinder to AM );
2. Am starts process B and creates a service. process B passes the ibinder object of service to AM, and am then passes the object to process a through iserviceconnection. Therefore, the transmission path of the Service's ibinder object is process B --> process system_server (AM) --> process.
Apply the above model to draw the conclusion of the first proposition in this article.
It is easy to understand the process of providing a service in BIND.