Skip to main content

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(&reg, 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}