Yet Another Deep Dive of Android Binder Framework, Part 1

There are already lots of excellent articles/presentations on the web talking about Android Binder. Most of them are focusing on the Java Service/AIDL, or talking only about the theory instead of looking at the actually classes involved. In this series of articles, we will take a close look at the major classes that form the Android Binder Framework (user space only), as shown in Figure 1 below. These classes are the foundation for implementing Java service and are the backbones of all system native services. And, of particular interest, we will talk about the implementation of several important binder features such as passing IBinder object or file descriptors across process.  



Figure 1 Android Binder Framework

IBinder, BpBinder and BBinder



IBinder defines the abstract interface for a binder object; BpBinder and BBinder are the concrete implementation in proxy and server side, respectively. Following table lists the most important methods of IBinder interface, and their corresponding implementation in BpBinder and BBinder.


IBinder
BpBinder
BBinder
constructor
BpBinder(int32_t handle)
Nothing special
transact(code,data,reply,flag)
call IPCThreadState::transact(mHandle)
call onTransact() overrided in subclass
linkToDeath
valid
N/A
localBinder(): BBinder *
return NULL
return this
remoteBinder():BpBinder *
return this
return NULL
queryLocalInterface
return NULL
return this


That constructor of BpBinder takes an integer handle, which is created by binder driver. When calling BpBinder::transact(), that handle will be passed to the binder driver and used by the driver to locate the transaction’s target process and target BBinder object. Once the BBinder object is found, binder driver will wake up one of the target process’s binder threads and invoke the BBinder’s transact() method. This describes briefly how the BpBinder and BBinder are connected and we will revisit again in following example of adding a customer interface (or services).


IInterface, BpInterface and BnInterface



The Binder classes we talked about in previous section deal with the low level inter-process communication, on top of the binder driver. The IInterface and it associated classes are to provide higher level abstraction, turning a inter-process communication into a remote procedure call.


To glue all those classes together and see how they interact with each other, we'll take a look at a simple example of implementing a simple interface, IAdd, which provides only single method of adding two int values. 

Here are the four steps needed to set up the binder based client/server architectures. You can see this pattern everywhere in Android native framework.


  1. Define IAdd by subclassing IInterface
  2. Subclass BpInterface with BpAdd, and implement interface method add
    1. Marshall the request data
    2. Call remote()->transact(code,data,reply) to send the transaction to bind driver
    3. Wait and unmarshall the reply
class BpAdd : public BpInterface {};

int  BpAdd::add(int a, int b)
{
       Parcel data, reply;
       // 2.a.
       data.writeInterfaceToken(IAdd::getInterfaceDescriptor());
       data.writeInt32(a);
       data.writeInt32(b);
       // 2.b
       if (remote()->transact(ADD, data, &reply) == NO_ERROR) {
           // 2.c
           int result = reply.readInt32();
           return result;
    }
   // on error, for simplicity, assume -1 means error.
   return -1;
}


  1. Subclass BnInterace with BnAdd, and implement onTransact()
    1. Dispatching based on transaction code
    2. Call the real implementation in the subclass of this class - see step 4
    3. Marshall the result into reply, which will be sent to driver and be received in the BpAdd
class BnAdd : public BnInterface {};

status_t BnAdd::onTransact(
   uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
   switch(code) {
       // 3.a
       case ADD: {
           CHECK_INTERFACE(IAdd, data, reply);
           int a = data.readInt32();
           int b = data.readInt32();
           // 3.b
           int result = add(a, b);     //implemented in AddImpl
           // 3.c
           reply->writeInt32(result);
           return NO_ERROR;
       } break;
       default:
           return BBinder::onTransact(code, data, reply, flags);
   }
}


4.  Subclass BnAdd with AddImpl, which provide the real implementation.
class AddImpl : public BnAdd {};

int AddImpl::add(int a, int b) {
   return a + b;
}


Figure 2 is what the class hierarchy looks like after adding our customer service; the red and blue line indicate the flow of calling its method remotely from the proxy.
Figure 2 Implement Custom Interface and Method Calling flow
                             
So far, we have covered the basic of the Android user space binder framework: the Binder classes, the Interface classes and how to add, implement and use new interface. You may have noticed that I have ignored some functions such as Parcel.writeStrongBinder and IInterface::asBinder and wondering what they are for. 

Yes, those are the real interesting stuff in Binder. In next article, we will take about how to pass an IBinder object across process, how to convert between the IBinder and IInterface, and when and who create the BpBinder object. We will also revisit in more detail regarding how the binder driver set up the handle <-> object mapping.