Baseline: AOSP android-14.0.0_r1. Binder is Android's IPC system (Binder overview). A client gets a reference to an interface and makes a method-like call; when the endpoint is remote, generated/userspace code marshals data and the Binder kernel driver routes a transaction to the server process. Binder transports calls; it does not decide whether the operation is authorized.
Before you start#
Read Java, ART, and Native Interfaces, init and Android services, Zygote, ART, and system_server, The Android Security Model, and Concurrency & Object Lifetime. You need to understand Java interfaces/exceptions, process boundaries, explicit serialization, UID, permissions, and basic threads/locks. No hidden-API invocation or system modification is required.
Learning goals#
Trace one ordinary Android API operation through framework code and an AIDL service; explain proxy, transaction code, Parcel, driver, stub, server implementation, reply, and caller identity; distinguish interface-token validation from authorization; and classify common failures without assuming “Binder is broken.”
Binder building blocks#
An AIDL interface defines operations and supported data types. Generated code provides client and server bindings for a selected backend. The client-side proxy implements the interface by encoding fields and requesting a transaction. The server-side stub decodes and dispatches it to the implementation. A Parcel is a typed serialization container: it is not an arbitrary Java heap dump. A Binder node is a server endpoint; a handle is a process-local reference to that endpoint, not a global raw pointer. Binder objects and file descriptors use special reference-transfer rules.
A synchronous remote call normally blocks the calling thread while the server handles the request and returns a reply. A method marked oneway in AIDL is asynchronous: the caller returns after submission, without an ordinary result/reply (Binder threading); this is not proof that work completed. A process can die or freeze before processing it. Binder servers need one or more Binder threads to handle incoming work. Nested synchronous calls can re-enter a process, so code must consider concurrency, lock ordering, and callbacks. Never infer “one call, one thread, serialized” unless the actual implementation guarantees it.
The generated interface descriptor/token is written so the receiver can verify that a transaction is for the expected interface. It is not a secret, password, or authorization credential. The kernel can provide transaction caller identity, and framework service code must apply permission/app-op/ownership checks. Some APIs also perform local checks before IPC. Possessing an IBinder is not equivalent to having permission.
Concrete source trace: a vibration request#
This example follows a framework API operation, not a hidden ServiceManager app API. Source paths and call names are pinned to AOSP 14; follow the linked method bodies because surrounding details and public API signatures may differ in later releases. The exact path depends on which overload is used, device state, and implementation branch.
1. App-facing API and manager
An app obtains a Vibrator through the public Android API (for example the vibrator system service) and invokes a documented vibrate(...) overload. In Vibrator.java, the vibrate(...) overloads are annotated @RequiresPermission(VIBRATE). In AOSP 14, inspect Vibrator.java and SystemVibrator.java. SystemVibrator delegates to a VibratorManager (SystemVibrator.java); the concrete SystemVibratorManager holds the IVibratorManagerService proxy, obtained with ServiceManager.getService(Context.VIBRATOR_MANAGER_SERVICE). The abstract VibratorManager class does not hold that field. The public API layer converts an effect/attributes and supplies caller package/UID context. This is framework Java and is distinct from a direct app call to the hidden ServiceManager lookup API.
2. AIDL contract and generated binding algorithm
The contract is IVibratorManagerService.aidl. Inspect the vibrate declaration and its argument types; among them are caller identity/package information, a CombinedVibration, vibration attributes/reason, and a Binder token. The declaration is the schema, not the implementation. Soong invokes the AIDL compiler and emits backend-specific Java bindings during a build (AIDL backends). Generated Java is not necessarily checked into the source tree. Do not mistake the following algorithm sketch for a verbatim generated file:
ALGORITHM SKETCH — not checked-in/generated AOSP source
proxy.vibrate(arguments):
data = Parcel.obtain()
data.writeInterfaceToken(DESCRIPTOR)
write arguments in AIDL-declared order
remote.transact(TRANSACTION_vibrate, data, reply, flags)
read exception/result from reply, when synchronous
recycle parcels
For an exact generated implementation, build the relevant AIDL module from the pinned checkout and inspect its generated output, or read the AIDL compiler/backend source and identify its version. Do not claim exact byte order, transaction integer, flags, or helper calls from a generic AIDL example: generation/backend options affect details. The general rule is that both sides agree on field order and type; generated code is responsible for this paired encoding/decoding.
3. Userspace, driver, dispatch, and server implementation
For a remote Binder, generated Java calls IBinder.transact (IBinder.java); framework native plumbing reaches libbinder/Binder driver interaction. The native Binder library issues BINDER_WRITE_READ ioctls to the process's Binder device (IPCThreadState.cpp, Android 14 tag). The Linux Binder driver resolves the target handle/node and queues work for a Binder thread in the server (binder_transaction in binder.c, which records from_pid and sender_euid; Binder overview). The server's generated Stub dispatches the transaction code, reads the Parcel fields in the declared order, and calls implementation code.
The service implementation is VibratorManagerService.java. Follow its AIDL method implementation and the service publication/registration path; the implementation applies policy and routes work toward the vibrator subsystem. The physical HAL and driver are a later boundary covered by HAL, Treble, and the Vendor Boundary. This call trace should stop at the framework service rather than pretend that a Java Binder call itself vibrates hardware.
For a synchronous method, a reply transaction returns through driver/userspace to the proxy; generated code reads exception/result fields and the framework method returns to the app. A local Binder can dispatch directly without a kernel round trip. Some public calls may do work locally, cache, or enqueue asynchronously, so not every call at the Java API is exactly one remote transaction.
4. Where identity and policy apply
- Descriptor / interface token
- Is this transaction encoded for the expected interface? Not a secret or credential.
- Binder caller UID / PID
- Who is making the transaction? Use the actual transaction identity, not client-supplied claims alone.
- Service authorization
- Is that caller allowed this operation with these arguments under permission, app-op, and service policy?
Follow the actual service method and its helper calls to identify authorization. The server can inspect Binder caller UID/PID during transaction handling, then enforce permissions, app-ops, package ownership, and service-specific rules. These checks are not supplied by the descriptor token. If code clears Binder calling identity to make a downstream call under the service's own identity, it must restore identity correctly (clearCallingIdentity() / restoreCallingIdentity(long) in the Binder API reference); the point where identity is cleared changes the security meaning. Do not generalize from method signatures—verify where checks occur in the pinned method path. SELinux and service-manager policy constrain which processes can discover/contact endpoints as well.
End-to-end diagram and failure distinctions
- App / proxyBinder driver
encode Parcel → transact / libbinder
- Binder driverServer / stub
route endpoint and caller identity
- Server / stubBinder driver
Return. dispatch, authorize, handle; reply or error
- Binder driverApp / proxy
Return. decode reply; calling thread resumes
Time runs downward. Dashed arrows show returns.
Read the text version
- App / proxy → Binder driver: encode Parcel → transact / libbinder
- Binder driver → Server / stub: route endpoint and caller identity
- Server / stub → Binder driver (return): dispatch, authorize, handle; reply or error
- Binder driver → App / proxy (return): decode reply; calling thread resumes
app calls public Vibrator API
→ framework SystemVibrator / VibratorManager
→ IVibratorManagerService proxy encodes AIDL arguments
→ IBinder.transact → libbinder → Binder driver
→ server Binder thread → generated Stub dispatch
→ VibratorManagerService policy + implementation
← reply/exception (sync path) through driver and proxy
→ public API returns
The driver is transport and dispatch machinery; it does not validate that a vibration is allowed by app-level policy. A failure can mean: service not registered; caller denied; argument rejected; transaction too large/malformed; server died; Binder thread blocked/deadlocked; hardware/HAL unavailable; or a deliberately asynchronous API returned before completion. Each has different evidence. Do not treat a Binder handle or successful transaction submission as proof of successful hardware output.
Practice#
Prerequisites: browser/Gitiles or pinned AOSP 14 checkout. Optional runtime observation requires workstation ADB Platform-Tools and a booted disposable emulator/Cuttlefish or authorized device. The required task is source-only. Do not call hidden APIs, send arbitrary transactions, or trigger vibration during this exercise.
Trace worksheet
Use the linked source files above and fill these rows with method/symbol and file path. If you cannot verify a hop, mark it unverified rather than inserting a guessed implementation:
| Hop | Record |
|---|---|
| Public entry | Vibrator overload and API-level permission annotation/check. |
| Delegation | SystemVibrator and VibratorManager methods that pass the request onward. |
| Interface | AIDL method signature and declared argument types/order. |
| Generated binding | AIDL module/backend; generated output location if built, otherwise clearly labeled algorithm sketch. |
| Transport | Java transact; native/libbinder boundary; Binder driver source entry/dispatch evidence. |
| Server | Stub dispatch, VibratorManagerService implementation method, downstream helper. |
| Identity | Exact caller UID/permission/app-op checks; note any clearCallingIdentity scope. |
| Return | Is it synchronous or oneway? What result/error reaches caller? |
Expected result is a source-linked sequence, not a claim that you executed the call. A method in an interface is not proof the server implementation is registered; a service listing is not proof of authorization or successful output. Generated code absent from the checkout is an expected limitation, not permission to fabricate its source.
Virtual evidence cases (reason from supplied facts)
- Name absent from
service list: possible unregistered/not-yet-started service or listing visibility limitation. Do not infer permission denial or hardware failure without more evidence. - Server throws
SecurityExceptionafter reaching implementation: transport worked far enough to dispatch; investigate the policy check and caller identity, not the Parcel's interface token. - Synchronous call hangs: consider a blocked server Binder thread, lock cycle, slow downstream call, or deadlock. The fact that the caller is blocked is not evidence the driver corrupts bytes.
- Oneway method returns but no visible effect: submission is not completion; server may be frozen/dead, policy may reject, or downstream hardware may not support the operation.
- Large payload transaction fails: inspect transaction size and consider a file descriptor/shared memory interface where the contract supports it; do not repeatedly increase payload or retry blindly.
Optional read-only device queries, from the HOST terminal:
adb devices -l
adb shell service list
adb shell dumpsys -l
Expected: service names vary by image and shell visibility. unauthorized means stop and authorize only your own workstation; no device means stop, do not use a daily device. These commands list registrations, not AIDL methods, permission policy, or hardware health. Keep diagnostic dumps private. No Android runtime transaction was exercised as part of this chapter.
Knowledge check#
- What does the AIDL interface token establish, and what does it not establish?
- Explain proxy → driver → stub in one sentence each.
- Which thread blocks for a synchronous remote call, and where does server code run?
- Does successful
transactsubmission prove hardware action completed? - Name two independent causes of a failed call.
Answers#
- It identifies the expected interface for dispatch; it is not authentication or authorization.
- Proxy encodes and submits; driver routes/queues; stub decodes and dispatches to implementation.
- The calling thread waits for a reply; server work runs on an available Binder thread (nested calls can re-enter).
- No. Transport acceptance and framework/service/HAL/hardware completion are distinct.
- Examples: permission denial, missing service, dead server, malformed/oversized Parcel, thread starvation/deadlock, or downstream HAL failure.
Checkpoint#
Submit the filled trace worksheet with source links to the public API, AIDL contract, implementation, and one verified identity/policy check. Include a sync/oneway note and classify the five virtual evidence cases by layer. Pass if no generated code is presented as verbatim unless you actually built and inspected it, and if you distinguish descriptor validation from permission enforcement. Remediation: return to the first unverified hop, locate its caller/definition in the pinned source, and mark unavailable implementation details honestly.
Sources#
- Pinned AOSP 14 framework:
Vibrator.java,SystemVibrator.java,VibratorManager.java,IVibratorManagerService.aidl, andVibratorManagerService.java. - AIDL overview and AIDL backends — compiler-generated binding behavior and backend differences.
- Binder overview, Binder threading, and pinned Android 14
IBinder.java. - Pinned Android 14
SystemVibratorManager.java— concrete owner of theIVibratorManagerServiceproxy. - Pinned Android 14
IPCThreadState.cpp— libbinderBINDER_WRITE_READioctl call site. - Binder API reference —
clearCallingIdentity()/restoreCallingIdentity()contract. - Pinned Android 14 Binder driver — driver implementation; kernel tag is a representative Android common-kernel tag, not a claim that every Android 14 device runs that exact kernel.
- Android security overview.
Next: Binder lifetime and deadlocks. Work through handle transfer, death notifications, nested callbacks, deferred caller identity, oneway ordering, and ambiguous retries before moving into the HAL boundary.