Banshan. tech/Binder%E6%A…

This article analyzes the source code based on Android P(9.0)

In the world of programming, communication, collaboration and cooperation between different processes are everywhere. Most of the time, people used to call them IPC(Inter Process Communication). Binder, for example, is often referred to as the IPC mechanism in the Android world. But from an app developer’s perspective, Binder can also be called the Remote Procedure Call (RPC) mechanism of the Android world.

  • IPC(Inter-process Communication)

    Refers to all is used to transfer information between processes (data transmission only a subset of the transfer of information), such as the socket/pipe/FIFO/semaphore, etc. These names represent the specific manner in which information is transmitted, rather than the processing and processing of information.

  • Remote Procedure Call (RPC)

    A method call based on IPC. As far as the client is concerned, it can only perceive the method call and the return value. However, this method actually completes a series of intermediate processes such as client side parameter packaging, data transmission, server side data unpacking, server side data processing, server side result return and so on. It’s just that these processes are “transparent” to the client. Therefore, IT can be said that IPC is only a link in RPC. In addition, RPC also includes data packaging, unpacking and processing processes, which can be collectively referred to as information processing and processing processes.

1. Why do you need a lot of RPC in Android?

Here’s an example of a clipboard to visually illustrate what RPC is all about.

We can copy a text to the clipboard with the following code. After line 8, you can copy the text to the clipboard. Just to be clear, line 8 is essentially an RPC.

1    // Get the system clipboard
2    ClipboardManager clipboard = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
3    
4    // Create a clipping dataset containing a plain text data entry (the data that needs to be copied)
5    ClipData clipData = ClipData.newPlainText(null."Text data to copy");
6    
7    // Set (copy) the data set to the clipboard
8    clipboard.setPrimaryClip(clipData);
Copy the code

So what exactly is the difference between RPC and non-RPC? Here is a picture to illustrate the problem.

In the RPC process, process B performs the real method execution, which is equivalent to providing some service for process A. Therefore, process B can also be called A Server process in the RPC process, and process A can correspondingly be called A Client process.

Next, why do you need a lot of RPC in Android? Isn’t it possible to change RPC to a local method?

The answer is no. Because Android itself is a centralized management system, RPC can ensure that a Server process can manage the call requests of many Client processes and achieve unified management of system status. For example, if we copy text to the clipboard in an App, assuming that the copying process is done by calling a local method (the copying state is limited to the App process), then the text will not appear in the new clipboard after we leave the App. On the other hand, if we use RPC to do the replication, the final text is passed to the Server process. Server processes are usually resident in memory, so the text on the clipboard remains even when we leave the App, ensuring that it can be pasted into other apps. In Android, a large number of system services are performed in the System_server process, such as ActivityManagerService.

2. What is service in the Binder world?

Before we go into specifics, let’s make a qualification. The services discussed below are services similar to ActivityManagerService, PackageManagerService, WindowManagerService, etc. They are services with Binder semantics. Rather than services, one of Android’s four major components.

But what does “service” really mean? It is a general term for a class of similar functions. For example, “merchant services” can include “buy potato chips”, “buy Coke”, “buy sofa”, “buy TV” and a series of functions. ActivityManagerService is a class that defines and implements a number of methods, detailing how each function should be provided. But it’s just a template and can’t actually provide a service. The real service is the object instantiated by ActivityManagerService. In Android, ActivityManagerService instantiates only one object, which is the person that actually provides the AMS service for the application.

/frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

