include!(concat!(env!("OUT_DIR"), "/service_manager_16.rs"));
use crate::*;
pub use android::os::IServiceManager::{
BnServiceManager, BpServiceManager, IServiceManager, DUMP_FLAG_PRIORITY_ALL,
DUMP_FLAG_PRIORITY_CRITICAL, DUMP_FLAG_PRIORITY_DEFAULT, DUMP_FLAG_PRIORITY_HIGH,
DUMP_FLAG_PRIORITY_NORMAL, DUMP_FLAG_PROTO, FLAG_IS_LAZY_SERVICE,
};
pub use android::os::ConnectionInfo::ConnectionInfo;
pub use android::os::IClientCallback::{BnClientCallback, IClientCallback};
pub use android::os::IServiceCallback::{BnServiceCallback, IServiceCallback};
pub use android::os::ServiceDebugInfo::ServiceDebugInfo;
#[cfg(feature = "rpc")]
fn resolve_accessor_arm(
name: &str,
accessor: Option<crate::SIBinder>,
) -> Option<android::os::ServiceWithMetadata::ServiceWithMetadata> {
let Some(accessor) = accessor else {
log::warn!("Service {name} returned a null Accessor binder");
return None;
};
super::accessor_16::resolve_accessor(name, accessor)
}
#[cfg(not(feature = "rpc"))]
fn resolve_accessor_arm(
name: &str,
_accessor: Option<crate::SIBinder>,
) -> Option<android::os::ServiceWithMetadata::ServiceWithMetadata> {
log::warn!(
"Service {name} is an Accessor but rsbinder was built without the \
`rpc` feature; cannot bridge to RPC root"
);
None
}
#[cfg(feature = "rpc")]
fn try_process_local_fallback(
name: &str,
) -> Option<android::os::ServiceWithMetadata::ServiceWithMetadata> {
let out = super::accessor_register::resolve_via_process_local(name)?;
log::debug!("servicemanager_16 fallback: {name} resolved via process-local AccessorProvider");
Some(out)
}
#[cfg(not(feature = "rpc"))]
fn try_process_local_fallback(
_name: &str,
) -> Option<android::os::ServiceWithMetadata::ServiceWithMetadata> {
None
}
fn dispatch_typed_service(
name: &str,
service: android::os::Service::Service,
) -> Option<android::os::ServiceWithMetadata::ServiceWithMetadata> {
use android::os::Service::Service;
match service {
Service::ServiceWithMetadata(swm) => {
if swm.service.is_some() {
Some(swm)
} else {
try_process_local_fallback(name).or(Some(swm))
}
}
Service::Accessor(accessor) => {
resolve_accessor_arm(name, accessor).or_else(|| try_process_local_fallback(name))
}
}
}
pub fn get_service(
sm: &BpServiceManager,
name: &str,
) -> Option<android::os::ServiceWithMetadata::ServiceWithMetadata> {
match sm.getService2(name) {
Ok(service) => dispatch_typed_service(name, service),
Err(err) => {
log::error!("Failed to get service {name}: {err}");
None
}
}
}
pub fn try_get_service(
sm: &BpServiceManager,
name: &str,
) -> Result<Option<android::os::ServiceWithMetadata::ServiceWithMetadata>> {
sm.getService2(name)
.map(|service| dispatch_typed_service(name, service))
.map_err(|e| e.into())
}
pub fn check_service(
sm: &BpServiceManager,
name: &str,
) -> Option<android::os::ServiceWithMetadata::ServiceWithMetadata> {
match sm.checkService2(name) {
Ok(service) => dispatch_typed_service(name, service),
Err(err) => {
log::error!("Failed to check service {name}: {err}");
None
}
}
}
pub fn list_services(sm: &BpServiceManager, dump_priority: i32) -> Vec<String> {
match sm.listServices(dump_priority) {
Ok(result) => result,
Err(err) => {
log::error!("Failed to list services: {err}");
Vec::new()
}
}
}
pub fn add_service(
sm: &BpServiceManager,
identifier: &str,
binder: SIBinder,
) -> std::result::Result<(), Status> {
sm.addService(identifier, &binder, false, DUMP_FLAG_PRIORITY_DEFAULT)
}
pub(crate) fn add_lazy_service(
sm: &BpServiceManager,
identifier: &str,
binder: SIBinder,
) -> std::result::Result<(), Status> {
sm.addService(
identifier,
&binder,
false,
DUMP_FLAG_PRIORITY_DEFAULT | FLAG_IS_LAZY_SERVICE,
)
}
pub fn register_for_notifications(
sm: &BpServiceManager,
name: &str,
callback: &crate::Strong<dyn IServiceCallback>,
) -> Result<()> {
sm.registerForNotifications(name, callback)
.map_err(|e| e.into())
}
pub fn unregister_for_notifications(
sm: &BpServiceManager,
name: &str,
callback: &crate::Strong<dyn IServiceCallback>,
) -> Result<()> {
sm.unregisterForNotifications(name, callback)
.map_err(|e| e.into())
}
pub fn register_client_callback(
sm: &BpServiceManager,
name: &str,
service: &SIBinder,
callback: &crate::Strong<dyn IClientCallback>,
) -> Result<()> {
sm.registerClientCallback(name, service, callback)
.map_err(|e| e.into())
}
pub fn try_unregister_service(sm: &BpServiceManager, name: &str, service: &SIBinder) -> Result<()> {
sm.tryUnregisterService(name, service).map_err(|e| e.into())
}
pub fn get_declared_instances(sm: &BpServiceManager, iface: &str) -> Vec<String> {
match sm.getDeclaredInstances(iface) {
Ok(result) => result,
Err(err) => {
log::error!("Failed to get_declared_instances({iface}): {err}");
Vec::new()
}
}
}
pub fn get_connection_info(
sm: &BpServiceManager,
name: &str,
) -> Option<android::os::ConnectionInfo::ConnectionInfo> {
match sm.getConnectionInfo(name) {
Ok(result) => result,
Err(err) => {
log::error!("Failed to get_connection_info({name}): {err}");
None
}
}
}
pub fn is_declared(sm: &BpServiceManager, name: &str) -> bool {
match sm.isDeclared(name) {
Ok(result) => result,
Err(err) => {
log::error!("Failed to is_declared({name}): {err}");
false
}
}
}
pub fn get_interface<T: FromIBinder + ?Sized>(
sm: &BpServiceManager,
name: &str,
) -> Result<Strong<T>> {
match get_service(sm, name) {
Some(service) => match service.service {
Some(service) => FromIBinder::try_from(service),
None => {
log::error!("Service {name} is not a valid IBinder");
Err(StatusCode::NameNotFound)
}
},
None => {
log::error!("Failed to get interface {name}");
Err(StatusCode::NameNotFound)
}
}
}
pub fn get_service_debug_info(
sm: &BpServiceManager,
) -> Result<Vec<android::os::ServiceDebugInfo::ServiceDebugInfo>> {
sm.getServiceDebugInfo().map_err(|e| e.into())
}
#[cfg(all(test, feature = "rpc"))]
mod tests {
use super::*;
use crate::hub::accessor_register::{
add_accessor_provider, create_accessor, AccessorAddrProvider, AccessorProviderFn,
AccessorSockAddr,
};
use std::collections::HashSet;
use std::path::PathBuf;
fn synth_null_swm() -> android::os::Service::Service {
android::os::Service::Service::ServiceWithMetadata(
android::os::ServiceWithMetadata::ServiceWithMetadata {
service: None,
isLazyService: false,
},
)
}
fn register_dummy_provider(
instance: &str,
) -> crate::hub::accessor_register::AccessorProviderHandle {
let path = PathBuf::from(format!(
"/tmp/rsb-dispatch-typed-test-{}-unused.sock",
std::process::id()
));
let want = instance.to_owned();
let provider: AccessorProviderFn = Box::new(move |n: &str| {
if n == want {
let addr_provider: AccessorAddrProvider = Box::new({
let p = path.clone();
move |_| Ok(AccessorSockAddr::Unix(p.clone()))
});
Some(create_accessor(n, addr_provider))
} else {
None
}
});
add_accessor_provider(HashSet::from([instance.to_owned()]), provider).expect("registry add")
}
fn register_observing_provider(
instance: &str,
) -> (
super::super::accessor_register::AccessorProviderHandle,
std::sync::Arc<std::sync::atomic::AtomicBool>,
) {
use std::sync::atomic::AtomicBool;
let called = std::sync::Arc::new(AtomicBool::new(false));
let path = PathBuf::from(format!(
"/tmp/rsb-dispatch-typed-test-{}-unused.sock",
std::process::id()
));
let want = instance.to_owned();
let called_for_closure = std::sync::Arc::clone(&called);
let provider: AccessorProviderFn = Box::new(move |n: &str| {
called_for_closure.store(true, std::sync::atomic::Ordering::SeqCst);
if n == want {
let addr_provider: AccessorAddrProvider = Box::new({
let p = path.clone();
move |_| Ok(AccessorSockAddr::Unix(p.clone()))
});
Some(create_accessor(n, addr_provider))
} else {
None
}
});
let handle = add_accessor_provider(HashSet::from([instance.to_owned()]), provider)
.expect("registry add");
(handle, called)
}
#[test]
fn dispatch_falls_back_when_servicemanager_returns_null_service() {
let instance = format!(
"rsb.test.svcmgr16.swm-null.{}.{}",
std::process::id(),
line!()
);
let out = dispatch_typed_service(&instance, synth_null_swm())
.expect("ServiceWithMetadata is always Some, even when inner is None");
assert!(
out.service.is_none(),
"no provider registered ⇒ fallback returns the original null swm \
(so caller can still observe metadata), got {:?}",
out.service
);
let (_handle, called) = register_observing_provider(&instance);
let _out = dispatch_typed_service(&instance, synth_null_swm())
.expect("registered provider must yield a SWM");
assert!(
called.load(std::sync::atomic::Ordering::SeqCst),
"dispatch_typed_service(swm.service=None) must invoke try_process_local_fallback \
— a mutant that removes the fallback call leaves `called == false`"
);
}
#[test]
fn dispatch_falls_back_when_accessor_arm_binder_is_none() {
let instance = format!(
"rsb.test.svcmgr16.acc-null.{}.{}",
std::process::id(),
line!()
);
let null_accessor = android::os::Service::Service::Accessor(None);
assert!(
dispatch_typed_service(&instance, null_accessor).is_none(),
"Accessor(None) with no fallback provider must return None"
);
let (_handle, called) = register_observing_provider(&instance);
let null_accessor = android::os::Service::Service::Accessor(None);
let _ = dispatch_typed_service(&instance, null_accessor);
assert!(
called.load(std::sync::atomic::Ordering::SeqCst),
"dispatch_typed_service(Accessor(None)) must `or_else` into try_process_local_fallback \
— a mutant that removes the `or_else` leaves `called == false`"
);
}
#[test]
fn dispatch_returns_servicemanager_service_unchanged_when_non_null() {
let instance = format!(
"rsb.test.svcmgr16.swm-pass.{}.{}",
std::process::id(),
line!()
);
let _handle = register_dummy_provider(&instance);
let dummy: crate::SIBinder =
crate::Interface::as_binder(&crate::Binder::new(DummyDescriptor));
let want_desc = dummy.descriptor().to_string();
let swm = android::os::Service::Service::ServiceWithMetadata(
android::os::ServiceWithMetadata::ServiceWithMetadata {
service: Some(dummy),
isLazyService: true,
},
);
let out =
dispatch_typed_service(&instance, swm).expect("non-null inner must round-trip as-is");
assert!(
out.isLazyService,
"non-null SWM must round-trip unchanged (including metadata)"
);
let inner = out.service.expect("non-null inner preserved");
assert_eq!(
inner.descriptor(),
want_desc,
"the dispatcher must NOT swap the servicemanager-supplied binder \
for a process-local provider (mutant: drop the `is_some()` guard \
⇒ this assertion fails)"
);
}
struct DummyDescriptor;
impl crate::Interface for DummyDescriptor {}
impl crate::Remotable for DummyDescriptor {
fn descriptor() -> &'static str {
"rsb.test.svcmgr16.dummy"
}
fn on_transact(
&self,
_code: crate::TransactionCode,
_reader: &mut crate::Parcel,
_reply: &mut crate::Parcel,
) -> crate::error::Result<()> {
Err(crate::error::StatusCode::UnknownTransaction)
}
fn on_dump(&self, _w: &mut dyn std::io::Write, _a: &[String]) -> crate::error::Result<()> {
Ok(())
}
}
}