Android from the foundation · Puzzle 13 of 16
Inside a Binder call
In puzzle 4, Binder carried messages between processes. Here's how a method call actually crosses: packed into bytes, marked with the sender's identity, and picked up by a waiting thread.
The problem
When your app calls startActivity(), the method that sends an Intent, it looks like an ordinary method call. But the object that does the work lives in system_server's memory, another process. And an object is data in a process's memory (puzzle 0), so it can't leave that process. So how does a call cross from your app to the town hall and back? The answer: each "message" from puzzle 4 is really a method call on an object in another process.
See it
Your app calls a method that lives in system_server. In your process sits a proxy: an object that looks exactly like the one in system_server.
Step 1 of 5Objects can't leave a process; bytes can. The proxy packs the arguments into a Parcel: a flat envelope of bytes (a byte is the smallest unit of data a program stores). Your thread that called now waits.
Step 2 of 5Binder, in the kernel, carries the Parcel and stamps it with your app's UID, its key. Predict: who picks it up at the town hall?
Step 3 of 5One of system_server's Binder threads: ordinary threads inside a process that wait for Binder to hand them calls. It unpacks the Parcel and calls the real method.
Step 4 of 5The reply (a result, or just "done") travels back the same way, and only now does your waiting thread go on. If it was the main thread, the screen waited too.
Step 5 of 5
The building block
On your side sits a proxy: an object that looks exactly like the object in the other process that does the work. You never write it: Android's own code hands it to your app. Calling it packs the arguments into a Parcel, a flat envelope of bytes. Binder carries the Parcel across, and stamps it with your app's UID.
Binder itself lives in the kernel. A Binder thread is different: an ordinary thread inside a process, one of a few that wait for Binder to hand them calls. In the receiving process, one of them unpacks the Parcel and calls the real method. Meanwhile the thread in your app that made the call waits (if that's the main thread, the screen waits too); the reply comes back the same way, and only then does the call return.
It works both ways: system_server holds proxies for your app's objects too, and its calls arrive on your app's own Binder threads.
The stamp is how permission checks from puzzle 7 work, and your app can't change it. (For the curious: the receiver reads it with Binder.getCallingUid()Native: the UID the kernel stamped on the call. The caller can't fake it. 338 public static final native int getCallingUid();Binder.java, line 338 · AOSP frameworks/base ↗; Binder's own code is in the Linux kernel, under drivers/android/binderThe Binder driver in mainline Linux (now written in Rust) stamps the sender's user ID on every transaction. Android phones run Android's own kernel branches, which may still use the older C driver. 392 from_parent,
393 sender_euid: Kuid::current_euid(),transaction.rs, line 392 · Linux kernel ↗.)
The analogy
You hand your letter to the counter in your house (the proxy). The post office carries the envelope, stamps your key (your UID) on it, and a courier at the town hall's door brings it to the right desk.
Check yourself
Sources
What you now know
proxy → Parcel → Binder stamps your UID → a Binder thread → the real method, and back. Meanwhile the thread that called waits.
Answer every question above correctly to unlock the next puzzle.