Stability tiers

rsbinder uses a three-tier model for public-API stability on the path to 1.0. Each tier states what kind of change can land between releases and what callers can rely on.

TierVersioningWire compatibilityWhen it can change
StableSemver-strictAOSP-faithful, byte-tested against real libbinderOnly on a major bump — and pre-1.0 the 0.x minor slot is the major slot (see the changelog preamble). Breaking changes are last-resort.
ProvisionalStabilizing toward 1.0AOSP-faithful in the validated cases (hermetic + real-libbinder STAGE3)May break in a minor bump if 1.0 review surfaces an issue. Will be re-classified as Stable or Experimental at 1.0.
ExperimentalOpt-in via Cargo featureNo guarantee until the corresponding real-libbinder interop gate passesAnytime, including the wire format. Default builds do not reach this code.

Stable

Surface that has shipped through multiple releases against real Android binder and is not expected to break.

  • Kernel binder core — Parcel, IBinder, Interface, Strong<I>, Weak<I>, Status, StatusCode, ParcelFileDescriptor, ProcessState::{init, init_default, start_thread_pool, join_thread_pool}, Binder::new, BinderFeatures.
  • Transaction code constants — FIRST_CALL_TRANSACTION, LAST_CALL_TRANSACTION, PING_TRANSACTION, INTERFACE_TRANSACTION, DUMP_TRANSACTION, FLAG_ONEWAY, FLAG_CLEAR_BUF.
  • Service manager (Android binder) — hub::add_service, hub::check_service. (hub::get_service / hub::get_interface were deprecated in 0.10.0 and removed in 0.11.0, in favor of wait_for_* / check_interface / try_* — which are Provisional (below). See Service Manager.)
  • AIDL compiler entry points — rsbinder_aidl::Builder::{new, source, output, version, generate}.
  • rsb_device binary CLI (binderfs setup); --group / --mode and the 0600 default node are new in 0.11.0.

Provisional

