AOSP Framework & Internals
3 min read

The Binder Driver

A deep dive into the custom kernel module that powers Inter-Process Communication (IPC) across the entire Android platform.

Android isolates every application into its own process, assigning it a unique UID and an isolated virtual memory space. While this is great for security, it creates a massive problem: how does the Camera app talk to the hardware Camera daemon if their memory spaces are completely isolated?

They must use Inter-Process Communication (IPC). While standard Linux offers several IPC mechanisms (pipes, sockets, shared memory), Google found them either too slow or too insecure for a modern smartphone, leading to the creation of the Binder Driver.

Binder as a Kernel Driver

Binder is a custom Linux kernel driver located at drivers/android/binder.c in the kernel source tree. It exposes a character device file in the filesystem at /dev/binder.

Any user-space process (like an app or a system service) that wants to communicate with another process simply opens the /dev/binder file and uses the ioctl() system call to send and receive binary transaction data.

// A simplified view of how user-space talks to the Binder driver
int fd = open("/dev/binder", O_RDWR);

struct binder_write_read bwr;
bwr.write_size = sizeof(write_buffer);
bwr.write_buffer = (uintptr_t)write_buffer;
// ... populate read buffer ...

// Send the transaction to the kernel
ioctl(fd, BINDER_WRITE_READ, &bwr);

The One-Copy Mechanism

The defining feature of Binder, and the reason it is vastly superior to traditional Linux pipes or sockets, is its incredible performance due to the One-Copy Mechanism.

In a traditional Linux socket IPC:

  1. The sender copies data from its user-space RAM into kernel-space RAM.
  2. The receiver copies that same data from kernel-space RAM into its own user-space RAM. This results in two heavy memory copies, draining the battery and lagging the UI when transferring large payloads.

In Binder:

  1. When a process starts, it uses the mmap() system call to map a chunk of its virtual user-space memory directly to the Binder kernel driver's memory.
  2. When the sender sends a message, the kernel copies the data once from the sender's memory directly into that pre-mapped shared memory block.
  3. The receiver instantly has access to the data because it is already mapped into its user-space.
// Mapping 1MB of memory for the Binder driver (One-Copy mechanism)
void *mapped_memory = mmap(NULL, 1024 * 1024, PROT_READ, MAP_PRIVATE, fd, 0);

By eliminating the second copy, Binder drastically reduces CPU overhead when transferring massive amounts of data.

Thread Pool Management

Binder requests are fundamentally synchronous by default. When App A calls a function in Service B via Binder, App A is blocked (put to sleep by the kernel) until Service B finishes the calculation and returns the result.

To handle hundreds of simultaneous requests without freezing, Binder utilizes kernel-managed thread pools. When a process opens /dev/binder, it registers a maximum number of Binder threads (typically 15).

The kernel tracks these threads in the task_struct. If 5 apps call Service B simultaneously, the Binder driver automatically wakes up 5 sleeping threads in Service B's pool and dispatches the work concurrently, ensuring the system remains highly responsive.

Inspecting Binder Transactions

You can view real-time Binder transaction statistics directly from the ADB shell using the debug filesystem:

adb shell cat /sys/kernel/debug/binder/state

This command outputs the exact number of active threads, pending transactions, and memory allocations inside the Binder kernel driver!