In a distributed system, components crash. In Android, processes die frequently—either due to a crash (NullPointerException, native segfault) or because the Low Memory Killer (LMK) terminated them to free up RAM.
When Process A holds a Binder proxy to an object in Process B, and Process B dies, the proxy becomes a stale pointer. If Process A tries to use it, it will encounter a DeadObjectException. To allow systems to gracefully handle these failures, Binder provides the "Death Recipient" mechanism.
linkToDeath() and unlinkToDeath()
A client process can ask the Binder driver to notify it if the process hosting a specific Binder object dies. This is done by registering a Death Recipient.
In Java, this is exposed on the IBinder interface:
IBinder.DeathRecipient recipient = new IBinder.DeathRecipient() {
@Override
public void binderDied() {
Log.w(TAG, "The remote service died!");
// Clean up state, maybe attempt to reconnect
}
};
try {
mRemoteBinder.linkToDeath(recipient, 0);
} catch (RemoteException e) {
// The binder might ALREADY be dead!
}
If the client no longer cares about the remote object, it should unregister to prevent memory leaks using mRemoteBinder.unlinkToDeath(recipient, 0).
binderDied() Callback
The magic happens in the kernel. The Binder driver tracks which processes own which binder_node structs. It also hooks into the Linux kernel's process teardown mechanisms (like the release file operation on the /dev/binder file descriptor when a process exits).
When Process B dies, the kernel driver:
- Cleans up Process B's Binder state.
- Finds all other processes that have registered a Death Recipient for objects hosted by Process B.
- Sends a
BR_DEAD_BINDERcommand to those processes. - The
libbinderframework in the client process receives this, looks up the registered recipient, and invokes thebinderDied()callback on a Binder thread.
Use Cases: Service Crashes and Watchdogs
Death Recipients are fundamentally how Android maintains system stability.
- System Server Watchdog: If
system_servercrashes, theinitprocess detects it and immediately killsZygote, forcing a soft reboot. But what about the HAL daemons? Many system services register death recipients on their respective HALs (likeAudioFlingerorCameraService). If a HAL daemon crashes, the system service catches thebinderDied()callback and immediately triggers a restart of the HAL connection, hiding the crash from the user. - ActivityManagerService (AMS): AMS uses death recipients extensively to track application lifecycles. When an app binds to a
Servicein another process, AMS links to the death of the remote process. If the remote process dies, AMS knows to update its internal state, notify the client app, and potentially schedule a restart of the Service.
Implementation in Native and Java
While the Java implementation is straightforward, it relies entirely on the native C++ BpBinder::linkToDeath method. In C++, a developer creates a class extending IBinder::DeathRecipient and overrides the pure virtual binderDied(const wp<IBinder>& who) method. The semantics and kernel interactions are identical across both languages.