Transport my post about Binder years ago from CSDN to Nuggets


Binder driver and communication library are the core of Binder. The driver is written in C language, and the communication library is written in C ++. The application is connected with the underlying library through JNI. If you have time, learn Unix Network Programming Volume 2 interprocess Communication.

First, the comparison with the traditional IPC:

Binder is a mechanism for interprocess communication, and therefore has to be compared with other traditional Unix IPC mechanisms (synchronization mechanisms such as mutex, semaphores, etc., are not strictly IPC mechanisms and will not be listed here):

1. Message transfer types: PIPE,FIFO(named pipe), Posix message queue, and System V message queue 2.Posix shared memory area and System V shared memory area 3

They are summed up in the following three ways:

In the first form, two processes share some information that is stored on a file in the file system. In order to access this information, each process has to traverse the kernel (read,write,lseek, etc.), which is the easiest way to understand communication. Of course, the files shared by the process can be either real files on the hard disk or virtual files that have only an access point in the file system.

The second form: Binders are also examples of this type of sharing. Processes can communicate with each other by accessing /dev/binder(open, Mmap, IOCTL system call). Binder drivers run in kernel space. It is equivalent to a centralized manager, everyone can access it, relying on it as the intermediary to achieve communication.

In the third form, two processes have a mutually accessible shared memory area. Once each process has set up this shared memory area, it can access the data in it without involving the kernel at all. The shared memory area is the fastest form of IPC available, and it relies on mMAP system calls.

Many interprocess communication mechanisms provided by Unix have different application scenarios and performance differences. In fact, Unix domain socket and pipe mechanisms are also widely used in Android code. However, binder mechanisms are the most common communication mechanisms in Android, mainly for two reasons. One is performance. One disadvantage of Unix’s traditional interprocess communication mechanism is that the process does not have an effective mechanism to identify the originator of the message. Although these forms of IPC are routed through the kernel, they do not provide any means for the kernel to identify the sender, so that some important interfaces cannot verify the sender for permission checks. Some privileged methods, such as ActivityManagerService and PackageManagerService, are not accessible to normal programs, but are open to system users. Many important services run in separate processes and require the ability to isolate and check permissions, without which the Security and ease of use of the Android platform would be poor. With binder communication, the kernel fills the pid and EUID of the sender, enabling very secure permission checking.

As for performance, the Android platform itself is very componentized, and many operations are involved in interprocess communication, such as starting an activity, registering broadcast, accessing system services, responding to user touch screen events, etc., so the consumption of interprocess communication itself greatly affects the performance of the Android system. An efficient mechanism is needed to implement these mechanisms. Although shared memory is the fastest IPC approach, implementing such a large and sophisticated RPC mechanism with shared memory is complex. Android uses its own binder mechanism, which also leverages MMAP to improve performance. The transfer process requires only one copy.

Binder also differs from traditional IPC mechanisms in that it implements RPC. Traditional interprocess communication mechanisms such as pipes, FIFOs, and message queues are all types of messaging. These mechanisms are only responsible for passing messages from one process to another. It is up to the receiver and the sender to define some kind of protocol for how to interpret the messages, and although message queues have message boundaries that distinguish each message, the interpretation of the messages is up to the application itself. However, the combination of drivers and binder libraries in binder communication forms an RPC mechanism. Applications can call objects of another process as if they were local process objects. All communication details are handled by AIDL and Binder libraries, including how to package and unpack. Applications only need to focus on implementing their own logic with binder notation, such as the underlying format for interprocess communication.

Ii. Communication concepts of Binder:

To understand the binder communication mechanism several concepts must be understood:

1.RPC : If one process A has an ObjectA and another process B wants to access the ObjectA through RPC, the format of the object must be the same in A and B. For the Java language, both parties can refer to classes in the same Java file. For c++, both parties can link to the same dynamic library. This consistency of object definitions is part of the communication convention between the two parties.

2. Data types: Communication between processes passing basic data types This is easy to understand. Passing an int to another process will be interpreted as an int, but how do you pass an object to another process? Serialization in Java, for example, converts an object into a byte stream that can be passed over the network to another process or written to a file (the success of serialization depends on the platform independence of Java bytecode and the dynamic loading mechanism of classes). Binder communication supports passing serialized Java byte streams, but it treats the stream as a normal data type. It is only passing the stream, and the interpretation of the stream is up to the parties. In fact, binder communication distinguishes only three data types: unexplained data types, binder data types, and file descriptors.

