/* * 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::os::fd::RawFd; use std::os::raw::{c_int, c_void}; 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 connections. pub fn set_max_outgoing_connections(&self, connections: usize) { // SAFETY: Only passes the 'self' pointer as an opaque handle. unsafe { binder_rpc_unstable_bindgen::ARpcSession_setMaxOutgoingConnections( self.as_ptr(), connections, ) }; } /// Connects to an RPC Binder server over vsock for a particular interface. #[cfg(not(target_os = "trusty"))] pub fn setup_vsock_client( &self, cid: u32, port: u32, ) -> Result, 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. #[cfg(not(target_os = "trusty"))] pub fn setup_unix_domain_client( &self, socket_name: &str, ) -> Result, StatusCode> { let socket_name = match std::ffi::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. #[cfg(not(target_os = "trusty"))] pub fn setup_unix_domain_bootstrap_client( &self, bootstrap_fd: std::os::fd::BorrowedFd, ) -> Result, StatusCode> { use std::os::fd::AsRawFd; // 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. #[cfg(not(target_os = "trusty"))] pub fn setup_inet_client( &self, address: &str, port: u32, ) -> Result, StatusCode> { let address = match std::ffi::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) } #[cfg(target_os = "trusty")] pub fn setup_trusty_client( &self, port: &std::ffi::CStr, ) -> Result, StatusCode> { self.setup_preconnected_client(|| { let h = tipc::Handle::connect(port) .expect("Failed to connect to service port {SERVICE_PORT}"); // Do not close the handle at the end of the scope let fd = h.as_raw_fd(); core::mem::forget(h); Some(fd) }) } /// 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( &self, mut request_fd: impl FnMut() -> Option, ) -> Result, 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( service: Option, ) -> Result, StatusCode> { if let Some(service) = service { FromIBinder::try_from(service) } else { Err(StatusCode::NAME_NOT_FOUND) } } } type RequestFd<'a> = &'a mut dyn FnMut() -> Option; unsafe extern "C" fn request_fd_wrapper(param: *mut c_void) -> c_int { let request_fd_ptr = param as *mut RequestFd; // 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 = unsafe { request_fd_ptr.as_mut().unwrap() }; request_fd().unwrap_or(-1) }