Binder Service Manager
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 BpBinder
s 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 BBinder
s 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 BinderProxy
s 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.