Binder Service Manager

Baiqin Wang
9 min readFeb 4, 2021

--

Service Manager is a first class component in Binder that is responsible for managing all system services. A system service is a special kind of BBinder that's created in a system process to provide certain kinds of services for other processes. In essence, these system services don't have much difference with the RemoteService class in our HamKing project [1]. It's just that they run in privileged processes and can access protected resources so they can provide services that apps cannot do on their own. Service Manager is such a component where system processes can register the system services they create. In essence, the Service Manager itself is just a special system service though. The interface it implements is simple:

The addService interface method is used by certain system processes to register the system services they create. The name parameter is a human readable string name and the service parameter is the Binder object that implements a kind of system service. For example, the readable name for the WindowManagerService is "window". Service Manager keeps a mapping between service names and BpBinders of registered services so that another process can call getService to get a reference to a registered service.

The listServices interface method is used to return the list of services registered. For example, you can use the following command to view all registered system services:

Note that line 9 lists the Service Manager itself as a system service. Like we just said, Service Manager is just special system service who manages all system services.

Service Manager code walk through

I would suggest you read previous articles in this series before looking at this code walk through because some Service Manager related topics are covered there so I won’t repeat in this article.

We haven’t covered line 9 to 14 in this series yet. These lines mainly creates a ServiceManager object and registers current process as the context manager. Before we go any further, we need to understand why we need a Service Manager in Binder.

When a user space process wants to get a reference to a remote Binder service, it needs to tell the Binder driver the id of the remote Binder service. The id scheme Binder uses is the handle value in binder_ref structure. As we have seen in previous articles, when a BBinder is sent to a target process for the first time, a handle value will be allocated as the id to reference that BBinder in the target process. In another word, this id is only generated when a process can tell the driver which BBinder it tries to get a reference to. So the problem is, the driver can only create an id for you when you tell the driver which BBinder you are trying to reference, but you need to give driver the id to tell it which BBinder it is. The solution to this chicken-and-egg problem is to create a special BBinder that has a fixed id in all processes and this special BBinder is the Service Manager. The special handle value for Service Manager is 0 so every process can just use handle value 0 to get a reference to Service Manager. So with this hard-coded knowledge about Service Manager breaks the chicken-and-egg problem and a process can use Service Manager as the starting point to get references to other BBinder s.

Why can’t other BBinders have fixed handles as well? Well, if Binder is design in a way that the system services are the only kind of Binder objects that can be created, then it is possible to label them with hard-coded handle values. But in fact, the majority of Binder objects are created on-the-fly such are the ICreditCard, IHamKingInterface and IOrderSession in the HamKing project. You cannot pre-label something that is created randomly at runtime.

Let’s get back to the Service Manager source code. Line 9 creates an instance of ServiceManager and registers itself as a system service. The ServiceManager data structure is pretty simple:

The mNameToService is a map between service name to Service class. The most important field in Service class is the binder field which points to a system service.

The core logic of this addService method is just creating a Service object and creates the mapping. The same method is called when a remote process tries to register a service. The difference is that the remote process calls this method through a Binder transaction.

The getService is called when a remote process tries to get reference to a service. The method just gets the IBinder from the mapping and return it to the remote caller. I won't post code here because the code logic is very simple.

The setTheContextObject method stores this ServiceManager object to a global variable so that it can be referenced later. The becomeContextManager method registers current process as the context manager in Binder driver. In general you can think of Context Manager and Service Manager as the same concept but the term "Context Manager" is mainly used in Binder driver. Please understand that the system services including the Service Manager are implemented by Android framework so it is purely a user space concept. Binder driver knows nothing about the system services. The only thing it knows is that handle value 0 references a special binder_node and this node can be used to setup communication context. So it makes sense that this special object is named "Context Manager" in kernel space.

The becomeContextManager function first tries BINDER_SET_CONTEXT_MGR_EXT command, if not successful then it will fall back to BINDER_SET_CONTEXT_MGR. The first command is an extended version of the second one with which the caller can set certain flags on the binder_node. The FLAT_BINDER_FLAG_TXN_SECURITY_CTX flag that Service Manager sets requires incoming transactions to contain the security id of the caller. The binder_ioctl_set_ctx_mgr creates a new binder_node structure as the context manager node and stores it in the binder_context_mgr_node field of the binder_context. Remember that each Binder driver has one and only one binder_context so each driver can have at most one context manager. Line 30 to 33 checks whether the context manager has already been set because a Binder driver can only have one context manager. Line 35 calls a SELinux security hook to check whether current process is allowed to register context manager. Only the service manager process itself is allowed to register context manager. For a regular BBinder, its user space addresses are stored in the ptr and cookie fields of flat_binder_object during serialization. However the ServiceManager object is not serialized when registering it as context manager. Both the ptr and cookie fields are zero in the binder_node of context manager and the zero value is actual reserved for context manager node, just like a zero handle value is reserved to reference a context manager. We know that for a regular binder_node, the cookie and ptr fields will be passed to user space during a transaction so that user space can find the corresponding BBinder. But if the cookie value is zero for context manager, how can the Service Manager process finds the address of the ServiceManager object? Well setTheContextObject method has already stored the address of the ServiceManager in a global variable so that it can be found without the cookie value.

