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