blob: 0b517cf613f738081def9662dbd50d9db99d7879 [file] [log] [blame]
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
use binder::unstable_api::new_spibinder;
use binder::{FromIBinder, SpIBinder, StatusCode, Strong};
use foreign_types::{foreign_type, ForeignType, ForeignTypeRef};
use std::ffi::CString;
use std::os::{
raw::{c_int, c_void},
unix::io::{AsRawFd, BorrowedFd, RawFd},
};
pub use binder_rpc_unstable_bindgen::ARpcSession_FileDescriptorTransportMode as FileDescriptorTransportMode;
foreign_type! {
type CType = binder_rpc_unstable_bindgen::ARpcSession;
fn drop = binder_rpc_unstable_bindgen::ARpcSession_free;
/// A type that represents a foreign instance of RpcSession.
#[derive(Debug)]
pub struct RpcSession;
/// A borrowed RpcSession.
pub struct RpcSessionRef;
}
/// SAFETY - The opaque handle can be cloned freely.
unsafe impl Send for RpcSession {}
/// SAFETY - The underlying C++ RpcSession class is thread-safe.
unsafe impl Sync for RpcSession {}
impl RpcSession {
/// Allocates a new RpcSession object.
pub fn new() -> RpcSession {
// SAFETY - Takes ownership of the returned handle, which has correct refcount.
unsafe { RpcSession::from_ptr(binder_rpc_unstable_bindgen::ARpcSession_new()) }
}
}
impl Default for RpcSession {
fn default() -> Self {
Self::new()
}
}
impl RpcSessionRef {
/// Sets the file descriptor transport mode for this session.
pub fn set_file_descriptor_transport_mode(&self, mode: FileDescriptorTransportMode) {
// SAFETY - Only passes the 'self' pointer as an opaque handle.
unsafe {
binder_rpc_unstable_bindgen::ARpcSession_setFileDescriptorTransportMode(
self.as_ptr(),
mode,
)
};
}
/// Sets the maximum number of incoming threads.
pub fn set_max_incoming_threads(&self, threads: usize) {
// SAFETY - Only passes the 'self' pointer as an opaque handle.
unsafe {
binder_rpc_unstable_bindgen::ARpcSession_setMaxIncomingThreads(self.as_ptr(), threads)
};
}
/// Sets the maximum number of outgoing threads.
pub fn set_max_outgoing_threads(&self, threads: usize) {
// SAFETY - Only passes the 'self' pointer as an opaque handle.
unsafe {
binder_rpc_unstable_bindgen::ARpcSession_setMaxOutgoingThreads(self.as_ptr(), threads)
};
}
/// Connects to an RPC Binder server over vsock for a particular interface.
pub fn setup_vsock_client<T: FromIBinder + ?Sized>(
&self,
cid: u32,
port: u32,
) -> Result<Strong<T>, StatusCode> {
// SAFETY: AIBinder returned by ARpcSession_setupVsockClient has correct
// reference count, and the ownership can safely be taken by new_spibinder.
let service = unsafe {
new_spibinder(binder_rpc_unstable_bindgen::ARpcSession_setupVsockClient(
self.as_ptr(),
cid,
port,
))
};
Self::get_interface(service)
}
/// Connects to an RPC Binder server over a names Unix Domain Socket for
/// a particular interface.
pub fn setup_unix_domain_client<T: FromIBinder + ?Sized>(
&self,
socket_name: &str,
) -> Result<Strong<T>, StatusCode> {
let socket_name = match CString::new(socket_name) {
Ok(s) => s,
Err(e) => {
log::error!("Cannot convert {} to CString. Error: {:?}", socket_name, e);
return Err(StatusCode::NAME_NOT_FOUND);
}
};
// SAFETY: AIBinder returned by ARpcSession_setupUnixDomainClient has correct
// reference count, and the ownership can safely be taken by new_spibinder.
let service = unsafe {
new_spibinder(binder_rpc_unstable_bindgen::ARpcSession_setupUnixDomainClient(
self.as_ptr(),
socket_name.as_ptr(),
))
};
Self::get_interface(service)
}
/// Connects to an RPC Binder server over a bootstrap Unix Domain Socket
/// for a particular interface.
pub fn setup_unix_domain_bootstrap_client<T: FromIBinder + ?Sized>(
&self,
bootstrap_fd: BorrowedFd,
) -> Result<Strong<T>, StatusCode> {
// SAFETY: ARpcSession_setupUnixDomainBootstrapClient does not take
// ownership of bootstrap_fd. The returned AIBinder has correct
// reference count, and the ownership can safely be taken by new_spibinder.
let service = unsafe {
new_spibinder(binder_rpc_unstable_bindgen::ARpcSession_setupUnixDomainBootstrapClient(
self.as_ptr(),
bootstrap_fd.as_raw_fd(),
))
};
Self::get_interface(service)
}
/// Connects to an RPC Binder server over inet socket at the given address and port.
pub fn setup_inet_client<T: FromIBinder + ?Sized>(
&self,
address: &str,
port: u32,
) -> Result<Strong<T>, StatusCode> {
let address = match CString::new(address) {
Ok(s) => s,
Err(e) => {
log::error!("Cannot convert {} to CString. Error: {:?}", address, e);
return Err(StatusCode::BAD_VALUE);
}
};
// SAFETY: AIBinder returned by ARpcSession_setupInet has correct reference
// count, and the ownership can safely be taken by new_spibinder.
let service = unsafe {
new_spibinder(binder_rpc_unstable_bindgen::ARpcSession_setupInet(
self.as_ptr(),
address.as_ptr(),
port,
))
};
Self::get_interface(service)
}
/// Connects to an RPC Binder server, using the given callback to get (and
/// take ownership of) file descriptors already connected to it.
pub fn setup_preconnected_client<T: FromIBinder + ?Sized>(
&self,
mut request_fd: impl FnMut() -> Option<RawFd>,
) -> Result<Strong<T>, StatusCode> {
// Double reference the factory because trait objects aren't FFI safe.
let mut request_fd_ref: RequestFd = &mut request_fd;
let param = &mut request_fd_ref as *mut RequestFd as *mut c_void;
// SAFETY: AIBinder returned by RpcPreconnectedClient has correct reference count, and the
// ownership can be safely taken by new_spibinder. RpcPreconnectedClient does not take ownership
// of param, only passing it to request_fd_wrapper.
let service = unsafe {
new_spibinder(binder_rpc_unstable_bindgen::ARpcSession_setupPreconnectedClient(
self.as_ptr(),
Some(request_fd_wrapper),
param,
))
};
Self::get_interface(service)
}
fn get_interface<T: FromIBinder + ?Sized>(
service: Option<SpIBinder>,
) -> Result<Strong<T>, StatusCode> {
if let Some(service) = service {
FromIBinder::try_from(service)
} else {
Err(StatusCode::NAME_NOT_FOUND)
}
}
}
type RequestFd<'a> = &'a mut dyn FnMut() -> Option<RawFd>;
unsafe extern "C" fn request_fd_wrapper(param: *mut c_void) -> c_int {
// SAFETY: This is only ever called by RpcPreconnectedClient, within the lifetime of the
// BinderFdFactory reference, with param being a properly aligned non-null pointer to an
// initialized instance.
let request_fd_ptr = param as *mut RequestFd;
let request_fd = request_fd_ptr.as_mut().unwrap();
request_fd().unwrap_or(-1)
}