Skip to main content

Crate rsbinder_macros

Crate rsbinder_macros 

Source
Expand description

Define a Binder interface as a Rust trait — no .aidl, no build.rs.

ⓘ
use rsbinder::{interface, BinderResult, Strong, Uri};

#[interface(descriptor = "com.example.IHello")]
pub trait IHello {
    fn echo(&self, msg: &str) -> BinderResult<String>;
    fn fill(&self, out: &mut Vec<i32>) -> BinderResult<()>;   // `&mut` = out
    #[oneway] fn ping(&self) -> BinderResult<()>;
}

rsbinder::serve(Uri::kernel())?.add("hello", BnHello::new_binder(Impl))?.run()?;
let h: Strong<dyn IHello> = rsbinder::connect(Uri::kernel().with_service("hello"))?;

§Relationship to .aidl

.aidl stays the canonical path (plan 2-17 D3): it is what Android tooling reads, what other languages generate from, and the only path that carries AIDL-only semantics. This macro is for rsbinder ↔ rsbinder interfaces, where the wire is private to both ends.

It is not a re-implementation. The macro fills the very same rsbinder_aidl::render structs the AIDL front-end fills and runs the same templates, so a trait here and the equivalent .aidl produce identical generated code — moving from one to the other never touches a call site. What it cannot express (unions, constants, @VintfStability, @EnforcePermission, ParcelableHolder, generics, nested types) is what needs AIDL semantics; write those in .aidl.

§What the signature means

SignatureMeaning
x: i32 — an AIDL scalar, or a bare name that may be an enum (asserted Copy)in argument, by value
x: &Cfg, x: &str, x: &[T] — every other in type borrowsin argument
x: &mut Tout argument
#[inout] x: &mut Twritten and read back
#[deprecated] / #[deprecated = "…"]AIDL’s @deprecated, on a trait or a method
Option<T>nullable
#[oneway] on a methodno reply; must return BinderResult<()>

Option<T> is AIDL’s @nullable, which AIDL allows only on the types that have a null representation on the wire. A #[derive(BinderEnum)] enum is carried as its repr scalar and is not one of them: Option<Mode> fails to compile inside the generated code, because the enum has no SerializeOption — the same shape .aidl rejects at the AIDL level. A primitive has no null either, so Option<i32> is refused outright, wherever it sits.

AIDL’s scalars are a fixed set. bool, i8 (byte), i32 (int), i64 (long), f32 (float), f64 (double) and u16 (char) are the only scalar spellings .aidl renders, joined by u8 as the element of a byte[] (&[u8]). byte is the one type whose spelling moves with the place — i8 as a scalar, u8 as an array element — and neither is accepted in the other’s place. Any other Rust scalar — u32, u64, i16, usize, u128, Rust’s own char, or a u8 outside an array — is refused wherever it sits, argument, return or #[derive(Parcelable)] field: the nearest .aidl renders a different Rust type, so moving the interface to .aidl would break every call site, which is the one thing this macro promises it will not do. u128 is worse than a mismatch — it writes sixteen bytes no conforming AIDL peer can decode.

Not everything can be an out parameter. AIDL passes a primitive, a String, an enum, an IBinder and an interface in only, and a ParcelFileDescriptor in or inout (AOSP GetArgumentAspect). Each &mut in a direction AIDL does not permit is refused — &mut String even as &mut Option<String>, because @nullable does not widen the direction, and a &mut ParcelFileDescriptor unless it is #[inout] — except for the enum: the macro sees only its name and cannot tell &mut Mode from a parcelable’s &mut Config, which is a legal out parameter. Never take an enum by &mut; no .aidl can express it. An array of any of these takes every direction.

A binder object (Strong<dyn IFoo>, SIBinder) and a ParcelFileDescriptor have no Default for the callee to start from, so .aidl wraps them in Option<_> wherever there is nothing to start from — a #[derive(Parcelable)] field and the slots of an array the callee must default. The table below has the exact spelling for every one of those; what matters here is that the Option is .aidl’s own and not a @nullable you wrote, and that an out fd array left None fails the call with UNEXPECTED_NULL, a null having no valid fd encoding. A ParcelableHolder is a parcelable field type only and cannot appear in a signature at all.

An out vector is an array, never a List. An out &mut Vec<T> is .aidl’s out T[], and &mut Option<Vec<T>> its out @nullable T[]: the proxy sends the caller’s current length (null for None) and the service starts from a vector pre-sized to it. .aidl’s out List<T> renders the very same signature but sends no length and starts the service from an empty vector (None when @nullable), so the two are not wire-compatible. out List<T> has no spelling here; it needs .aidl.

