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