1 //! FFI interfaces for the Connection module.
2 
3 use std::{fmt::Debug, pin::Pin};
4 
5 use bt_common::init_flags;
6 use cxx::UniquePtr;
7 pub use inner::*;
8 use log::warn;
9 use tokio::{
10     sync::mpsc::{unbounded_channel, UnboundedSender},
11     task::spawn_local,
12 };
13 
14 use crate::do_in_rust_thread;
15 
16 use super::{
17     attempt_manager::ConnectionMode,
18     le_manager::{ErrorCode, InactiveLeAclManager, LeAclManager, LeAclManagerConnectionCallbacks},
19     ConnectionManagerClient, LeConnection,
20 };
21 
22 // SAFETY: `LeAclManagerShim` can be passed between threads.
23 unsafe impl Send for LeAclManagerShim {}
24 
25 #[cxx::bridge]
26 #[allow(clippy::needless_lifetimes)]
27 #[allow(clippy::too_many_arguments)]
28 #[allow(missing_docs)]
29 #[allow(unsafe_op_in_unsafe_fn)]
30 mod inner {
31     impl UniquePtr<LeAclManagerShim> {}
32 
33     #[namespace = "bluetooth::core"]
34     extern "C++" {
35         type AddressWithType = crate::core::address::AddressWithType;
36     }
37 
38     #[namespace = "bluetooth::connection"]
39     unsafe extern "C++" {
40         include!("src/connection/ffi/connection_shim.h");
41 
42         /// This lets us send HCI commands, either directly,
43         /// or via the address manager
44         type LeAclManagerShim;
45 
46         /// Add address to direct/background connect list, if not already connected
47         /// If connected, then adding to direct list is a no-op, but adding to the
48         /// background list will still take place.
49         #[cxx_name = "CreateLeConnection"]
create_le_connection(&self, address: AddressWithType, is_direct: bool)50         fn create_le_connection(&self, address: AddressWithType, is_direct: bool);
51 
52         /// Remove address from both direct + background connect lists
53         #[cxx_name = "CancelLeConnect"]
cancel_le_connect(&self, address: AddressWithType)54         fn cancel_le_connect(&self, address: AddressWithType);
55 
56         /// Register Rust callbacks for connection events
57         ///
58         /// # Safety
59         /// `callbacks` must be Send + Sync, since C++ moves it to a different thread and
60         /// invokes it from several others (GD + legacy threads).
61         #[cxx_name = "RegisterRustCallbacks"]
unchecked_register_rust_callbacks( self: Pin<&mut Self>, callbacks: Box<LeAclManagerCallbackShim>, )62         unsafe fn unchecked_register_rust_callbacks(
63             self: Pin<&mut Self>,
64             callbacks: Box<LeAclManagerCallbackShim>,
65         );
66     }
67 
68     #[namespace = "bluetooth::connection"]
69     extern "Rust" {
70         type LeAclManagerCallbackShim;
71         #[cxx_name = "OnLeConnectSuccess"]
on_le_connect_success(&self, address: AddressWithType)72         fn on_le_connect_success(&self, address: AddressWithType);
73         #[cxx_name = "OnLeConnectFail"]
on_le_connect_fail(&self, address: AddressWithType, status: u8)74         fn on_le_connect_fail(&self, address: AddressWithType, status: u8);
75         #[cxx_name = "OnLeDisconnection"]
on_disconnect(&self, address: AddressWithType)76         fn on_disconnect(&self, address: AddressWithType);
77     }
78 
79     #[namespace = "bluetooth::connection"]
80     unsafe extern "C++" {
81         include!("stack/arbiter/acl_arbiter.h");
82 
83         /// Register APIs exposed by Rust
RegisterRustApis( start_direct_connection: fn(client_id: u8, address: AddressWithType), stop_direct_connection: fn(client_id: u8, address: AddressWithType), add_background_connection: fn(client_id: u8, address: AddressWithType), remove_background_connection: fn(client_id: u8, address: AddressWithType), remove_client: fn(client_id: u8), stop_all_connections_to_device: fn(address: AddressWithType), )84         fn RegisterRustApis(
85             start_direct_connection: fn(client_id: u8, address: AddressWithType),
86             stop_direct_connection: fn(client_id: u8, address: AddressWithType),
87             add_background_connection: fn(client_id: u8, address: AddressWithType),
88             remove_background_connection: fn(client_id: u8, address: AddressWithType),
89             remove_client: fn(client_id: u8),
90             stop_all_connections_to_device: fn(address: AddressWithType),
91         );
92     }
93 }
94 
95 impl LeAclManagerShim {
register_rust_callbacks( self: Pin<&mut LeAclManagerShim>, callbacks: Box<LeAclManagerCallbackShim>, ) where Box<LeAclManagerCallbackShim>: Send + Sync,96     fn register_rust_callbacks(
97         self: Pin<&mut LeAclManagerShim>,
98         callbacks: Box<LeAclManagerCallbackShim>,
99     ) where
100         Box<LeAclManagerCallbackShim>: Send + Sync,
101     {
102         // SAFETY: The requirements of this method are enforced
103         // by our own trait bounds.
104         unsafe {
105             self.unchecked_register_rust_callbacks(callbacks);
106         }
107     }
108 }
109 
110 /// Implementation of HciConnectProxy wrapping the corresponding C++ methods
111 pub struct LeAclManagerImpl(pub UniquePtr<LeAclManagerShim>);
112 
113 pub struct LeAclManagerCallbackShim(
114     UnboundedSender<Box<dyn FnOnce(&dyn LeAclManagerConnectionCallbacks) + Send>>,
115 );
116 
117 impl LeAclManagerCallbackShim {
on_le_connect_success(&self, address: AddressWithType)118     fn on_le_connect_success(&self, address: AddressWithType) {
119         let _ = self.0.send(Box::new(move |callback| {
120             callback.on_le_connect(address, Ok(LeConnection { remote_address: address }))
121         }));
122     }
123 
on_le_connect_fail(&self, address: AddressWithType, status: u8)124     fn on_le_connect_fail(&self, address: AddressWithType, status: u8) {
125         let _ = self.0.send(Box::new(move |callback| {
126             callback.on_le_connect(address, Err(ErrorCode(status)))
127         }));
128     }
129 
on_disconnect(&self, address: AddressWithType)130     fn on_disconnect(&self, address: AddressWithType) {
131         let _ = self.0.send(Box::new(move |callback| {
132             callback.on_disconnect(address);
133         }));
134     }
135 }
136 
137 impl InactiveLeAclManager for LeAclManagerImpl {
138     type ActiveManager = Self;
139 
register_callbacks( mut self, callbacks: impl LeAclManagerConnectionCallbacks + 'static, ) -> Self::ActiveManager140     fn register_callbacks(
141         mut self,
142         callbacks: impl LeAclManagerConnectionCallbacks + 'static,
143     ) -> Self::ActiveManager {
144         let (tx, mut rx) = unbounded_channel();
145 
146         // only register callbacks if the feature is enabled
147         if init_flags::use_unified_connection_manager_is_enabled() {
148             self.0.pin_mut().register_rust_callbacks(Box::new(LeAclManagerCallbackShim(tx)));
149         }
150 
151         spawn_local(async move {
152             while let Some(f) = rx.recv().await {
153                 f(&callbacks)
154             }
155         });
156         self
157     }
158 }
159 
160 impl LeAclManager for LeAclManagerImpl {
add_to_direct_list(&self, address: AddressWithType)161     fn add_to_direct_list(&self, address: AddressWithType) {
162         self.0.create_le_connection(address, /* is_direct= */ true)
163     }
164 
add_to_background_list(&self, address: AddressWithType)165     fn add_to_background_list(&self, address: AddressWithType) {
166         self.0.create_le_connection(address, /* is_direct= */ false)
167     }
168 
remove_from_all_lists(&self, address: AddressWithType)169     fn remove_from_all_lists(&self, address: AddressWithType) {
170         self.0.cancel_le_connect(address)
171     }
172 }
173 
174 impl Debug for LeAclManagerImpl {
fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result175     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
176         f.debug_tuple("LeAclManagerImpl").finish()
177     }
178 }
179 
180 /// Registers all connection-manager callbacks into C++ dependencies
register_callbacks()181 pub fn register_callbacks() {
182     RegisterRustApis(
183         |client, address| {
184             let client = ConnectionManagerClient::GattClient(client);
185             do_in_rust_thread(move |modules| {
186                 let result =
187                     modules.connection_manager.as_ref().start_direct_connection(client, address);
188                 if let Err(err) = result {
189                     warn!("Failed to start direct connection from {client:?} to {address:?} ({err:?})")
190                 }
191             });
192         },
193         |client, address| {
194             let client = ConnectionManagerClient::GattClient(client);
195             do_in_rust_thread(move |modules| {
196                 let result = modules.connection_manager.cancel_connection(
197                     client,
198                     address,
199                     ConnectionMode::Direct,
200                 );
201                 if let Err(err) = result {
202                     warn!("Failed to cancel direct connection from {client:?} to {address:?} ({err:?})")
203                 }
204             })
205         },
206         |client, address| {
207             let client = ConnectionManagerClient::GattClient(client);
208             do_in_rust_thread(move |modules| {
209                 let result = modules.connection_manager.add_background_connection(client, address);
210                 if let Err(err) = result {
211                     warn!("Failed to add background connection from {client:?} to {address:?} ({err:?})")
212                 }
213             })
214         },
215         |client, address| {
216             let client = ConnectionManagerClient::GattClient(client);
217             do_in_rust_thread(move |modules| {
218                 let result = modules.connection_manager.cancel_connection(
219                     client,
220                     address,
221                     ConnectionMode::Background,
222                 );
223                 if let Err(err) = result {
224                     warn!("Failed to remove background connection from {client:?} to {address:?} ({err:?})")
225                 }
226             })
227         },
228         |client| {
229             let client = ConnectionManagerClient::GattClient(client);
230             do_in_rust_thread(move |modules| {
231                 modules.connection_manager.remove_client(client);
232             })
233         },
234         |address| {
235             do_in_rust_thread(move |modules| {
236                 modules.connection_manager.cancel_unconditionally(address);
237             })
238         },
239     )
240 }
241