A @nullable array wraps its elements unless the element is a primitive or an enum, in every direction and at every arity. A non-@nullable array wraps them only where the slot starts empty: an out array, or a fixed-size parcelable field, of a binder, an interface or a fd. Every cell of the rule is in the table below, which is generated from the rules and checked against them.

One cell no table can decide for you. A #[derive(BinderEnum)] element counts as a primitive and stays bare; a parcelable element takes an Option<_>. The macro sees a name and not which of the two it is, so it accepts either and cannot tell you which is right — spell an enum element bare and a parcelable element Option<_>, as .aidl renders them.

A Box appears in one place only. .aidl boxes a parcelable field that is @nullable(heap=true), or a bare @nullable one that closes a reference cycle — Option<Box<T>> — and nothing else, so a Box anywhere else is refused, including every argument and return: a cycle is judged against the declaration being generated, and an interface is never part of one. Which .aidl field that spelling stands for depends on annotations and declarations the macro cannot see, so it accepts it on any parcelable field.

Paths resolve inside the generated module. The body lands in a {Trait}_binder module one level below where the macro was written, and it reaches the surrounding scope through a use super::*;. A bare name is therefore the user’s own — but super::X names the module the macro was written in, not its parent, so a signature that means the parent has to say crate::X. self:: is rejected outright for the same reason; super:: is left legal because it is the only way to name a type the generated module shadows (BnFoo, BpFoo, transactions, the trait’s own name). Use the macro at module scope: inside a function body super::* skips the block, so items declared in that block are not visible to the signature.

The generated body names prelude items by absolute path, so a parent item called Ok, Option or Default reaches only the signatures that name it. Two names still resolve in the module: Box, which async-trait’s expansion uses bare under the async feature, and the owned String / Vec<T> the stub declares for a &str / &[T] argument. The async half declares its own type parameters and helper struct next to the signatures, all named with the __Rsb prefix, so the macro refuses a trait name that starts with it and a signature path whose first segment does (crate::… still reaches such an item). A parent item named rsbinder takes over the generated code’s rsbinder:: paths; the last paragraph of What it emits says why.

Spell types directly. A proc macro cannot see through a type alias or a use … as rename, and every check above reads the spelling. &mut Ids for type Ids = Vec<i32> reads as an opaque named type: it loses the length word .aidl writes for an out array (T[]), and an aliased ParcelFileDescriptor element loses the null guard that keeps a null fd off the wire. Likewise a renamed ParcelableHolder or an alias of Option<i32> slips past the check that refuses it and generates the code that check exists to prevent, and a renamed Strong or ParcelFileDescriptor escapes the out-parameter rule above. Path qualification is fine — std::vec::Vec<i32> is matched structurally — but an alias or a rename is not.

A doc comment on the trait or a method is accepted but not carried: the render layer has no doc field, so the emitted IFoo, its methods and BnFoo/BpFoo carry no rustdoc from here.

§What it emits

The generated items — the trait, BnFoo, BpFoo, IFooDefault, and with the async feature the IFooAsync halves — land in the module where the macro was written, so they are usable without a path. They physically live in a #[doc(hidden)] mod {Trait}_binder that is glob re-exported: the generated body reuses .aidl’s fixed internal names (transactions, on_transact, DEFAULT_IMPL), which would collide if two interfaces were emitted side by side. That module is an implementation detail — never name it. IFoo and Foo both name their halves BnFoo/BpFoo (AOSP ClassName), so one module holds at most one of them: a second fails with E0428 on __rsbinder_one_interface_per_stem_Foo. Put them in separate modules.

Transaction codes are assigned by declaration order (FIRST_CALL_TRANSACTION + i), exactly as .aidl does without explicit codes — so reordering methods is a wire break. The descriptor defaults to the bare trait name; pass descriptor = "…" for anything shared across crates.

The generated code names rsbinder:: directly, so the dependency has to keep that name — a package = "rsbinder" rename will not resolve. The path is relative, not ::rsbinder::: the generated module glob-imports the module the macro was written in, so a mod rsbinder or use … as rsbinder there is what every generated rsbinder:: path resolves to.

§What .aidl renders

The spelling the AIDL compiler produces for each kind of type, by position. One scalar stands for its kind: long, float, double and char follow the int rows as i64, f32, f64 and u16. List<T> and generic parcelables are not listed. A trait written with these is a trait an .aidl port reproduces exactly; any other spelling of these types the macro refuses, naming the cell you wanted — except between the Cfg and Mode rows, which are both a bare name to the macro, and a field’s Option<Box<Cfg>>, which .aidl writes for a @nullable(heap=true) parcelable field and for a @nullable one that closes a reference cycle.

— marks a combination AIDL itself rejects.

