Skip to main content

Binder IPC — why Android needs it

Begin with the problem​

Android normally runs applications and important system services in different Linux processes. Different processes do not share ordinary Kotlin objects or memory.

Suppose a shopping app wants to post a notification:

notificationManager.notify(42, notification)

The application process cannot directly modify the notification shade. Android’s notification service owns that device-wide responsibility in another process. The two processes need a controlled communication mechanism.

Binder is Android’s primary inter-process communication mechanism. It lets code in one process request a method-like operation from an object hosted in another process.

:::warning Android-only chapter Binder, Parcel, Service, and AIDL require Android. These examples intentionally do not link to Kotlin Playground. :::

Local call versus Binder call​

Local Kotlin call​

val total = priceCalculator.total(cart)

The caller and priceCalculator normally share one heap. The program can pass a direct reference to cart.

Remote Binder call​

val status = remoteOrderService.status("ORD-42")

If remoteOrderService lives in another process, the cart-style direct reference cannot cross the boundary. Arguments must be copied into a transport representation, sent through the Binder driver, reconstructed in the server process, and the result must travel back.

The syntax looks similar. The cost and failure model are not.

The five participants​

CLIENT PROCESS SERVER PROCESS

Client code Service implementation
│ ▲
▼ │
Proxy ── writes arguments ──► Parcel Parcel ──► Stub/onTransact
│ │ ▲ │
└──────────────────────────────▼────────┘ │
Binder kernel driver │
ParticipantJob
ClientCalls the interface
ProxyConverts the call into a Binder transaction
ParcelCompact container carrying arguments or a reply
Binder driverRoutes the transaction between processes
Stub/nodeDecodes the transaction and calls server code

The official AOSP Binder overview describes the client endpoint as a proxy and the server endpoint as a Binder node. AIDL generates much of the proxy/stub transport code for application developers.

Why not use sockets for everything?​

Sockets can communicate between processes, but Binder is integrated with Android’s object, identity, lifecycle, and permission model.

Binder provides useful platform behaviour such as:

  • routing method-like transactions;
  • transporting the caller’s UID/PID to the server;
  • passing Binder object references;
  • detecting a dead remote process;
  • integrating with Android system services and AIDL;
  • kernel-mediated transaction delivery.

This does not make Binder appropriate for bulk data. Use files, shared memory, pipes, databases, or streaming APIs when the payload demands them. Binder is excellent for control messages and bounded structured data.

Where an app developer meets Binder​

You use Binder even if you never write AIDL:

  • framework managers talk to system services;
  • an Activity launch is coordinated with system services;
  • Bundles and saved-state data can cross Binder-backed boundaries;
  • ContentResolver can communicate with another process’s provider;
  • PendingIntent represents a system-held capability;
  • bound services can expose an IBinder interface.

One-line call, ten-step journey​

For a synchronous call such as remote.status("ORD-42"):

  1. Client code calls a generated proxy.
  2. The proxy creates a request Parcel.
  3. It writes an interface token, method identifier, and arguments.
  4. The proxy calls transact() on IBinder.
  5. The Binder kernel driver finds the process hosting the target node.
  6. The transaction is copied to the server process.
  7. A server Binder thread receives it.
  8. The Stub validates/decodes it and invokes status("ORD-42").
  9. The result or supported exception is written to a reply Parcel.
  10. The reply returns through the driver; the proxy decodes and returns it.

While steps 5–9 execute, the calling thread normally waits. Calling a slow synchronous Binder method from the main thread can therefore freeze UI.

Binder is not the same as a Service​

ConceptMeaning
ServiceAndroid application component with started/bound lifecycle
BinderIPC/RPC mechanism used to communicate across processes
AIDLLanguage used to define a Binder interface and generate transport code
Bound serviceA Service that returns an IBinder to connected clients

A Service does not automatically create a background thread. A bound Service can be in the same process, in which case Binder may optimize to a local interface, or in another process, which requires IPC.

When should an ordinary app use AIDL?​

Usually, it should not.

Use AIDL when a genuine process boundary exists, such as:

  • a device/OEM integration exposes a remote service;
  • two separately installed apps need a stable IPC contract;
  • a deliberate isolated process hosts risky or specialized work;
  • a platform-level component must support concurrent remote clients.

Do not add another process and AIDL merely to “make architecture cleaner.” Interfaces between modules in the same process should normally be Kotlin interfaces with constructor injection.

Decision table​

NeedBetter starting point
Call a class in the same processKotlin interface/direct call
Screen-bound asynchronous workLifecycle-aware coroutine
Durable deferred workWorkManager
Sequential message-based IPCMessenger may be sufficient
Concurrent typed cross-process APIAIDL/Binder
Share structured data with other appsContentProvider
Transfer a large filecontent:// URI/FileProvider/stream

Practical mistakes​

❌ Avoid: pretending a remote object is an ordinary repository​

items.map { item -> remoteCatalog.price(item.id) }

One list rendering can cause hundreds of synchronous transactions.

✅ Prefer: design a coarse but bounded operation​

val prices = remoteCatalog.prices(itemIds.take(100))

Batching reduces round trips, but “send everything” is also wrong because Binder payloads are bounded. Define a sensible maximum or use pagination/streaming.

❌ Avoid: sending a bitmap through an Intent extra​

intent.putExtra("receipt", receiptBitmap)

✅ Prefer: send a URI or stable identifier​

intent.putExtra("receiptUri", receiptUri.toString())

Grant URI permission if another app/process needs access, and stream the content.

Interview-ready answer

Binder is Android’s primary IPC mechanism. A client calls a proxy, the proxy marshals arguments into a Parcel, the Binder kernel driver routes the transaction to a Binder thread in the server process, and a Stub unmarshals and invokes the implementation. Synchronous calls block for a reply, remote processes can die, payload capacity is limited, and incoming calls can be concurrent. I use Binder for small control messages and stable cross-process contracts, not bulk data or ordinary same-process layering.

Continue the chapter​

  1. Proxy, Stub, Parcel, and the complete call path
  2. Practical AIDL client and server
  3. Binder threading, reentrancy, and deadlocks
  4. Failures, transaction limits, identity, and security
  5. Binder interview questions and design exercises