1use fission_core::{
2 BluetoothAdvertiseReceipt, BluetoothAdvertiseRequest, BluetoothAvailability,
3 BluetoothConnectRequest, BluetoothConnection, BluetoothDevice, BluetoothDisconnectRequest,
4 BluetoothError, BluetoothMode, BluetoothPermission, BluetoothPermissionRequest,
5 BluetoothReadRequest, BluetoothReadResult, BluetoothScanRequest, BluetoothScanResult,
6 BluetoothStopAdvertiseRequest, BluetoothWriteRequest, CONNECT_BLUETOOTH_DEVICE,
7 DISCONNECT_BLUETOOTH_DEVICE, GET_BLUETOOTH_AVAILABILITY, READ_BLUETOOTH_CHARACTERISTIC,
8 REQUEST_BLUETOOTH_PERMISSION, SCAN_BLUETOOTH_DEVICES, START_BLUETOOTH_ADVERTISING,
9 STOP_BLUETOOTH_ADVERTISING, WRITE_BLUETOOTH_CHARACTERISTIC,
10};
11use fission_shell::async_host::AsyncRegistry;
12use std::sync::Arc;
13
14pub trait BluetoothHost: Send + Sync + 'static {
16 fn availability(&self) -> Result<BluetoothAvailability, BluetoothError>;
18 fn request_permission(
20 &self,
21 request: BluetoothPermissionRequest,
22 ) -> Result<BluetoothPermission, BluetoothError>;
23 fn scan_devices(
25 &self,
26 request: BluetoothScanRequest,
27 ) -> Result<BluetoothScanResult, BluetoothError>;
28 fn connect_device(
30 &self,
31 request: BluetoothConnectRequest,
32 ) -> Result<BluetoothConnection, BluetoothError>;
33 fn disconnect_device(&self, request: BluetoothDisconnectRequest) -> Result<(), BluetoothError>;
35 fn read_characteristic(
37 &self,
38 request: BluetoothReadRequest,
39 ) -> Result<BluetoothReadResult, BluetoothError>;
40 fn write_characteristic(&self, request: BluetoothWriteRequest) -> Result<(), BluetoothError>;
42 fn start_advertising(
44 &self,
45 request: BluetoothAdvertiseRequest,
46 ) -> Result<BluetoothAdvertiseReceipt, BluetoothError>;
47 fn stop_advertising(
49 &self,
50 request: BluetoothStopAdvertiseRequest,
51 ) -> Result<(), BluetoothError>;
52}
53
54#[derive(Debug, Default)]
55pub struct UnsupportedBluetoothHost;
56
57impl BluetoothHost for UnsupportedBluetoothHost {
58 fn availability(&self) -> Result<BluetoothAvailability, BluetoothError> {
59 Ok(BluetoothAvailability {
60 permission: BluetoothPermission::Denied,
61 enabled: false,
62 supports_classic: false,
63 supports_low_energy: false,
64 })
65 }
66
67 fn request_permission(
68 &self,
69 _request: BluetoothPermissionRequest,
70 ) -> Result<BluetoothPermission, BluetoothError> {
71 Err(BluetoothError::unsupported("request_permission"))
72 }
73
74 fn scan_devices(
75 &self,
76 _request: BluetoothScanRequest,
77 ) -> Result<BluetoothScanResult, BluetoothError> {
78 Err(BluetoothError::unsupported("scan_devices"))
79 }
80
81 fn connect_device(
82 &self,
83 _request: BluetoothConnectRequest,
84 ) -> Result<BluetoothConnection, BluetoothError> {
85 Err(BluetoothError::unsupported("connect_device"))
86 }
87
88 fn disconnect_device(
89 &self,
90 _request: BluetoothDisconnectRequest,
91 ) -> Result<(), BluetoothError> {
92 Err(BluetoothError::unsupported("disconnect_device"))
93 }
94
95 fn read_characteristic(
96 &self,
97 _request: BluetoothReadRequest,
98 ) -> Result<BluetoothReadResult, BluetoothError> {
99 Err(BluetoothError::unsupported("read_characteristic"))
100 }
101
102 fn write_characteristic(&self, _request: BluetoothWriteRequest) -> Result<(), BluetoothError> {
103 Err(BluetoothError::unsupported("write_characteristic"))
104 }
105
106 fn start_advertising(
107 &self,
108 _request: BluetoothAdvertiseRequest,
109 ) -> Result<BluetoothAdvertiseReceipt, BluetoothError> {
110 Err(BluetoothError::unsupported("start_advertising"))
111 }
112
113 fn stop_advertising(
114 &self,
115 _request: BluetoothStopAdvertiseRequest,
116 ) -> Result<(), BluetoothError> {
117 Err(BluetoothError::unsupported("stop_advertising"))
118 }
119}
120
121#[derive(Debug, Clone)]
122pub struct MemoryBluetoothHost {
123 availability: BluetoothAvailability,
124 devices: Vec<BluetoothDevice>,
125 read_result: BluetoothReadResult,
126}
127
128impl MemoryBluetoothHost {
129 pub fn new(
130 availability: BluetoothAvailability,
131 devices: Vec<BluetoothDevice>,
132 read_result: BluetoothReadResult,
133 ) -> Self {
134 Self {
135 availability,
136 devices,
137 read_result,
138 }
139 }
140}
141
142impl Default for MemoryBluetoothHost {
143 fn default() -> Self {
144 let device = BluetoothDevice {
145 id: "memory-bluetooth".into(),
146 name: Some("Memory Bluetooth".into()),
147 address: Some("00:11:22:33:44:55".into()),
148 rssi: Some(-42),
149 paired: true,
150 modes: vec![BluetoothMode::Classic, BluetoothMode::LowEnergy],
151 };
152 Self::new(
153 BluetoothAvailability {
154 permission: BluetoothPermission::Granted,
155 enabled: true,
156 supports_classic: true,
157 supports_low_energy: true,
158 },
159 vec![device],
160 BluetoothReadResult {
161 value: b"fission".to_vec(),
162 },
163 )
164 }
165}
166
167impl BluetoothHost for MemoryBluetoothHost {
168 fn availability(&self) -> Result<BluetoothAvailability, BluetoothError> {
169 Ok(self.availability.clone())
170 }
171
172 fn request_permission(
173 &self,
174 _request: BluetoothPermissionRequest,
175 ) -> Result<BluetoothPermission, BluetoothError> {
176 Ok(self.availability.permission)
177 }
178
179 fn scan_devices(
180 &self,
181 _request: BluetoothScanRequest,
182 ) -> Result<BluetoothScanResult, BluetoothError> {
183 Ok(BluetoothScanResult {
184 devices: self.devices.clone(),
185 })
186 }
187
188 fn connect_device(
189 &self,
190 request: BluetoothConnectRequest,
191 ) -> Result<BluetoothConnection, BluetoothError> {
192 let device = self
193 .devices
194 .iter()
195 .find(|device| device.id == request.device_id)
196 .cloned()
197 .ok_or_else(|| BluetoothError::new("not_found", "Bluetooth device not found"))?;
198 Ok(BluetoothConnection {
199 connection_id: format!("memory:{}", device.id),
200 device,
201 })
202 }
203
204 fn disconnect_device(
205 &self,
206 _request: BluetoothDisconnectRequest,
207 ) -> Result<(), BluetoothError> {
208 Ok(())
209 }
210
211 fn read_characteristic(
212 &self,
213 _request: BluetoothReadRequest,
214 ) -> Result<BluetoothReadResult, BluetoothError> {
215 Ok(self.read_result.clone())
216 }
217
218 fn write_characteristic(&self, _request: BluetoothWriteRequest) -> Result<(), BluetoothError> {
219 Ok(())
220 }
221
222 fn start_advertising(
223 &self,
224 _request: BluetoothAdvertiseRequest,
225 ) -> Result<BluetoothAdvertiseReceipt, BluetoothError> {
226 Ok(BluetoothAdvertiseReceipt {
227 advertisement_id: "memory-advertisement".into(),
228 })
229 }
230
231 fn stop_advertising(
232 &self,
233 _request: BluetoothStopAdvertiseRequest,
234 ) -> Result<(), BluetoothError> {
235 Ok(())
236 }
237}
238
239pub(crate) fn register_bluetooth_capabilities(
240 async_registry: &mut AsyncRegistry,
241 host: Arc<dyn BluetoothHost>,
242) {
243 let availability_host = host.clone();
244 async_registry.register_operation_capability(GET_BLUETOOTH_AVAILABILITY, move |(), _| {
245 let host = availability_host.clone();
246 async move { host.availability() }
247 });
248
249 let permission_host = host.clone();
250 async_registry.register_operation_capability(
251 REQUEST_BLUETOOTH_PERMISSION,
252 move |request, _| {
253 let host = permission_host.clone();
254 async move { host.request_permission(request) }
255 },
256 );
257
258 let scan_host = host.clone();
259 async_registry.register_operation_capability(SCAN_BLUETOOTH_DEVICES, move |request, _| {
260 let host = scan_host.clone();
261 async move { host.scan_devices(request) }
262 });
263
264 let connect_host = host.clone();
265 async_registry.register_operation_capability(CONNECT_BLUETOOTH_DEVICE, move |request, _| {
266 let host = connect_host.clone();
267 async move { host.connect_device(request) }
268 });
269
270 let disconnect_host = host.clone();
271 async_registry.register_operation_capability(DISCONNECT_BLUETOOTH_DEVICE, move |request, _| {
272 let host = disconnect_host.clone();
273 async move { host.disconnect_device(request) }
274 });
275
276 let read_host = host.clone();
277 async_registry.register_operation_capability(
278 READ_BLUETOOTH_CHARACTERISTIC,
279 move |request, _| {
280 let host = read_host.clone();
281 async move { host.read_characteristic(request) }
282 },
283 );
284
285 let write_host = host.clone();
286 async_registry.register_operation_capability(
287 WRITE_BLUETOOTH_CHARACTERISTIC,
288 move |request, _| {
289 let host = write_host.clone();
290 async move { host.write_characteristic(request) }
291 },
292 );
293
294 let advertise_host = host.clone();
295 async_registry.register_operation_capability(START_BLUETOOTH_ADVERTISING, move |request, _| {
296 let host = advertise_host.clone();
297 async move { host.start_advertising(request) }
298 });
299
300 async_registry.register_operation_capability(STOP_BLUETOOTH_ADVERTISING, move |request, _| {
301 let host = host.clone();
302 async move { host.stop_advertising(request) }
303 });
304}
305
306#[cfg(test)]
307mod tests {
308 use super::*;
309
310 #[test]
311 fn unsupported_host_reports_errors() {
312 let host = UnsupportedBluetoothHost;
313 assert!(host.scan_devices(BluetoothScanRequest::default()).is_err());
314 }
315
316 #[test]
317 fn memory_host_scans_connects_and_reads() {
318 let host = MemoryBluetoothHost::default();
319 let scan = host.scan_devices(BluetoothScanRequest::default()).unwrap();
320 assert_eq!(scan.devices.len(), 1);
321
322 let connection = host
323 .connect_device(BluetoothConnectRequest {
324 device_id: scan.devices[0].id.clone(),
325 service_uuids: Vec::new(),
326 })
327 .unwrap();
328 assert_eq!(connection.device.id, "memory-bluetooth");
329
330 let read = host
331 .read_characteristic(BluetoothReadRequest::default())
332 .unwrap();
333 assert_eq!(read.value, b"fission".to_vec());
334 }
335}