AIDL typeinout#[inout]returnfield
inti32——i32i32
int[]&[i32]&mut Vec<i32>&mut Vec<i32>Vec<i32>Vec<i32>
@nullable int[]Option<&[i32]>&mut Option<Vec<i32>>&mut Option<Vec<i32>>Option<Vec<i32>>Option<Vec<i32>>
int[N]&[i32; N]&mut [i32; N]&mut [i32; N][i32; N][i32; N]
@nullable int[N]Option<&[i32; N]>&mut Option<[i32; N]>&mut Option<[i32; N]>Option<[i32; N]>Option<[i32; N]>
booleanbool——boolbool
boolean[]&[bool]&mut Vec<bool>&mut Vec<bool>Vec<bool>Vec<bool>
@nullable boolean[]Option<&[bool]>&mut Option<Vec<bool>>&mut Option<Vec<bool>>Option<Vec<bool>>Option<Vec<bool>>
boolean[N]&[bool; N]&mut [bool; N]&mut [bool; N][bool; N][bool; N]
@nullable boolean[N]Option<&[bool; N]>&mut Option<[bool; N]>&mut Option<[bool; N]>Option<[bool; N]>Option<[bool; N]>
bytei8——i8i8
byte[]&[u8]&mut Vec<u8>&mut Vec<u8>Vec<u8>Vec<u8>
@nullable byte[]Option<&[u8]>&mut Option<Vec<u8>>&mut Option<Vec<u8>>Option<Vec<u8>>Option<Vec<u8>>
byte[N]&[u8; N]&mut [u8; N]&mut [u8; N][u8; N][u8; N]
@nullable byte[N]Option<&[u8; N]>&mut Option<[u8; N]>&mut Option<[u8; N]>Option<[u8; N]>Option<[u8; N]>
String&str——StringString
@nullable StringOption<&str>——Option<String>Option<String>
String[]&[String]&mut Vec<String>&mut Vec<String>Vec<String>Vec<String>
@nullable String[]Option<&[Option<String>]>&mut Option<Vec<Option<String>>>&mut Option<Vec<Option<String>>>Option<Vec<Option<String>>>Option<Vec<Option<String>>>
String[N]&[String; N]&mut [String; N]&mut [String; N][String; N][String; N]
@nullable String[N]Option<&[Option<String>; N]>&mut Option<[Option<String>; N]>&mut Option<[Option<String>; N]>Option<[Option<String>; N]>Option<[Option<String>; N]>
Cfg&Cfg&mut Cfg&mut CfgCfgCfg
@nullable CfgOption<&Cfg>&mut Option<Cfg>&mut Option<Cfg>Option<Cfg>Option<Cfg>
Cfg[]&[Cfg]&mut Vec<Cfg>&mut Vec<Cfg>Vec<Cfg>Vec<Cfg>
@nullable Cfg[]Option<&[Option<Cfg>]>&mut Option<Vec<Option<Cfg>>>&mut Option<Vec<Option<Cfg>>>Option<Vec<Option<Cfg>>>Option<Vec<Option<Cfg>>>
Cfg[N]&[Cfg; N]&mut [Cfg; N]&mut [Cfg; N][Cfg; N][Cfg; N]
@nullable Cfg[N]Option<&[Option<Cfg>; N]>&mut Option<[Option<Cfg>; N]>&mut Option<[Option<Cfg>; N]>Option<[Option<Cfg>; N]>Option<[Option<Cfg>; N]>
ModeMode——ModeMode
Mode[]&[Mode]&mut Vec<Mode>&mut Vec<Mode>Vec<Mode>Vec<Mode>
@nullable Mode[]Option<&[Mode]>&mut Option<Vec<Mode>>&mut Option<Vec<Mode>>Option<Vec<Mode>>Option<Vec<Mode>>
Mode[N]&[Mode; N]&mut [Mode; N]&mut [Mode; N][Mode; N][Mode; N]
@nullable Mode[N]Option<&[Mode; N]>&mut Option<[Mode; N]>&mut Option<[Mode; N]>Option<[Mode; N]>Option<[Mode; N]>
ParcelFileDescriptor&rsbinder::ParcelFileDescriptor—&mut rsbinder::ParcelFileDescriptorrsbinder::ParcelFileDescriptorOption<rsbinder::ParcelFileDescriptor>
@nullable ParcelFileDescriptorOption<&rsbinder::ParcelFileDescriptor>—&mut Option<rsbinder::ParcelFileDescriptor>Option<rsbinder::ParcelFileDescriptor>Option<rsbinder::ParcelFileDescriptor>
ParcelFileDescriptor[]&[rsbinder::ParcelFileDescriptor]&mut Vec<Option<rsbinder::ParcelFileDescriptor>>&mut Vec<rsbinder::ParcelFileDescriptor>Vec<rsbinder::ParcelFileDescriptor>Vec<rsbinder::ParcelFileDescriptor>
@nullable ParcelFileDescriptor[]Option<&[Option<rsbinder::ParcelFileDescriptor>]>&mut Option<Vec<Option<rsbinder::ParcelFileDescriptor>>>&mut Option<Vec<Option<rsbinder::ParcelFileDescriptor>>>Option<Vec<Option<rsbinder::ParcelFileDescriptor>>>Option<Vec<Option<rsbinder::ParcelFileDescriptor>>>
ParcelFileDescriptor[N]&[rsbinder::ParcelFileDescriptor; N]&mut [Option<rsbinder::ParcelFileDescriptor>; N]&mut [rsbinder::ParcelFileDescriptor; N][rsbinder::ParcelFileDescriptor; N][Option<rsbinder::ParcelFileDescriptor>; N]
@nullable ParcelFileDescriptor[N]Option<&[Option<rsbinder::ParcelFileDescriptor>; N]>&mut Option<[Option<rsbinder::ParcelFileDescriptor>; N]>&mut Option<[Option<rsbinder::ParcelFileDescriptor>; N]>Option<[Option<rsbinder::ParcelFileDescriptor>; N]>Option<[Option<rsbinder::ParcelFileDescriptor>; N]>
IFoo&rsbinder::Strong<dyn IFoo>——rsbinder::Strong<dyn IFoo>Option<rsbinder::Strong<dyn IFoo>>
@nullable IFooOption<&rsbinder::Strong<dyn IFoo>>——Option<rsbinder::Strong<dyn IFoo>>Option<rsbinder::Strong<dyn IFoo>>
IFoo[]&[rsbinder::Strong<dyn IFoo>]&mut Vec<Option<rsbinder::Strong<dyn IFoo>>>&mut Vec<rsbinder::Strong<dyn IFoo>>Vec<rsbinder::Strong<dyn IFoo>>Vec<rsbinder::Strong<dyn IFoo>>
@nullable IFoo[]Option<&[Option<rsbinder::Strong<dyn IFoo>>]>&mut Option<Vec<Option<rsbinder::Strong<dyn IFoo>>>>&mut Option<Vec<Option<rsbinder::Strong<dyn IFoo>>>>Option<Vec<Option<rsbinder::Strong<dyn IFoo>>>>Option<Vec<Option<rsbinder::Strong<dyn IFoo>>>>
IFoo[N]&[rsbinder::Strong<dyn IFoo>; N]&mut [Option<rsbinder::Strong<dyn IFoo>>; N]&mut [rsbinder::Strong<dyn IFoo>; N][rsbinder::Strong<dyn IFoo>; N][Option<rsbinder::Strong<dyn IFoo>>; N]
@nullable IFoo[N]Option<&[Option<rsbinder::Strong<dyn IFoo>>; N]>&mut Option<[Option<rsbinder::Strong<dyn IFoo>>; N]>&mut Option<[Option<rsbinder::Strong<dyn IFoo>>; N]>Option<[Option<rsbinder::Strong<dyn IFoo>>; N]>Option<[Option<rsbinder::Strong<dyn IFoo>>; N]>
IBinder&rsbinder::SIBinder——rsbinder::SIBinderOption<rsbinder::SIBinder>
@nullable IBinderOption<&rsbinder::SIBinder>——Option<rsbinder::SIBinder>Option<rsbinder::SIBinder>
IBinder[]&[rsbinder::SIBinder]&mut Vec<Option<rsbinder::SIBinder>>&mut Vec<rsbinder::SIBinder>Vec<rsbinder::SIBinder>Vec<rsbinder::SIBinder>
@nullable IBinder[]Option<&[Option<rsbinder::SIBinder>]>&mut Option<Vec<Option<rsbinder::SIBinder>>>&mut Option<Vec<Option<rsbinder::SIBinder>>>Option<Vec<Option<rsbinder::SIBinder>>>Option<Vec<Option<rsbinder::SIBinder>>>
IBinder[N]&[rsbinder::SIBinder; N]&mut [Option<rsbinder::SIBinder>; N]&mut [rsbinder::SIBinder; N][rsbinder::SIBinder; N][Option<rsbinder::SIBinder>; N]
@nullable IBinder[N]Option<&[Option<rsbinder::SIBinder>; N]>&mut Option<[Option<rsbinder::SIBinder>; N]>&mut Option<[Option<rsbinder::SIBinder>; N]>Option<[Option<rsbinder::SIBinder>; N]>Option<[Option<rsbinder::SIBinder>; N]>

Attribute Macros§

interface
A binder interface from a plain Rust trait — the .aidl-free interface.

Derive Macros§

BinderEnum
Parcel codec for a plain Rust enum, carried as its #[repr(..)] scalar.
Parcelable
Parcel codec for a plain Rust struct — the .aidl-free parcelable.
ServiceSpecificError
Binder service-specific error codes for a plain Rust enum.