mattias | niklewski.com

File Descriptors and Binder

Android's Binder is documented as a light-weight IPC mechanism. It allows process A to make remote procedure calls to process B. The remote calls can include parameters, which have to be either primitive, Parcelable, or Binders themselves.

The calls can be synchronous or async. For synchronous calls, binder supports reentrancy, i.e. B may call back into A. This is important to maintain the illusion of an in-process call.

There are many interesting concepts in Binder. ServiceManager, for instance, makes it possible to look up IPC endpoints by name and connect to them. If you have seen other IPC implementations like Microsoft's COM, most things are familiar. But there is one feature that caught my attention.

Binder supports passing file descriptors between processes. If you know about processes and files in Linux, this may sound odd. File descriptors are usually not shared.

There are a couple of exceptions. The first is that open file descriptors are inherited when a process is forked, but this is a special case that requires that

Second, it is possible to share file descriptors over Unix Domain Sockets (as ancillary data).

Binder is not based on domain sockets, however. And still it allows any two processes to share file descriptors. So the logical conclusion is that it involves a kernel hack of some kind.

And indeed, that's exactly what's going on. Part of binder is a kernel driver, and this driver implements a sort of cross-process version of dup(). I'm not a kernel hacker evidently, because my reaction to this was a mixture of delight and terror. I know now that a real kernel developer would feel no delight. I poked around the kernel patches related to binder, and found some hilarious passive-aggressive comments on Binder's design.

NOTE: __fd_install() variant is really, really low-level; don't use it unless you are forced to by truly lousy API shoved down your throat.

Essentially, alloc_fd() in a files_struct we own a reference to. Most of the time wanting to use it is a sign of lousy API design (such as android/binder).

Note that 'API' in this context refers to the ioctl() interface between the driver and Binder's user space component.

Before it was cleaned up, the implementation copy/pasted a page of code from dup() and simply changed a few references from 'current process' to 'this other process'. But it works, and it's kind of neat from a user space point of view. It provides a mechanism by which one process may grant another process access to an open file descriptor. This is used in high-level APIs like openFile().

If you want to read more about Binder's inclusion in the kernel's staging area, there is an interesting discussion on the kernel mailing list here.