I have been reading all I can find trying to figure out the software architecture behind Android's BINDER IPC mechanism. From what I understand, BINDER sits in Kernel-space and temporarily gives user-space applications an allocation of shared memory through which messages can be relayed between processes.
Where I begin to lose my grip is how the actual implementation works - specifically related to parcels.
I was looking on the net and found an implementation of an arbitrary service provided by Android, like Network/Wifi/Notificaiton/Battery, ect (Docs). From my reading I've learned that a user-space program should not instantiate a service class itself, but rather get a reference to one though Context.getSystemService(Context.X)
. As such, I took this as an indirect way of saying that Android already has the service running, or at least has the resources to start it when needed. The implementation was essentially laid out something like:
Battery.class
BatteryManager.setBatteryState(){
Parcel parcelLocal = Parcel.obtain();
parcelLocal.writeInterfaceToken("android.power.BatteryManager");
parcelLocal.writeInt(1/0); //Depending on requested state
BinderObject.transact //Send the data using BINDER
CheckForExceptions(); //Upon return, check for exceptions
ReadResponse(); //If none, read the response from the target
DoAppropriateAction(); //Whatever we need to do after setting the state
parcelLocal.recycle(); //Return the parcel resource
}
At first it seems simple: When the user does something like:
BatteryMonitor bMonitor = Context.getSystemService(Context.POWER_SERVICE);
bMonitor.setBatteryStatus(1);
Then the user's
instance will use the BINDER mechanism to communicate with the system's
actual service controller (Which is an instance of the same class?). But, however, the code shown above IS the implementation for the system's battery monitoring service, so who is actually receiving the BINDER data?
TL;DR: If this is all very confusing, which it very well may be as I tried to condense a thousand lines of code into 10, the summary is: When a user intends to control the state of hardware - such as Network/Wifi/Location/Notifcations(the touchscreen) - what is actually going on within Android and who is really controlling the hardware associated with these abstracted services?
Note: The above code is completely fabricated and was intended to only show general structure.
Most System Services run as threads within the
system_server
process. At boot, they pass an invitation-to-call (see calls toaddService()
inSystemServer.java
) toservicemanager
which then is able to distribute the invitation to apps callinggetSystemService
.Once things are rolling, you can think of the whole setup as a kind of client-server architecture where your app is the client (remote or proxy side) and the server (local or stub side) is the system service that you're talking to. The client and server communicate through the inter-process communication (IPC) subsystem known as binder. There are different parts to the binder: Framework components perform the marshalling and unmarshalling of parcels, while the kernel driver does the actual memory copies to/from ioctl calls and keeps track of who's been invited to call at the process and thread level.
Apps interface with the binder via a proxy. For example, when you use
LocationManagerService
, you get an instance ofandroid.location.ILocationManager
. One of the methods in the Proxy class isgetLastLocation()
:Here you can see that the transaction code
TRANSACTION_getLastLocation
is written to the interface along with any necessary data, and a result is read. On the stub side, there's anonTransact()
method running in the service's process space which processes all of the incoming transactions according to the transaction code:In a nutshell, the
system_service
process acts on behalf of the caller. This allows it to do what are usually privileged operations on hardware or other system resources. The security is based on 1) the app having the invitation to call (obtained fromservice_manager
viagetSystemService
) and 2) passing whatever checks the service itself implements, such as checkingACCESS_COARSE_LOCATION
orACCESS_FINE_LOCATION
in the case ofLocationManagerService
(declared in the manifest and approved at install-time by the end-user).UPDATE: In the case of location service, these hardware operations entail getting the actual NMEA data from the GPS hardware. The way this is currently accomplished is via the
GpsLocationProvider
class which interfaces to native code via JNI. This native code (com_android_server_location_GpsLocationProvider.cpp
) is where the hardware device is opened (via an abstraction layer held in ahw_module_t
struct), location callbacks are made (e.g.,location_callback()
), etc. All of this runs within thesystem_server
process space with privileged UIDsystem
. You can verify this by running a location-enabled app, looking forGpsLocationProvider
tags in the logcat and confirming that the logged PID is that ofsystem_server
. For example:and
Finally, I highly recommend the video tutorial Deep Dive Into Android IPC/Binder Framework to learn more about this. The talk's slides can be found here.