3. Binder references and Binder entities: As stated in article 2, binder supports passing binder data types, which are subdivided into binder references and binder entities, both described by the flat_binder_object structure. Binder entities are defined as objects in a process that provide implementations of service interfaces for other processes to access and are therefore considered service components. When this data is passed to the driver, the type is set to BINDER_TYPE_BINDER to tell the driver to do something special with this data. A binder reference is used by the process that needs to be serviced. A binder reference of type BINDER_TYPE_HANDLE is passed to the driver so that the driver knows it is a binder reference. Like using Foo *p = new Foo() in a process; Again, Foo objects created on the heap can be thought of as entities and p Pointers can be thought of as references, but this analogy is not accurate and will be described below.

4. Communication roles and access points: Like Unix traditional IPC mechanism, two processes to communicate, there must be a both of us can access name/access point, through the access point to establish communication links, it is used for clients and services connected to each other and exchange information means, ordinary pipe is used in the process of genetic relationship between, they have a copy of the virtual address space of the same, So you don’t need access points. Other communication mechanisms have such an access point. For example, if two processes communicate through a common file, the file name is known to both parties and is an access point. FIFO has the corresponding path name, Posix communication mechanism has the Posix IPC name, and Socket through IP address and port number to establish communication, Unix domain Socket through the local Socket file path to establish communication.

Binder communication has four roles: Client component, Server component, ServiceManager, and Binder driver. The client component wants to communicate with the server component. It must go to the ServiceManager to query for a reference to the Server component through a string, a communication access point, that the Server component fills in when registering itself with the ServiceManager. The ServiceManager is a common access point, and the handle value 0 is the reference identifier of the ServiceManager. So the summary is that client and Server access the ServiceManager with the well-known 0 identifier. Server registers a Binder entity with the ServiceManager and associates it with a string that says it can communicate with me by querying this string. The Client passes this string to the ServiceManager. The ServiceManager passes a reference to the Binder entities registered with the Server to the client. The client takes this reference and communicates with the Server. Therefore, the ServiceManager acts as a DNS server in a network environment, and the driver acts as a router to distribute and dump data.

Iii. Communication process with Binder:

Getting the ServiceManager involved in the first place can complicate matters, so here’s a scenario to consider: C process is a common process,S process is a service process, C process and S process have established communication connection,S process passes its own binder entity to C process (this is similar to process passes binder entity to ServiceManager to request registration process), All C gets is a binder reference, which C then uses to call a function in S’s Binder entity. Of course, BINDER references and binder entities are not actively passed by PROCESS S to process C, but are usually requested by process C to do so in a way that simplifies the irrelevant aspects and only considers the two-step process.

Binder references and Binder entities behave in user space as BpBinder classes and BBinder classes, respectively, which inherit from the IBinder class. Here are two of Luo sheng Yang’s class diagrams for further understanding:

IMediaPlayerService is a mutually agreed interface class. BnMediaPlayerService is also a class that needs to be defined. It inherits IMediaPlayerService and BBinder. This class implements its own onTransact function, parses the Parcel passed by the client, and calls the corresponding function. These functions are implemented in the MediaPlayerService derived class, which is the actual implementation of the interface. Inheritance is a type relationship, so MediaPlayerService is a Binder entity.

The above diagram is the class diagram of the client side. BpMediaPlayerService also inherits the IMediaPlayerService interface, but the implementation of these interfaces is only a proxy method for data transmission. Finally, it references the BpBinder class. This class transact is used to pass information needed for function calls to the binder driver. In this case, BpBinder is a binder reference that holds a handle of type INT32_T. This handle is similar to the file descriptor FD and corresponds to a binder entity. If I have a Handle, I can send it to the Binder driver to say I want to access the Corresponding binder entities of the Handle, and the corresponding relationship between the Handle and binder entities is maintained by the kernel. The ServiceManager handle value is 0. The Client must explicitly request the Handle of a server from the ServiceManager. Its value is similar to that of the file descriptor, always the smallest unused value. Of course, if the ServiceManager does not explicitly fetch this value, and sends 1 directly to the kernel, the kernel does not recognize it because binder references and entities have not been established, so the handle value is assigned to the client process by the kernel.