After Service Manager registers itself as the context manager of Binder driver, it will just start a Looper to observe incoming transactions.

System service registration

Most of the system services are registered in the system_server process and the corresponding source code can be found in SystemServer.java. Let's trace through how WindowManagerService is registered to Service Manager. Other system services are registered in a similar way:

This class runs in the system_server process. Line 16 creates an instance of WindowManagerService and line 20 registers the service to Service Manager. Context.WINDOW_SERVICE is the readable service name of WindowManagerService and it is "window" in current Android version.

The sServiceManager field caches the proxy side Java interface class IServiceManager which is auto-generated by aidl tool from IServiceManager.aidl. This field needs to be initialized if getIServiceManager is called for the first time.

The getContextObject method in BinderInternal just calls android_os_BinderInternal_getContextObject through JNI. The getContextMethod in ProcessState returns BpBinder pointing to Service Manager. As we have seen in the "Binder architecture and core components" article, javaObjectForIBinder will create a Java BinderProxy class as the return value of BinderInternal.getContextObject. ProcessState maintains a global cache of BpBinder so that only one BpBinder instance will be created for the same remote BBinder. For example, only one BpBinder that points to Service Manager will be created no matter how many places in a process reference Service Manager. The cache is basically a map from the handle value to the corresponding BpBinder. The binder field in handle_entry stores the BpBinder and the index of the handle_entry inside the mHandleToObject represents the handle value. Note that the value passed to getStrongProxyForHandle is a hard-coded 0 which is reserved for the context manager. If getContextObject is called for the first time, a BpBinder wrapping the handle value 0 will be created and eventually return to Java as a BinderProxy.

Note that during the whole process, Binder driver never passes a flat_binder_object representing the context manager binder_node to the current process. The current process just creates a BpBinder with handle value 0 in user space and returns. For comparison purpose, let check how a normal BpBinder gets created. Let's again use the requestOrder example in HamKing project. When the server app handles the incoming BR_TRANSACTION, the serialized ICreditCard object is already copied to server app's data buffer and the server code will read it out via readStrongBinder:

In the auto-generated server side Java code, ClientInfo.CREATOR.createFromParcel is called to initialize a ClientInfo from incoming data and readStrongBinder is used to initialize the ICreditCard field. The native method readStrongBinder will be called to do the actual work:

In our case, the ICreditCard instance is created in client app process and passed to server app process, so the type field in the flat_binder_object will be BINDER_TYPE_HANDLE. We know from the article "Binder transaction" that Binder driver will allocate a handle value for the ICreditCard when it is passed to server app process and the handle field in flat_binder_object contains that value. Line 27 then calls getStrongProxyHandle to initialize a BpBinder wrapping that handle value for down stream code to use. Note that for a normal BpBinder, the handle value is allocated by Binder driver and passed to the process in a flat_binder_object structure. But for Service Manager the handle value 0 is just a hard-coded value that wasn't generated by Binder driver.

Let’s get back to ServiceManager.java, the Binder.allowBlocking method just sets a flag on the returned BinderProxy class. And the ServiceManagerNative.asInterface method does an interface casting on the BinderProxy to get a IServiceManager interface class:

A ServiceManagerProxy object is created to wrap the Proxy object auto-generated by aidl. This ServiceManagerProxy and ServiceManagerNative class are not necessary and they will probably get removed in the future. Instead, the code should call IServiceManager.Stub.asInterface directly, just like what we did in HamKing project. Anyway, after this ServiceManagerProxy class is returned to the caller, the system_server process can add a system service via the addService interface method.

System service fetching

After a system service is registered to Service Manager, another process can use this service by requesting a reference to the service through Service Manager. The client app in HamKing project uses bindService method to require a reference to the remote IHamKingInterface. The bindService method actually calls the corresponding service method in ActivityManagerService to do the actual work:

The ActivityManagerService implements the interface methods in IActivityManager.aidl and it is also created in system_server process and registered to Service Manager which is the same as WindowManagerService. We will not look at how the bindService is actually implemented ActivityManagerService, we are only interested in how app process gets a reference to ActivityManagerService.

The bindService method inside Activity class comes from the ContextWrapper base class and the ContextWrapper class delegate the call to the ContextImpl class it wraps around:

Line 16 uses ActivityManager.getService to get a reference to the remote ActivityManagerService and calls the bindIsolatedService method in it.

ActivityManager.getService eventually calls ServiceManager.getService method to get back a BinderProxy pointing to the remote ActivityManagerService. We just saw how SystemServer uses ServiceManager class to add a system service to the remote Service Manager. This utility class is also used by app processes to interact with Service Manager. Line 12 gets back a BinderProxy pointing to ActivityManagerService and line 14 casts this BinderProxy to IActivityManager interface.

We have seen the implementation of getIServiceManager moments ago. The rawGetService method calls the corresponding remote method in Service Manager to get a BinderProxy referencing the ActivityManagerService. Since an app will interact with system services very often, you don't want this method to be called each time. So the BinderProxys pointing to system services are cached in a global sCache for better performance. After ContextImpl gets the IActivityManager interface, it will call the bindIsolatedService method in ActivityManagerService to lookup the requested Service.

External links

[1] https://github.com/androidonekb/HamKing

--

--