2

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.

Onik
  • 19,396
  • 14
  • 68
  • 91
sherrellbc
  • 4,650
  • 9
  • 48
  • 77

1 Answers1

1

Most System Services run as threads within the system_server process. At boot, they pass an invitation-to-call (see calls to addService() in SystemServer.java) to servicemanager which then is able to distribute the invitation to apps calling getSystemService.

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 of android.location.ILocationManager. One of the methods in the Proxy class is getLastLocation():

...
@Override public android.location.Location getLastLocation(android.location.LocationRequest request, java.lang.String packageName) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
android.location.Location _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
if ((request!=null)) {
_data.writeInt(1);
request.writeToParcel(_data, 0);
}
else {
_data.writeInt(0);
}
_data.writeString(packageName);
mRemote.transact(Stub.TRANSACTION_getLastLocation, _data, _reply, 0);
_reply.readException();
if ((0!=_reply.readInt())) {
_result = android.location.Location.CREATOR.createFromParcel(_reply);
}
else {
_result = null;
}
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
...

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 an onTransact() method running in the service's process space which processes all of the incoming transactions according to the transaction code:

...
case TRANSACTION_getLastLocation:
{
data.enforceInterface(DESCRIPTOR);
android.location.LocationRequest _arg0;
if ((0!=data.readInt())) {
_arg0 = android.location.LocationRequest.CREATOR.createFromParcel(data);
}
else {
_arg0 = null;
}
java.lang.String _arg1;
_arg1 = data.readString();
android.location.Location _result = this.getLastLocation(_arg0, _arg1);
reply.writeNoException();
if ((_result!=null)) {
reply.writeInt(1);
_result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
}
else {
reply.writeInt(0);
}
return true;
}
...

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 from service_manager via getSystemService) and 2) passing whatever checks the service itself implements, such as checking ACCESS_COARSE_LOCATION or ACCESS_FINE_LOCATION in the case of LocationManagerService (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 a hw_module_t struct), location callbacks are made (e.g., location_callback()), etc. All of this runs within the system_server process space with privileged UID system. You can verify this by running a location-enabled app, looking for GpsLocationProvider tags in the logcat and confirming that the logged PID is that of system_server. For example:

$ adb logcat | grep -i gps
...
D/GpsLocationProvider(  731): Reset GPS properties, previous size = 8
...

and

$ adb shell ps | grep system_server
system    731   441   1094884 89232 ffffffff b74d1d05 S system_server
$

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.

Paul Ratazzi
  • 6,289
  • 3
  • 38
  • 50
  • This is probably one of the most helpful posts I have read on this topic yet. The code you present above is nearly identical to what I found floating around the net earlier today. There is one glaring problem I see with the code and am unable to reconcile: Your second code snippet (run by the service) actually calls *this.getLastLocation* which you already had shown to be defined in such a way get it obtained two parcels and then sent them to the service stub; it appears to be recursive. – sherrellbc Jun 22 '15 at 21:02
  • Also, the service stub appears to set a private member within itself (_result) by querying calling *this.getLastLocation* (there's that seemingly recursive problem) and returns this result to the client requesting it. The problem still exists of how the actual GPS hardware is being driven such that the service even has the information to return to a requesting client. Is the service stub somehow linked (via binder) to a Kernel driver or something? How, and who, is actually driving the hardware and who is commanding them to do it? – sherrellbc Jun 22 '15 at 21:05
  • I did take the time to watch that video and review the slides. I do believe I understand the architecture of the Binder quite a bit more now, at least in terms of message passing between processes, marshalling of data, etc. I am still a bit confused on how the lower-level code is ran to control the hardware, however. Consider page 20 of the slide link you posted above. It shows that the `LocationManagerService` creates and uses the `location.GpsLocationProvider` which `links` to calls in the underlying .cpp hardware driver implementation. Yet, if you look at your code above .. – sherrellbc Jun 23 '15 at 14:57
  • The client (Stub.Proxy) goes through the Binder driver and talks to the service process (Stub). This stub unmarshalls the data and appears to call `getLastLocation` once more. On the client side, we have this member as `Stub.Proxy.getLastLocation`. Is the service instance of this LocationManager somehow different? If not, why is it not the case that when the Stub calls `getLastLocation` that is does not use the Binder once more to pass messages to itself? – sherrellbc Jun 23 '15 at 15:00
  • The problem I stated yesterday regarding the recursive still seems to be the case. Is the Service instance dynamically linked at run-time to those libraries, while the client is not? Or, perhaps, is it that since the service itself extends the Service.Stub that is auto-generated that it overrides the implementation of `getLastLocation`? That would make much more sense. – sherrellbc Jun 23 '15 at 15:10
  • 1
    I'm not sure if I can give you the exact answer you're looking for. However, I've experimented a bit with the GPS provider and have updated my answer with what I know. I'm pretty sure there isn't any recursion or 'service calling itself' going on. I do know that on the service/stub side, everything is done in the process space of `system_server`. This includes the native code that actually interfaces with the hardware devices and the JNI callbacks or whatever is necessary to generate the reply that is sent back to the proxy. And yes, `LocationManagerService` overrides `getLastLocation`. – Paul Ratazzi Jun 24 '15 at 01:59
  • Wonderful. Thank you for the updated information. How did you learn all of this about the location services (the call backs, the hw_module_t)? Where could I go to find similar information for the other services? – sherrellbc Jun 24 '15 at 02:53
  • Unfortunately, there isn't much reference information out there. And things change rapidly from release to release. What I posted above is the result of a long time of hacking around with the source code to try different things as part of my group's security research. For example, we figured out that the native location code was making callbacks to the Java layers after we accidentally broke `LocationManagerService` and then had to dig deeper to see how to fix it. Good luck! – Paul Ratazzi Jun 25 '15 at 17:12