Skip to main content

servo_bluetooth/
lib.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5pub mod adapter;
6pub mod bluetooth;
7pub mod test;
8
9use std::borrow::ToOwned;
10use std::collections::{HashMap, HashSet};
11use std::string::String;
12use std::thread;
13use std::time::Duration;
14
15use bitflags::bitflags;
16use embedder_traits::{BluetoothDeviceDescription, EmbedderMsg, EmbedderProxy};
17use log::warn;
18use rand::{self, RngExt};
19#[cfg(not(feature = "native-bluetooth"))]
20use servo_base::generic_channel::GenericReceiver;
21use servo_base::generic_channel::{self, GenericSender};
22use servo_base::id::WebViewId;
23use servo_bluetooth_traits::blocklist::{Blocklist, uuid_is_blocklisted};
24use servo_bluetooth_traits::scanfilter::{
25    BluetoothScanfilter, BluetoothScanfilterSequence, RequestDeviceoptions,
26};
27use servo_bluetooth_traits::{
28    BluetoothCharacteristicMsg, BluetoothDescriptorMsg, BluetoothDeviceMsg, BluetoothError,
29    BluetoothRequest, BluetoothResponse, BluetoothResponseResult, BluetoothResult,
30    BluetoothServiceMsg, GATTType,
31};
32use servo_config::pref;
33
34use crate::bluetooth::{
35    BluetoothAdapter, BluetoothDevice, BluetoothGATTCharacteristic, BluetoothGATTDescriptor,
36    BluetoothGATTService,
37};
38
39// A transaction not completed within 30 seconds shall time out. Such a transaction shall be considered to have failed.
40// https://www.bluetooth.org/DocMan/handlers/DownloadDoc.ashx?doc_id=286439 (Vol. 3, page 480)
41const MAXIMUM_TRANSACTION_TIME: u8 = 30;
42const CONNECTION_TIMEOUT_MS: u64 = 1000;
43// The discovery session needs some time to find any nearby devices
44const DISCOVERY_TIMEOUT_MS: u64 = 1500;
45
46bitflags! {
47    struct Flags: u32 {
48        const BROADCAST                   = 0b000000001;
49        const READ                        = 0b000000010;
50        const WRITE_WITHOUT_RESPONSE      = 0b000000100;
51        const WRITE                       = 0b000001000;
52        const NOTIFY                      = 0b000010000;
53        const INDICATE                    = 0b000100000;
54        const AUTHENTICATED_SIGNED_WRITES = 0b001000000;
55        const RELIABLE_WRITE              = 0b010000000;
56        const WRITABLE_AUXILIARIES        = 0b100000000;
57    }
58}
59
60pub trait BluetoothThreadFactory {
61    fn new(embedder_proxy: EmbedderProxy) -> Self;
62}
63
64impl BluetoothThreadFactory for GenericSender<BluetoothRequest> {
65    #[cfg(feature = "native-bluetooth")]
66    fn new(embedder_proxy: EmbedderProxy) -> GenericSender<BluetoothRequest> {
67        let (sender, receiver) = generic_channel::channel().unwrap();
68
69        thread::Builder::new()
70            .name("Bluetooth".to_owned())
71            .spawn(move || {
72                let runtime = tokio::runtime::Builder::new_current_thread()
73                    .enable_all()
74                    .build()
75                    .expect("Failed to create bluetooth tokio runtime");
76
77                let adapter = runtime.block_on(async {
78                    if pref!(dom_bluetooth_enabled) {
79                        BluetoothAdapter::new().await.ok()
80                    } else {
81                        BluetoothAdapter::new_mock().ok()
82                    }
83                });
84
85                // Bridge: GenericReceiver -> tokio mpsc channel
86                let (async_sender, async_receiver) = tokio::sync::mpsc::unbounded_channel();
87                thread::Builder::new()
88                    .name("BluetoothTokioBridge".to_owned())
89                    .spawn(move || {
90                        while let Ok(message) = receiver.recv() {
91                            if async_sender.send(message).is_err() {
92                                break;
93                            }
94                        }
95                    })
96                    .expect("BT Tokio Bridge thread spawning failed");
97
98                let mut manager = BluetoothManager::new(adapter, embedder_proxy);
99                runtime.block_on(manager.start(async_receiver));
100            })
101            .expect("Thread spawning failed");
102        sender
103    }
104
105    #[cfg(not(feature = "native-bluetooth"))]
106    fn new(embedder_proxy: EmbedderProxy) -> GenericSender<BluetoothRequest> {
107        let (sender, receiver) = generic_channel::channel().unwrap();
108        let adapter = if pref!(dom_bluetooth_enabled) {
109            BluetoothAdapter::new()
110        } else {
111            BluetoothAdapter::new_mock()
112        }
113        .ok();
114        thread::Builder::new()
115            .name("Bluetooth".to_owned())
116            .spawn(move || {
117                BluetoothManager::new(adapter, embedder_proxy).start_sync(receiver);
118            })
119            .expect("Thread spawning failed");
120        sender
121    }
122}
123
124/// <https://webbluetoothcg.github.io/web-bluetooth/#matches-a-filter>
125async fn matches_filter(device: &BluetoothDevice, filter: &BluetoothScanfilter) -> bool {
126    if filter.is_empty_or_invalid() {
127        return false;
128    }
129
130    // Step 1.
131    if let Some(name) = filter.get_name() &&
132        device.get_name().await.ok() != Some(name.to_string())
133    {
134        return false;
135    }
136
137    // Step 2.
138    if !filter.get_name_prefix().is_empty() {
139        if let Ok(device_name) = device.get_name().await {
140            if !device_name.starts_with(filter.get_name_prefix()) {
141                return false;
142            }
143        } else {
144            return false;
145        }
146    }
147
148    // Step 3.
149    if !filter.get_services().is_empty() &&
150        let Ok(device_uuids) = device.get_uuids().await
151    {
152        for service in filter.get_services() {
153            if !device_uuids.iter().any(|x| x == service) {
154                return false;
155            }
156        }
157    }
158
159    // Step 4.
160    if let Some(manufacturer_data) = filter.get_manufacturer_data() {
161        let advertised_manufacturer_data = match device.get_manufacturer_data().await {
162            Ok(data) => data,
163            Err(_) => return false,
164        };
165        for (id, (prefix, mask)) in manufacturer_data.iter() {
166            if let Some(advertised_data) = advertised_manufacturer_data.get(id) {
167                if !data_filter_matches(advertised_data, prefix, mask) {
168                    return false;
169                }
170            } else {
171                return false;
172            }
173        }
174    }
175
176    // Step 5.
177    if let Some(service_data) = filter.get_service_data() {
178        let advertised_service_data = match device.get_service_data().await {
179            Ok(data) => data,
180            Err(_) => return false,
181        };
182        for (uuid, (prefix, mask)) in service_data.iter() {
183            if let Some(advertised_data) = advertised_service_data.get(uuid.as_str()) {
184                if !data_filter_matches(advertised_data, prefix, mask) {
185                    return false;
186                }
187            } else {
188                return false;
189            }
190        }
191    }
192
193    // Step 6.
194    true
195}
196
197/// <https://webbluetoothcg.github.io/web-bluetooth/#bluetoothdatafilterinit-matches>
198fn data_filter_matches(data: &[u8], prefix: &[u8], mask: &[u8]) -> bool {
199    // Step 1-2: No need to copy the bytes here.
200    // Step 3.
201    if data.len() < prefix.len() {
202        return false;
203    }
204
205    // Step 4.
206    for ((data, mask), prefix) in data.iter().zip(mask.iter()).zip(prefix.iter()) {
207        if data & mask != prefix & mask {
208            return false;
209        }
210    }
211
212    // Step 5.
213    true
214}
215
216async fn matches_filters(device: &BluetoothDevice, filters: &BluetoothScanfilterSequence) -> bool {
217    if filters.has_empty_or_invalid_filter() {
218        return false;
219    }
220    for f in filters.iter() {
221        if matches_filter(device, f).await {
222            return true;
223        }
224    }
225    false
226}
227
228/// Async sleep that works in both tokio and non-tokio (mock) contexts.
229async fn async_sleep(duration: Duration) {
230    #[cfg(feature = "native-bluetooth")]
231    {
232        tokio::time::sleep(duration).await;
233    }
234    #[cfg(not(feature = "native-bluetooth"))]
235    {
236        thread::sleep(duration);
237    }
238}
239
240fn is_mock_adapter(adapter: &BluetoothAdapter) -> bool {
241    matches!(adapter, &BluetoothAdapter::Mock(_))
242}
243
244pub struct BluetoothManager {
245    adapter: Option<BluetoothAdapter>,
246    address_to_id: HashMap<String, String>,
247    service_to_device: HashMap<String, String>,
248    characteristic_to_service: HashMap<String, String>,
249    descriptor_to_characteristic: HashMap<String, String>,
250    cached_devices: HashMap<String, BluetoothDevice>,
251    cached_services: HashMap<String, BluetoothGATTService>,
252    cached_characteristics: HashMap<String, BluetoothGATTCharacteristic>,
253    cached_descriptors: HashMap<String, BluetoothGATTDescriptor>,
254    allowed_services: HashMap<String, HashSet<String>>,
255    embedder_proxy: EmbedderProxy,
256}
257
258impl BluetoothManager {
259    pub fn new(
260        adapter: Option<BluetoothAdapter>,
261        embedder_proxy: EmbedderProxy,
262    ) -> BluetoothManager {
263        BluetoothManager {
264            adapter,
265            address_to_id: HashMap::new(),
266            service_to_device: HashMap::new(),
267            characteristic_to_service: HashMap::new(),
268            descriptor_to_characteristic: HashMap::new(),
269            cached_devices: HashMap::new(),
270            cached_services: HashMap::new(),
271            cached_characteristics: HashMap::new(),
272            cached_descriptors: HashMap::new(),
273            allowed_services: HashMap::new(),
274            embedder_proxy,
275        }
276    }
277
278    /// Async message loop.
279    /// The tokio runtime is always active, so btleplug's background tasks run continuously.
280    #[cfg(feature = "native-bluetooth")]
281    async fn start(&mut self, mut rx: tokio::sync::mpsc::UnboundedReceiver<BluetoothRequest>) {
282        while let Some(msg) = rx.recv().await {
283            if self.handle_message(msg).await {
284                break;
285            }
286        }
287    }
288
289    /// Synchronous message loop.
290    #[cfg(not(feature = "native-bluetooth"))]
291    fn start_sync(&mut self, receiver: GenericReceiver<BluetoothRequest>) {
292        // For mock-only builds, we don't have a tokio runtime.
293        // Create a minimal one just to call async methods (which are fast for mock).
294        while let Ok(msg) = receiver.recv() {
295            let should_exit = futures::executor::block_on(self.handle_message(msg));
296            if should_exit {
297                break;
298            }
299        }
300    }
301
302    /// Handle a single message. Returns true if the event loop should exit.
303    async fn handle_message(&mut self, msg: BluetoothRequest) -> bool {
304        match msg {
305            BluetoothRequest::RequestDevice(options, sender) => {
306                let _ = sender.send(self.request_device(options).await);
307            },
308            BluetoothRequest::GATTServerConnect(device_id, sender) => {
309                let _ = sender.send(self.gatt_server_connect(device_id).await);
310            },
311            BluetoothRequest::GATTServerDisconnect(device_id, sender) => {
312                let _ = sender.send(self.gatt_server_disconnect(device_id).await);
313            },
314            BluetoothRequest::GetGATTChildren(id, uuid, single, child_type, sender) => {
315                let _ = sender.send(self.get_gatt_children(id, uuid, single, child_type).await);
316            },
317            BluetoothRequest::ReadValue(id, sender) => {
318                let _ = sender.send(self.read_value(id).await);
319            },
320            BluetoothRequest::WriteValue(id, value, sender) => {
321                let _ = sender.send(self.write_value(id, value).await);
322            },
323            BluetoothRequest::EnableNotification(id, enable, sender) => {
324                let _ = sender.send(self.enable_notification(id, enable).await);
325            },
326            BluetoothRequest::WatchAdvertisements(id, sender) => {
327                let _ = sender.send(self.watch_advertisements(id));
328            },
329            BluetoothRequest::Test(data_set_name, sender) => {
330                let _ = sender.send(self.test(data_set_name).await);
331            },
332            BluetoothRequest::SetRepresentedToNull(
333                service_ids,
334                characteristic_ids,
335                descriptor_ids,
336            ) => self.remove_ids_from_caches(service_ids, characteristic_ids, descriptor_ids),
337            BluetoothRequest::IsRepresentedDeviceNull(id, sender) => {
338                let _ = sender.send(!self.device_is_cached(&id));
339            },
340            BluetoothRequest::GetAvailability(sender) => {
341                let _ = sender.send(self.get_availability().await);
342            },
343            BluetoothRequest::MatchesFilter(id, filters, sender) => {
344                let _ = sender.send(self.device_matches_filter(&id, &filters).await);
345            },
346            BluetoothRequest::Exit => return true,
347        }
348        false
349    }
350
351    // Test
352
353    async fn test(&mut self, data_set_name: String) -> BluetoothResult<()> {
354        self.address_to_id.clear();
355        self.service_to_device.clear();
356        self.characteristic_to_service.clear();
357        self.descriptor_to_characteristic.clear();
358        self.cached_devices.clear();
359        self.cached_services.clear();
360        self.cached_characteristics.clear();
361        self.cached_descriptors.clear();
362        self.allowed_services.clear();
363        self.adapter = BluetoothAdapter::new_mock().ok();
364        match test::test(self, data_set_name).await {
365            Ok(_) => Ok(()),
366            Err(error) => Err(BluetoothError::Type(error.to_string())),
367        }
368    }
369
370    fn remove_ids_from_caches(
371        &mut self,
372        service_ids: Vec<String>,
373        characteristic_ids: Vec<String>,
374        descriptor_ids: Vec<String>,
375    ) {
376        for id in service_ids {
377            self.cached_services.remove(&id);
378            self.service_to_device.remove(&id);
379        }
380
381        for id in characteristic_ids {
382            self.cached_characteristics.remove(&id);
383            self.characteristic_to_service.remove(&id);
384        }
385
386        for id in descriptor_ids {
387            self.cached_descriptors.remove(&id);
388            self.descriptor_to_characteristic.remove(&id);
389        }
390    }
391
392    // Adapter
393
394    pub async fn get_or_create_adapter(&mut self) -> Option<BluetoothAdapter> {
395        let adapter_valid = match self.adapter.as_ref() {
396            Some(a) => a.get_address().await.is_ok(),
397            None => false,
398        };
399        if !adapter_valid {
400            #[cfg(feature = "native-bluetooth")]
401            {
402                self.adapter = BluetoothAdapter::new().await.ok();
403            }
404            #[cfg(not(feature = "native-bluetooth"))]
405            {
406                self.adapter = BluetoothAdapter::new().ok();
407            }
408        }
409
410        let adapter = self.adapter.as_ref()?;
411
412        if is_mock_adapter(adapter) && !adapter.is_present().unwrap_or(false) {
413            return None;
414        }
415
416        self.adapter.clone()
417    }
418
419    async fn get_adapter(&mut self) -> BluetoothResult<BluetoothAdapter> {
420        match self.get_or_create_adapter().await {
421            Some(adapter) => {
422                if !adapter.is_powered().await.unwrap_or(false) {
423                    return Err(BluetoothError::NotFound);
424                }
425                Ok(adapter)
426            },
427            None => Err(BluetoothError::NotFound),
428        }
429    }
430
431    // Device
432
433    async fn get_and_cache_devices(
434        &mut self,
435        adapter: &mut BluetoothAdapter,
436    ) -> Vec<BluetoothDevice> {
437        let devices = adapter.get_devices().await.unwrap_or_default();
438        for device in &devices {
439            if let Ok(address) = device.get_address() {
440                #[allow(clippy::map_entry)] // False positive, the fix creates a borrowing error
441                if !self.address_to_id.contains_key(&address) {
442                    let generated_id = self.generate_device_id();
443                    self.address_to_id.insert(address, generated_id.clone());
444                    self.cached_devices
445                        .insert(generated_id.clone(), device.clone());
446                    self.allowed_services.insert(generated_id, HashSet::new());
447                }
448            }
449        }
450        self.cached_devices.values().cloned().collect()
451    }
452
453    async fn get_device(
454        &mut self,
455        adapter: &mut BluetoothAdapter,
456        device_id: &str,
457    ) -> Option<&BluetoothDevice> {
458        if self.cached_devices.contains_key(device_id) {
459            return self.cached_devices.get(device_id);
460        }
461        self.get_and_cache_devices(adapter).await;
462        self.cached_devices.get(device_id)
463    }
464
465    async fn select_device(
466        &mut self,
467        webview_id: WebViewId,
468        devices: Vec<BluetoothDevice>,
469        adapter: &BluetoothAdapter,
470    ) -> Option<String> {
471        if is_mock_adapter(adapter) {
472            for device in &devices {
473                if let Ok(address) = device.get_address() {
474                    return Some(address);
475                }
476            }
477            return None;
478        }
479
480        let mut device_descriptions = Vec::with_capacity(devices.len());
481        for device in devices {
482            let address = device.get_address().unwrap_or_default();
483            let name = device.get_name().await.unwrap_or_else(|_| {
484                let short = if address.len() > 8 {
485                    &address[..8]
486                } else {
487                    &address
488                };
489                format!("Unknown ({}...)", short)
490            });
491            device_descriptions.push(BluetoothDeviceDescription { address, name });
492        }
493
494        let (ipc_sender, ipc_receiver) =
495            generic_channel::channel().expect("Failed to create IPC channel!");
496        self.embedder_proxy
497            .send(EmbedderMsg::GetSelectedBluetoothDevice(
498                webview_id,
499                device_descriptions,
500                ipc_sender,
501            ));
502
503        match ipc_receiver.recv() {
504            Ok(result) => result,
505            Err(e) => {
506                warn!("Failed to receive files from embedder ({:?}).", e);
507                None
508            },
509        }
510    }
511
512    fn generate_device_id(&mut self) -> String {
513        let mut device_id;
514        let mut rng = rand::rng();
515        loop {
516            device_id = rng.random::<u32>().to_string();
517            if !self.cached_devices.contains_key(&device_id) {
518                break;
519            }
520        }
521        device_id
522    }
523
524    fn device_from_service_id(&self, service_id: &str) -> Option<BluetoothDevice> {
525        let device_id = self.service_to_device.get(service_id)?;
526        self.cached_devices.get(device_id).cloned()
527    }
528
529    fn device_is_cached(&self, device_id: &str) -> bool {
530        self.cached_devices.contains_key(device_id) &&
531            self.address_to_id.values().any(|v| v == device_id)
532    }
533
534    async fn device_matches_filter(
535        &mut self,
536        device_id: &str,
537        filters: &BluetoothScanfilterSequence,
538    ) -> BluetoothResult<bool> {
539        let mut adapter = self.get_adapter().await?;
540        match self.get_device(&mut adapter, device_id).await {
541            Some(device) => Ok(matches_filters(device, filters).await),
542            None => Ok(false),
543        }
544    }
545
546    // Service
547
548    async fn get_and_cache_gatt_services(
549        &mut self,
550        adapter: &mut BluetoothAdapter,
551        device_id: &str,
552    ) -> Vec<BluetoothGATTService> {
553        let mut services = match self.get_device(adapter, device_id).await {
554            Some(d) => d.get_gatt_services().await.unwrap_or_default(),
555            None => vec![],
556        };
557
558        services.retain(|s| {
559            !uuid_is_blocklisted(&s.get_uuid().unwrap_or_default(), Blocklist::All) &&
560                self.allowed_services
561                    .get(device_id)
562                    .is_some_and(|uuids| uuids.contains(&s.get_uuid().unwrap_or_default()))
563        });
564        for service in &services {
565            self.cached_services
566                .insert(service.get_id(), service.clone());
567            self.service_to_device
568                .insert(service.get_id(), device_id.to_owned());
569        }
570        services
571    }
572
573    async fn get_gatt_service(
574        &mut self,
575        adapter: &mut BluetoothAdapter,
576        service_id: &str,
577    ) -> Option<&BluetoothGATTService> {
578        if self.cached_services.contains_key(service_id) {
579            return self.cached_services.get(service_id);
580        }
581        let device_id = self.service_to_device.get(service_id)?.clone();
582        self.get_and_cache_gatt_services(adapter, &device_id).await;
583        self.cached_services.get(service_id)
584    }
585
586    fn service_is_cached(&self, service_id: &str) -> bool {
587        self.cached_services.contains_key(service_id) &&
588            self.service_to_device.contains_key(service_id)
589    }
590
591    // Characteristic
592
593    async fn get_and_cache_gatt_characteristics(
594        &mut self,
595        adapter: &mut BluetoothAdapter,
596        service_id: &str,
597    ) -> Vec<BluetoothGATTCharacteristic> {
598        let mut characteristics = match self.get_gatt_service(adapter, service_id).await {
599            Some(s) => s.get_gatt_characteristics().unwrap_or_default(),
600            None => vec![],
601        };
602
603        characteristics
604            .retain(|c| !uuid_is_blocklisted(&c.get_uuid().unwrap_or_default(), Blocklist::All));
605        for characteristic in &characteristics {
606            self.cached_characteristics
607                .insert(characteristic.get_id(), characteristic.clone());
608            self.characteristic_to_service
609                .insert(characteristic.get_id(), service_id.to_owned());
610        }
611        characteristics
612    }
613
614    async fn get_gatt_characteristic(
615        &mut self,
616        adapter: &mut BluetoothAdapter,
617        characteristic_id: &str,
618    ) -> Option<&BluetoothGATTCharacteristic> {
619        if self.cached_characteristics.contains_key(characteristic_id) {
620            return self.cached_characteristics.get(characteristic_id);
621        }
622        let service_id = self
623            .characteristic_to_service
624            .get(characteristic_id)?
625            .clone();
626        self.get_and_cache_gatt_characteristics(adapter, &service_id)
627            .await;
628        self.cached_characteristics.get(characteristic_id)
629    }
630
631    fn get_characteristic_properties(&self, characteristic: &BluetoothGATTCharacteristic) -> Flags {
632        let mut props: Flags = Flags::empty();
633        let flags = characteristic.get_flags().unwrap_or_default();
634        for flag in flags {
635            match flag.as_ref() {
636                "broadcast" => props.insert(Flags::BROADCAST),
637                "read" => props.insert(Flags::READ),
638                "write-without-response" => props.insert(Flags::WRITE_WITHOUT_RESPONSE),
639                "write" => props.insert(Flags::WRITE),
640                "notify" => props.insert(Flags::NOTIFY),
641                "indicate" => props.insert(Flags::INDICATE),
642                "authenticated-signed-writes" => props.insert(Flags::AUTHENTICATED_SIGNED_WRITES),
643                "reliable-write" => props.insert(Flags::RELIABLE_WRITE),
644                "writable-auxiliaries" => props.insert(Flags::WRITABLE_AUXILIARIES),
645                _ => (),
646            }
647        }
648        props
649    }
650
651    fn characteristic_is_cached(&self, characteristic_id: &str) -> bool {
652        self.cached_characteristics.contains_key(characteristic_id) &&
653            self.characteristic_to_service
654                .contains_key(characteristic_id)
655    }
656
657    // Descriptor
658
659    async fn get_and_cache_gatt_descriptors(
660        &mut self,
661        adapter: &mut BluetoothAdapter,
662        characteristic_id: &str,
663    ) -> Vec<BluetoothGATTDescriptor> {
664        let mut descriptors = match self
665            .get_gatt_characteristic(adapter, characteristic_id)
666            .await
667        {
668            Some(c) => c.get_gatt_descriptors().unwrap_or_default(),
669            None => vec![],
670        };
671
672        descriptors
673            .retain(|d| !uuid_is_blocklisted(&d.get_uuid().unwrap_or_default(), Blocklist::All));
674        for descriptor in &descriptors {
675            self.cached_descriptors
676                .insert(descriptor.get_id(), descriptor.clone());
677            self.descriptor_to_characteristic
678                .insert(descriptor.get_id(), characteristic_id.to_owned());
679        }
680        descriptors
681    }
682
683    async fn get_gatt_descriptor(
684        &mut self,
685        adapter: &mut BluetoothAdapter,
686        descriptor_id: &str,
687    ) -> Option<&BluetoothGATTDescriptor> {
688        if self.cached_descriptors.contains_key(descriptor_id) {
689            return self.cached_descriptors.get(descriptor_id);
690        }
691        let characteristic_id = self
692            .descriptor_to_characteristic
693            .get(descriptor_id)?
694            .clone();
695        self.get_and_cache_gatt_descriptors(adapter, &characteristic_id)
696            .await;
697        self.cached_descriptors.get(descriptor_id)
698    }
699
700    // Methods
701
702    /// <https://webbluetoothcg.github.io/web-bluetooth/#request-bluetooth-devices>
703    async fn request_device(&mut self, options: RequestDeviceoptions) -> BluetoothResponseResult {
704        // Step 6.
705        let mut adapter = self.get_adapter().await?;
706
707        // Step 7.
708        // Note: There are no requiredServiceUUIDS, we scan for all devices.
709        if let Ok(ref session) = adapter.create_discovery_session() {
710            if session.start_discovery().await.is_ok() && !is_mock_adapter(&adapter) {
711                async_sleep(Duration::from_millis(DISCOVERY_TIMEOUT_MS)).await;
712            }
713            let _ = session.stop_discovery().await;
714        }
715
716        let mut matched_devices = self.get_and_cache_devices(&mut adapter).await;
717
718        // Step 8.
719        if !options.is_accepting_all_devices() {
720            let mut filtered = Vec::new();
721            for d in matched_devices {
722                if matches_filters(&d, options.get_filters()).await {
723                    filtered.push(d);
724                }
725            }
726            matched_devices = filtered;
727        }
728
729        // Step 9.
730        if let Some(address) = self
731            .select_device(options.webview_id(), matched_devices, &adapter)
732            .await
733        {
734            let device_id = match self.address_to_id.get(&address) {
735                Some(id) => id.clone(),
736                None => return Err(BluetoothError::NotFound),
737            };
738            let mut services = options.get_services_set();
739            if let Some(services_set) = self.allowed_services.get(&device_id) {
740                services = services_set | &services;
741            }
742            self.allowed_services.insert(device_id.clone(), services);
743            if let Some(device) = self.get_device(&mut adapter, &device_id).await {
744                let message = BluetoothDeviceMsg {
745                    id: device_id,
746                    name: device.get_name().await.ok(),
747                };
748                return Ok(BluetoothResponse::RequestDevice(message));
749            }
750        }
751        // Step 10.
752        Err(BluetoothError::NotFound)
753        // Step 12: Missing, because it is optional.
754    }
755
756    /// <https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattserver-connect>
757    async fn gatt_server_connect(&mut self, device_id: String) -> BluetoothResponseResult {
758        // Step 2.
759        if !self.device_is_cached(&device_id) {
760            return Err(BluetoothError::Network);
761        }
762        let mut adapter = self.get_adapter().await?;
763
764        // Step 5.1.1.
765        match self.get_device(&mut adapter, &device_id).await {
766            Some(d) => {
767                if d.is_connected().await.unwrap_or(false) {
768                    return Ok(BluetoothResponse::GATTServerConnect(true));
769                }
770                let _ = d.connect().await;
771                for _ in 0..MAXIMUM_TRANSACTION_TIME {
772                    if d.is_connected().await.unwrap_or(false) {
773                        return Ok(BluetoothResponse::GATTServerConnect(true));
774                    } else {
775                        if is_mock_adapter(&adapter) {
776                            break;
777                        }
778                        async_sleep(Duration::from_millis(CONNECTION_TIMEOUT_MS)).await;
779                    }
780                    // TODO: Step 5.1.4: Use the exchange MTU procedure.
781                }
782                // Step 5.1.3.
783                Err(BluetoothError::Network)
784            },
785            None => Err(BluetoothError::NotFound),
786        }
787    }
788
789    /// <https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattserver-disconnect>
790    async fn gatt_server_disconnect(&mut self, device_id: String) -> BluetoothResult<()> {
791        let mut adapter = self.get_adapter().await?;
792        match self.get_device(&mut adapter, &device_id).await {
793            Some(d) => {
794                // Step 2.
795                if !d.is_connected().await.unwrap_or(true) {
796                    return Ok(());
797                }
798                let _ = d.disconnect().await;
799                for _ in 0..MAXIMUM_TRANSACTION_TIME {
800                    if d.is_connected().await.unwrap_or(true) {
801                        async_sleep(Duration::from_millis(CONNECTION_TIMEOUT_MS)).await;
802                    } else {
803                        return Ok(());
804                    }
805                }
806                Err(BluetoothError::Network)
807            },
808            None => Err(BluetoothError::NotFound),
809        }
810    }
811
812    /// <https://webbluetoothcg.github.io/web-bluetooth/#getgattchildren>
813    async fn get_gatt_children(
814        &mut self,
815        id: String,
816        uuid: Option<String>,
817        single: bool,
818        child_type: GATTType,
819    ) -> BluetoothResponseResult {
820        let mut adapter = self.get_adapter().await?;
821        match child_type {
822            GATTType::PrimaryService => {
823                // Step 5.
824                if !self.device_is_cached(&id) {
825                    return Err(BluetoothError::InvalidState);
826                }
827                // Step 6.
828                if let Some(ref uuid) = uuid &&
829                    !self
830                        .allowed_services
831                        .get(&id)
832                        .is_some_and(|s| s.contains(uuid))
833                {
834                    return Err(BluetoothError::Security);
835                }
836                let mut services = self.get_and_cache_gatt_services(&mut adapter, &id).await;
837                if let Some(uuid) = uuid {
838                    services.retain(|e| e.get_uuid().unwrap_or_default() == uuid);
839                }
840                let mut services_vec = vec![];
841                for service in services {
842                    if service.is_primary().unwrap_or(false) &&
843                        let Ok(uuid) = service.get_uuid()
844                    {
845                        services_vec.push(BluetoothServiceMsg {
846                            uuid,
847                            is_primary: true,
848                            instance_id: service.get_id(),
849                        });
850                    }
851                }
852
853                // Step 7.
854                if services_vec.is_empty() {
855                    return Err(BluetoothError::NotFound);
856                }
857
858                Ok(BluetoothResponse::GetPrimaryServices(services_vec, single))
859            },
860            GATTType::Characteristic => {
861                // Step 5.
862                if !self.service_is_cached(&id) {
863                    return Err(BluetoothError::InvalidState);
864                }
865                // Step 6.
866                let mut characteristics = self
867                    .get_and_cache_gatt_characteristics(&mut adapter, &id)
868                    .await;
869                if let Some(uuid) = uuid {
870                    characteristics.retain(|e| e.get_uuid().unwrap_or_default() == uuid);
871                }
872                let mut characteristics_vec = vec![];
873                for characteristic in characteristics {
874                    if let Ok(uuid) = characteristic.get_uuid() {
875                        let properties = self.get_characteristic_properties(&characteristic);
876                        characteristics_vec.push(BluetoothCharacteristicMsg {
877                            uuid,
878                            instance_id: characteristic.get_id(),
879                            broadcast: properties.contains(Flags::BROADCAST),
880                            read: properties.contains(Flags::READ),
881                            write_without_response: properties
882                                .contains(Flags::WRITE_WITHOUT_RESPONSE),
883                            write: properties.contains(Flags::WRITE),
884                            notify: properties.contains(Flags::NOTIFY),
885                            indicate: properties.contains(Flags::INDICATE),
886                            authenticated_signed_writes: properties
887                                .contains(Flags::AUTHENTICATED_SIGNED_WRITES),
888                            reliable_write: properties.contains(Flags::RELIABLE_WRITE),
889                            writable_auxiliaries: properties.contains(Flags::WRITABLE_AUXILIARIES),
890                        });
891                    }
892                }
893
894                // Step 7.
895                if characteristics_vec.is_empty() {
896                    return Err(BluetoothError::NotFound);
897                }
898
899                Ok(BluetoothResponse::GetCharacteristics(
900                    characteristics_vec,
901                    single,
902                ))
903            },
904            GATTType::IncludedService => {
905                // Step 5.
906                if !self.service_is_cached(&id) {
907                    return Err(BluetoothError::InvalidState);
908                }
909                // Step 6.
910                let device = match self.device_from_service_id(&id) {
911                    Some(device) => device,
912                    None => return Err(BluetoothError::NotFound),
913                };
914                let primary_service = match self.get_gatt_service(&mut adapter, &id).await {
915                    Some(s) => s,
916                    None => return Err(BluetoothError::NotFound),
917                };
918                let services = primary_service.get_includes(device).unwrap_or(vec![]);
919                let mut services_vec = vec![];
920                for service in services {
921                    if let Ok(service_uuid) = service.get_uuid() {
922                        services_vec.push(BluetoothServiceMsg {
923                            uuid: service_uuid,
924                            is_primary: service.is_primary().unwrap_or(false),
925                            instance_id: service.get_id(),
926                        });
927                    }
928                }
929                if let Some(uuid) = uuid {
930                    services_vec.retain(|s| s.uuid == uuid);
931                }
932                services_vec.retain(|s| !uuid_is_blocklisted(&s.uuid, Blocklist::All));
933
934                // Step 7.
935                if services_vec.is_empty() {
936                    return Err(BluetoothError::NotFound);
937                }
938
939                Ok(BluetoothResponse::GetIncludedServices(services_vec, single))
940            },
941            GATTType::Descriptor => {
942                // Step 5.
943                if !self.characteristic_is_cached(&id) {
944                    return Err(BluetoothError::InvalidState);
945                }
946                // Step 6.
947                let mut descriptors = self.get_and_cache_gatt_descriptors(&mut adapter, &id).await;
948                if let Some(uuid) = uuid {
949                    descriptors.retain(|e| e.get_uuid().unwrap_or_default() == uuid);
950                }
951                let mut descriptors_vec = vec![];
952                for descriptor in descriptors {
953                    if let Ok(uuid) = descriptor.get_uuid() {
954                        descriptors_vec.push(BluetoothDescriptorMsg {
955                            uuid,
956                            instance_id: descriptor.get_id(),
957                        });
958                    }
959                }
960
961                // Step 7.
962                if descriptors_vec.is_empty() {
963                    return Err(BluetoothError::NotFound);
964                }
965                Ok(BluetoothResponse::GetDescriptors(descriptors_vec, single))
966            },
967        }
968    }
969
970    /// <https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattcharacteristic-readvalue>
971    /// <https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattdescriptor-readvalue>
972    async fn read_value(&mut self, id: String) -> BluetoothResponseResult {
973        // (Characteristic) Step 5.2: Missing because it is optional.
974        // (Descriptor)     Step 5.1: Missing because it is optional.
975        let mut adapter = self.get_adapter().await?;
976
977        // (Characteristic) Step 5.3.
978        let mut value = match self.get_gatt_characteristic(&mut adapter, &id).await {
979            Some(c) => Some(c.read_value().await.unwrap_or_default()),
980            None => None,
981        };
982
983        // (Characteristic) TODO: Step 5.4: Handle all the errors returned from the read_value call.
984
985        // (Descriptor) Step 5.2.
986        if value.is_none() {
987            value = match self.get_gatt_descriptor(&mut adapter, &id).await {
988                Some(d) => Some(d.read_value().await.unwrap_or_default()),
989                None => None,
990            };
991        }
992
993        // (Descriptor) TODO: Step 5.3: Handle all the errors returned from the read_value call.
994
995        match value {
996            // (Characteristic) Step 5.5.4.
997            // (Descriptor)     Step 5.4.3.
998            Some(v) => Ok(BluetoothResponse::ReadValue(v)),
999
1000            // (Characteristic) Step 4.
1001            // (Descriptor)     Step 4.
1002            None => Err(BluetoothError::InvalidState),
1003        }
1004    }
1005
1006    /// <https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattcharacteristic-writevalue>
1007    /// <https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattdescriptor-writevalue>
1008    async fn write_value(&mut self, id: String, value: Vec<u8>) -> BluetoothResponseResult {
1009        // (Characteristic) Step 7.2: Missing because it is optional.
1010        // (Descriptor)     Step 7.1: Missing because it is optional.
1011        let mut adapter = self.get_adapter().await?;
1012
1013        // (Characteristic) Step 7.3.
1014        let mut result = match self.get_gatt_characteristic(&mut adapter, &id).await {
1015            Some(c) => Some(c.write_value(value.clone()).await),
1016            None => None,
1017        };
1018
1019        // (Characteristic) TODO: Step 7.4: Handle all the errors returned from the write_value call.
1020
1021        // (Descriptor) Step 7.2.
1022        if result.is_none() {
1023            result = match self.get_gatt_descriptor(&mut adapter, &id).await {
1024                Some(d) => Some(d.write_value(value.clone()).await),
1025                None => None,
1026            };
1027        }
1028
1029        // (Descriptor) TODO: Step 7.3: Handle all the errors returned from the write_value call.
1030
1031        match result {
1032            // (Characteristic) Step 7.5.3.
1033            // (Descriptor) Step 7.4.3.
1034            Some(Ok(_)) => Ok(BluetoothResponse::WriteValue(value)),
1035
1036            // (Characteristic) Step 7.1.
1037            Some(Err(_)) => Err(BluetoothError::NotSupported),
1038
1039            // (Characteristic) Step 6.
1040            // (Descriptor)     Step 6.
1041            None => Err(BluetoothError::InvalidState),
1042        }
1043    }
1044
1045    /// <https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattcharacteristic-startnotifications>
1046    /// <https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattcharacteristic-stopnotifications>
1047    async fn enable_notification(&mut self, id: String, enable: bool) -> BluetoothResponseResult {
1048        // (StartNotifications) Step 3 - 4.
1049        // (StopNotifications) Step 1 - 2.
1050        if !self.characteristic_is_cached(&id) {
1051            return Err(BluetoothError::InvalidState);
1052        }
1053
1054        // (StartNotification) TODO: Step 7: Missing because it is optional.
1055        let mut adapter = self.get_adapter().await?;
1056        match self.get_gatt_characteristic(&mut adapter, &id).await {
1057            Some(c) => {
1058                let result = if enable {
1059                    // (StartNotification) Step 8.
1060                    // TODO: Handle all the errors returned from the start_notify call.
1061                    c.start_notify().await
1062                } else {
1063                    // (StopNotification) Step 4.
1064                    c.stop_notify().await
1065                };
1066                match result {
1067                    // (StartNotification) Step 11.
1068                    // (StopNotification)  Step 5.
1069                    Ok(_) => Ok(BluetoothResponse::EnableNotification(())),
1070
1071                    // (StartNotification) Step 5.
1072                    Err(_) => Err(BluetoothError::NotSupported),
1073                }
1074            },
1075            // (StartNotification) Step 4.
1076            None => Err(BluetoothError::InvalidState),
1077        }
1078    }
1079
1080    /// <https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothdevice-watchadvertisements>
1081    fn watch_advertisements(&mut self, _device_id: String) -> BluetoothResponseResult {
1082        // Step 2.
1083        // TODO: Implement this when supported in lower level
1084        Err(BluetoothError::NotSupported)
1085    }
1086
1087    /// <https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetooth-getavailability>
1088    async fn get_availability(&mut self) -> BluetoothResponseResult {
1089        Ok(BluetoothResponse::GetAvailability(
1090            self.get_adapter().await.is_ok(),
1091        ))
1092    }
1093}