rpcbinder/
session.rs

1/*
2 * Copyright (C) 2022 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17use binder::unstable_api::new_spibinder;
18use binder::{FromIBinder, SpIBinder, StatusCode, Strong};
19use foreign_types::{foreign_type, ForeignType, ForeignTypeRef};
20use std::os::fd::RawFd;
21use std::os::raw::{c_int, c_void};
22
23pub use binder_rpc_unstable_bindgen::ARpcSession_FileDescriptorTransportMode as FileDescriptorTransportMode;
24
25foreign_type! {
26    type CType = binder_rpc_unstable_bindgen::ARpcSession;
27    fn drop = binder_rpc_unstable_bindgen::ARpcSession_free;
28
29    /// A type that represents a foreign instance of RpcSession.
30    #[derive(Debug)]
31    pub struct RpcSession;
32    /// A borrowed RpcSession.
33    pub struct RpcSessionRef;
34}
35
36/// SAFETY: The opaque handle can be cloned freely.
37unsafe impl Send for RpcSession {}
38/// SAFETY: The underlying C++ RpcSession class is thread-safe.
39unsafe impl Sync for RpcSession {}
40
41impl RpcSession {
42    /// Allocates a new RpcSession object.
43    pub fn new() -> RpcSession {
44        // SAFETY: Takes ownership of the returned handle, which has correct refcount.
45        unsafe { RpcSession::from_ptr(binder_rpc_unstable_bindgen::ARpcSession_new()) }
46    }
47}
48
49impl Default for RpcSession {
50    fn default() -> Self {
51        Self::new()
52    }
53}
54
55impl RpcSessionRef {
56    /// Sets the file descriptor transport mode for this session.
57    pub fn set_file_descriptor_transport_mode(&self, mode: FileDescriptorTransportMode) {
58        // SAFETY: Only passes the 'self' pointer as an opaque handle.
59        unsafe {
60            binder_rpc_unstable_bindgen::ARpcSession_setFileDescriptorTransportMode(
61                self.as_ptr(),
62                mode,
63            )
64        };
65    }
66
67    /// Sets the maximum number of incoming threads.
68    pub fn set_max_incoming_threads(&self, threads: usize) {
69        // SAFETY: Only passes the 'self' pointer as an opaque handle.
70        unsafe {
71            binder_rpc_unstable_bindgen::ARpcSession_setMaxIncomingThreads(self.as_ptr(), threads)
72        };
73    }
74
75    /// Sets the maximum number of outgoing connections.
76    pub fn set_max_outgoing_connections(&self, connections: usize) {
77        // SAFETY: Only passes the 'self' pointer as an opaque handle.
78        unsafe {
79            binder_rpc_unstable_bindgen::ARpcSession_setMaxOutgoingConnections(
80                self.as_ptr(),
81                connections,
82            )
83        };
84    }
85
86    /// Connects to an RPC Binder server over vsock for a particular interface.
87    #[cfg(not(target_os = "trusty"))]
88    pub fn setup_vsock_client<T: FromIBinder + ?Sized>(
89        &self,
90        cid: u32,
91        port: u32,
92    ) -> Result<Strong<T>, StatusCode> {
93        // SAFETY: AIBinder returned by ARpcSession_setupVsockClient has correct
94        // reference count, and the ownership can safely be taken by new_spibinder.
95        let service = unsafe {
96            new_spibinder(binder_rpc_unstable_bindgen::ARpcSession_setupVsockClient(
97                self.as_ptr(),
98                cid,
99                port,
100            ))
101        };
102        Self::get_interface(service)
103    }
104
105    /// Connects to an RPC Binder server over a names Unix Domain Socket for
106    /// a particular interface.
107    #[cfg(not(target_os = "trusty"))]
108    pub fn setup_unix_domain_client<T: FromIBinder + ?Sized>(
109        &self,
110        socket_name: &str,
111    ) -> Result<Strong<T>, StatusCode> {
112        let socket_name = match std::ffi::CString::new(socket_name) {
113            Ok(s) => s,
114            Err(e) => {
115                log::error!("Cannot convert {} to CString. Error: {:?}", socket_name, e);
116                return Err(StatusCode::NAME_NOT_FOUND);
117            }
118        };
119
120        // SAFETY: AIBinder returned by ARpcSession_setupUnixDomainClient has correct
121        // reference count, and the ownership can safely be taken by new_spibinder.
122        let service = unsafe {
123            new_spibinder(binder_rpc_unstable_bindgen::ARpcSession_setupUnixDomainClient(
124                self.as_ptr(),
125                socket_name.as_ptr(),
126            ))
127        };
128        Self::get_interface(service)
129    }
130
131    /// Connects to an RPC Binder server over a bootstrap Unix Domain Socket
132    /// for a particular interface.
133    #[cfg(not(target_os = "trusty"))]
134    pub fn setup_unix_domain_bootstrap_client<T: FromIBinder + ?Sized>(
135        &self,
136        bootstrap_fd: std::os::fd::BorrowedFd,
137    ) -> Result<Strong<T>, StatusCode> {
138        use std::os::fd::AsRawFd;
139        // SAFETY: ARpcSession_setupUnixDomainBootstrapClient does not take
140        // ownership of bootstrap_fd. The returned AIBinder has correct
141        // reference count, and the ownership can safely be taken by new_spibinder.
142        let service = unsafe {
143            new_spibinder(binder_rpc_unstable_bindgen::ARpcSession_setupUnixDomainBootstrapClient(
144                self.as_ptr(),
145                bootstrap_fd.as_raw_fd(),
146            ))
147        };
148        Self::get_interface(service)
149    }
150
151    /// Connects to an RPC Binder server over inet socket at the given address and port.
152    #[cfg(not(target_os = "trusty"))]
153    pub fn setup_inet_client<T: FromIBinder + ?Sized>(
154        &self,
155        address: &str,
156        port: u32,
157    ) -> Result<Strong<T>, StatusCode> {
158        let address = match std::ffi::CString::new(address) {
159            Ok(s) => s,
160            Err(e) => {
161                log::error!("Cannot convert {} to CString. Error: {:?}", address, e);
162                return Err(StatusCode::BAD_VALUE);
163            }
164        };
165
166        // SAFETY: AIBinder returned by ARpcSession_setupInet has correct reference
167        // count, and the ownership can safely be taken by new_spibinder.
168        let service = unsafe {
169            new_spibinder(binder_rpc_unstable_bindgen::ARpcSession_setupInet(
170                self.as_ptr(),
171                address.as_ptr(),
172                port,
173            ))
174        };
175        Self::get_interface(service)
176    }
177
178    #[cfg(target_os = "trusty")]
179    pub fn setup_trusty_client<T: FromIBinder + ?Sized>(
180        &self,
181        port: &std::ffi::CStr,
182    ) -> Result<Strong<T>, StatusCode> {
183        self.setup_preconnected_client(|| {
184            let h = tipc::Handle::connect(port)
185                .expect("Failed to connect to service port {SERVICE_PORT}");
186
187            // Do not close the handle at the end of the scope
188            let fd = h.as_raw_fd();
189            core::mem::forget(h);
190            Some(fd)
191        })
192    }
193
194    /// Connects to an RPC Binder server, using the given callback to get (and
195    /// take ownership of) file descriptors already connected to it.
196    pub fn setup_preconnected_client<T: FromIBinder + ?Sized>(
197        &self,
198        mut request_fd: impl FnMut() -> Option<RawFd>,
199    ) -> Result<Strong<T>, StatusCode> {
200        // Double reference the factory because trait objects aren't FFI safe.
201        let mut request_fd_ref: RequestFd = &mut request_fd;
202        let param = &mut request_fd_ref as *mut RequestFd as *mut c_void;
203
204        // SAFETY: AIBinder returned by RpcPreconnectedClient has correct reference count, and the
205        // ownership can be safely taken by new_spibinder. RpcPreconnectedClient does not take ownership
206        // of param, only passing it to request_fd_wrapper.
207        let service = unsafe {
208            new_spibinder(binder_rpc_unstable_bindgen::ARpcSession_setupPreconnectedClient(
209                self.as_ptr(),
210                Some(request_fd_wrapper),
211                param,
212            ))
213        };
214        Self::get_interface(service)
215    }
216
217    fn get_interface<T: FromIBinder + ?Sized>(
218        service: Option<SpIBinder>,
219    ) -> Result<Strong<T>, StatusCode> {
220        if let Some(service) = service {
221            FromIBinder::try_from(service)
222        } else {
223            Err(StatusCode::NAME_NOT_FOUND)
224        }
225    }
226}
227
228type RequestFd<'a> = &'a mut dyn FnMut() -> Option<RawFd>;
229
230unsafe extern "C" fn request_fd_wrapper(param: *mut c_void) -> c_int {
231    let request_fd_ptr = param as *mut RequestFd;
232    // SAFETY: This is only ever called by RpcPreconnectedClient, within the lifetime of the
233    // BinderFdFactory reference, with param being a properly aligned non-null pointer to an
234    // initialized instance.
235    let request_fd = unsafe { request_fd_ptr.as_mut().unwrap() };
236    request_fd().unwrap_or(-1)
237}