Implemented and validated, but the public-API shape is still under review for 1.0. Expect at most a single round of renames or signature tweaks; wire formats are already locked.

  • Service-manager lookups (new in 0.10.0) — hub::wait_for_service, wait_for_interface, check_interface, try_get_service, try_get_interface, register_client_callback, try_unregister_service, and the impl Into<SIBinder> form of add_service. Wire behavior matches AOSP waitForService / checkService; only the Rust signatures are still settling.
  • RPC transport — RpcServer, RpcSession, setup_unix_server, setup_unix_client*, from_preconnected_fd, set_root, get_root, set_android13plus, set_max_threads(N) (both N == 1 and N >= 2 multi-connection), set_max_connections, set_supported_fd_modes(&[FileDescriptorTransportMode::Unix]). Validated against real android-13 / 14 / 15 / 16 libbinder (plans 2-1 … 2-12 / 2-13 / 2-14 STAGE3).
  • FD-over-RPC (v1+ AOSP-faithful, AC-11.3 gate passed).
  • RPC death notification (session-disconnect, AOSP-faithful).
  • IAccessor client / server (plans 2-13 D.8 + 2-14 D.9 STAGE3 passed against real libbinder).
  • rsb_hub — addService descriptor auto-detect, getService2, checkService2, Service::Accessor routing. Linux-native bringup. New in 0.11.0: per-name access control, service declarations, on-demand start, dump, supervisor integration, and the rsb_service CLI — the configuration file format is settling with them.
  • macOS first-class support (plan 2-9 Phase A+B).
  • Entry API (new in 0.11.0) — serve, connect, connect_async, Client, Server, ServerGuard, ServeOptions, ClientOptions, Endpoint. Replaced the rsbinder::service facade. The URI grammar and the two option structs are what may still change.
  • Interface macros (new in 0.11.0, macros feature) — #[rsbinder::interface], #[derive(Parcelable)], #[derive(BinderEnum)]. Feature-gated but not Experimental: the emitted code is byte-for-byte the .aidl code, so the wire is whatever .aidl already guarantees. Only the Rust-facing spelling is provisional; 0.12.0 narrowed it to what .aidl renders and added #[deprecated]. See Interface Macros.
  • Shared memory (new in 0.11.0) — SharedMemory, MemoryHeapBase, MappedHeap, MemoryDealer, HeapCache, and the IMemory / IMemoryHeap wire (handwritten AOSP wire, not AIDL). STAGE3-gated against real libbinder in both directions.
  • Data serialization (new in 0.11.0) — to_bytes / from_bytes, no feature required since 0.12.0. The bytes are the IPC bytes, so only the two signatures may still change. See Storing Values.
  • Cross-stack bridging (new in 0.11.0) — impl Interface for Strong<I> (a proxy publishes as a local service, so a gateway is one line) and bridge::Rewrap. See Cross-Transport Services.
  • lazy_service (new in 0.11.0) — LazyServiceRegistrar and the AOSP registerService / client-callback lifecycle, gated against Android's own servicemanager.
  • Receive mapping size (new in 0.12.0) — ProcessState::init_with_mmap_size, binder://?mmap=, ClientOptions::mmap_size / ServeOptions::mmap_size. The size is local to the receiving process and nothing about it crosses the wire.
  • TransportCaps (new in 0.12.0) — Client::caps(), RpcSession::caps(), Endpoint::static_caps(), calling_caps(). Each bit is derived from a check another type still performs, so the set of bits is what may grow.
  • Cancellation (new in 0.12.0) — the cancel module over AOSP's android.os.ICancellationSignal, STAGE3-gated against real libbinder in both directions.
  • Typed service-specific errors (new in 0.12.0) — ServiceSpecificError, #[derive(ServiceSpecificError)], impl_service_specific_error!, Status::service_specific, Status::service_error, Status::message. The code on the wire is the i32 AOSP writes; only the Rust mapping is new.
  • Large payloads and pipes (new in 0.12.0) — Parcel::write_blob / read_blob (AOSP's blob wire, inline up to 16 KB and a memfd region above), ParcelFileDescriptor::pipe() and its Read / Write impls.
  • Awaitable lookups and death (new in 0.12.0, tokio feature) — wait_for_interface_async, check_interface_async, death_signal / DeathSignal. Wire behavior is that of the synchronous calls they wrap.
  • Fast Message Queue (new in 0.12.0) — the rsbinder-fmq crate (MessageQueue, EventFlag, Descriptor, AttachPolicy; the synchronized flavor, primitive elements, Linux and Android) and rsbinder::fmq, which re-exports it on every platform beside the AOSP parcelables MQDescriptor, GrantorDescriptor, SynchronizedReadWrite, UnsynchronizedWrite and NativeHandle, with the two TryFrom conversions between MQDescriptor and Descriptor. rsbinder-aidl resolves imports of android.hardware.common and android.hardware.common.fmq to those types and compiles generic parcelables such as MQDescriptor<T, Flavor>. The parcelables are AOSP's VINTF-stable wire, and the queue keeps libfmq's memory layout, counters and futex protocol, so only the Rust signatures may still change. For an NDK peer, which cannot link libfmq, the reference implementation is two header-only C11 files: rsbinder-fmq/c/rsbinder_fmq.h (the queue) and contrib/ndk/rsbinder_stream.h (the stream's records on it). Validated against AOSP's own libfmq on the host, both roles (rsbinder-fmq/tests/libfmq_host.rs), and across binder on an emulator with either side as the service (example-hello/cpp/run_fmq_interop.sh); the queue header against the crate on the host (rsbinder-fmq/tests/c_header.rs), the record header with both ends under AddressSanitizer (tests/tests/c_stream_header.rs), and both against rsbinder on an emulator (example-hello/cpp/run_stream_interop.sh). See Builtin AOSP Types and Generic Parcelables.
  • Streaming (new in 0.12.0) — the stream module: Sink, Receiver, and the three AIDL files in rsbinder/aidl/stream/: the rsbinder.stream.StreamEndpoint<T> parcelable (T the item type; the ring's record layout and EventFlag bits on the kernel path) and the two interfaces IStreamSink / IStreamSource (the RPC path). The contract is rsbinder's own rather than AOSP's, written as ordinary AIDL so a C++ or NDK peer can be either end (a Java peer is out of scope: AOSP has no Java FMQ). Validated rsbinder-to-rsbinder over kernel binder (two processes, including a real Android driver) and over RPC, and over kernel binder against an NDK peer built on the C reference headers and against one whose ring is AOSP's own libfmq, both directions (example-hello/cpp/run_stream_interop.sh), and over RPC against the device's libbinder RpcSession, both directions (example-hello/cpp/run_stream_rpc_interop.sh). See Streaming.
  • Observability (new in 0.12.0) — the work source functions (set_calling_work_source_uid and the five beside it, the AOSP IPCThreadState set), Remotable::transaction_name with rsbinder_aidl::Builder::trace, the observe module (TransactionObserver, set_observer, LogObserver, StatsObserver), and the tracing feature (TracingObserver and the client spans of proxies generated with trace(true)). None of it changes the wire: the work source uses a header field kernel binder already has. The work source is STAGE3-gated against real libbinder. See Observability.
  • Reconnecting (new in 0.12.0) — Reconnecting and the reconnect module: one service named by a Uri, looked up again after its process dies or its RPC session ends, with an on_connect hook rerun on each new connection. It adds nothing to the wire. On kernel binder it sends, beyond a plain connect, a death link and, while the name is absent, a service-manager notification registration, both existing commands; over RPC it sends nothing a plain connect does not. Validated over kernel binder and RPC, and against a real libbinder RpcServer that is killed and restarted on an emulator (example-hello/cpp/run_rpc_reconnect_interop.sh). See Reconnecting to a Service.
  • Freeze notifications (new in 0.12.0) — IBinder::add_frozen_state_change_callback / remove_frozen_state_change_callback, FrozenStateChangeCallback, FrozenState, and ProcessState::freeze_process / process_freeze_info: AOSP addFrozenStateChangeCallback (libbinder android-15.0.0_r6+) over the driver's own commands, so the contract is the kernel UAPI rather than a libbinder wire. Validated on the Rust binder driver, on the C driver of an Android 17 emulator, and on an Android 15 driver without features/freeze_notification, where add_frozen_state_change_callback returns InvalidOperation (tests/tests/freeze_kernel.rs).
  • RpcClientConfig (new in 0.12.0) — one android-13+ client configuration for every transport (unix, unix_abstract, vsock, tls, tcp_debug, new), with incoming_connections and outgoing_connections on all of them. It replaces RpcUnixClientConfig and the Unix-only multi-connection helpers, which are deprecated and go in the release after.
  • AIDL compiler, beyond the Stable entry points — Builder::{dest_dir, hash} and the public render module, the seam rsbinder-macros plugs into. Its input structs are #[non_exhaustive], so a template gaining a field stays a minor release.

Experimental (opt-in)

Off by default, gated by a Cargo feature. Wire format and API may change without a deprecation cycle.

FeatureSurfaceStatus
rpc-tcp-debugTcpDebugTransport — plain TCP backendBring-up / interop only, never production.
rpc-vsockVsockTransport — host↔VMLinux/Android only; loopback testing requires vsock_loopback.ko.
rpc-tlsTLS over rustls, socket-orthogonal (tcp / unix / vsock)Hermetic green; the StreamOwned decoupling (plan 2-15) has landed. Real-libbinder STAGE3 not yet attempted — stock emulator images ship no libbinder_tls.

Non-goals

Surface that rsbinder will not support, even after 1.0. Listed here so contributors don't burn cycles implementing something that has already been ruled out — and so the AIDL compiler's deliberate rejection of these types reads as intentional rather than as a gap.

  • AIDL Map<K, V> — AOSP's own Rust / C++ / NDK backends reject Map<K, V> at the language layer (aidl_language.cpp:1612-1615: "Currently, only Java backend supports Map."), and have done so since 2019. The wire format is built on Java Parcel's runtime type-tag system (VAL_STRING, VAL_INTEGER, VAL_MAP, …) which has no typed-Rust analogue. The rsbinder-aidl frontend therefore emits unknown type 'Map' for any Map<…> reference — matching AOSP. Use parcelable (typed struct), List<Entry>, or ParcelableHolder instead. Re-examine if AOSP removes the cross-backend block.

What this means in practice

  • A 0.x → 0.(x+1) minor bump can change Provisional signatures. Pre-1.0 the minor slot is the major slot, so a Stable item can change there too — what the tier buys you is that it is last-resort and always lands in that release's Migrating from … section of the CHANGELOG, named with its replacement. That section carries the changes that have a replacement to name; one that only alters a value or a behavior may be recorded under Fixed instead. Changed / Removed carry the rationale for the entries that have one, but not every entry appears there. rsbinder runs cargo-semver-checks for both rsbinder and rsbinder-aidl (default features and RPC features) on pull requests targeting master; it reports signature changes, and cannot see a change that alters only a value or a behavior. Neither can your build — for those, read Migrating and Fixed both.
  • Experimental features never break the default build's wire format. Enabling one is an explicit acknowledgement that the corresponding interop gate has not passed.
  • After 1.0, all Stable items follow Semver strictly. Provisional items get re-classified as Stable or moved into a feature gate.