AOSP Framework & Internals
3 min read

Binder Transactions

Learn about Binder Transactions.

A Binder transaction is the act of passing data from a client process to a server process and, optionally, waiting for a reply. This exchange is heavily orchestrated by the Binder kernel driver using a specific command protocol.

BC_TRANSACTION and BR_TRANSACTION

Communication with the Binder driver (/dev/binder) happens via the ioctl system call using the BINDER_WRITE_READ command. The payload is a mix of commands starting with BC_ (Binder Command, sent to the driver) and BR_ (Binder Return, received from the driver).

  1. Client: The client constructs a payload and sends a BC_TRANSACTION command to the driver via ioctl. The client thread then blocks.
  2. Driver: The kernel driver finds the target process, wakes up a sleeping thread in the target's Binder thread pool, and queues a BR_TRANSACTION.
  3. Server: The server thread wakes up, reads the BR_TRANSACTION from ioctl, unparcels the data, and executes the requested method.
  4. Reply: The server thread packs the result, sends a BC_REPLY to the driver, and goes back to sleep.
  5. Return: The driver wakes the client thread and delivers a BR_REPLY. The client reads the result and continues execution.

Synchronous vs One-way Transactions

By default, Binder calls are synchronous. The client thread halts execution until the server finishes processing and sends a BC_REPLY. This can lead to ANRs (Application Not Responding) if an app calls a system service from its main thread and the service is slow.

To prevent blocking, developers can use one-way transactions. In AIDL, this is denoted by the oneway keyword. When a transaction is marked FLAG_ONEWAY, the kernel driver simply queues the BR_TRANSACTION on the server and immediately returns a BR_TRANSACTION_COMPLETE to the client. The client thread resumes execution without waiting for the server to actually process the method.

Transaction Data: Code, Flags, Data, Objects

A raw binder_transaction_data struct contains several key fields:

  • target: The handle pointing to the target Binder object.
  • code: A 32-bit integer indicating which method to call (e.g., TRANSACTION_startActivity).
  • flags: Bitmask indicating behavior (e.g., TF_ONE_WAY).
  • data: The serialized payload (the Parcel).
  • offsets: A pointer to an array of offsets. This is crucial: if the data payload contains nested IBinder objects or file descriptors, the offsets array tells the kernel exactly where they are, so the kernel can translate handles and duplicate file descriptors across the process boundary.

Nested Binder Calls

Binder supports recursive/nested calls across processes. If Process A calls Process B, and Process B needs to call Process A to fulfill the request, the driver recognizes that Process A's thread is currently blocked waiting for a reply. Instead of spawning a new thread in Process A, the driver pushes the new BR_TRANSACTION onto Process A's blocked thread, essentially treating it as a standard function call stack spanning multiple processes.