With binder references and binder entities in mind, consider the hypothetical scenario where S sends a MediaPlayerService entity to C. S sends the address of the MediaPlayerService. This address is obviously a virtual address of the S process, which is meaningless to other processes. When sent, it is packaged into the data using the Parcel class’s writeStrongBinder function, which generates a flat_binder_object structure representing a binder reference or entity, It determines what to fill in the type value based on whether the localBinder function of the IBinder object returned null. Since both BBinder and BpBinder inherit from IBinder, both types can be passed in. MediaPlayerService inherits from BBinder, so localBinder is not null. The type field of the structure is specified as BINDER_TYPE_BINDER, otherwise BINDER_TYPE_HANDLE. Depending on the value of the type field, the driver knows whether a reference or entity is being passed and does different things.

Binder references and binder entities are represented in kernel space as binder_ref and binder_node structures. Their relationship is one-to-many. A binder_ref refers to multiple binder_nodes. It finds the binder_node structure for the Binder entity, creates one if it can’t find one, stores the address of the binder entity in the binder_node structure, and then finds the target process, in this case C process, Assign it a handle value, and generate a binder_ref structure stored in the C process kernel data. The handle value is stored in the DESC field of binder_ref, and the binder_ref establishes a relationship with the binder_node entity. The next time you use the handle value, you will find the binder_ref structure, which will find the corresponding binder_node, which will eventually find the MediaPlayerService address that was passed in because it is stored as a cookie in the binder_node structure. After binder_ref is allocated, a flat_binder_object object is generated and passed to the C process. The type field of this object is BINDER_TYPE_HANDLE, indicating that this is a binder reference, and the handle value is the allocated handle value.

The C process takes the Flat_binder_object and passes the Handle to a newly created BpBinder object, which is then converted to a BpMediaPlayerService object by interface_cast. All the interface_cast does is create a New BpMediaPlayerService object and hold BpBinder so that C can call the BpMediaPlayerService object to access S’s interface.

C calls BpMediaPlayerService to access S’s interface, passing the resulting handle value to the kernel, which finds the binder_ref object and thus the binder_node object. The binder_node structure holds the information and address values of the process that the entity belongs to, so the PROCESS S and its MediaPlayerService entity can be found. The PROCESS S is sleeping in the binder thread waiting for the request to arrive, so it will wake up. Strong-cast the address value into a pointer of type BBinder, call its onTransact method, and finally call the corresponding method in MediaPlayerService.

Process and ServiceManager interact also perform similar to the above operation, the server process pass a string and binder entity ServiceManager, ServiceManager is a binder to quote, It holds strings associated with binder references in a linked list called Svclist. The client process passes the same string to the ServiceManager to request a Binder reference. The ServiceManager responds to the Client process with the binder reference it gets. When the kernel finds the binder_node object, it finds that the process it belongs to is not the same as the client process. It knows that the client process also wants a binder reference. The kernel assigns a binder reference to the client and passes it to it. Both the ServiceManager and client have a Binder reference to the same binder entity.

Once the C and S processes have established a communication channel through the ServiceManager, They can then pass additional binder entities or references through this channel without passing through ServiceManager. One argument is that these binder types of data are called anonymous binders, and corresponding binder objects registered with ServiceManager are called real name binders A connection must be established with a real name binder before an anonymous binder object can be sent to the connection. There are special examples of this later in the blog.

Binder references and Binder entities:

Iv. Thread Management:

Traditional communication mechanisms such as sockets, belongs to the client – server model, server can be divided into two types, of iterative and concurrent iteration types of servers at the same time only for a customer service, subsequent requests must be queued, and concurrent types of servers can open multiple processes or threads to serve multiple clients, if there is no request to arrive, The server blocks on the Accept () system call. After the connection is established, the client reads the data sent from the server through read(), and blocks in read() if there is no data.

