rsbinder/hub/accessor_register.rs
1// Copyright 2022 Jeff Kim <hiking90@gmail.com>
2// SPDX-License-Identifier: Apache-2.0
3
4//! Register side of the RPC Accessor bridge — the AOSP `createAccessor` /
5//! `addAccessorProvider` analogue in pure Rust. Lets a process **vend**
6//! an `IAccessor` binder without going through the system service
7//! manager: a [`LocalAccessor`] implements `BnAccessor`, a
8//! provider closure maps an instance name to an [`AccessorSockAddr`],
9//! and `addConnection()` opens a connected socket whose `OwnedFd` rides
10//! back over the kernel binder reply. The consume side
11//! ([`accessor_16`](super::accessor_16)) then performs the v2/v1
12//! handshake against that fd.
13//!
14//! Interop against a real libbinder on an android-16 emulator is the
15//! non-negotiable validation for this path.
16//!
17//! Cross-platform: the [`Unix`](AccessorSockAddr::Unix) variant is
18//! always present so macOS host hermetic tests can exercise the
19//! register-side path without `rpc-vsock` / `rpc-tcp-debug` features.
20//! Abstract Unix, `Vsock`, and `Inet` variants are present in the type
21//! at all times for ABI stability; whether they can be *connected* is a
22//! runtime check inside the [`AccessorSockAddr::connect_owned_fd`]
23//! family of helpers — unavailable variants map to
24//! `ERROR_UNSUPPORTED_SOCKET_FAMILY` so an instance configured for a
25//! backend the binary/target cannot use surfaces as the same AOSP-
26//! faithful service-specific error a wrong-family `addConnection`
27//! returns.
28
29// cfg lives on the mod decl in super — duplicating here trips
30// clippy::duplicated_attributes.
31
32use std::io;
33use std::net::SocketAddrV4;
34#[cfg(target_os = "android")]
35use std::os::android::net::SocketAddrExt;
36use std::os::fd::OwnedFd;
37#[cfg(target_os = "linux")]
38use std::os::linux::net::SocketAddrExt;
39#[cfg(any(target_os = "linux", target_os = "android"))]
40use std::os::unix::net::SocketAddr as UnixSocketAddr;
41use std::path::PathBuf;
42
43use crate::error::Result;
44
45/// The socket address a [`LocalAccessor`] hands back from its
46/// `addConnection()` callback. AOSP `RpcSocketAddressProvider` returns
47/// `sockaddr_storage` populated with one of `sockaddr_un` /
48/// `sockaddr_vm` / `sockaddr_in`; this enum is the strongly-typed
49/// equivalent.
50///
51/// All variants exist regardless of build features so the enum's shape
52/// stays stable across feature flags (matching exhaustively in
53/// downstream `match` is therefore predictable). The
54/// `connect_*_owned_fd` helpers gate the *connect path* on the
55/// matching target or `rpc-vsock`/`rpc-tcp-debug` feature and return
56/// `ERROR_UNSUPPORTED_SOCKET_FAMILY` otherwise.
57///
58/// `Debug`/`Clone` are provided so a provider closure can build the
59/// address ahead of time and the bridge can log it on `addConnection`
60/// failures without consuming it.
61///
62/// [`LocalAccessor`]: super::accessor_register::LocalAccessor
63#[derive(Clone, Debug)]
64pub enum AccessorSockAddr {
65 /// AF_UNIX path. Cross-platform — works on Linux + macOS host.
66 Unix(PathBuf),
67 /// AF_UNIX abstract name. Variant is always present for enum-shape
68 /// stability; connect is supported only on Linux / Android.
69 ///
70 /// **Security**: the abstract namespace has no filesystem
71 /// permissions — any same-network-namespace process can bind a name
72 /// first and impersonate the server (path-bound sockets can rely on
73 /// directory modes to prevent this). Trust in the peer must come
74 /// from elsewhere, e.g. LSM policy or post-connect verification.
75 UnixAbstract(Vec<u8>),
76 /// AF_VSOCK `(cid, port)`. Only usable when the binary was built
77 /// with the `rpc-vsock` feature; otherwise the connect helper
78 /// returns `ERROR_UNSUPPORTED_SOCKET_FAMILY`.
79 Vsock { cid: u32, port: u32 },
80 /// AF_INET v4 address. Only usable when the binary was built with
81 /// the `rpc-tcp-debug` feature (debug builds only — see
82 /// `rpc::transport::tcp_debug`); otherwise the connect
83 /// helper returns `ERROR_UNSUPPORTED_SOCKET_FAMILY`.
84 Inet(SocketAddrV4),
85}
86
87impl AccessorSockAddr {
88 /// AOSP-faithful family hint, useful for `accessor_error_name`-style
89 /// logging from a provider closure that wants to report which
90 /// family it picked before any connect attempt.
91 pub fn family_str(&self) -> &'static str {
92 match self {
93 AccessorSockAddr::Unix(_) => "AF_UNIX",
94 AccessorSockAddr::UnixAbstract(_) => "AF_UNIX",
95 AccessorSockAddr::Vsock { .. } => "AF_VSOCK",
96 AccessorSockAddr::Inet(_) => "AF_INET",
97 }
98 }
99
100 /// Open a *server-process* client connection to
101 /// the address this `AccessorSockAddr` describes and return the
102 /// raw `OwnedFd`. This is what AOSP `singleSocketConnection` does
103 /// inside `LocalAccessor::addConnection` — a plain `connect(2)`
104 /// from the process hosting the `BnAccessor`, whose resulting
105 /// fd is then transferred to the remote `BpAccessor` client via
106 /// `ParcelFileDescriptor` (kernel-level fd duplication).
107 ///
108 /// Errors map onto the AOSP `IAccessor::ERROR_*` codes; the
109 /// `LocalAccessor::addConnection` impl translates these into
110 /// `Status::from_service_specific_error(ERROR_*)`. The mapping is:
111 ///
112 /// | error | AOSP code |
113 /// |---|---|
114 /// | `UnsupportedFamily` | `ERROR_UNSUPPORTED_SOCKET_FAMILY` |
115 /// | `ConnectFailedEacces` | `ERROR_FAILED_TO_CONNECT_EACCES` |
116 /// | `ConnectFailed(io)` (other) | `ERROR_FAILED_TO_CONNECT_TO_SOCKET` |
117 /// | `CreateSocketFailed(io)` | `ERROR_FAILED_TO_CREATE_SOCKET` |
118 ///
119 /// Feature-disabled families (`Vsock` without `rpc-vsock`, `Inet`
120 /// without `rpc-tcp-debug`) return `UnsupportedFamily` — the same
121 /// error a wrong-family addr would get at the AOSP boundary, so an
122 /// instance configured for a backend the binary wasn't built with
123 /// surfaces with AOSP-faithful semantics rather than a compile-
124 /// time mismatch.
125 pub fn connect_owned_fd(&self) -> std::result::Result<OwnedFd, AccessorConnectError> {
126 match self {
127 AccessorSockAddr::Unix(path) => connect_unix_owned_fd(path),
128 AccessorSockAddr::UnixAbstract(name) => connect_unix_abstract_owned_fd(name),
129 AccessorSockAddr::Vsock { cid, port } => connect_vsock_owned_fd(*cid, *port),
130 AccessorSockAddr::Inet(addr) => connect_inet_owned_fd(*addr),
131 }
132 }
133}
134
135/// AOSP `IAccessor::ERROR_*`-aligned classification of a failure
136/// returned by [`AccessorSockAddr::connect_owned_fd`]. The
137/// `LocalAccessor::addConnection` impl converts each variant into the
138/// corresponding AIDL `Status::from_service_specific_error(...)`.
139///
140/// `Display` is implemented for log lines; the inner `io::Error` is
141/// preserved so callers can surface the OS-level cause alongside the
142/// AOSP code in their `log::warn!` line (matching the pattern used by
143/// [`super::accessor_16::resolve_accessor`]).
144#[derive(Debug)]
145pub enum AccessorConnectError {
146 /// AOSP `ERROR_UNSUPPORTED_SOCKET_FAMILY` — the address family
147 /// either isn't valid (none of `AF_UNIX/AF_VSOCK/AF_INET`) or the
148 /// target / crate features cannot connect that backend.
149 UnsupportedFamily,
150 /// AOSP `ERROR_FAILED_TO_CREATE_SOCKET` — `socket(2)` failed
151 /// before `connect(2)` was even attempted.
152 CreateSocketFailed(io::Error),
153 /// AOSP `ERROR_FAILED_TO_CONNECT_EACCES` — `connect(2)` was
154 /// rejected with `EACCES` (peer rejected the principal — caller
155 /// likely lacks permission to open this RPC endpoint).
156 ConnectFailedEacces,
157 /// AOSP `ERROR_FAILED_TO_CONNECT_TO_SOCKET` — `connect(2)` failed
158 /// for any reason other than `EACCES` (peer not listening,
159 /// network unreachable, …).
160 ConnectFailed(io::Error),
161}
162
163impl std::fmt::Display for AccessorConnectError {
164 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
165 match self {
166 AccessorConnectError::UnsupportedFamily => {
167 write!(f, "unsupported socket family for IAccessor::addConnection")
168 }
169 AccessorConnectError::CreateSocketFailed(e) => {
170 write!(f, "socket(2) failed: {e}")
171 }
172 AccessorConnectError::ConnectFailedEacces => {
173 write!(f, "connect(2) refused with EACCES")
174 }
175 AccessorConnectError::ConnectFailed(e) => {
176 write!(f, "connect(2) failed: {e}")
177 }
178 }
179 }
180}
181
182impl std::error::Error for AccessorConnectError {}
183
184/// AF_UNIX connect helper. Cross-platform (Linux + macOS) — `std`'s
185/// `UnixStream::connect(path)` is stable on both. The resulting
186/// stream's `OwnedFd` is the *client-side endpoint of a connected
187/// socket pair* whose server-side endpoint is whoever accepted on
188/// `path` (typically the same process's `RpcServer::setup_unix_server`
189/// listener).
190fn connect_unix_owned_fd(path: &PathBuf) -> std::result::Result<OwnedFd, AccessorConnectError> {
191 use std::os::unix::net::UnixStream;
192 match UnixStream::connect(path) {
193 Ok(stream) => Ok(OwnedFd::from(stream)),
194 Err(e) if e.kind() == std::io::ErrorKind::PermissionDenied => {
195 Err(AccessorConnectError::ConnectFailedEacces)
196 }
197 // `socket(2)` and `connect(2)` failures both surface as
198 // `io::Error` from `UnixStream::connect` — AOSP's
199 // `singleSocketConnection` distinguishes them with a separate
200 // `::socket()` call. Without retracing that we report all
201 // pre-EACCES failures as `ConnectFailed` (the more common
202 // case); `CreateSocketFailed` is reserved for backends where
203 // `socket(2)` is a distinct syscall the helper can inspect.
204 Err(e) => Err(AccessorConnectError::ConnectFailed(e)),
205 }
206}
207
208#[cfg(any(target_os = "linux", target_os = "android"))]
209fn connect_unix_abstract_owned_fd(
210 name: &[u8],
211) -> std::result::Result<OwnedFd, AccessorConnectError> {
212 use std::os::unix::net::UnixStream;
213 let addr =
214 UnixSocketAddr::from_abstract_name(name).map_err(AccessorConnectError::ConnectFailed)?;
215 match UnixStream::connect_addr(&addr) {
216 Ok(stream) => Ok(OwnedFd::from(stream)),
217 Err(e) if e.kind() == std::io::ErrorKind::PermissionDenied => {
218 Err(AccessorConnectError::ConnectFailedEacces)
219 }
220 Err(e) => Err(AccessorConnectError::ConnectFailed(e)),
221 }
222}
223
224#[cfg(not(any(target_os = "linux", target_os = "android")))]
225fn connect_unix_abstract_owned_fd(
226 _name: &[u8],
227) -> std::result::Result<OwnedFd, AccessorConnectError> {
228 Err(AccessorConnectError::UnsupportedFamily)
229}
230
231/// AF_VSOCK connect helper. Behind `rpc-vsock`; when the feature is
232/// off, returns `UnsupportedFamily` so an instance configured for
233/// vsock surfaces with AOSP-faithful semantics on a `rpc`-only build.
234#[cfg(feature = "rpc-vsock")]
235fn connect_vsock_owned_fd(
236 cid: u32,
237 port: u32,
238) -> std::result::Result<OwnedFd, AccessorConnectError> {
239 use std::os::fd::IntoRawFd;
240 use vsock::{VsockAddr, VsockStream};
241 match VsockStream::connect(&VsockAddr::new(cid, port)) {
242 Ok(stream) => {
243 // SAFETY: `into_raw_fd` transfers ownership; wrapping in
244 // `OwnedFd` immediately re-establishes RAII. AOSP-sanctioned
245 // pattern, identical to `VsockTransport::from_owned_fd` in
246 // [`crate::rpc::transport::vsock`].
247 let raw = stream.into_raw_fd();
248 Ok(unsafe { OwnedFd::from_raw_fd(raw) })
249 }
250 Err(e) if e.kind() == std::io::ErrorKind::PermissionDenied => {
251 Err(AccessorConnectError::ConnectFailedEacces)
252 }
253 Err(e) => Err(AccessorConnectError::ConnectFailed(e)),
254 }
255}
256
257#[cfg(not(feature = "rpc-vsock"))]
258fn connect_vsock_owned_fd(
259 _cid: u32,
260 _port: u32,
261) -> std::result::Result<OwnedFd, AccessorConnectError> {
262 Err(AccessorConnectError::UnsupportedFamily)
263}
264
265/// AF_INET v4 connect helper. Behind `rpc-tcp-debug` (debug-only
266/// transport); when off, returns `UnsupportedFamily`.
267#[cfg(feature = "rpc-tcp-debug")]
268fn connect_inet_owned_fd(addr: SocketAddrV4) -> std::result::Result<OwnedFd, AccessorConnectError> {
269 use std::net::TcpStream;
270 match TcpStream::connect(addr) {
271 Ok(stream) => Ok(OwnedFd::from(stream)),
272 Err(e) if e.kind() == std::io::ErrorKind::PermissionDenied => {
273 Err(AccessorConnectError::ConnectFailedEacces)
274 }
275 Err(e) => Err(AccessorConnectError::ConnectFailed(e)),
276 }
277}
278
279#[cfg(not(feature = "rpc-tcp-debug"))]
280fn connect_inet_owned_fd(
281 _addr: SocketAddrV4,
282) -> std::result::Result<OwnedFd, AccessorConnectError> {
283 Err(AccessorConnectError::UnsupportedFamily)
284}
285
286#[cfg(feature = "rpc-vsock")]
287use std::os::fd::FromRawFd;
288
289// --- `LocalAccessor` (`BnAccessor` impl) -------
290
291use super::accessor_16::{
292 BnAccessor, IAccessor, ERROR_CONNECTION_INFO_NOT_FOUND, ERROR_FAILED_TO_CONNECT_EACCES,
293 ERROR_FAILED_TO_CONNECT_TO_SOCKET, ERROR_FAILED_TO_CREATE_SOCKET,
294 ERROR_UNSUPPORTED_SOCKET_FAMILY,
295};
296use crate::binder::{Interface, Strong};
297use crate::file_descriptor::ParcelFileDescriptor;
298use crate::status::Status;
299
300/// The AOSP `LocalAccessor` analog — a server-side
301/// `BnAccessor` whose `addConnection()` calls a user-supplied
302/// [`AccessorAddrProvider`] for an [`AccessorSockAddr`], opens a
303/// `connect(2)` to it inside *this* process, and ships the resulting
304/// `OwnedFd` to the remote `BpAccessor` client as a
305/// `ParcelFileDescriptor`. The kernel binder driver duplicates the fd
306/// into the client's fd table on Parcel-marshal — both sides end up
307/// holding the two ends of one socket pair (the *client* end of the
308/// server-process `connect`, plus the matching *accepted* end the
309/// `RpcServer::setup_unix_server` loop picked up at the listening
310/// path).
311///
312/// AOSP parity:
313///
314/// * `singleSocketConnection` ⇒
315/// [`AccessorSockAddr::connect_owned_fd`]
316/// * `RpcSocketAddressProvider` ⇒ [`AccessorAddrProvider`]
317/// * `Status::fromServiceSpecificError(ERROR_*, msg)` ⇒
318/// `Status::new_service_specific_error_str` with the matching
319/// `IAccessor::ERROR_*` constant
320///
321/// Use via [`Self::new_binder`] (the `BnAccessor::new_binder` AIDL
322/// stub) — the returned `SIBinder` is what
323/// [`create_accessor`](crate::hub::android_16::create_accessor) wraps
324/// for registration and what `kernel addService(name, _)`
325/// publishes to the system service manager.
326pub struct LocalAccessor {
327 instance: String,
328 addr_provider: AccessorAddrProvider,
329}
330
331impl LocalAccessor {
332 /// Build a new `LocalAccessor` bound to `instance` (the name the
333 /// `getInstanceName()` RPC must report — AOSP `validateAccessor`
334 /// rejects mismatch) and `addr_provider` (resolves the name to the
335 /// socket address `addConnection()` should connect to).
336 ///
337 /// Returned wrapped as a `Strong<dyn IAccessor>` so callers can
338 /// trivially `as_binder()` for `addService` or stash on a server's
339 /// service directory. The `Strong<dyn IAccessor>` is also what a
340 /// future [`add_accessor_provider`] returns from its
341 /// closure.
342 pub fn new_binder(
343 instance: impl Into<String>,
344 addr_provider: AccessorAddrProvider,
345 ) -> Strong<dyn IAccessor> {
346 BnAccessor::new_binder(LocalAccessor {
347 instance: instance.into(),
348 addr_provider,
349 })
350 }
351}
352
353impl Interface for LocalAccessor {}
354
355impl IAccessor for LocalAccessor {
356 fn addConnection(&self) -> crate::status::Result<ParcelFileDescriptor> {
357 // Step 1: ask the user-supplied closure for a socket address.
358 // A `Result::Err` here is AOSP `ERROR_CONNECTION_INFO_NOT_FOUND`
359 // (the provider could not derive an address for this name —
360 // `singleSocketConnection` was never even attempted).
361 let addr = match (self.addr_provider)(&self.instance) {
362 Ok(a) => a,
363 Err(e) => {
364 let msg = format!("AccessorAddrProvider({:?}) failed: {e}", self.instance);
365 log::warn!("{msg}");
366 return Err(Status::new_service_specific_error(
367 ERROR_CONNECTION_INFO_NOT_FOUND,
368 Some(msg),
369 ));
370 }
371 };
372
373 // Step 2: open the server-process side of the socket pair (an
374 // ordinary `connect(2)` inside *this* process; the peer is the
375 // `RpcServer::setup_unix_server` listener at the same path,
376 // not the remote client) and return the fd.
377 match addr.connect_owned_fd() {
378 Ok(fd) => Ok(ParcelFileDescriptor::new(fd)),
379 Err(err) => {
380 let (code, label) = accessor_error_code_for(&err);
381 let msg = format!(
382 "LocalAccessor({:?}) {} ({}): {err}",
383 self.instance,
384 addr.family_str(),
385 label,
386 );
387 log::warn!("{msg}");
388 Err(Status::new_service_specific_error(code, Some(msg)))
389 }
390 }
391 }
392
393 fn getInstanceName(&self) -> crate::status::Result<String> {
394 Ok(self.instance.clone())
395 }
396}
397
398/// Map an [`AccessorConnectError`] onto the AOSP `IAccessor::ERROR_*`
399/// service-specific code + a symbolic label for logging.
400fn accessor_error_code_for(err: &AccessorConnectError) -> (i32, &'static str) {
401 match err {
402 AccessorConnectError::UnsupportedFamily => (
403 ERROR_UNSUPPORTED_SOCKET_FAMILY,
404 "ERROR_UNSUPPORTED_SOCKET_FAMILY",
405 ),
406 AccessorConnectError::CreateSocketFailed(_) => (
407 ERROR_FAILED_TO_CREATE_SOCKET,
408 "ERROR_FAILED_TO_CREATE_SOCKET",
409 ),
410 AccessorConnectError::ConnectFailedEacces => (
411 ERROR_FAILED_TO_CONNECT_EACCES,
412 "ERROR_FAILED_TO_CONNECT_EACCES",
413 ),
414 AccessorConnectError::ConnectFailed(_) => (
415 ERROR_FAILED_TO_CONNECT_TO_SOCKET,
416 "ERROR_FAILED_TO_CONNECT_TO_SOCKET",
417 ),
418 }
419}
420
421/// Closure type returned by [`add_accessor_provider`] and called
422/// by [`LocalAccessor::addConnection`] to resolve an instance
423/// name to the socket address it should connect to. Mirrors AOSP
424/// `RpcSocketAddressProvider`'s `(name, sockaddr*, size_t) -> status_t`
425/// signature but returns the strongly-typed [`AccessorSockAddr`]
426/// instead of an out-pointer + status int.
427///
428/// A `Result::Err` here surfaces as
429/// `IAccessor::ERROR_CONNECTION_INFO_NOT_FOUND` at the AIDL boundary
430/// (AOSP parity — see [`super::accessor_16::accessor_error_name`]).
431/// `Send + Sync` so the same provider can be reached from any thread
432/// servicing the AIDL `BnAccessor::addConnection` call (the kernel
433/// binder driver may dispatch on a different thread than the one that
434/// registered the provider).
435///
436/// [`add_accessor_provider`]: super::accessor_register::add_accessor_provider
437/// [`LocalAccessor::addConnection`]: super::accessor_register::LocalAccessor
438pub type AccessorAddrProvider = Box<dyn Fn(&str) -> Result<AccessorSockAddr> + Send + Sync>;
439
440// --- process-local AccessorProvider registry ----
441//
442// AOSP `gAccessorProviders` (`IServiceManager.cpp:200-201`) is a
443// process-local list of `AccessorProvider{ instances, providerCallback }`.
444// `addAccessorProvider` checks for duplicate instances and refuses,
445// then appends; `removeAccessorProvider` walks the list and drops the
446// matching shared_ptr. The cross-process pickup is via
447// `getInjectedAccessor` — `BackendUnifiedServiceManager::getService2`
448// calls it as a *fallback* when servicemanager returns no binder for
449// `name`.
450
451use std::collections::HashSet;
452use std::sync::{Arc, OnceLock, Weak};
453
454/// Closure type for [`add_accessor_provider`]. Maps an instance name
455/// to an `IAccessor` `SIBinder` (typically built via
456/// [`create_accessor`]) — `None` ⇒ "this provider doesn't know about
457/// `name`, try the next provider in the registry".
458///
459/// `Send + Sync` so the registry can be walked under a mutex by any
460/// thread issuing a `hub::get_service` lookup; the closure should be
461/// idempotent across threads (lookup dispatches without re-locking
462/// the registry — see `IServiceManager.cpp:286-291` snapshot pattern).
463pub type AccessorProviderFn = Box<dyn Fn(&str) -> Option<crate::binder::SIBinder> + Send + Sync>;
464
465/// One entry in the process-local accessor registry. Owns its set of
466/// instance names (so `lookup_accessor_provider` can match without
467/// taking shared ownership of the closure) and a reference-counted
468/// callback (so a [`AccessorProviderHandle`] can identify "its" entry
469/// on drop). `Arc` not `Box` so the handle's `Weak` upgrade is the
470/// canonical liveness test.
471struct AccessorProviderEntry {
472 instances: HashSet<String>,
473 provider: Arc<AccessorProviderFn>,
474}
475
476/// Process-wide accessor provider registry. AOSP `gAccessorProviders`
477/// byte-equivalent — a single global, locked with a `Mutex`, walked on
478/// `add`/`remove`/lookup.
479///
480/// Initialized lazily via `OnceLock` so the `rpc`-OFF /
481/// non-android-16 build never allocates the mutex. The
482/// `Mutex<Vec<_>>` shape is the AOSP-faithful one — readers (lookup)
483/// snapshot the entries inside the lock, then release
484/// before calling the provider closure (no callback under the lock
485/// — see `IServiceManager.cpp:286-291`).
486static REGISTRY: OnceLock<std::sync::Mutex<Vec<AccessorProviderEntry>>> = OnceLock::new();
487
488fn registry() -> &'static std::sync::Mutex<Vec<AccessorProviderEntry>> {
489 REGISTRY.get_or_init(|| std::sync::Mutex::new(Vec::new()))
490}
491
492/// AOSP `IServiceManager.cpp:359-366` `isInstanceProvidedLocked`. The
493/// caller already holds the registry lock.
494fn is_instance_provided_locked(entries: &[AccessorProviderEntry], instance: &str) -> bool {
495 entries.iter().any(|e| e.instances.contains(instance))
496}
497
498/// Register a process-local accessor provider for
499/// `instances`. AOSP `addAccessorProvider`
500/// (`IServiceManager.cpp:368-389`). Returns a
501/// [`AccessorProviderHandle`] whose `Drop` un-registers the provider
502/// (RAII) — drop the handle to remove the provider, or call
503/// [`remove_accessor_provider`] for explicit removal with an error
504/// return.
505///
506/// **Duplicate-instance reject**: if *any* name in `instances` is
507/// already provided for by a live entry, the call returns
508/// `Err(StatusCode::AlreadyExists)` and registers nothing (AOSP
509/// returns an empty `weak_ptr<AccessorProvider>()`; rsbinder surfaces
510/// the error directly so a caller can distinguish "no instances" from
511/// "duplicate" without inspecting a weak pointer).
512///
513/// **Empty-set reject**: `instances.is_empty()` ⇒
514/// `Err(StatusCode::BadValue)` (AOSP `ALOGE`+empty-weak).
515///
516/// The provider closure must be `Send + Sync` — it is invoked from
517/// whatever thread services a `hub::get_service` fallback.
518pub fn add_accessor_provider(
519 instances: HashSet<String>,
520 provider: AccessorProviderFn,
521) -> std::result::Result<AccessorProviderHandle, crate::error::StatusCode> {
522 if instances.is_empty() {
523 log::warn!("add_accessor_provider: empty instance set rejected");
524 return Err(crate::error::StatusCode::BadValue);
525 }
526 let mut reg = registry().lock().expect("accessor registry poisoned");
527 for instance in &instances {
528 if is_instance_provided_locked(®, instance) {
529 log::warn!("add_accessor_provider: instance {instance:?} already provided — rejecting");
530 return Err(crate::error::StatusCode::AlreadyExists);
531 }
532 }
533 let arc_provider = Arc::new(provider);
534 let handle = AccessorProviderHandle {
535 provider: Arc::downgrade(&arc_provider),
536 };
537 reg.push(AccessorProviderEntry {
538 instances,
539 provider: arc_provider,
540 });
541 Ok(handle)
542}
543
544/// Explicit removal of a provider previously
545/// registered via [`add_accessor_provider`]. AOSP
546/// `removeAccessorProvider` (`IServiceManager.cpp:391-411`). Returns
547/// `Err(StatusCode::BadValue)` if the handle is already dead (the
548/// Arc has been dropped — typically because
549/// [`AccessorProviderHandle`] already ran its Drop), and
550/// `Err(StatusCode::NameNotFound)` if no entry with this provider
551/// exists in the registry (shouldn't happen in practice — the handle
552/// holds a `Weak` and the entry holds the `Arc`, so a live handle
553/// implies a live entry).
554///
555/// **Idiomatic use**: prefer simply dropping the
556/// [`AccessorProviderHandle`] — the Drop impl calls the same removal
557/// logic and ignores the result. Direct calls to this function are
558/// for callers who want to surface removal failures (rare).
559pub fn remove_accessor_provider(
560 handle: AccessorProviderHandle,
561) -> std::result::Result<(), crate::error::StatusCode> {
562 let Some(target) = handle.provider.upgrade() else {
563 return Err(crate::error::StatusCode::BadValue);
564 };
565 let mut reg = registry().lock().expect("accessor registry poisoned");
566 let before = reg.len();
567 reg.retain(|e| !Arc::ptr_eq(&e.provider, &target));
568 if reg.len() == before {
569 return Err(crate::error::StatusCode::NameNotFound);
570 }
571 Ok(())
572}
573
574/// RAII handle returned by
575/// [`add_accessor_provider`]. Dropping the handle un-registers the
576/// provider; ignore the handle (`_handle`) only if you want the
577/// provider to live for the whole process lifetime.
578///
579/// The handle carries a `Weak` reference to the provider entry's
580/// `Arc<AccessorProviderFn>` so its lifetime tracks the registry
581/// entry — a successful `add_accessor_provider` returns a live
582/// handle, `Drop` removes it, and a subsequent
583/// [`remove_accessor_provider`] surfaces `BadValue` for the
584/// already-removed case.
585///
586/// `Debug` is implemented so callers can `.expect()`/`.expect_err()`
587/// against `Result<AccessorProviderHandle, _>`; the body deliberately
588/// omits the inner `Weak` pointer value (only liveness is meaningful).
589pub struct AccessorProviderHandle {
590 provider: Weak<AccessorProviderFn>,
591}
592
593impl std::fmt::Debug for AccessorProviderHandle {
594 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
595 f.debug_struct("AccessorProviderHandle")
596 .field("live", &(self.provider.strong_count() > 0))
597 .finish()
598 }
599}
600
601impl AccessorProviderHandle {
602 /// Liveness probe — `true` iff the underlying provider entry is
603 /// still in the registry (the `Arc` hasn't been dropped via
604 /// [`remove_accessor_provider`] or by the handle's own Drop).
605 /// Used by tests; not part of the public API contract.
606 #[cfg(test)]
607 fn is_live(&self) -> bool {
608 self.provider.strong_count() > 0
609 }
610}
611
612impl Drop for AccessorProviderHandle {
613 fn drop(&mut self) {
614 // RAII unregister. Race with explicit
615 // `remove_accessor_provider` is fine: the second remove sees a
616 // stale `Weak` and is a no-op (the entry has already been
617 // dropped, and the `Arc` count is 0). Never panic in Drop.
618 let Some(target) = self.provider.upgrade() else {
619 return;
620 };
621 if let Ok(mut reg) = registry().lock() {
622 reg.retain(|e| !Arc::ptr_eq(&e.provider, &target));
623 }
624 }
625}
626
627/// Lookup a provider by instance name. AOSP
628/// `getInjectedAccessor` (`IServiceManager.cpp:284-312`) — snapshot
629/// the registry under the lock, then walk the snapshot *outside* the
630/// lock calling each provider's closure until one returns
631/// `Some(binder)`. `None` ⇒ no live provider knows about `name`
632/// (fallback caller should return the original
633/// `Service::Accessor(None)` / `ServiceWithMetadata(None)` they were
634/// trying to backfill).
635///
636/// Consumed by [`resolve_via_process_local`] (the fallback the public
637/// `hub::get_service` arm uses) and by the hermetic tests.
638/// `pub(crate)` keeps all callers inside the crate.
639pub(crate) fn lookup_accessor_provider(name: &str) -> Option<crate::binder::SIBinder> {
640 // Snapshot of `Arc<AccessorProviderFn>` for entries that include
641 // `name`. We deliberately keep `Arc` instead of `Weak` here so the
642 // closure can't disappear between snapshot and call — AOSP keeps
643 // `shared_ptr` copies for the same reason
644 // (`IServiceManager.cpp:289-290`).
645 let snapshot: Vec<Arc<AccessorProviderFn>> = {
646 let reg = registry().lock().expect("accessor registry poisoned");
647 reg.iter()
648 .filter(|e| e.instances.contains(name))
649 .map(|e| Arc::clone(&e.provider))
650 .collect()
651 };
652 // Call closures *outside* the lock (AOSP comment: "Unlocked to
653 // call the providers. This requires the providers to be
654 // threadsafe and not contain any references to objects that could
655 // be deleted.").
656 for provider in snapshot {
657 if let Some(binder) = provider(name) {
658 return Some(binder);
659 }
660 }
661 None
662}
663
664/// Process-local "consume" — look up a registered
665/// `IAccessor` provider for `name` (via the registry) and bridge
666/// it to an `RpcSession` root using
667/// [`super::accessor_16::resolve_accessor`]. Returns `None` if no
668/// provider claims `name`, or if the bridge fails
669/// (instance-name mismatch / addConnection failure / handshake / no
670/// root). This is the AOSP `getInjectedAccessor` →
671/// `Service::accessor → toBinder` *combined* path, exposed as a
672/// single public helper so a caller without a kernel servicemanager
673/// (hermetic tests on macOS, no-binderfs hosts) can drive the
674/// fallback directly.
675///
676/// `cfg(feature = "rpc")` — without `rpc`, no `IAccessor` stub exists
677/// so the function isn't compiled.
678pub fn resolve_via_process_local(
679 name: &str,
680) -> Option<super::servicemanager_16::android::os::ServiceWithMetadata::ServiceWithMetadata> {
681 let accessor = lookup_accessor_provider(name)?;
682 super::accessor_16::resolve_accessor(name, accessor)
683}
684
685/// AOSP `createAccessor`
686/// (`IServiceManager.cpp:439-450`). Wrap an [`AccessorAddrProvider`]
687/// in a [`LocalAccessor`] `BnAccessor`, return the binder.
688/// Convenience over [`LocalAccessor::new_binder`] for the "I just
689/// want an `SIBinder`" case (most callers want to register the binder
690/// with `addService` or stash it in an [`add_accessor_provider`]
691/// closure — both want an `SIBinder`, not a `Strong<dyn IAccessor>`).
692pub fn create_accessor(
693 instance: impl Into<String>,
694 addr_provider: AccessorAddrProvider,
695) -> crate::binder::SIBinder {
696 LocalAccessor::new_binder(instance, addr_provider).as_binder()
697}
698
699#[cfg(test)]
700mod tests {
701 use super::*;
702 use std::os::fd::AsRawFd;
703 use std::os::unix::net::{UnixListener, UnixStream};
704
705 /// Family-string dispatch is order-independent across variants and
706 /// stable across feature flags (`Vsock`/`Inet` variants exist
707 /// unconditionally — see the module doc).
708 #[test]
709 fn family_str_covers_all_variants() {
710 let unix = AccessorSockAddr::Unix(PathBuf::from("/tmp/rsbinder-test-2-14.sock"));
711 let unix_abstract = AccessorSockAddr::UnixAbstract(b"rsbinder-test-2-14".to_vec());
712 let vsock = AccessorSockAddr::Vsock { cid: 2, port: 5555 };
713 let inet = AccessorSockAddr::Inet("127.0.0.1:5555".parse().expect("addr"));
714 assert_eq!(unix.family_str(), "AF_UNIX");
715 assert_eq!(unix_abstract.family_str(), "AF_UNIX");
716 assert_eq!(vsock.family_str(), "AF_VSOCK");
717 assert_eq!(inet.family_str(), "AF_INET");
718 }
719
720 /// `Clone`/`Debug` derives compile and round-trip the inner data
721 /// (defensive — a future `Clone` regression would otherwise leak
722 /// fd-bearing types into the variant).
723 #[test]
724 fn clone_and_debug_roundtrip() {
725 let a = AccessorSockAddr::Unix(PathBuf::from("/tmp/x"));
726 let b = a.clone();
727 let dbg = format!("{b:?}");
728 assert!(dbg.contains("Unix"));
729 assert!(dbg.contains("/tmp/x"));
730 }
731
732 /// The provider closure type accepts the obvious `|name| Ok(...)`
733 /// shape; this is a compile-time witness so a future refactor that
734 /// breaks `Fn` boxing (e.g. accidentally requiring `FnMut`) fails
735 /// here rather than at the call site.
736 #[test]
737 fn provider_closure_compiles() {
738 let path = PathBuf::from("/tmp/rsbinder-test-provider.sock");
739 let provider: AccessorAddrProvider =
740 Box::new(move |_name: &str| Ok(AccessorSockAddr::Unix(path.clone())));
741 let got = provider("instance-x").expect("provider ok");
742 match got {
743 AccessorSockAddr::Unix(p) => {
744 assert_eq!(p, PathBuf::from("/tmp/rsbinder-test-provider.sock"))
745 }
746 other => panic!("expected Unix, got {other:?}"),
747 }
748 }
749
750 /// Helper: bind+listen a unique-path Unix socket and return
751 /// `(listener, path)`. The path is removed before bind to make the
752 /// test idempotent under crashed previous runs. macOS' default
753 /// `std::env::temp_dir()` is `/var/folders/.../T/` (~47 chars), so
754 /// we keep the file name short (`rsb_${tag}_${pid}.sock` —
755 /// `tag` capped to ≤ 8 chars at call sites) to stay well under
756 /// `SUN_LEN = 104`.
757 fn unique_unix_listener(tag: &str) -> (UnixListener, PathBuf) {
758 let mut p = std::env::temp_dir();
759 p.push(format!("rsb_{}_{}.sock", tag, std::process::id()));
760 let _ = std::fs::remove_file(&p);
761 let l = UnixListener::bind(&p).expect("bind");
762 (l, p)
763 }
764
765 /// Unix path — `connect_owned_fd` returns a real, connected
766 /// `OwnedFd` whose other endpoint is the listener's `accept`. The
767 /// fd is byte-functional (echo round-trip) — proves the fd isn't
768 /// dropped/closed somewhere in the wrapping.
769 #[test]
770 fn connect_owned_fd_unix_roundtrips() {
771 use std::io::{Read, Write};
772 let (listener, path) = unique_unix_listener("a02-rt");
773 // Server accept thread — copies one byte the client writes.
774 let server_handle = std::thread::spawn(move || {
775 let (mut s, _) = listener.accept().expect("accept");
776 let mut buf = [0u8; 1];
777 s.read_exact(&mut buf).expect("read");
778 s.write_all(&buf).expect("write");
779 });
780
781 let addr = AccessorSockAddr::Unix(path.clone());
782 let fd = addr.connect_owned_fd().expect("connect");
783 // Drive bytes through the adopted fd. `UnixStream::from(fd)`
784 // takes ownership of the fd, so the fd lives exactly as long as
785 // the stream.
786 let mut stream = UnixStream::from(fd);
787 stream.write_all(&[0x42]).expect("client write");
788 let mut got = [0u8; 1];
789 stream.read_exact(&mut got).expect("client read");
790 assert_eq!(got, [0x42]);
791 drop(stream);
792
793 server_handle.join().expect("server join");
794 let _ = std::fs::remove_file(&path);
795 }
796
797 #[cfg(any(target_os = "linux", target_os = "android"))]
798 #[test]
799 fn connect_owned_fd_unix_abstract_roundtrips() {
800 use std::io::{Read, Write};
801 #[cfg(target_os = "android")]
802 use std::os::android::net::SocketAddrExt;
803 #[cfg(target_os = "linux")]
804 use std::os::linux::net::SocketAddrExt;
805
806 let name = format!("rsb_a02_abs_{}", std::process::id()).into_bytes();
807 let addr = std::os::unix::net::SocketAddr::from_abstract_name(&name).expect("addr");
808 let listener = UnixListener::bind_addr(&addr).expect("bind");
809 let server_handle = std::thread::spawn(move || {
810 let (mut s, _) = listener.accept().expect("accept");
811 let mut buf = [0u8; 1];
812 s.read_exact(&mut buf).expect("read");
813 s.write_all(&buf).expect("write");
814 });
815
816 let fd = AccessorSockAddr::UnixAbstract(name)
817 .connect_owned_fd()
818 .expect("connect");
819 let mut stream = UnixStream::from(fd);
820 stream.write_all(&[0x42]).expect("client write");
821 let mut got = [0u8; 1];
822 stream.read_exact(&mut got).expect("client read");
823 assert_eq!(got, [0x42]);
824
825 drop(stream);
826 server_handle.join().expect("server join");
827 }
828
829 /// Unix path — connect to a non-existent path surfaces as
830 /// `ConnectFailed` (not `UnsupportedFamily` or panic). The exact
831 /// `io::ErrorKind` is host-OS dependent (Linux = `ConnectionRefused`,
832 /// macOS = `NotFound`), so we only assert the variant.
833 #[test]
834 fn connect_owned_fd_unix_missing_path_is_connect_failed() {
835 let path = PathBuf::from(format!("/tmp/rsb_2_14_nope_{}.sock", std::process::id()));
836 let addr = AccessorSockAddr::Unix(path);
837 let err = addr.connect_owned_fd().expect_err("must fail");
838 assert!(
839 matches!(err, AccessorConnectError::ConnectFailed(_)),
840 "expected ConnectFailed, got {err:?}"
841 );
842 }
843
844 /// Vsock / inet `UnsupportedFamily` when the feature is off
845 /// (= rsbinder default build on macOS). This is the AOSP-faithful
846 /// `ERROR_UNSUPPORTED_SOCKET_FAMILY` path. When the feature **is**
847 /// built in, the connect attempt would surface as `ConnectFailed`
848 /// (no listener on `cid:port` / `127.0.0.1:0`) — that arm is left
849 /// for the integration test in `tests/rpc_accessor_register.rs`,
850 /// since exercising it requires a live listener and the
851 /// vsock crate's connect behaviour differs on macOS.
852 #[cfg(not(feature = "rpc-vsock"))]
853 #[test]
854 fn connect_owned_fd_vsock_disabled_is_unsupported() {
855 let addr = AccessorSockAddr::Vsock { cid: 2, port: 5555 };
856 let err = addr.connect_owned_fd().expect_err("vsock disabled");
857 assert!(
858 matches!(err, AccessorConnectError::UnsupportedFamily),
859 "expected UnsupportedFamily, got {err:?}"
860 );
861 }
862
863 #[cfg(not(feature = "rpc-tcp-debug"))]
864 #[test]
865 fn connect_owned_fd_inet_disabled_is_unsupported() {
866 let addr = AccessorSockAddr::Inet("127.0.0.1:5555".parse().expect("addr"));
867 let err = addr.connect_owned_fd().expect_err("inet disabled");
868 assert!(
869 matches!(err, AccessorConnectError::UnsupportedFamily),
870 "expected UnsupportedFamily, got {err:?}"
871 );
872 }
873
874 /// `AccessorConnectError::Display` includes the inner `io::Error`
875 /// message when present — important for `log::warn!` lines that
876 /// surface the OS-level cause next to the AOSP error code.
877 #[test]
878 fn connect_error_display_includes_inner_cause() {
879 let e = AccessorConnectError::ConnectFailed(io::Error::new(
880 io::ErrorKind::ConnectionRefused,
881 "test cause",
882 ));
883 let s = format!("{e}");
884 assert!(s.contains("test cause"), "Display missing inner: {s}");
885 assert!(s.contains("connect(2)"), "Display missing op label: {s}");
886 }
887
888 /// Anti-leak invariant — `connect_owned_fd` on a successful
889 /// connect returns a fd whose `AsRawFd` is non-negative and
890 /// non-stdio (>= 3). Compile-time witness for the OwnedFd shape.
891 #[test]
892 fn connect_owned_fd_returns_real_fd() {
893 let (listener, path) = unique_unix_listener("a02-fd");
894 let server_handle = std::thread::spawn(move || {
895 let _ = listener.accept();
896 });
897 let addr = AccessorSockAddr::Unix(path.clone());
898 let fd = addr.connect_owned_fd().expect("connect");
899 assert!(
900 fd.as_raw_fd() >= 3,
901 "expected real fd, got {}",
902 fd.as_raw_fd()
903 );
904 drop(fd);
905 server_handle.join().expect("server join");
906 let _ = std::fs::remove_file(&path);
907 }
908
909 // --- LocalAccessor tests (in-process Bn/Bp round-trip) -----
910
911 use crate::FromIBinder;
912
913 /// Happy path: `LocalAccessor::new_binder(...)` ⇒ Bn-side
914 /// SIBinder ⇒ in-process `BpAccessor::from_binder` (via the
915 /// generated `IAccessor::FromIBinder` hook) ⇒
916 /// `getInstanceName()` returns the stored name + `addConnection()`
917 /// returns a fd connected to a listener on the provider's path.
918 /// This is the round-trip witness that the AIDL marshalling +
919 /// kernel binder driver path works on the *same* process (no
920 /// cross-process kernel binder needed — `Binder::new` + local
921 /// proxy stand in for the kernel hop).
922 #[test]
923 fn local_accessor_inprocess_roundtrip() {
924 use std::io::{Read, Write};
925 use std::os::unix::net::UnixStream;
926 let (listener, path) = unique_unix_listener("a03-rt");
927 // Server thread — echo one byte so we can prove the fd is
928 // bidirectionally functional after transfer.
929 let server_handle = std::thread::spawn(move || {
930 let (mut s, _) = listener.accept().expect("accept");
931 let mut buf = [0u8; 1];
932 s.read_exact(&mut buf).expect("read");
933 s.write_all(&buf).expect("write");
934 });
935
936 let provider_path = path.clone();
937 let provider: AccessorAddrProvider =
938 Box::new(move |_name: &str| Ok(AccessorSockAddr::Unix(provider_path.clone())));
939 let strong = LocalAccessor::new_binder("rsbinder.test.a03", provider);
940 let sib: crate::binder::SIBinder = strong.as_binder();
941
942 // Cast back to `dyn IAccessor` via the AIDL `FromIBinder` hook
943 // — exactly the path a consumer would take.
944 let bp = <dyn IAccessor as FromIBinder>::try_from(sib).expect("FromIBinder");
945 assert_eq!(
946 bp.getInstanceName().expect("getInstanceName"),
947 "rsbinder.test.a03"
948 );
949
950 let pfd = bp.addConnection().expect("addConnection");
951 let fd: std::os::fd::OwnedFd = pfd.into();
952 let mut stream = UnixStream::from(fd);
953 stream.write_all(&[0xA5]).expect("client write");
954 let mut got = [0u8; 1];
955 stream.read_exact(&mut got).expect("client read");
956 assert_eq!(got, [0xA5]);
957 drop(stream);
958
959 server_handle.join().expect("server join");
960 let _ = std::fs::remove_file(&path);
961 }
962
963 /// Provider error path: provider returns `Err` ⇒
964 /// `addConnection()` ⇒ `Status::ServiceSpecific(ERROR_CONNECTION_INFO_NOT_FOUND)`.
965 /// `getInstanceName()` keeps working — the error is *per-call*,
966 /// not per-instance.
967 #[test]
968 fn local_accessor_provider_error_maps_to_connection_info_not_found() {
969 let provider: AccessorAddrProvider =
970 Box::new(|_name: &str| Err(crate::error::StatusCode::BadValue));
971 let strong = LocalAccessor::new_binder("rsbinder.test.a03.provider-err", provider);
972 let sib = strong.as_binder();
973 let bp = <dyn IAccessor as FromIBinder>::try_from(sib).expect("FromIBinder");
974
975 // getInstanceName still works.
976 assert_eq!(
977 bp.getInstanceName().unwrap(),
978 "rsbinder.test.a03.provider-err"
979 );
980 // addConnection bubbles the AOSP-faithful service-specific error.
981 let status = bp.addConnection().expect_err("must fail");
982 assert_eq!(
983 status.service_specific_error(),
984 ERROR_CONNECTION_INFO_NOT_FOUND,
985 "expected ERROR_CONNECTION_INFO_NOT_FOUND, got {status:?}"
986 );
987 }
988
989 /// Connect-failure path: provider returns a valid
990 /// `AccessorSockAddr::Unix` whose path has no listener ⇒
991 /// `addConnection()` ⇒
992 /// `Status::ServiceSpecific(ERROR_FAILED_TO_CONNECT_TO_SOCKET)`.
993 /// Confirms the (AccessorConnectError → ERROR_* code) mapping is
994 /// wired through the AIDL marshal correctly.
995 #[test]
996 fn local_accessor_connect_failure_maps_to_connect_to_socket() {
997 let path = PathBuf::from(format!(
998 "/tmp/rsb_2_14_a03_nope_{}.sock",
999 std::process::id()
1000 ));
1001 let provider: AccessorAddrProvider =
1002 Box::new(move |_name: &str| Ok(AccessorSockAddr::Unix(path.clone())));
1003 let strong = LocalAccessor::new_binder("rsbinder.test.a03.no-listener", provider);
1004 let sib = strong.as_binder();
1005 let bp = <dyn IAccessor as FromIBinder>::try_from(sib).expect("FromIBinder");
1006
1007 let status = bp.addConnection().expect_err("must fail (no listener)");
1008 assert_eq!(
1009 status.service_specific_error(),
1010 ERROR_FAILED_TO_CONNECT_TO_SOCKET,
1011 "expected ERROR_FAILED_TO_CONNECT_TO_SOCKET, got {status:?}"
1012 );
1013 }
1014
1015 /// Unsupported-family path (feature-disabled vsock on
1016 /// macOS-default build): provider returns `Vsock { .. }` ⇒
1017 /// `addConnection()` ⇒
1018 /// `Status::ServiceSpecific(ERROR_UNSUPPORTED_SOCKET_FAMILY)`.
1019 /// The same code path that AOSP returns for a wrong family — so an
1020 /// `instance` configured for vsock on a build without
1021 /// `rpc-vsock` surfaces with AOSP-faithful semantics rather than a
1022 /// compile-time mismatch.
1023 #[cfg(not(feature = "rpc-vsock"))]
1024 #[test]
1025 fn local_accessor_unsupported_family_maps_to_unsupported() {
1026 let provider: AccessorAddrProvider =
1027 Box::new(|_name: &str| Ok(AccessorSockAddr::Vsock { cid: 2, port: 5555 }));
1028 let strong = LocalAccessor::new_binder("rsbinder.test.a03.vsock-disabled", provider);
1029 let sib = strong.as_binder();
1030 let bp = <dyn IAccessor as FromIBinder>::try_from(sib).expect("FromIBinder");
1031
1032 let status = bp.addConnection().expect_err("must fail (vsock disabled)");
1033 assert_eq!(
1034 status.service_specific_error(),
1035 ERROR_UNSUPPORTED_SOCKET_FAMILY,
1036 "expected ERROR_UNSUPPORTED_SOCKET_FAMILY, got {status:?}"
1037 );
1038 }
1039
1040 // --- process-local registry tests --------------------------
1041 //
1042 // `REGISTRY` is a process-wide `OnceLock<Mutex<Vec<_>>>`; under
1043 // `cargo test` multiple test threads share it. We give every test
1044 // a unique instance-name namespace (`format!(..., line!())`) so
1045 // concurrent registrations from sibling tests don't false-collide
1046 // on the duplicate-instance reject — AOSP test scaffolding uses
1047 // the same pattern (`gAccessorProviders` is global, tests
1048 // namespace their fake instances).
1049
1050 fn unique_instance(tag: &str) -> String {
1051 format!(
1052 "rsb.test.a4.{}.{}.{}",
1053 tag,
1054 std::process::id(),
1055 line!() // distinct per-source-line at the call site (each !invocation)
1056 )
1057 }
1058
1059 /// AOSP `addAccessorProvider` happy path — register two providers
1060 /// for disjoint instance sets, verify each handle is live, verify
1061 /// `lookup_accessor_provider` dispatches to the matching
1062 /// provider's closure and returns its binder.
1063 #[test]
1064 fn add_two_providers_disjoint_instances() {
1065 let instance_a = unique_instance("disjoint-a");
1066 let instance_b = unique_instance("disjoint-b");
1067
1068 let path_a = PathBuf::from(format!("/tmp/rsb_a4_disj_a_{}.sock", std::process::id()));
1069 let path_b = PathBuf::from(format!("/tmp/rsb_a4_disj_b_{}.sock", std::process::id()));
1070 let cap_a = path_a.clone();
1071 let cap_b = path_b.clone();
1072
1073 let provider_a: AccessorProviderFn = {
1074 let want = instance_a.clone();
1075 Box::new(move |name: &str| {
1076 if name == want {
1077 Some(create_accessor(
1078 name,
1079 Box::new({
1080 let p = cap_a.clone();
1081 move |_| Ok(AccessorSockAddr::Unix(p.clone()))
1082 }),
1083 ))
1084 } else {
1085 None
1086 }
1087 })
1088 };
1089 let provider_b: AccessorProviderFn = {
1090 let want = instance_b.clone();
1091 Box::new(move |name: &str| {
1092 if name == want {
1093 Some(create_accessor(
1094 name,
1095 Box::new({
1096 let p = cap_b.clone();
1097 move |_| Ok(AccessorSockAddr::Unix(p.clone()))
1098 }),
1099 ))
1100 } else {
1101 None
1102 }
1103 })
1104 };
1105
1106 let h_a =
1107 add_accessor_provider(HashSet::from([instance_a.clone()]), provider_a).expect("add a");
1108 let h_b =
1109 add_accessor_provider(HashSet::from([instance_b.clone()]), provider_b).expect("add b");
1110 assert!(h_a.is_live());
1111 assert!(h_b.is_live());
1112
1113 // Lookup dispatches to the matching provider. Each provider
1114 // returns *its* binder; `getInstanceName` round-trips the
1115 // configured instance name.
1116 let bp_a = <dyn IAccessor as FromIBinder>::try_from(
1117 lookup_accessor_provider(&instance_a).expect("lookup a"),
1118 )
1119 .expect("cast a");
1120 assert_eq!(bp_a.getInstanceName().unwrap(), instance_a);
1121
1122 let bp_b = <dyn IAccessor as FromIBinder>::try_from(
1123 lookup_accessor_provider(&instance_b).expect("lookup b"),
1124 )
1125 .expect("cast b");
1126 assert_eq!(bp_b.getInstanceName().unwrap(), instance_b);
1127
1128 // Unknown instance returns None — neither provider claims it.
1129 assert!(lookup_accessor_provider("rsb.test.a4.absent.unknown").is_none());
1130
1131 // Drop the handles — registry should shrink (lookups now fail).
1132 drop(h_a);
1133 drop(h_b);
1134 assert!(lookup_accessor_provider(&instance_a).is_none());
1135 assert!(lookup_accessor_provider(&instance_b).is_none());
1136 }
1137
1138 /// AOSP duplicate-instance reject: a second
1139 /// `add_accessor_provider` with an instance already claimed by a
1140 /// live entry returns `Err(StatusCode::AlreadyExists)` and adds
1141 /// nothing. The first handle must stay live and authoritative.
1142 #[test]
1143 fn add_accessor_provider_rejects_duplicate_instance() {
1144 let instance = unique_instance("dup");
1145 let first: AccessorProviderFn = Box::new({
1146 let want = instance.clone();
1147 move |name: &str| {
1148 if name == want {
1149 Some(create_accessor(
1150 name,
1151 Box::new(|_| {
1152 Ok(AccessorSockAddr::Unix(PathBuf::from(
1153 "/tmp/rsb_a4_dup_1.sock",
1154 )))
1155 }),
1156 ))
1157 } else {
1158 None
1159 }
1160 }
1161 });
1162 let h1 = add_accessor_provider(HashSet::from([instance.clone()]), first).expect("add 1");
1163 assert!(h1.is_live());
1164
1165 let second: AccessorProviderFn = Box::new(|_name| {
1166 Some(create_accessor(
1167 "ignored",
1168 Box::new(|_| Ok(AccessorSockAddr::Unix(PathBuf::from("/tmp/x")))),
1169 ))
1170 });
1171 let err = add_accessor_provider(HashSet::from([instance.clone()]), second)
1172 .expect_err("dup must reject");
1173 assert_eq!(
1174 err,
1175 crate::error::StatusCode::AlreadyExists,
1176 "dup-instance must surface AlreadyExists, got {err:?}"
1177 );
1178
1179 // Lookup still hits the *first* provider — second add added
1180 // nothing.
1181 let bp = <dyn IAccessor as FromIBinder>::try_from(
1182 lookup_accessor_provider(&instance).expect("lookup hits first"),
1183 )
1184 .expect("cast");
1185 assert_eq!(bp.getInstanceName().unwrap(), instance);
1186
1187 drop(h1);
1188 assert!(lookup_accessor_provider(&instance).is_none());
1189 }
1190
1191 /// `add_accessor_provider(HashSet::new(), ...)` ⇒ `BadValue`. AOSP
1192 /// `ALOGE("Set of instances is empty!")`+empty-weak.
1193 #[test]
1194 fn add_accessor_provider_rejects_empty_instances() {
1195 let provider: AccessorProviderFn = Box::new(|_| None);
1196 let err =
1197 add_accessor_provider(HashSet::new(), provider).expect_err("empty set must reject");
1198 assert_eq!(err, crate::error::StatusCode::BadValue);
1199 }
1200
1201 /// RAII unregister — dropping the handle removes the entry from
1202 /// the registry; subsequent lookups return `None`.
1203 /// `remove_accessor_provider` on the already-dropped handle would
1204 /// be `BadValue` (we can't test that directly because handle drop
1205 /// consumes the handle; instead we test the explicit-removal path
1206 /// in [`explicit_remove_succeeds`] / [`explicit_remove_on_dead_returns_bad_value`]).
1207 #[test]
1208 fn handle_drop_unregisters() {
1209 let instance = unique_instance("raii");
1210 let provider: AccessorProviderFn = {
1211 let want = instance.clone();
1212 Box::new(move |name: &str| {
1213 if name == want {
1214 Some(create_accessor(
1215 name,
1216 Box::new(|_| {
1217 Ok(AccessorSockAddr::Unix(PathBuf::from(
1218 "/tmp/rsb_a4_raii.sock",
1219 )))
1220 }),
1221 ))
1222 } else {
1223 None
1224 }
1225 })
1226 };
1227 let h = add_accessor_provider(HashSet::from([instance.clone()]), provider).expect("add");
1228 assert!(lookup_accessor_provider(&instance).is_some());
1229 drop(h);
1230 assert!(
1231 lookup_accessor_provider(&instance).is_none(),
1232 "handle drop must unregister"
1233 );
1234 }
1235
1236 /// `remove_accessor_provider(live_handle)` ⇒ `Ok(())` and the
1237 /// entry is gone. Test name is also the explicit-removal-success
1238 /// witness.
1239 #[test]
1240 fn explicit_remove_succeeds() {
1241 let instance = unique_instance("explicit");
1242 let provider: AccessorProviderFn = {
1243 let want = instance.clone();
1244 Box::new(move |name: &str| {
1245 if name == want {
1246 Some(create_accessor(
1247 name,
1248 Box::new(|_| {
1249 Ok(AccessorSockAddr::Unix(PathBuf::from(
1250 "/tmp/rsb_a4_explicit.sock",
1251 )))
1252 }),
1253 ))
1254 } else {
1255 None
1256 }
1257 })
1258 };
1259 let h = add_accessor_provider(HashSet::from([instance.clone()]), provider).expect("add");
1260 remove_accessor_provider(h).expect("explicit remove");
1261 assert!(
1262 lookup_accessor_provider(&instance).is_none(),
1263 "after explicit remove, lookup must be None"
1264 );
1265 }
1266
1267 /// Concurrent register + lookup — thread-safety witness. Multiple
1268 /// threads register disjoint instances and look up each other's
1269 /// instances; no panic, no deadlock, and every lookup either
1270 /// finds the registered provider or returns `None` (never a stale
1271 /// proxy from another thread's registration in flight).
1272 #[test]
1273 fn concurrent_register_and_lookup_p6() {
1274 const N: usize = 8;
1275 let mut handles = Vec::new();
1276 let started = Arc::new(std::sync::Barrier::new(N));
1277
1278 for i in 0..N {
1279 let bar = Arc::clone(&started);
1280 handles.push(std::thread::spawn(move || {
1281 bar.wait();
1282 let instance = format!(
1283 "rsb.test.a4.p6.{}.{:?}.{}",
1284 std::process::id(),
1285 std::thread::current().id(),
1286 i,
1287 );
1288 let cap = instance.clone();
1289 let provider: AccessorProviderFn = Box::new(move |name: &str| {
1290 if name == cap {
1291 Some(create_accessor(
1292 name,
1293 Box::new(|_| {
1294 Ok(AccessorSockAddr::Unix(PathBuf::from("/tmp/rsb_a4_p6.sock")))
1295 }),
1296 ))
1297 } else {
1298 None
1299 }
1300 });
1301 let h = add_accessor_provider(HashSet::from([instance.clone()]), provider)
1302 .expect("add");
1303 // Self-lookup must succeed.
1304 let bp = <dyn IAccessor as FromIBinder>::try_from(
1305 lookup_accessor_provider(&instance).expect("self lookup"),
1306 )
1307 .expect("cast");
1308 assert_eq!(bp.getInstanceName().unwrap(), instance);
1309 // Cross-lookup of an instance that wasn't registered
1310 // by anyone returns None (validates that the registry
1311 // doesn't return stale entries between threads).
1312 assert!(lookup_accessor_provider("rsb.test.a4.p6.never").is_none());
1313 drop(h);
1314 }));
1315 }
1316
1317 for h in handles {
1318 h.join().expect("thread join");
1319 }
1320 }
1321}