2877    public static final class Lifecycle extends SystemService {
2878        private final ActivityManagerService mService;
2879
2880        public Lifecycle(Context context) {
2881            super(context);
2882            mService = new ActivityManagerService(context);
2883        }
Copy the code

This object is created in the process of system_server start-up time, concrete is in ActivityManagerService. The complete Lifecycle of the process of constructing.

/frameworks/base/services/java/com/android/server/SystemServer.java

559        mActivityManagerService = mSystemServiceManager.startService(
560                ActivityManagerService.Lifecycle.class).getService();
Copy the code

Since there is only one object instantiated by ActivityManagerService in the entire system, it doesn’t matter if you call it “AMS” or “AMS instantiated object.” It’s like there’s only one chef in the whole restaurant. It doesn’t matter if you call him “Chef” or “Chef Wang.”

2.1 Relationship between Service and Process/thread

This problem is solved if you think of a Service as a Java object that can provide a service.

Java objects are stored on the heap and can therefore be shared between different threads in the same process, so services (or, more accurately, methods of Service objects) can also be run in different threads.

In addition, thousands of objects can be constructed in a single process, and therefore thousands of services can exist in a single process. For example, we can construct ActivityManagerService objects A/B/C in the system_server process. Three AMS objects represent three entities that can provide services. This is like having three chefs in A restaurant. You can request service from any of them. Of course, in the real Android system, a service of the same type will most likely have only one object. But just because a service of the same type does not exist does not mean it is theoretically impossible to implement, so it is theoretically possible for multiple objects of the same type to exist in a process.

3. How is the interior of RPC structured?

As mentioned at the beginning of this article, the only two processes that a client can perceive are the method call and the return value. In the case of the clipboard, there are only two things that an Android app developer can perceive:

For Android app developers, there are only two things they perceive:

  1. I called the clipboard.setprimaryClip () method
  2. The text I wanted to copy appeared on the clipboard

In this process, the application developer is not aware that this is a cross-process call or the data transfer behind the call. RPC encapsulates all of this, so developers can naively assume that all of this is happening in the application process. And this is exactly what system designers hope to bring to developers, both in terms of understanding and in terms of use.

But an ambitious developer will often opt for simplicity in use, not in understanding. So I’m going to continue in an interesting way.

3.1 Examples of “Talent centers

All algorithms and design patterns are abstracted from social life. Therefore, in line with the principle of “from the masses, to the masses”, we gave life to the cold source code, and understood RPC and Binder from the perspective of social life.

In the past few years, several talent centers with their own characteristics have been built in the city, each of which brings together people from all over the world. Among them are skilled cooks, opportunistic merchants, writers who write well, and diligent fruit growers. The talent center has many telephone booths, so that they can call each other without interrupting each other. One day, Xiao Ming wanted to buy A keyboard from jia merchant in talent center A, so he dialed A building…

The above example can be mapped to a single RPC. The talent center represents the process, the telephone room represents the thread, the profession represents the class that service belongs to, the jia merchant represents a specific object that provides service, buys the keyboard to represent the method of service, makes a phone call to represent the way of data transmission, and xiaoming represents the Client process.

Ming makes A call to the talent center A. The Client process communicates with Server process A. In addition, the Client process can also communicate with Server process B, C, and D.

Xiao Ming wants to buy a keyboard from Jia Merchant (pseudocode is shown as follows) :

Line 1 indicates that Businessman Jia has appeared, and line 2 indicates that he has entered his name in the talent registry (non-talent center).

1    Merchant jia = new Merchant("Jia");
2    ServiceManager.addService("MerchantJia", jia);
Copy the code

The following code shows the process of Xiao Ming making a phone call to buy a keyboard. Line 1 says he got Jia’s number from the talent registry, and line 2 says he called Jia to offer to buy the keyboard. Jia merchant received the request, immediately shipped, return value result indicates that Xiaoming received the keyboard.

1    IMerchant merchantJia = IMerchant.Stub.asInterface(ServiceManager.getService("MerchantJia"));
2    Keyboard result = merchantJia.buyKeyboard();
Copy the code

What will happen if Xiao Ming doesn’t want to buy from Jia merchant, but zhen Merchant instead?

Zhen merchants register at talent Registry (Non-Talent Center) :

1    Merchant zhen = new Merchant("Zhen");
2    ServiceManager.addService("MerchantZhen", zhen);
Copy the code

Xiao Ming calls to buy a keyboard:

1    IMerchant merchantZhen = IMerchant.Stub.asInterface(ServiceManager.getService("MerchantZhen"));
2    Keyboard result = merchantZhen.buyKeyboard();
Copy the code

Both jia Merchant and Zhen Merchant are merchants, so they are instantiated from the Merchant class. Therefore, the professional “Merchant” is mapped to the “Merchant” class, and the instantiated objects of the class are the specific service providers (merchants and zhen merchants), representing the entities that provide a certain type of service.

Jia businessman to telephone indirect telephone:

The operator of the human resources center received a call from Xiao Ming, saying that he wanted to talk to Jia Businessman, so he assigned him a telephone room D. The reason for the assignment of D is that the three telephones A/B/C are now in use. Analogy back to the source code, the talent center represents the Server process, the phone represents the thread.

If the talent center receives Ming’s request, the Server process receives data from the Client process. It is then decided to hand it over to the merchant, which means that the Server process will transfer the data from the Client to the MerchantJia object. Next, A/B/C call booth is being used, indicating that three Binder threads are currently processing additional requests. The caller D is eventually assigned to the dealer, indicating that the service method will then run in thread D.

3.2 Clipboard example

The clipboard copy text code above is then used as a sample to illustrate the specific process covered in RPC.

1    // Get the system clipboard
2    ClipboardManager clipboard = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
3    
4    // Create a clipping dataset containing a plain text data entry (the data that needs to be copied)
5    ClipData clipData = ClipData.newPlainText(null."Text data to copy");
6    
7    // Set (copy) the data set to the clipboard
8    clipboard.setPrimaryClip(clipData);
Copy the code

3.2.1 Process for Creating a Service Object

The clipboard service object is located in the system_server process, and its proxy objects can be distributed among all App processes that require this service.

Therefore, the creation of the clipboard service object also occurs in the system_server process (Server process). It is of type ClipboardImpl, and the methods in this class are concrete implementations of the clipboard service.

/frameworks/base/services/core/java/com/android/server/clipboard/ClipboardService.java

232    private class ClipboardImpl extends IClipboard.Stub {
233        @Override
234        public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
235                throws RemoteException {
236            try {
237                return super.onTransact(code, data, reply, flags);
238            } catch (RuntimeException e) {
239                if(! (einstanceof SecurityException)) {
240                    Slog.wtf("clipboard"."Exception: ", e);
241                }
242                throw e;
243            }
244
245        }
246
247        @Override
248        public void setPrimaryClip(ClipData clip, String callingPackage) {
249            synchronized (this) {
250                if (clip == null || clip.getItemCount() <= 0) {
251                    throw new IllegalArgumentException("No items");
252                }
253                final int callingUid = Binder.getCallingUid();
254                if(! clipboardAccessAllowed(AppOpsManager.OP_WRITE_CLIPBOARD, callingPackage,255                            callingUid)) {
256                    return;
257                }
258                checkDataOwnerLocked(clip, callingUid);
259                setPrimaryClipInternal(clip, callingUid);
260            }
261        }

Copy the code

The ClipboardService object is created in the clipboardservice. onStart method and registered with the ServiceManager during the startup of the system_server process.

/frameworks/base/services/core/java/com/android/server/clipboard/ClipboardService.java

192    @Override
193    public void onStart(a) {
194        publishBinderService(Context.CLIPBOARD_SERVICE, new ClipboardImpl());
195    }

Copy the code

/frameworks/base/services/java/com/android/server/SystemServer.java

1064            traceBeginAndSlog("StartClipboardService");
1065            mSystemServiceManager.startService(ClipboardService.class);
1066            traceEnd();

Copy the code

3.2.2 Finding proxy Objects for Service Objects

1    // Get the system clipboard
2    ClipboardManager clipboard = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
3    
4    // Create a clipping dataset containing a plain text data entry (the data that needs to be copied)
5    ClipData clipData = ClipData.newPlainText(null."Text data to copy");
6    
7    // Set (copy) the data set to the clipboard
8    clipboard.setPrimaryClip(clipData);

Copy the code

The search for the proxy object occurs in the Client process. The Clipboard object in line 2 above is a layer of encapsulation that inside is a proxy object for the Clipboard service object (forgive me for being so tricky, but there’s nothing wrong with sacrificing some linguistic beauty for the sake of accurate meaning).

/frameworks/base/core/java/android/app/ContextImpl.java

1719    @Override
1720    public Object getSystemService(String name) {
1721        return SystemServiceRegistry.getSystemService(this, name);
1722    }

Copy the code

/frameworks/base/core/java/android/app/SystemServiceRegistry.java

1012    public static Object getSystemService(ContextImpl ctx, String name) {
1013ServiceFetcher<? > fetcher = SYSTEM_SERVICE_FETCHERS.get(name);1014        returnfetcher ! =null ? fetcher.getService(ctx) : null;
1015    }

Copy the code

/frameworks/base/core/java/android/app/SystemServiceRegistry.java

178    private static finalHashMap<String, ServiceFetcher<? >> SYSTEM_SERVICE_FETCHERS =179            newHashMap<String, ServiceFetcher<? > > ();Copy the code

/frameworks/base/core/java/android/app/SystemServiceRegistry.java

261        registerService(Context.CLIPBOARD_SERVICE, ClipboardManager.class,
262                new CachedServiceFetcher<ClipboardManager>() {
263            @Override
264            public ClipboardManager createService(ContextImpl ctx) throws ServiceNotFoundException {
265                return new ClipboardManager(ctx.getOuterContext(),
266                        ctx.mMainThread.getHandler());
267            }});

Copy the code

SYSTEM_SERVICE_FETCHERS is a HashMap that stores proxy objects (or their Wrapper objects) for many system service objects as key-value pairs. For the clipboard, the getSystemService method eventually creates a ClipboardManager object and returns it.

/frameworks/base/core/java/android/content/ClipboardManager.java

85    public ClipboardManager(Context context, Handler handler) throws ServiceNotFoundException {
86        mContext = context;
87        mHandler = handler;
88        mService = IClipboard.Stub.asInterface(
89                ServiceManager.getServiceOrThrow(Context.CLIPBOARD_SERVICE));
90    }

Copy the code

In the constructor of ClipboardManager, line 88 is particularly critical. The proxy object of the Clipboard service object is found in the ServiceManger based on the string “clipboard”. The proxy object obtained at this time only has the ability to communicate across processes. This proxy object is then given clipboard capabilities via asInterface.

/frameworks/base/core/java/android/content/Context.java

3727    public static final String CLIPBOARD_SERVICE = "clipboard";

Copy the code

For Java, an interface is a capability. In the Binder world, a proxy object for a service object usually encapsulates a particular service interface. For example, a clipboard is an IClipboard, indicating that the object has many of the capabilities that clipboard services can provide, such as copying and pasting text. In addition, there is a field inside the proxy object that encapsulates the IBinder interface, indicating that the field has the ability to communicate across processes, and that it comes into play during each IPC.

Stub is a class automatically generated by the AIDL file, and iclipboard.stub.proxy is a wrapper over Binder native classes in the final generated file. Binder native classes offer an additional layer of data packaging and unpacking.

Iclipboard.java (not in source, generated indirectly at compile time from iclipboard.aidl file)

1    /** Local-side IPC implementation stub class. */
2    public static abstract class Stub extends android.os.Binder implements android.content.IClipboard

Copy the code
1    private static class Proxy implements android.content.IClipboard

Copy the code

IClipboard. Stub. AsInterface method to originally only has IBinder (cross-process communication) ability of object gives IClipboard (clipboard). In this way, the resulting proxy object has both cross-process communication and clipboard capabilities. The ability to communicate across processes is transparent to developers, but the ability to clipboard is what they really care about.

3.2.3 RPC Through proxy Objects

When clipboard.setPrimaryClip(clipData) is called to write data to the clipboard, the mService.setPrimaryClip method is actually called at the bottom.

/frameworks/base/core/java/android/content/ClipboardManager.java

100    public void setPrimaryClip(@NonNull ClipData clip) {
101        try {
102            Preconditions.checkNotNull(clip);
103            clip.prepareToLeaveProcess(true);
104            mService.setPrimaryClip(clip, mContext.getOpPackageName());
105        } catch (RemoteException e) {
106            throw e.rethrowFromSystemServer();
107        }
108    }

Copy the code

MService. The final call is IClipboard setPrimaryClip method. The Stub. Proxy. SetPrimaryClip method, will be packaged in _data while forming parameters, Unpack the _reply and read the return value from the Server (there is no return value in the method used for the example). The actual cross-process transfer is done through line 16 below. The type of mRemote is Android.os.ibinder, indicating that it has cross-process transmission capability. Calling its Transact method means sending the packaged parameters to the Server process.

Iclipboard.java (not in source, generated indirectly at compile time from iclipboard.aidl file)

1    @Override public void setPrimaryClip(android.content.ClipData clip, java.lang.String callingPackage, int userId) throws android.os.RemoteException
2    {
3      android.os.Parcel _data = android.os.Parcel.obtain();
4      android.os.Parcel _reply = android.os.Parcel.obtain();
5      try {
6        _data.writeInterfaceToken(DESCRIPTOR);
7        if((clip! =null)) {
8          _data.writeInt(1);
9          clip.writeToParcel(_data, 0);
10       }
11       else {
12         _data.writeInt(0);
13       }
14       _data.writeString(callingPackage);
15       _data.writeInt(userId);
16       boolean _status = mRemote.transact(Stub.TRANSACTION_setPrimaryClip, _data, _reply, 0);
17       if(! _status && getDefaultImpl() ! =null) {
18         getDefaultImpl().setPrimaryClip(clip, callingPackage, userId);
19         return;
20       }
21       _reply.readException();
22     }
23     finally {
24       _reply.recycle();
25       _data.recycle();
26     }
27   }

Copy the code

Binder drivers are ultimately responsible for data transfer, so the above transact methods eventually enter kernel space via iocTL system calls and send data to the Server process through a series of Driver functions.

3.2.4 The service object processes the received request

After the Server process receives the parameter data from the Client process, the actual processing begins. We will skip the Binder thread selection process here, because this process occurs in Binder drivers, and we will discuss it later when we write about Binder drivers.

When the Clipboard service object receives the request, it eventually calls the ClipBoardimpl.onTransact method.

/frameworks/base/services/core/java/com/android/server/clipboard/ClipboardService.java

234        public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
235                throws RemoteException {
236            try {
237                return super.onTransact(code, data, reply, flags);
238            } catch (RuntimeException e) {
239                if(! (einstanceof SecurityException)) {
240                    Slog.wtf("clipboard"."Exception: ", e);
241                }
242                throw e;
243            }
244
245        }

Copy the code

This method then calls the onTransact method of the parent iclipboard.stub class.

Iclipboard.java (not in source, generated indirectly at compile time from iclipboard.aidl file)

1    @Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
2    {
3      java.lang.String descriptor = DESCRIPTOR;
4      switch (code)
5      {
6        case INTERFACE_TRANSACTION:
7        {
8          reply.writeString(descriptor);
9          return true;
10       }
11       case TRANSACTION_setPrimaryClip:
12       {
13         data.enforceInterface(descriptor);
14         android.content.ClipData _arg0;
15         if ((0! =data.readInt())) {16           _arg0 = android.content.ClipData.CREATOR.createFromParcel(data);
17         }
18         else {
20           _arg0 = null;
21         }
22         java.lang.String _arg1;
23         _arg1 = data.readString();
24         int _arg2;
25         _arg2 = data.readInt();
26         this.setPrimaryClip(_arg0, _arg1, _arg2);
27         reply.writeNoException();
28         return true;
29}... .131  }

Copy the code

The onTransact method is a large switch-case scene that determines which method to call by passing the code in the data. For example, when the Client process is following RPC, the Server process will walk into the code branch at line 11 above.

1    clipboard.setPrimaryClip(clipData);

Copy the code

Once you walk into the branch, the parameters are unpacked and the this.setPrimaryClip method is finally called. Go back to the original ClipboardImpl class and execute its setPrimaryClip method.

/frameworks/base/services/core/java/com/android/server/clipboard/ClipboardService.java

247        @Override
248        public void setPrimaryClip(ClipData clip, String callingPackage) {
249            synchronized (this) {
250                if (clip == null || clip.getItemCount() <= 0) {
251                    throw new IllegalArgumentException("No items");
252                }
253                final int callingUid = Binder.getCallingUid();
254                if(! clipboardAccessAllowed(AppOpsManager.OP_WRITE_CLIPBOARD, callingPackage,255                            callingUid)) {
256                    return;
257                }
258                checkDataOwnerLocked(clip, callingUid);
259                setPrimaryClipInternal(clip, callingUid);
260            }
261        }

Copy the code

When setPrimaryClip is executed, (assuming it has a return value) the return value is passed layer by layer to the Native layer and eventually sent back to the Client process through a system call to the Binder Driver.

Upon receiving the return value, the Client process terminates the RPC and continues to execute the code following the RPC.

At this point, a complete RPC procedure is complete.

4. To summarize

Binder is seen as the Remote Procedure Call (RPC) mechanism in the Android world from an application developer’s perspective. I first introduced the general concepts of RPC and why you need a lot of RPC in Android. Then it goes into the Binder mechanism, explains the whole process of Binder RPC, and visualizes the essence of Binder through the case of “talent center”.

In addition, the relationship between services/processes/threads has been clarified to help clear up confusion and confusion in daily development.

Banshan. tech/Binder%E6%A…