The binder server can also serve multiple clients at the same time, allowing multiple clients to call some functions of some components of the server process at the same time. The server adds the current thread to the binder thread pool by calling the joinThreadPool function of IPCThreadState. Its behavior is to call iocTL in a loop to get data from the TODO queue. If there is no data in the queue, the iocTL call to Wait_EVENT_INTERRUPtible () blocks. Otherwise, executeCommand() is called to process the request from the client. There are two TODO queues, one is the global TODO queue of the process and the other is the private TODO queue of the current thread. As long as a thread does not receive the returned packet, it should wait for the new task in the global TODO queue. Otherwise, it should wait for the returned data communicated by other processes in its private TODO queue. A binder thread pool is created by calling ProcessState’s startThreadPool() function and is added to the binder thread pool. The default binder thread pool is two. If a large number of requests come in a short time, The Binder driver issues the BR_SPAWN_LOOPER command to notify the process that additional threads need to be added to the thread pool, and the program generates threads to be added to the thread pool via ProcessState’s startThreadPool() function. After a client sends an interprocess communication request, it loops and blocks in ioctl() waiting for a response from the server process until the kernel tells it that the communication is over via BR_REPLY. Then the client returns from the loop, meaning that the method has returned.

Five. Example code:

A good example is worth thousands of words. The following example mainly involves three types of processes, one is a server process, one is a client process, and one is an Android process. The server process provides five functions accessed by the client process. The Android process accesses these interfaces of the Server process through JNI. The following examples are based on the examples in Luo’s book to make some expansions, with his consent to modify and add some of their own content.

1. Process communication protocol file: ——————– ifregservice.h:

#define IFREGSERVICE_H_
 
#include <utils/RefBase.h>
#include <binder/IInterface.h>
#include <binder/Parcel.h>
#include <utils/String8.h>
 
#define FREG_SERVICE "shy.luo.FregService"
 
using namespace android;
 
class ICallback;
class IServerFoo;
 
class IFregService: public IInterface
{
public:
	DECLARE_META_INTERFACE(FregService);
	virtual int32_t getVal() = 0;
	virtual void setVal(int32_t val) = 0;
	
	virtual void testCallback(const sp<ICallback>& cb) = 0;
	
	virtual const sp<IServerFoo> getServerFoo() = 0;
	virtual void foo(const sp<IServerFoo>&) = 0;
};
 
class BnFregService: public BnInterface<IFregService>
{
    public:
	    virtual status_t onTransact(uint32_t code, const Parcel& data, 
	        Parcel* reply, uint32_t flags = 0);
};
 
//MyCallback----------------------------------------
class ICallback : public IInterface{
    public :
        DECLARE_META_INTERFACE(Callback);
        virtual void onReturn(const String8 &) = 0;
        
        enum{
            PRINT_NAME_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION,
        };
};
class BnCallback : public BnInterface<ICallback>{
    public:
	    virtual status_t onTransact(uint32_t code, const Parcel& data, 
	        Parcel* reply, uint32_t flags = 0);
};
 
//MyClass-------------------------------------------
class IServerFoo : public IInterface{
    public :
        DECLARE_META_INTERFACE(ServerFoo);
        virtual void printUid() = 0;
        
        enum{
            PRINT_UID_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION,
        };
};
class BnServerFoo : public BnInterface<IServerFoo>{
    public:
	    virtual status_t onTransact(uint32_t code, const Parcel& data, 
	        Parcel* reply, uint32_t flags = 0);
};
 
#endif
Copy the code

Lo’s example has only two interfaces, getVal() and setVal(), which transfer basic data types, and here we add three more functions.

IFregService defines an interface that a Server component needs to provide:

GetVal and setVal pass basic data types and are simply used to set a value and print it out. The testCallback is used to testCallback functions. The client process passes an ICallback entity to the server process. The server process calls the ICallback, which is called the anonymous binder, and its entity is in the client process. Server processes get only binder references. ICallback provides a function called onReturn(const String8&). The server process passes a string of type 8 and the client prints it out. The getServerFoo() interface asks the server process to pass in a reference to IServerFoo, and the foo() function calls the IServerFoo interface, which simply prints out the UID of the server process via printUid. The Binder entity exists in the Server process. The first BnXXX classes define an inheritance relationship. Binder entity objects need to inherit from these classes to fill in their own implementation. BnXXX’s onTransact is used to unpack the data transferred from the client and invoke the corresponding implementation.

— — — — — — — — — — — — — — — — — — — – IFregService. CPP:

#include <utils/Log.h> #include "IFregService.h" using namespace android; enum { GET_VAL = IBinder::FIRST_CALL_TRANSACTION, SET_VAL, TEST_CALLBACK, GET_SERVER_FOO, USE_FOO, }; class BpFregService: public BpInterface<IFregService> { public: BpFregService(const sp<IBinder>& impl) : BpInterface<IFregService>(impl) { } public: int32_t getVal() { Parcel data; data.writeInterfaceToken(IFregService::getInterfaceDescriptor()); Parcel reply; remote()->transact(GET_VAL, data, &reply); int32_t val = reply.readInt32(); return val; } void setVal(int32_t val){ Parcel data; data.writeInterfaceToken(IFregService::getInterfaceDescriptor()); data.writeInt32(val); Parcel reply; remote()->transact(SET_VAL, data, &reply); } void testCallback(const sp<ICallback>& cb){ Parcel data; data.writeInterfaceToken(IFregService::getInterfaceDescriptor()); data.writeStrongBinder(cb->asBinder()); Parcel reply; remote()->transact(TEST_CALLBACK, data, &reply); } const sp<IServerFoo> getServerFoo(){ Parcel data; data.writeInterfaceToken(IFregService::getInterfaceDescriptor()); Parcel reply; remote()->transact(GET_SERVER_FOO, data, &reply); return interface_cast<IServerFoo>(reply.readStrongBinder()); } void foo(const sp<IServerFoo>& foo){ Parcel data; data.writeInterfaceToken(IFregService::getInterfaceDescriptor()); data.writeStrongBinder(foo->asBinder()); Parcel reply; remote()->transact(USE_FOO, data, &reply); }}; class BpCallback : public BpInterface<ICallback>{ public: BpCallback(const sp<IBinder>& impl) : BpInterface<ICallback>(impl) { } void onReturn(const String8& name){ Parcel data; data.writeInterfaceToken(ICallback::getInterfaceDescriptor()); data.writeString8(name); Parcel reply; remote()->transact(PRINT_NAME_TRANSACTION, data, &reply); }}; class BpServerFoo : public BpInterface<IServerFoo>{ public: BpServerFoo(const sp<IBinder>& impl) : BpInterface<IServerFoo>(impl) { } void printUid(){ Parcel data; data.writeInterfaceToken(IServerFoo::getInterfaceDescriptor()); Parcel reply; remote()->transact(IServerFoo::PRINT_UID_TRANSACTION, data, &reply); }}; IMPLEMENT_META_INTERFACE(FregService, "shy.luo.IFregService"); IMPLEMENT_META_INTERFACE(Callback, "my.ICallback"); IMPLEMENT_META_INTERFACE(ServerFoo, "my.IServerFoo"); status_t BnFregService::onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { switch(code) { case GET_VAL: { CHECK_INTERFACE(IFregService, data, reply); int32_t val = getVal(); reply->writeInt32(val); return NO_ERROR; } case SET_VAL: { CHECK_INTERFACE(IFregService, data, reply); int32_t val = data.readInt32(); setVal(val); return NO_ERROR; } case TEST_CALLBACK: { CHECK_INTERFACE(IFregService, data, reply); sp<ICallback> cb = ICallback::asInterface(data.readStrongBinder()); testCallback(cb); return NO_ERROR; } case GET_SERVER_FOO: { CHECK_INTERFACE(IFregService, data, reply); sp<IServerFoo> foo = getServerFoo(); reply->writeStrongBinder(foo->asBinder()); return NO_ERROR; } case USE_FOO: { CHECK_INTERFACE(IFregService, data, reply); sp<IServerFoo> fp = IServerFoo::asInterface(data.readStrongBinder()); foo(fp); return NO_ERROR; } default: { return BBinder::onTransact(code, data, reply, flags); } } } status_t BnCallback::onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { switch(code) { case PRINT_NAME_TRANSACTION: { CHECK_INTERFACE(ICallback, data, reply); String8 name = data.readString8(); onReturn(name); return NO_ERROR; } default: { return BBinder::onTransact(code, data, reply, flags); } } } status_t BnServerFoo::onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { switch(code) { case PRINT_UID_TRANSACTION: { CHECK_INTERFACE(IServerFoo, data, reply); printUid(); return NO_ERROR; } default: { return BBinder::onTransact(code, data, reply, flags); }}}Copy the code

The implementation classes in ifregService.h file, BpXXX and BnXXX implementations are in the same file, which is very similar to the Java file generated by aiDL tool. In this way, the communication protocol of both parties can be agreed, the order of packaging and unpacking must be the same, and the code identification of functions should be the same. H file and link to ifregService. CPP file can achieve the consistency of communication protocol between client and server processes. This is the same as Java processes have the same AIDL file to communicate. Binder(INT32_t handle). The client calls the function in BpXXX, In fact, BpXXX transmits data to binder drivers via BpBinder’s transact function and waits for a reply.

2. Client process code:

— — — — — — — — — — — — — — — — — — — – IFregService. CPP:

#include <utils/Log.h> #include <binder/IServiceManager.h> #include ".. /common/IFregService.h" #include <binder/BpBinder.h> class Callback : public BnCallback{ public : void onReturn(const String8 & name){ printf("i am the callback %s! \n",name.string()); }}; int main() { sp<IBinder> binder = defaultServiceManager()->getService(String16(FREG_SERVICE)); if(binder == NULL) { ALOGE("Failed to get freg service: %s.\n", FREG_SERVICE); return -1; } BpBinder* bin = reinterpret_cast<BpBinder *>(binder.get()); printf("i got the ref value is :%d\n",bin->handle()); sp<IFregService> service = IFregService::asInterface(binder); if(service == NULL) { ALOGE("Failed to get freg service interface.\n"); return -2; } printf("Read original value from FregService:\n"); int32_t val = service->getVal(); printf(" %d.\n", val); printf("Add value 1 to FregService.\n"); val += 1; service->setVal(val); printf("Read the value from FregService again:\n"); val = service->getVal(); printf(" %d.\n", val); service->testCallback(new Callback()); sp<IServerFoo> fp = service->getServerFoo(); service->foo(fp); return 0; }Copy the code

Sp Binder = defaultServiceManager()->getService(String16(FREG_SERVICE)); Access to the server process of binder references, and through IFregService: : asInterface (binder); Convert the IBinder object to a concrete interface object, which, as described above, creates a BpFregService object and holds the IBinder object.

The client process then calls the server process functions via the generated SP service, as if these functions were implemented in the server process. Note the Callback class. The client process calls service->testCallback(new Callback()), passing a binder entity to the server process, which passes through the client process when called back. This is anonymous binder binder entity object, specific how server and back to the client, the reader can see IPCThreadState: : waitForResponse function default branch of switch statements, here are analyzed in detail.

Server process code:

— — — — — — — — — — — — — — — — — — — – FregService. CPP:

#include <stdlib.h> #include <fcntl.h> #include <utils/Log.h> #include <unistd.h> #include <sys/types.h> #include <binder/IServiceManager.h> #include <binder/IPCThreadState.h> #include ".. /common/IFregService.h" class MyServerFoo : public BnServerFoo{ public : void printUid(){ printf("server pid is : %ld\n",(long int)getpid()); ALOGI("server pid is : %ld\n",(long int)getpid()); }}; class FregService : public BnFregService { public: FregService():val(88) { } virtual ~FregService() { } public: static void instantiate() { defaultServiceManager()->addService(String16(FREG_SERVICE), new FregService()); } int32_t getVal() { return val; } void setVal(int32_t v){ val = v; } void testCallback(const sp<ICallback>& cb){ String8 name("Mr Tough Guy!!!" ); cb->onReturn(name); } const sp<IServerFoo> getServerFoo(){ return new MyServerFoo(); } void foo(const sp<IServerFoo>& fp){ fp->printUid(); } private: int32_t val; }; int main(int argc, char** argv) { FregService::instantiate(); ProcessState::self()->startThreadPool(); IPCThreadState::self()->joinThreadPool(); return 0; }Copy the code

After the Server component registers its binder entities with the ServiceManager, it creates a thread through startThreadPool() and joinThreadPool() and adds it to the binder thread pool along with the main thread to wait for client requests.

A complete example is now in place to gain a deeper understanding of binder usage.

The Java example code is very similar to the flow of MediaPlayer, which calls the functions in android_media_mediaPlayer.cpp through JNI, eventually calling MediaPlayerService.

Finally, a general diagram is added, which is messy and generally shows the communication process of binder: