From 6a804cd775e3976103269ac54ae997a3accc8618 Mon Sep 17 00:00:00 2001 From: Martin Robinson Date: Tue, 9 Jan 2024 10:13:41 +0100 Subject: Integrate the `devices` respository (#30974) Despite the name of this dependency, it only handles bluetooth. Because it's a separate repository. Integrating it, allows changes here to be tested more consistently. In addition, it's likely that new bluetooth libraries will allow removing the majority of the platform-specific code in this directory. This is based on the version of this dependency from: https://github.com/servo/devices/pull/34 --- components/bluetooth/Cargo.toml | 14 +- components/bluetooth/README.md | 55 +++ components/bluetooth/adapter.rs | 408 +++++++++++++++++++++ components/bluetooth/bluetooth.rs | 753 ++++++++++++++++++++++++++++++++++++++ components/bluetooth/empty.rs | 377 +++++++++++++++++++ components/bluetooth/lib.rs | 26 +- components/bluetooth/macros.rs | 98 +++++ components/bluetooth/test.rs | 8 +- 8 files changed, 1725 insertions(+), 14 deletions(-) create mode 100644 components/bluetooth/README.md create mode 100644 components/bluetooth/adapter.rs create mode 100644 components/bluetooth/bluetooth.rs create mode 100644 components/bluetooth/empty.rs create mode 100644 components/bluetooth/macros.rs (limited to 'components/bluetooth') diff --git a/components/bluetooth/Cargo.toml b/components/bluetooth/Cargo.toml index fa2a6feb742..4046b256a1b 100644 --- a/components/bluetooth/Cargo.toml +++ b/components/bluetooth/Cargo.toml @@ -13,7 +13,7 @@ path = "lib.rs" [dependencies] bitflags = { workspace = true } bluetooth_traits = { workspace = true } -device = { git = "https://github.com/servo/devices", features = ["bluetooth-test"], rev = "cb28c4725ffbfece99dab842d17d3e8c50774778" } +blurmock = { version = "0.1.2", optional = true } embedder_traits = { workspace = true } ipc-channel = { workspace = true } log = { workspace = true } @@ -22,4 +22,14 @@ servo_rand = { path = "../rand" } uuid = { workspace = true } [features] -native-bluetooth = ["device/bluetooth"] +native-bluetooth = ["blurz", "blurdroid", "blurmac", "bluetooth-test"] +bluetooth-test = ["blurmock"] + +[target.'cfg(target_os = "linux")'.dependencies] +blurz = { version = "0.3", optional = true } + +[target.'cfg(target_os = "android")'.dependencies] +blurdroid = { version = "0.1.2", optional = true } + +[target.'cfg(target_os = "macos")'.dependencies] +blurmac = { path = "../../third_party/blurmac", optional = true } diff --git a/components/bluetooth/README.md b/components/bluetooth/README.md new file mode 100644 index 00000000000..ec6e536d66a --- /dev/null +++ b/components/bluetooth/README.md @@ -0,0 +1,55 @@ +# Bluetooth Rust lib using macOS CoreBluetooth + +[![Build Status](https://travis-ci.org/akosthekiss/blurmac.svg?branch=master)](https://travis-ci.org/akosthekiss/blurmac) +[![Crates.io](https://img.shields.io/crates/v/blurmac.svg)](https://crates.io/crates/blurmac) + +The main aim of BlurMac is to enable [WebBluetooth](https://webbluetoothcg.github.io) +in [Servo](https://github.com/servo/servo) on macOS. Thus, API and implementation +decisions are affected by the encapsulating [Devices](https://github.com/servo/devices), +and the sibling [BlurZ](https://github.com/szeged/blurz) and [BlurDroid](https://github.com/szeged/blurdroid) +crates. + + +## Run Servo with WebBluetooth Enabled + +Usually, you don't want to work with BlurMac on its own but use it within Servo. +So, most probably you'll want to run Servo with WebBluetooth enabled: + +``` +RUST_LOG=blurmac \ +./mach run \ + --dev \ + --pref=dom.bluetooth.enabled \ + --pref=dom.permissions.testing.allowed_in_nonsecure_contexts \ + URL +``` + +Notes: +* The above command is actually not really BlurMac-specific (except for the `RUST_LOG` + part). It runs Servo with WBT enabled on any platform where WBT is supported. +* You don't need the `RUST_LOG=blurmac` part if you don't want to see BlurMac debug + messages on the console. +* You don't need the `--dev` part if you want to run a release build. +* You don't need the `--pref=dom.permissions.testing.allowed_in_nonsecure_contexts` + part if your `URL` is https (but you do need it if you test a local file). + + +## Known Issues + +* Device RSSI can not be retrieved yet. +* Support for included services is incomplete. +* Descriptors are not supported yet. +* Notifications on characteristics are not supported yet (the limitation comes from + Devices). + + +## Compatibility + +Tested on: + +* macOS Sierra 10.12. + + +## Copyright and Licensing + +Licensed under the BSD 3-Clause [License](LICENSE.md). diff --git a/components/bluetooth/adapter.rs b/components/bluetooth/adapter.rs new file mode 100644 index 00000000000..b5cd71b4543 --- /dev/null +++ b/components/bluetooth/adapter.rs @@ -0,0 +1,408 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +use std::error::Error; +use std::sync::Arc; + +#[cfg(all(target_os = "android", feature = "bluetooth"))] +use blurdroid::bluetooth_adapter::Adapter as BluetoothAdapterAndroid; +#[cfg(all(target_os = "android", feature = "bluetooth"))] +use blurdroid::bluetooth_device::Device as BluetoothDeviceAndroid; +#[cfg(all(target_os = "android", feature = "bluetooth"))] +use blurdroid::bluetooth_discovery_session::DiscoverySession as BluetoothDiscoverySessionAndroid; +#[cfg(all(target_os = "macos", feature = "bluetooth"))] +use blurmac::BluetoothAdapter as BluetoothAdapterMac; +#[cfg(all(target_os = "macos", feature = "bluetooth"))] +use blurmac::BluetoothDevice as BluetoothDeviceMac; +#[cfg(all(target_os = "macos", feature = "bluetooth"))] +use blurmac::BluetoothDiscoverySession as BluetoothDiscoverySessionMac; +#[cfg(feature = "bluetooth-test")] +use blurmock::fake_adapter::FakeBluetoothAdapter; +#[cfg(feature = "bluetooth-test")] +use blurmock::fake_device::FakeBluetoothDevice; +#[cfg(feature = "bluetooth-test")] +use blurmock::fake_discovery_session::FakeBluetoothDiscoverySession; +#[cfg(all(target_os = "linux", feature = "bluetooth"))] +use blurz::bluetooth_adapter::BluetoothAdapter as BluetoothAdapterBluez; +#[cfg(all(target_os = "linux", feature = "bluetooth"))] +use blurz::bluetooth_device::BluetoothDevice as BluetoothDeviceBluez; +#[cfg(all(target_os = "linux", feature = "bluetooth"))] +use blurz::bluetooth_discovery_session::BluetoothDiscoverySession as BluetoothDiscoverySessionBluez; + +use super::bluetooth::{BluetoothDevice, BluetoothDiscoverySession}; +#[cfg(not(any( + all(target_os = "linux", feature = "bluetooth"), + all(target_os = "android", feature = "bluetooth"), + all(target_os = "macos", feature = "bluetooth") +)))] +use super::empty::BluetoothDevice as BluetoothDeviceEmpty; +#[cfg(not(any( + all(target_os = "linux", feature = "bluetooth"), + all(target_os = "android", feature = "bluetooth"), + all(target_os = "macos", feature = "bluetooth") +)))] +use super::empty::BluetoothDiscoverySession as BluetoothDiscoverySessionEmpty; +#[cfg(not(any( + all(target_os = "linux", feature = "bluetooth"), + all(target_os = "android", feature = "bluetooth"), + all(target_os = "macos", feature = "bluetooth") +)))] +use super::empty::EmptyAdapter as BluetoothAdapterEmpty; +use super::macros::get_inner_and_call; +#[cfg(feature = "bluetooth-test")] +use super::macros::get_inner_and_call_test_func; + +#[derive(Clone, Debug)] +pub enum BluetoothAdapter { + #[cfg(all(target_os = "linux", feature = "bluetooth"))] + Bluez(Arc), + #[cfg(all(target_os = "android", feature = "bluetooth"))] + Android(Arc), + #[cfg(all(target_os = "macos", feature = "bluetooth"))] + Mac(Arc), + #[cfg(not(any( + all(target_os = "linux", feature = "bluetooth"), + all(target_os = "android", feature = "bluetooth"), + all(target_os = "macos", feature = "bluetooth") + )))] + Empty(Arc), + #[cfg(feature = "bluetooth-test")] + Mock(Arc), +} + +impl BluetoothAdapter { + #[cfg(all(target_os = "linux", feature = "bluetooth"))] + pub fn new() -> Result> { + let bluez_adapter = BluetoothAdapterBluez::init()?; + Ok(Self::Bluez(Arc::new(bluez_adapter))) + } + + #[cfg(all(target_os = "android", feature = "bluetooth"))] + pub fn new() -> Result> { + let blurdroid_adapter = BluetoothAdapterAndroid::get_adapter()?; + Ok(Self::Android(Arc::new(blurdroid_adapter))) + } + + #[cfg(all(target_os = "macos", feature = "bluetooth"))] + pub fn new() -> Result> { + let mac_adapter = BluetoothAdapterMac::init()?; + Ok(Self::Mac(Arc::new(mac_adapter))) + } + + #[cfg(not(any( + all(target_os = "linux", feature = "bluetooth"), + all(target_os = "android", feature = "bluetooth"), + all(target_os = "macos", feature = "bluetooth") + )))] + pub fn new() -> Result> { + let adapter = BluetoothAdapterEmpty::init()?; + Ok(Self::Empty(Arc::new(adapter))) + } + + #[cfg(feature = "bluetooth-test")] + pub fn new_mock() -> Result> { + Ok(Self::Mock(FakeBluetoothAdapter::new_empty())) + } + + pub fn get_id(&self) -> String { + get_inner_and_call!(self, BluetoothAdapter, get_id) + } + + pub fn get_devices(&self) -> Result, Box> { + match self { + #[cfg(all(target_os = "linux", feature = "bluetooth"))] + BluetoothAdapter::Bluez(inner) => { + let device_list = inner.get_device_list()?; + Ok(device_list + .into_iter() + .map(|device| { + BluetoothDevice::Bluez(BluetoothDeviceBluez::new_empty( + self.0.clone(), + device, + )) + }) + .collect()) + }, + #[cfg(all(target_os = "android", feature = "bluetooth"))] + BluetoothAdapter::Android(inner) => { + let device_list = inner.get_device_list()?; + Ok(device_list + .into_iter() + .map(|device| { + BluetoothDevice::Android(BluetoothDeviceAndroid::new_empty( + self.0.clone(), + device, + )) + }) + .collect()) + }, + #[cfg(all(target_os = "macos", feature = "bluetooth"))] + BluetoothAdapter::Mac(inner) => { + let device_list = inner.get_device_list()?; + Ok(device_list + .into_iter() + .map(|device| { + BluetoothDevice::Mac(Arc::new(BluetoothDeviceMac::new( + inner.clone(), + device, + ))) + }) + .collect()) + }, + #[cfg(not(any( + all(target_os = "linux", feature = "bluetooth"), + all(target_os = "android", feature = "bluetooth"), + all(target_os = "macos", feature = "bluetooth") + )))] + BluetoothAdapter::Empty(inner) => { + let device_list = inner.get_device_list()?; + Ok(device_list + .into_iter() + .map(|device| { + BluetoothDevice::Empty(Arc::new(BluetoothDeviceEmpty::new(device))) + }) + .collect()) + }, + #[cfg(feature = "bluetooth-test")] + BluetoothAdapter::Mock(inner) => { + let device_list = inner.get_device_list()?; + Ok(device_list + .into_iter() + .map(|device| { + BluetoothDevice::Mock(FakeBluetoothDevice::new_empty(inner.clone(), device)) + }) + .collect()) + }, + } + } + + pub fn get_device(&self, address: String) -> Result, Box> { + let devices = self.get_devices()?; + for device in devices { + if device.get_address()? == address { + return Ok(Some(device)); + } + } + Ok(None) + } + + pub fn create_mock_device(&self, _device: String) -> Result> { + match self { + #[cfg(feature = "bluetooth-test")] + BluetoothAdapter::Mock(inner) => Ok(BluetoothDevice::Mock( + FakeBluetoothDevice::new_empty(inner.clone(), _device), + )), + _ => Err(Box::from( + "Error! Test functions are not supported on real devices!", + )), + } + } + + pub fn create_discovery_session(&self) -> Result> { + let discovery_session = match self { + #[cfg(all(target_os = "linux", feature = "bluetooth"))] + BluetoothAdapter::Bluez(inner) => { + BluetoothDiscoverySession::Bluez(Arc::new( + BluetoothDiscoverySessionBluez::create_session(inner.get_id())?, + )); + }, + #[cfg(all(target_os = "android", feature = "bluetooth"))] + BluetoothAdapter::Android(inner) => BluetoothDiscoverySession::Android(Arc::new( + BluetoothDiscoverySessionAndroid::create_session(inner.clone())?, + )), + #[cfg(all(target_os = "macos", feature = "bluetooth"))] + BluetoothAdapter::Mac(_) => { + BluetoothDiscoverySession::Mac(Arc::new(BluetoothDiscoverySessionMac {})) + }, + #[cfg(not(any( + all(target_os = "linux", feature = "bluetooth"), + all(target_os = "android", feature = "bluetooth"), + all(target_os = "macos", feature = "bluetooth") + )))] + BluetoothAdapter::Empty(_) => { + BluetoothDiscoverySession::Empty(Arc::new(BluetoothDiscoverySessionEmpty {})) + }, + #[cfg(feature = "bluetooth-test")] + BluetoothAdapter::Mock(inner) => BluetoothDiscoverySession::Mock(Arc::new( + FakeBluetoothDiscoverySession::create_session(inner.clone())?, + )), + }; + Ok(discovery_session) + } + + pub fn get_address(&self) -> Result> { + get_inner_and_call!(self, BluetoothAdapter, get_address) + } + + pub fn get_name(&self) -> Result> { + get_inner_and_call!(self, BluetoothAdapter, get_name) + } + + pub fn get_alias(&self) -> Result> { + get_inner_and_call!(self, BluetoothAdapter, get_alias) + } + + pub fn get_class(&self) -> Result> { + get_inner_and_call!(self, BluetoothAdapter, get_class) + } + + pub fn is_powered(&self) -> Result> { + get_inner_and_call!(self, BluetoothAdapter, is_powered) + } + + pub fn is_discoverable(&self) -> Result> { + get_inner_and_call!(self, BluetoothAdapter, is_discoverable) + } + + pub fn is_pairable(&self) -> Result> { + get_inner_and_call!(self, BluetoothAdapter, is_pairable) + } + + pub fn get_pairable_timeout(&self) -> Result> { + get_inner_and_call!(self, BluetoothAdapter, get_pairable_timeout) + } + + pub fn get_discoverable_timeout(&self) -> Result> { + get_inner_and_call!(self, BluetoothAdapter, get_discoverable_timeout) + } + + pub fn is_discovering(&self) -> Result> { + get_inner_and_call!(self, BluetoothAdapter, is_discovering) + } + + pub fn get_uuids(&self) -> Result, Box> { + get_inner_and_call!(self, BluetoothAdapter, get_uuids) + } + + pub fn get_vendor_id_source(&self) -> Result> { + get_inner_and_call!(self, BluetoothAdapter, get_vendor_id_source) + } + + pub fn get_vendor_id(&self) -> Result> { + get_inner_and_call!(self, BluetoothAdapter, get_vendor_id) + } + + pub fn get_product_id(&self) -> Result> { + get_inner_and_call!(self, BluetoothAdapter, get_product_id) + } + + pub fn get_device_id(&self) -> Result> { + get_inner_and_call!(self, BluetoothAdapter, get_device_id) + } + + pub fn get_modalias(&self) -> Result<(String, u32, u32, u32), Box> { + get_inner_and_call!(self, BluetoothAdapter, get_modalias) + } + + #[cfg(feature = "bluetooth-test")] + pub fn set_id(&self, id: String) -> Result<(), Box> { + match self { + #[cfg(feature = "bluetooth-test")] + BluetoothAdapter::Mock(inner) => Ok(inner.set_id(id)), + _ => Err(Box::from( + "Error! Test functions are not supported on real devices!", + )), + } + } + + #[cfg(feature = "bluetooth-test")] + pub fn set_address(&self, address: String) -> Result<(), Box> { + get_inner_and_call_test_func!(self, BluetoothAdapter, set_address, address) + } + + #[cfg(feature = "bluetooth-test")] + pub fn set_name(&self, name: String) -> Result<(), Box> { + get_inner_and_call_test_func!(self, BluetoothAdapter, set_name, name) + } + + #[cfg(feature = "bluetooth-test")] + pub fn set_alias(&self, alias: String) -> Result<(), Box> { + get_inner_and_call_test_func!(self, BluetoothAdapter, set_alias, alias) + } + + #[cfg(feature = "bluetooth-test")] + pub fn set_class(&self, class: u32) -> Result<(), Box> { + get_inner_and_call_test_func!(self, BluetoothAdapter, set_class, class) + } + + #[cfg(feature = "bluetooth-test")] + pub fn set_powered(&self, powered: bool) -> Result<(), Box> { + get_inner_and_call_test_func!(self, BluetoothAdapter, set_powered, powered) + } + + #[cfg(feature = "bluetooth-test")] + pub fn is_present(&self) -> Result> { + get_inner_and_call_test_func!(self, BluetoothAdapter, is_present) + } + + #[cfg(feature = "bluetooth-test")] + pub fn set_present(&self, present: bool) -> Result<(), Box> { + get_inner_and_call_test_func!(self, BluetoothAdapter, set_present, present) + } + + #[cfg(feature = "bluetooth-test")] + pub fn set_discoverable(&self, discoverable: bool) -> Result<(), Box> { + get_inner_and_call_test_func!(self, BluetoothAdapter, set_discoverable, discoverable) + } + + #[cfg(feature = "bluetooth-test")] + pub fn set_pairable(&self, pairable: bool) -> Result<(), Box> { + get_inner_and_call_test_func!(self, BluetoothAdapter, set_pairable, pairable) + } + + #[cfg(feature = "bluetooth-test")] + pub fn set_pairable_timeout(&self, timeout: u32) -> Result<(), Box> { + get_inner_and_call_test_func!(self, BluetoothAdapter, set_pairable_timeout, timeout) + } + + #[cfg(feature = "bluetooth-test")] + pub fn set_can_start_discovery(&self, can_start_discovery: bool) -> Result<(), Box> { + get_inner_and_call_test_func!( + self, + BluetoothAdapter, + set_can_start_discovery, + can_start_discovery + ) + } + + #[cfg(feature = "bluetooth-test")] + pub fn set_discoverable_timeout(&self, timeout: u32) -> Result<(), Box> { + get_inner_and_call_test_func!(self, BluetoothAdapter, set_discoverable_timeout, timeout) + } + + #[cfg(feature = "bluetooth-test")] + pub fn set_discovering(&self, discovering: bool) -> Result<(), Box> { + get_inner_and_call_test_func!(self, BluetoothAdapter, set_discovering, discovering) + } + + #[cfg(feature = "bluetooth-test")] + pub fn set_can_stop_discovery(&self, can_stop_discovery: bool) -> Result<(), Box> { + get_inner_and_call_test_func!( + self, + BluetoothAdapter, + set_can_stop_discovery, + can_stop_discovery + ) + } + + #[cfg(feature = "bluetooth-test")] + pub fn set_uuids(&self, uuids: Vec) -> Result<(), Box> { + get_inner_and_call_test_func!(self, BluetoothAdapter, set_uuids, uuids) + } + + #[cfg(feature = "bluetooth-test")] + pub fn set_modalias(&self, modalias: String) -> Result<(), Box> { + get_inner_and_call_test_func!(self, BluetoothAdapter, set_modalias, modalias) + } + + #[cfg(feature = "bluetooth-test")] + pub fn get_ad_datas(&self) -> Result, Box> { + get_inner_and_call_test_func!(self, BluetoothAdapter, get_ad_datas) + } + + #[cfg(feature = "bluetooth-test")] + pub fn set_ad_datas(&self, ad_datas: Vec) -> Result<(), Box> { + get_inner_and_call_test_func!(self, BluetoothAdapter, set_ad_datas, ad_datas) + } +} diff --git a/components/bluetooth/bluetooth.rs b/components/bluetooth/bluetooth.rs new file mode 100644 index 00000000000..71766160849 --- /dev/null +++ b/components/bluetooth/bluetooth.rs @@ -0,0 +1,753 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +use std::collections::HashMap; +use std::error::Error; +use std::sync::Arc; + +#[cfg(all(target_os = "android", feature = "bluetooth"))] +use blurdroid::bluetooth_device::Device as BluetoothDeviceAndroid; +#[cfg(all(target_os = "android", feature = "bluetooth"))] +use blurdroid::bluetooth_discovery_session::DiscoverySession as BluetoothDiscoverySessionAndroid; +#[cfg(all(target_os = "android", feature = "bluetooth"))] +use blurdroid::bluetooth_gatt_characteristic::Characteristic as BluetoothGATTCharacteristicAndroid; +#[cfg(all(target_os = "android", feature = "bluetooth"))] +use blurdroid::bluetooth_gatt_descriptor::Descriptor as BluetoothGATTDescriptorAndroid; +#[cfg(all(target_os = "android", feature = "bluetooth"))] +use blurdroid::bluetooth_gatt_service::Service as BluetoothGATTServiceAndroid; +#[cfg(all(target_os = "macos", feature = "bluetooth"))] +use blurmac::BluetoothDevice as BluetoothDeviceMac; +#[cfg(all(target_os = "macos", feature = "bluetooth"))] +use blurmac::BluetoothDiscoverySession as BluetoothDiscoverySessionMac; +#[cfg(all(target_os = "macos", feature = "bluetooth"))] +use blurmac::BluetoothGATTCharacteristic as BluetoothGATTCharacteristicMac; +#[cfg(all(target_os = "macos", feature = "bluetooth"))] +use blurmac::BluetoothGATTDescriptor as BluetoothGATTDescriptorMac; +#[cfg(all(target_os = "macos", feature = "bluetooth"))] +use blurmac::BluetoothGATTService as BluetoothGATTServiceMac; +#[cfg(feature = "bluetooth-test")] +use blurmock::fake_characteristic::FakeBluetoothGATTCharacteristic; +#[cfg(feature = "bluetooth-test")] +use blurmock::fake_descriptor::FakeBluetoothGATTDescriptor; +#[cfg(feature = "bluetooth-test")] +use blurmock::fake_device::FakeBluetoothDevice; +#[cfg(feature = "bluetooth-test")] +use blurmock::fake_discovery_session::FakeBluetoothDiscoverySession; +#[cfg(feature = "bluetooth-test")] +use blurmock::fake_service::FakeBluetoothGATTService; +#[cfg(all(target_os = "linux", feature = "bluetooth"))] +use blurz::bluetooth_device::BluetoothDevice as BluetoothDeviceBluez; +#[cfg(all(target_os = "linux", feature = "bluetooth"))] +use blurz::bluetooth_discovery_session::BluetoothDiscoverySession as BluetoothDiscoverySessionBluez; +#[cfg(all(target_os = "linux", feature = "bluetooth"))] +use blurz::bluetooth_gatt_characteristic::BluetoothGATTCharacteristic as BluetoothGATTCharacteristicBluez; +#[cfg(all(target_os = "linux", feature = "bluetooth"))] +use blurz::bluetooth_gatt_descriptor::BluetoothGATTDescriptor as BluetoothGATTDescriptorBluez; +#[cfg(all(target_os = "linux", feature = "bluetooth"))] +use blurz::bluetooth_gatt_service::BluetoothGATTService as BluetoothGATTServiceBluez; + +pub use super::adapter::BluetoothAdapter; +#[cfg(not(any( + all(target_os = "linux", feature = "bluetooth"), + all(target_os = "android", feature = "bluetooth"), + all(target_os = "macos", feature = "bluetooth") +)))] +use super::empty::BluetoothDevice as BluetoothDeviceEmpty; +#[cfg(not(any( + all(target_os = "linux", feature = "bluetooth"), + all(target_os = "android", feature = "bluetooth"), + all(target_os = "macos", feature = "bluetooth") +)))] +use super::empty::BluetoothDiscoverySession as BluetoothDiscoverySessionEmpty; +#[cfg(not(any( + all(target_os = "linux", feature = "bluetooth"), + all(target_os = "android", feature = "bluetooth"), + all(target_os = "macos", feature = "bluetooth") +)))] +use super::empty::BluetoothGATTCharacteristic as BluetoothGATTCharacteristicEmpty; +#[cfg(not(any( + all(target_os = "linux", feature = "bluetooth"), + all(target_os = "android", feature = "bluetooth"), + all(target_os = "macos", feature = "bluetooth") +)))] +use super::empty::BluetoothGATTDescriptor as BluetoothGATTDescriptorEmpty; +#[cfg(not(any( + all(target_os = "linux", feature = "bluetooth"), + all(target_os = "android", feature = "bluetooth"), + all(target_os = "macos", feature = "bluetooth") +)))] +use super::empty::BluetoothGATTService as BluetoothGATTServiceEmpty; +use super::macros::get_inner_and_call; +#[cfg(feature = "bluetooth-test")] +use super::macros::get_inner_and_call_test_func; + +#[cfg(feature = "bluetooth-test")] +const NOT_SUPPORTED_ON_MOCK_ERROR: &'static str = + "Error! The first parameter must be a mock structure!"; + +#[derive(Debug)] +pub enum BluetoothDiscoverySession { + #[cfg(all(target_os = "linux", feature = "bluetooth"))] + Bluez(Arc), + #[cfg(all(target_os = "android", feature = "bluetooth"))] + Android(Arc), + #[cfg(all(target_os = "macos", feature = "bluetooth"))] + Mac(Arc), + #[cfg(not(any( + all(target_os = "linux", feature = "bluetooth"), + all(target_os = "android", feature = "bluetooth"), + all(target_os = "macos", feature = "bluetooth") + )))] + Empty(Arc), + #[cfg(feature = "bluetooth-test")] + Mock(Arc), +} + +#[derive(Clone, Debug)] +pub enum BluetoothDevice { + #[cfg(all(target_os = "linux", feature = "bluetooth"))] + Bluez(Arc), + #[cfg(all(target_os = "android", feature = "bluetooth"))] + Android(Arc), + #[cfg(all(target_os = "macos", feature = "bluetooth"))] + Mac(Arc), + #[cfg(not(any( + all(target_os = "linux", feature = "bluetooth"), + all(target_os = "android", feature = "bluetooth"), + all(target_os = "macos", feature = "bluetooth") + )))] + Empty(Arc), + #[cfg(feature = "bluetooth-test")] + Mock(Arc), +} + +#[derive(Clone, Debug)] +pub enum BluetoothGATTService { + #[cfg(all(target_os = "linux", feature = "bluetooth"))] + Bluez(Arc), + #[cfg(all(target_os = "android", feature = "bluetooth"))] + Android(Arc), + #[cfg(all(target_os = "macos", feature = "bluetooth"))] + Mac(Arc), + #[cfg(not(any( + all(target_os = "linux", feature = "bluetooth"), + all(target_os = "android", feature = "bluetooth"), + all(target_os = "macos", feature = "bluetooth") + )))] + Empty(Arc), + #[cfg(feature = "bluetooth-test")] + Mock(Arc), +} + +#[derive(Clone, Debug)] +pub enum BluetoothGATTCharacteristic { + #[cfg(all(target_os = "linux", feature = "bluetooth"))] + Bluez(Arc), + #[cfg(all(target_os = "android", feature = "bluetooth"))] + Android(Arc), + #[cfg(all(target_os = "macos", feature = "bluetooth"))] + Mac(Arc), + #[cfg(not(any( + all(target_os = "linux", feature = "bluetooth"), + all(target_os = "android", feature = "bluetooth"), + all(target_os = "macos", feature = "bluetooth") + )))] + Empty(Arc), + #[cfg(feature = "bluetooth-test")] + Mock(Arc), +} + +#[derive(Clone, Debug)] +pub enum BluetoothGATTDescriptor { + #[cfg(all(target_os = "linux", feature = "bluetooth"))] + Bluez(Arc), + #[cfg(all(target_os = "android", feature = "bluetooth"))] + Android(Arc), + #[cfg(all(target_os = "macos", feature = "bluetooth"))] + Mac(Arc), + #[cfg(not(any( + all(target_os = "linux", feature = "bluetooth"), + all(target_os = "android", feature = "bluetooth"), + all(target_os = "macos", feature = "bluetooth") + )))] + Empty(Arc), + #[cfg(feature = "bluetooth-test")] + Mock(Arc), +} + +impl BluetoothDiscoverySession { + pub fn start_discovery(&self) -> Result<(), Box> { + get_inner_and_call!(self, BluetoothDiscoverySession, start_discovery) + } + + pub fn stop_discovery(&self) -> Result<(), Box> { + get_inner_and_call!(self, BluetoothDiscoverySession, stop_discovery) + } +} + +impl BluetoothDevice { + pub fn get_id(&self) -> String { + get_inner_and_call!(self, BluetoothDevice, get_id) + } + + #[cfg(feature = "bluetooth-test")] + pub fn set_id(&self, id: String) { + match self { + &BluetoothDevice::Mock(ref fake_adapter) => fake_adapter.set_id(id), + _ => (), + } + } + + pub fn get_address(&self) -> Result> { + get_inner_and_call!(self, BluetoothDevice, get_address) + } + + #[cfg(feature = "bluetooth-test")] + pub fn set_address(&self, address: String) -> Result<(), Box> { + get_inner_and_call_test_func!(self, BluetoothDevice, set_address, address) + } + + pub fn get_name(&self) -> Result> { + get_inner_and_call!(self, BluetoothDevice, get_name) + } + + #[cfg(feature = "bluetooth-test")] + pub fn set_name(&self, name: Option) -> Result<(), Box> { + get_inner_and_call_test_func!(self, BluetoothDevice, set_name, name) + } + + pub fn get_icon(&self) -> Result> { + get_inner_and_call!(self, BluetoothDevice, get_icon) + } + + #[cfg(feature = "bluetooth-test")] + pub fn set_icon(&self, icon: String) -> Result<(), Box> { + get_inner_and_call_test_func!(self, BluetoothDevice, set_icon, icon) + } + + pub fn get_class(&self) -> Result> { + get_inner_and_call!(self, BluetoothDevice, get_class) + } + + #[cfg(feature = "bluetooth-test")] + pub fn set_class(&self, class: u32) -> Result<(), Box> { + get_inner_and_call_test_func!(self, BluetoothDevice, set_class, class) + } + + pub fn get_appearance(&self) -> Result> { + get_inner_and_call!(self, BluetoothDevice, get_appearance) + } + + #[cfg(feature = "bluetooth-test")] + pub fn set_appearance(&self, appearance: u16) -> Result<(), Box> { + get_inner_and_call_test_func!(self, BluetoothDevice, set_appearance, Some(appearance)) + } + + pub fn get_uuids(&self) -> Result, Box> { + get_inner_and_call!(self, BluetoothDevice, get_uuids) + } + + #[cfg(feature = "bluetooth-test")] + pub fn set_uuids(&self, uuids: Vec) -> Result<(), Box> { + get_inner_and_call_test_func!(self, BluetoothDevice, set_uuids, uuids) + } + + pub fn is_paired(&self) -> Result> { + get_inner_and_call!(self, BluetoothDevice, is_paired) + } + + #[cfg(feature = "bluetooth-test")] + pub fn set_paired(&self, paired: bool) -> Result<(), Box> { + get_inner_and_call_test_func!(self, BluetoothDevice, set_paired, paired) + } + + pub fn is_connected(&self) -> Result> { + get_inner_and_call!(self, BluetoothDevice, is_connected) + } + + #[cfg(feature = "bluetooth-test")] + pub fn set_connected(&self, connected: bool) -> Result<(), Box> { + get_inner_and_call_test_func!(self, BluetoothDevice, set_connected, connected) + } + + #[cfg(feature = "bluetooth-test")] + pub fn is_connectable(&self) -> Result> { + get_inner_and_call_test_func!(self, BluetoothDevice, is_connectable) + } + + #[cfg(feature = "bluetooth-test")] + pub fn set_connectable(&self, connectable: bool) -> Result<(), Box> { + get_inner_and_call_test_func!(self, BluetoothDevice, set_connectable, connectable) + } + + pub fn is_trusted(&self) -> Result> { + get_inner_and_call!(self, BluetoothDevice, is_trusted) + } + + #[cfg(feature = "bluetooth-test")] + pub fn set_trusted(&self, trusted: bool) -> Result<(), Box> { + get_inner_and_call_test_func!(self, BluetoothDevice, set_trusted, trusted) + } + + pub fn is_blocked(&self) -> Result> { + get_inner_and_call!(self, BluetoothDevice, is_blocked) + } + + #[cfg(feature = "bluetooth-test")] + pub fn set_blocked(&self, blocked: bool) -> Result<(), Box> { + get_inner_and_call_test_func!(self, BluetoothDevice, set_blocked, blocked) + } + + pub fn get_alias(&self) -> Result> { + get_inner_and_call!(self, BluetoothDevice, get_alias) + } + + #[cfg(feature = "bluetooth-test")] + pub fn set_alias(&self, alias: String) -> Result<(), Box> { + get_inner_and_call_test_func!(self, BluetoothDevice, set_alias, alias) + } + + pub fn is_legacy_pairing(&self) -> Result> { + get_inner_and_call!(self, BluetoothDevice, is_legacy_pairing) + } + + #[cfg(feature = "bluetooth-test")] + pub fn set_legacy_pairing(&self, legacy_pairing: bool) -> Result<(), Box> { + get_inner_and_call_test_func!(self, BluetoothDevice, set_legacy_pairing, legacy_pairing) + } + + pub fn get_vendor_id_source(&self) -> Result> { + get_inner_and_call!(self, BluetoothDevice, get_vendor_id_source) + } + + pub fn get_vendor_id(&self) -> Result> { + get_inner_and_call!(self, BluetoothDevice, get_vendor_id) + } + + pub fn get_product_id(&self) -> Result> { + get_inner_and_call!(self, BluetoothDevice, get_product_id) + } + + pub fn get_device_id(&self) -> Result> { + get_inner_and_call!(self, BluetoothDevice, get_device_id) + } + + pub fn get_modalias(&self) -> Result<(String, u32, u32, u32), Box> { + get_inner_and_call!(self, BluetoothDevice, get_modalias) + } + + #[cfg(feature = "bluetooth-test")] + pub fn set_modalias(&self, modalias: String) -> Result<(), Box> { + get_inner_and_call_test_func!(self, BluetoothDevice, set_modalias, modalias) + } + + pub fn get_rssi(&self) -> Result> { + get_inner_and_call!(self, BluetoothDevice, get_rssi) + } + + #[cfg(feature = "bluetooth-test")] + pub fn set_rssi(&self, rssi: i16) -> Result<(), Box> { + get_inner_and_call_test_func!(self, BluetoothDevice, set_rssi, Some(rssi)) + } + + pub fn get_tx_power(&self) -> Result> { + get_inner_and_call!(self, BluetoothDevice, get_tx_power) + } + + #[cfg(feature = "bluetooth-test")] + pub fn set_tx_power(&self, tx_power: i16) -> Result<(), Box> { + get_inner_and_call_test_func!(self, BluetoothDevice, set_tx_power, Some(tx_power)) + } + + pub fn get_manufacturer_data(&self) -> Result>, Box> { + get_inner_and_call!(self, BluetoothDevice, get_manufacturer_data) + } + + #[cfg(feature = "bluetooth-test")] + pub fn set_manufacturer_data( + &self, + manufacturer_data: HashMap>, + ) -> Result<(), Box> { + get_inner_and_call_test_func!( + self, + BluetoothDevice, + set_manufacturer_data, + Some(manufacturer_data) + ) + } + + pub fn get_service_data(&self) -> Result>, Box> { + get_inner_and_call!(self, BluetoothDevice, get_service_data) + } + + #[cfg(feature = "bluetooth-test")] + pub fn set_service_data( + &self, + service_data: HashMap>, + ) -> Result<(), Box> { + get_inner_and_call_test_func!(self, BluetoothDevice, set_service_data, Some(service_data)) + } + + pub fn get_gatt_services(&self) -> Result, Box> { + let services = get_inner_and_call!(self, BluetoothDevice, get_gatt_services)?; + Ok(services + .into_iter() + .map(|service| BluetoothGATTService::create_service(self.clone(), service)) + .collect()) + } + + pub fn connect(&self) -> Result<(), Box> { + get_inner_and_call!(self, BluetoothDevice, connect) + } + + pub fn disconnect(&self) -> Result<(), Box> { + get_inner_and_call!(self, BluetoothDevice, disconnect) + } + + pub fn connect_profile(&self, uuid: String) -> Result<(), Box> { + get_inner_and_call!(self, BluetoothDevice, connect_profile, uuid) + } + + pub fn disconnect_profile(&self, uuid: String) -> Result<(), Box> { + get_inner_and_call!(self, BluetoothDevice, disconnect_profile, uuid) + } + + pub fn pair(&self) -> Result<(), Box> { + get_inner_and_call!(self, BluetoothDevice, pair) + } + + pub fn cancel_pairing(&self) -> Result<(), Box> { + get_inner_and_call!(self, BluetoothDevice, cancel_pairing) + } +} + +impl BluetoothGATTService { + fn create_service(device: BluetoothDevice, service: String) -> BluetoothGATTService { + match device { + #[cfg(all(target_os = "linux", feature = "bluetooth"))] + BluetoothDevice::Bluez(_bluez_device) => { + BluetoothGATTService::Bluez(Arc::new(BluetoothGATTServiceBluez::new(service))) + }, + #[cfg(all(target_os = "android", feature = "bluetooth"))] + BluetoothDevice::Android(android_device) => BluetoothGATTService::Android(Arc::new( + BluetoothGATTServiceAndroid::new(android_device, service), + )), + #[cfg(all(target_os = "macos", feature = "bluetooth"))] + BluetoothDevice::Mac(mac_device) => BluetoothGATTService::Mac(Arc::new( + BluetoothGATTServiceMac::new(mac_device, service), + )), + #[cfg(not(any( + all(target_os = "linux", feature = "bluetooth"), + all(target_os = "android", feature = "bluetooth"), + all(target_os = "macos", feature = "bluetooth") + )))] + BluetoothDevice::Empty(_device) => { + BluetoothGATTService::Empty(Arc::new(BluetoothGATTServiceEmpty::new(service))) + }, + #[cfg(feature = "bluetooth-test")] + BluetoothDevice::Mock(fake_device) => BluetoothGATTService::Mock( + FakeBluetoothGATTService::new_empty(fake_device, service), + ), + } + } + + #[cfg(feature = "bluetooth-test")] + pub fn create_mock_service( + device: BluetoothDevice, + service: String, + ) -> Result> { + match device { + BluetoothDevice::Mock(fake_device) => Ok(BluetoothGATTService::Mock( + FakeBluetoothGATTService::new_empty(fake_device, service), + )), + _ => Err(Box::from( + "Error! The first parameter must be a mock structure!", + )), + } + } + + pub fn get_id(&self) -> String { + get_inner_and_call!(self, BluetoothGATTService, get_id) + } + + #[cfg(feature = "bluetooth-test")] + pub fn set_id(&self, id: String) { + match self { + &BluetoothGATTService::Mock(ref fake_service) => fake_service.set_id(id), + _ => (), + } + } + + pub fn get_uuid(&self) -> Result> { + get_inner_and_call!(self, BluetoothGATTService, get_uuid) + } + + #[cfg(feature = "bluetooth-test")] + pub fn set_uuid(&self, uuid: String) -> Result<(), Box> { + get_inner_and_call_test_func!(self, BluetoothGATTService, set_uuid, uuid) + } + + pub fn is_primary(&self) -> Result> { + get_inner_and_call!(self, BluetoothGATTService, is_primary) + } + + #[cfg(feature = "bluetooth-test")] + pub fn set_primary(&self, primary: bool) -> Result<(), Box> { + get_inner_and_call_test_func!(self, BluetoothGATTService, set_is_primary, primary) + } + + pub fn get_includes( + &self, + device: BluetoothDevice, + ) -> Result, Box> { + let services = get_inner_and_call!(self, BluetoothGATTService, get_includes)?; + Ok(services + .into_iter() + .map(|service| BluetoothGATTService::create_service(device.clone(), service)) + .collect()) + } + + pub fn get_gatt_characteristics( + &self, + ) -> Result, Box> { + let characteristics = + get_inner_and_call!(self, BluetoothGATTService, get_gatt_characteristics)?; + Ok(characteristics + .into_iter() + .map(|characteristic| { + BluetoothGATTCharacteristic::create_characteristic(self.clone(), characteristic) + }) + .collect()) + } +} + +impl BluetoothGATTCharacteristic { + fn create_characteristic( + service: BluetoothGATTService, + characteristic: String, + ) -> BluetoothGATTCharacteristic { + match service { + #[cfg(all(target_os = "linux", feature = "bluetooth"))] + BluetoothGATTService::Bluez(_bluez_service) => BluetoothGATTCharacteristic::Bluez( + Arc::new(BluetoothGATTCharacteristicBluez::new(characteristic)), + ), + #[cfg(all(target_os = "android", feature = "bluetooth"))] + BluetoothGATTService::Android(android_service) => { + BluetoothGATTCharacteristic::Android(Arc::new( + BluetoothGATTCharacteristicAndroid::new(android_service, characteristic), + )) + }, + #[cfg(all(target_os = "macos", feature = "bluetooth"))] + BluetoothGATTService::Mac(mac_service) => BluetoothGATTCharacteristic::Mac(Arc::new( + BluetoothGATTCharacteristicMac::new(mac_service, characteristic), + )), + #[cfg(not(any( + all(target_os = "linux", feature = "bluetooth"), + all(target_os = "android", feature = "bluetooth"), + all(target_os = "macos", feature = "bluetooth") + )))] + BluetoothGATTService::Empty(_service) => BluetoothGATTCharacteristic::Empty(Arc::new( + BluetoothGATTCharacteristicEmpty::new(characteristic), + )), + #[cfg(feature = "bluetooth-test")] + BluetoothGATTService::Mock(fake_service) => BluetoothGATTCharacteristic::Mock( + FakeBluetoothGATTCharacteristic::new_empty(fake_service, characteristic), + ), + } + } + + #[cfg(feature = "bluetooth-test")] + pub fn create_mock_characteristic( + service: BluetoothGATTService, + characteristic: String, + ) -> Result> { + match service { + BluetoothGATTService::Mock(fake_service) => Ok(BluetoothGATTCharacteristic::Mock( + FakeBluetoothGATTCharacteristic::new_empty(fake_service, characteristic), + )), + _ => Err(Box::from( + "Error! The first parameter must be a mock structure!", + )), + } + } + + pub fn get_id(&self) -> String { + get_inner_and_call!(self, BluetoothGATTCharacteristic, get_id) + } + + #[cfg(feature = "bluetooth-test")] + pub fn set_id(&self, id: String) { + match self { + &BluetoothGATTCharacteristic::Mock(ref fake_characteristic) => { + fake_characteristic.set_id(id) + }, + _ => (), + } + } + + pub fn get_uuid(&self) -> Result> { + get_inner_and_call!(self, BluetoothGATTCharacteristic, get_uuid) + } + + #[cfg(feature = "bluetooth-test")] + pub fn set_uuid(&self, uuid: String) -> Result<(), Box> { + get_inner_and_call_test_func!(self, BluetoothGATTCharacteristic, set_uuid, uuid) + } + + pub fn get_value(&self) -> Result, Box> { + get_inner_and_call!(self, BluetoothGATTCharacteristic, get_value) + } + + #[cfg(feature = "bluetooth-test")] + pub fn set_value(&self, value: Vec) -> Result<(), Box> { + get_inner_and_call_test_func!(self, BluetoothGATTCharacteristic, set_value, Some(value)) + } + + pub fn is_notifying(&self) -> Result> { + get_inner_and_call!(self, BluetoothGATTCharacteristic, is_notifying) + } + + #[cfg(feature = "bluetooth-test")] + pub fn set_notifying(&self, notifying: bool) -> Result<(), Box> { + get_inner_and_call_test_func!(self, BluetoothGATTCharacteristic, set_notifying, notifying) + } + + pub fn get_flags(&self) -> Result, Box> { + get_inner_and_call!(self, BluetoothGATTCharacteristic, get_flags) + } + + #[cfg(feature = "bluetooth-test")] + pub fn set_flags(&self, flags: Vec) -> Result<(), Box> { + get_inner_and_call_test_func!(self, BluetoothGATTCharacteristic, set_flags, flags) + } + + pub fn get_gatt_descriptors(&self) -> Result, Box> { + let descriptors = + get_inner_and_call!(self, BluetoothGATTCharacteristic, get_gatt_descriptors)?; + Ok(descriptors + .into_iter() + .map(|descriptor| BluetoothGATTDescriptor::create_descriptor(self.clone(), descriptor)) + .collect()) + } + + pub fn read_value(&self) -> Result, Box> { + get_inner_and_call!(@with_bluez_offset, self, BluetoothGATTCharacteristic, read_value) + } + + pub fn write_value(&self, values: Vec) -> Result<(), Box> { + get_inner_and_call!(@with_bluez_offset, self, BluetoothGATTCharacteristic, write_value, values) + } + + pub fn start_notify(&self) -> Result<(), Box> { + get_inner_and_call!(self, BluetoothGATTCharacteristic, start_notify) + } + + pub fn stop_notify(&self) -> Result<(), Box> { + get_inner_and_call!(self, BluetoothGATTCharacteristic, stop_notify) + } +} + +impl BluetoothGATTDescriptor { + fn create_descriptor( + characteristic: BluetoothGATTCharacteristic, + descriptor: String, + ) -> BluetoothGATTDescriptor { + match characteristic { + #[cfg(all(target_os = "linux", feature = "bluetooth"))] + BluetoothGATTCharacteristic::Bluez(_bluez_characteristic) => { + BluetoothGATTDescriptor::Bluez(Arc::new(BluetoothGATTDescriptorBluez::new( + descriptor, + ))) + }, + #[cfg(all(target_os = "android", feature = "bluetooth"))] + BluetoothGATTCharacteristic::Android(android_characteristic) => { + BluetoothGATTDescriptor::Android(Arc::new(BluetoothGATTDescriptorAndroid::new( + android_characteristic, + descriptor, + ))) + }, + #[cfg(all(target_os = "macos", feature = "bluetooth"))] + BluetoothGATTCharacteristic::Mac(_mac_characteristic) => { + BluetoothGATTDescriptor::Mac(Arc::new(BluetoothGATTDescriptorMac::new(descriptor))) + }, + #[cfg(not(any( + all(target_os = "linux", feature = "bluetooth"), + all(target_os = "android", feature = "bluetooth"), + all(target_os = "macos", feature = "bluetooth") + )))] + BluetoothGATTCharacteristic::Empty(_characteristic) => BluetoothGATTDescriptor::Empty( + Arc::new(BluetoothGATTDescriptorEmpty::new(descriptor)), + ), + #[cfg(feature = "bluetooth-test")] + BluetoothGATTCharacteristic::Mock(fake_characteristic) => { + BluetoothGATTDescriptor::Mock(FakeBluetoothGATTDescriptor::new_empty( + fake_characteristic, + descriptor, + )) + }, + } + } + + #[cfg(feature = "bluetooth-test")] + pub fn create_mock_descriptor( + characteristic: BluetoothGATTCharacteristic, + descriptor: String, + ) -> Result> { + match characteristic { + BluetoothGATTCharacteristic::Mock(fake_characteristic) => { + Ok(BluetoothGATTDescriptor::Mock( + FakeBluetoothGATTDescriptor::new_empty(fake_characteristic, descriptor), + )) + }, + _ => Err(Box::from(NOT_SUPPORTED_ON_MOCK_ERROR)), + } + } + + pub fn get_id(&self) -> String { + get_inner_and_call!(self, BluetoothGATTDescriptor, get_id) + } + + #[cfg(feature = "bluetooth-test")] + pub fn set_id(&self, id: String) { + match self { + &BluetoothGATTDescriptor::Mock(ref fake_descriptor) => fake_descriptor.set_id(id), + _ => (), + } + } + + pub fn get_uuid(&self) -> Result> { + get_inner_and_call!(self, BluetoothGATTDescriptor, get_uuid) + } + + #[cfg(feature = "bluetooth-test")] + pub fn set_uuid(&self, uuid: String) -> Result<(), Box> { + get_inner_and_call_test_func!(self, BluetoothGATTDescriptor, set_uuid, uuid) + } + + pub fn get_value(&self) -> Result, Box> { + get_inner_and_call!(self, BluetoothGATTDescriptor, get_value) + } + + #[cfg(feature = "bluetooth-test")] + pub fn set_value(&self, value: Vec) -> Result<(), Box> { + get_inner_and_call_test_func!(self, BluetoothGATTDescriptor, set_value, Some(value)) + } + + pub fn get_flags(&self) -> Result, Box> { + get_inner_and_call!(self, BluetoothGATTDescriptor, get_flags) + } + + #[cfg(feature = "bluetooth-test")] + pub fn set_flags(&self, flags: Vec) -> Result<(), Box> { + get_inner_and_call_test_func!(self, BluetoothGATTDescriptor, set_flags, flags) + } + + pub fn read_value(&self) -> Result, Box> { + get_inner_and_call!(@with_bluez_offset, self, BluetoothGATTDescriptor, read_value) + } + + pub fn write_value(&self, values: Vec) -> Result<(), Box> { + get_inner_and_call!(@with_bluez_offset, self, BluetoothGATTDescriptor, write_value, values) + } +} diff --git a/components/bluetooth/empty.rs b/components/bluetooth/empty.rs new file mode 100644 index 00000000000..27773a10a47 --- /dev/null +++ b/components/bluetooth/empty.rs @@ -0,0 +1,377 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +use std::collections::HashMap; +use std::error::Error; +use std::sync::Arc; + +const NOT_SUPPORTED_ERROR: &'static str = "Error! Not supported platform!"; + +#[derive(Clone, Debug)] +pub struct EmptyAdapter {} + +impl EmptyAdapter { + pub fn init() -> Result> { + Ok(EmptyAdapter::new()) + } + + fn new() -> EmptyAdapter { + EmptyAdapter {} + } + + pub fn get_id(&self) -> String { + String::new() + } + + pub fn get_device_list(&self) -> Result, Box> { + Err(Box::from(NOT_SUPPORTED_ERROR)) + } + + pub fn get_address(&self) -> Result> { + Err(Box::from(NOT_SUPPORTED_ERROR)) + } + + pub fn get_name(&self) -> Result> { + Err(Box::from(NOT_SUPPORTED_ERROR)) + } + + pub fn get_alias(&self) -> Result> { + Err(Box::from(NOT_SUPPORTED_ERROR)) + } + + pub fn set_alias(&self, _value: String) -> Result<(), Box> { + Err(Box::from(NOT_SUPPORTED_ERROR)) + } + + pub fn get_class(&self) -> Result> { + Err(Box::from(NOT_SUPPORTED_ERROR)) + } + + pub fn is_powered(&self) -> Result> { + Err(Box::from(NOT_SUPPORTED_ERROR)) + } + + pub fn set_powered(&self, _value: bool) -> Result<(), Box> { + Err(Box::from(NOT_SUPPORTED_ERROR)) + } + + pub fn is_discoverable(&self) -> Result> { + Err(Box::from(NOT_SUPPORTED_ERROR)) + } + + pub fn set_discoverable(&self, _value: bool) -> Result<(), Box> { + Err(Box::from(NOT_SUPPORTED_ERROR)) + } + + pub fn is_pairable(&self) -> Result> { + Err(Box::from(NOT_SUPPORTED_ERROR)) + } + + pub fn set_pairable(&self, _value: bool) -> Result<(), Box> { + Err(Box::from(NOT_SUPPORTED_ERROR)) + } + + pub fn get_pairable_timeout(&self) -> Result> { + Err(Box::from(NOT_SUPPORTED_ERROR)) + } + + pub fn set_pairable_timeout(&self, _value: u32) -> Result<(), Box> { + Err(Box::from(NOT_SUPPORTED_ERROR)) + } + + pub fn get_discoverable_timeout(&self) -> Result> { + Err(Box::from(NOT_SUPPORTED_ERROR)) + } + + pub fn set_discoverable_timeout(&self, _value: u32) -> Result<(), Box> { + Err(Box::from(NOT_SUPPORTED_ERROR)) + } + + pub fn is_discovering(&self) -> Result> { + Err(Box::from(NOT_SUPPORTED_ERROR)) + } + + pub fn get_uuids(&self) -> Result, Box> { + Err(Box::from(NOT_SUPPORTED_ERROR)) + } + + pub fn get_vendor_id_source(&self) -> Result> { + Err(Box::from(NOT_SUPPORTED_ERROR)) + } + + pub fn get_vendor_id(&self) -> Result> { + Err(Box::from(NOT_SUPPORTED_ERROR)) + } + + pub fn get_product_id(&self) -> Result> { + Err(Box::from(NOT_SUPPORTED_ERROR)) + } + + pub fn get_device_id(&self) -> Result> { + Err(Box::from(NOT_SUPPORTED_ERROR)) + } + + pub fn get_modalias(&self) -> Result<(String, u32, u32, u32), Box> { + Err(Box::from(NOT_SUPPORTED_ERROR)) + } +} + +#[derive(Clone, Debug)] +pub struct BluetoothDiscoverySession {} + +impl BluetoothDiscoverySession { + pub fn create_session( + _adapter: Arc, + ) -> Result> { + Ok(BluetoothDiscoverySession {}) + } + + pub fn start_discovery(&self) -> Result<(), Box> { + Err(Box::from(NOT_SUPPORTED_ERROR)) + } + + pub fn stop_discovery(&self) -> Result<(), Box> { + Err(Box::from(NOT_SUPPORTED_ERROR)) + } +} + +#[derive(Clone, Debug)] +pub struct BluetoothDevice {} + +impl BluetoothDevice { + pub fn new(_device: String) -> BluetoothDevice { + BluetoothDevice {} + } + + pub fn get_id(&self) -> String { + String::new() + } + + pub fn get_address(&self) -> Result> { + Err(Box::from(NOT_SUPPORTED_ERROR)) + } + + pub fn get_name(&self) -> Result> { + Err(Box::from(NOT_SUPPORTED_ERROR)) + } + + pub fn get_icon(&self) -> Result> { + Err(Box::from(NOT_SUPPORTED_ERROR)) + } + + pub fn get_class(&self) -> Result> { + Err(Box::from(NOT_SUPPORTED_ERROR)) + } + + pub fn get_appearance(&self) -> Result> { + Err(Box::from(NOT_SUPPORTED_ERROR)) + } + + pub fn get_uuids(&self) -> Result, Box> { + Err(Box::from(NOT_SUPPORTED_ERROR)) + } + + pub fn is_paired(&self) -> Result> { + Err(Box::from(NOT_SUPPORTED_ERROR)) + } + + pub fn is_connected(&self) -> Result> { + Err(Box::from(NOT_SUPPORTED_ERROR)) + } + + pub fn is_trusted(&self) -> Result> { + Err(Box::from(NOT_SUPPORTED_ERROR)) + } + + pub fn is_blocked(&self) -> Result> { + Err(Box::from(NOT_SUPPORTED_ERROR)) + } + + pub fn get_alias(&self) -> Result> { + Err(Box::from(NOT_SUPPORTED_ERROR)) + } + + pub fn set_alias(&self, _value: String) -> Result<(), Box> { + Err(Box::from(NOT_SUPPORTED_ERROR)) + } + + pub fn is_legacy_pairing(&self) -> Result> { + Err(Box::from(NOT_SUPPORTED_ERROR)) + } + + pub fn get_vendor_id_source(&self) -> Result> { + Err(Box::from(NOT_SUPPORTED_ERROR)) + } + + pub fn get_vendor_id(&self) -> Result> { + Err(Box::from(NOT_SUPPORTED_ERROR)) + } + + pub fn get_product_id(&self) -> Result> { + Err(Box::from(NOT_SUPPORTED_ERROR)) + } + + pub fn get_device_id(&self) -> Result> { + Err(Box::from(NOT_SUPPORTED_ERROR)) + } + + pub fn get_modalias(&self) -> Result<(String, u32, u32, u32), Box> { + Err(Box::from(NOT_SUPPORTED_ERROR)) + } + + pub fn get_rssi(&self) -> Result> { + Err(Box::from(NOT_SUPPORTED_ERROR)) + } + + pub fn get_tx_power(&self) -> Result> { + Err(Box::from(NOT_SUPPORTED_ERROR)) + } + + pub fn get_manufacturer_data(&self) -> Result>, Box> { + Err(Box::from(NOT_SUPPORTED_ERROR)) + } + + pub fn get_service_data(&self) -> Result>, Box> { + Err(Box::from(NOT_SUPPORTED_ERROR)) + } + + pub fn get_gatt_services(&self) -> Result, Box> { + Err(Box::from(NOT_SUPPORTED_ERROR)) + } + + pub fn connect(&self) -> Result<(), Box> { + Err(Box::from(NOT_SUPPORTED_ERROR)) + } + + pub fn disconnect(&self) -> Result<(), Box> { + Err(Box::from(NOT_SUPPORTED_ERROR)) + } + + pub fn connect_profile(&self, _uuid: String) -> Result<(), Box> { + Err(Box::from(NOT_SUPPORTED_ERROR)) + } + + pub fn disconnect_profile(&self, _uuid: String) -> Result<(), Box> { + Err(Box::from(NOT_SUPPORTED_ERROR)) + } + + pub fn pair(&self) -> Result<(), Box> { + Err(Box::from(NOT_SUPPORTED_ERROR)) + } + + pub fn cancel_pairing(&self) -> Result<(), Box> { + Err(Box::from(NOT_SUPPORTED_ERROR)) + } +} + +#[derive(Clone, Debug)] +pub struct BluetoothGATTService {} + +impl BluetoothGATTService { + pub fn new(_service: String) -> BluetoothGATTService { + BluetoothGATTService {} + } + + pub fn get_id(&self) -> String { + String::new() + } + + pub fn get_uuid(&self) -> Result> { + Err(Box::from(NOT_SUPPORTED_ERROR)) + } + + pub fn is_primary(&self) -> Result> { + Err(Box::from(NOT_SUPPORTED_ERROR)) + } + + pub fn get_includes(&self) -> Result, Box> { + Err(Box::from(NOT_SUPPORTED_ERROR)) + } + + pub fn get_gatt_characteristics(&self) -> Result, Box> { + Err(Box::from(NOT_SUPPORTED_ERROR)) + } +} + +#[derive(Clone, Debug)] +pub struct BluetoothGATTCharacteristic {} + +impl BluetoothGATTCharacteristic { + pub fn new(_characteristic: String) -> BluetoothGATTCharacteristic { + BluetoothGATTCharacteristic {} + } + + pub fn get_id(&self) -> String { + String::new() + } + + pub fn get_uuid(&self) -> Result> { + Err(Box::from(NOT_SUPPORTED_ERROR)) + } + + pub fn get_value(&self) -> Result, Box> { + Err(Box::from(NOT_SUPPORTED_ERROR)) + } + + pub fn is_notifying(&self) -> Result> { + Err(Box::from(NOT_SUPPORTED_ERROR)) + } + + pub fn get_flags(&self) -> Result, Box> { + Err(Box::from(NOT_SUPPORTED_ERROR)) + } + + pub fn get_gatt_descriptors(&self) -> Result, Box> { + Err(Box::from(NOT_SUPPORTED_ERROR)) + } + + pub fn read_value(&self) -> Result, Box> { + Err(Box::from(NOT_SUPPORTED_ERROR)) + } + + pub fn write_value(&self, _values: Vec) -> Result<(), Box> { + Err(Box::from(NOT_SUPPORTED_ERROR)) + } + + pub fn start_notify(&self) -> Result<(), Box> { + Err(Box::from(NOT_SUPPORTED_ERROR)) + } + + pub fn stop_notify(&self) -> Result<(), Box> { + Err(Box::from(NOT_SUPPORTED_ERROR)) + } +} + +#[derive(Clone, Debug)] +pub struct BluetoothGATTDescriptor {} + +impl BluetoothGATTDescriptor { + pub fn new(_descriptor: String) -> BluetoothGATTDescriptor { + BluetoothGATTDescriptor {} + } + + pub fn get_id(&self) -> String { + String::new() + } + + pub fn get_uuid(&self) -> Result> { + Err(Box::from(NOT_SUPPORTED_ERROR)) + } + + pub fn get_value(&self) -> Result, Box> { + Err(Box::from(NOT_SUPPORTED_ERROR)) + } + + pub fn get_flags(&self) -> Result, Box> { + Err(Box::from(NOT_SUPPORTED_ERROR)) + } + + pub fn read_value(&self) -> Result, Box> { + Err(Box::from(NOT_SUPPORTED_ERROR)) + } + + pub fn write_value(&self, _values: Vec) -> Result<(), Box> { + Err(Box::from(NOT_SUPPORTED_ERROR)) + } +} diff --git a/components/bluetooth/lib.rs b/components/bluetooth/lib.rs index e6d7a533a53..f2a7bc4de70 100644 --- a/components/bluetooth/lib.rs +++ b/components/bluetooth/lib.rs @@ -2,6 +2,15 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ +pub mod adapter; +pub mod bluetooth; +#[cfg(not(any( + all(target_os = "linux", feature = "bluetooth"), + all(target_os = "android", feature = "bluetooth"), + all(target_os = "macos", feature = "bluetooth") +)))] +mod empty; +mod macros; pub mod test; use std::borrow::ToOwned; @@ -20,16 +29,17 @@ use bluetooth_traits::{ BluetoothRequest, BluetoothResponse, BluetoothResponseResult, BluetoothResult, BluetoothServiceMsg, GATTType, }; -use device::bluetooth::{ - BluetoothAdapter, BluetoothDevice, BluetoothGATTCharacteristic, BluetoothGATTDescriptor, - BluetoothGATTService, -}; use embedder_traits::{EmbedderMsg, EmbedderProxy}; use ipc_channel::ipc::{self, IpcReceiver, IpcSender}; use log::warn; use servo_config::pref; use servo_rand::{self, Rng}; +use crate::bluetooth::{ + BluetoothAdapter, BluetoothDevice, BluetoothGATTCharacteristic, BluetoothGATTDescriptor, + BluetoothGATTService, +}; + // A transaction not completed within 30 seconds shall time out. Such a transaction shall be considered to have failed. // https://www.bluetooth.org/DocMan/handlers/DownloadDoc.ashx?doc_id=286439 (Vol. 3, page 480) const MAXIMUM_TRANSACTION_TIME: u8 = 30; @@ -67,9 +77,9 @@ impl BluetoothThreadFactory for IpcSender { fn new(embedder_proxy: EmbedderProxy) -> IpcSender { let (sender, receiver) = ipc::channel().unwrap(); let adapter = if pref!(dom.bluetooth.enabled) { - BluetoothAdapter::init() + BluetoothAdapter::new() } else { - BluetoothAdapter::init_mock() + BluetoothAdapter::new_mock() } .ok(); thread::Builder::new() @@ -287,7 +297,7 @@ impl BluetoothManager { self.cached_characteristics.clear(); self.cached_descriptors.clear(); self.allowed_services.clear(); - self.adapter = BluetoothAdapter::init_mock().ok(); + self.adapter = BluetoothAdapter::new_mock().ok(); match test::test(self, data_set_name) { Ok(_) => return Ok(()), Err(error) => Err(BluetoothError::Type(error.to_string())), @@ -324,7 +334,7 @@ impl BluetoothManager { .as_ref() .map_or(false, |a| a.get_address().is_ok()); if !adapter_valid { - self.adapter = BluetoothAdapter::init().ok(); + self.adapter = BluetoothAdapter::new().ok(); } let adapter = self.adapter.as_ref()?; diff --git a/components/bluetooth/macros.rs b/components/bluetooth/macros.rs new file mode 100644 index 00000000000..fd76d1eb549 --- /dev/null +++ b/components/bluetooth/macros.rs @@ -0,0 +1,98 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +macro_rules! get_inner_and_call( + ($enum_value: expr, $enum_type: ident, $function_name: ident) => { + match $enum_value { + #[cfg(all(target_os = "linux", feature = "bluetooth"))] + &$enum_type::Bluez(ref bluez) => bluez.$function_name(), + #[cfg(all(target_os = "android", feature = "bluetooth"))] + &$enum_type::Android(ref android) => android.$function_name(), + #[cfg(all(target_os = "macos", feature = "bluetooth"))] + &$enum_type::Mac(ref mac) => mac.$function_name(), + #[cfg(not(any(all(target_os = "linux", feature = "bluetooth"), + all(target_os = "android", feature = "bluetooth"), + all(target_os = "macos", feature = "bluetooth"))))] + &$enum_type::Empty(ref empty) => empty.$function_name(), + #[cfg(feature = "bluetooth-test")] + &$enum_type::Mock(ref fake) => fake.$function_name(), + } + }; + + (@with_bluez_offset, $enum_value: expr, $enum_type: ident, $function_name: ident) => { + match $enum_value { + #[cfg(all(target_os = "linux", feature = "bluetooth"))] + &$enum_type::Bluez(ref bluez) => bluez.$function_name(None), + #[cfg(all(target_os = "android", feature = "bluetooth"))] + &$enum_type::Android(ref android) => android.$function_name(), + #[cfg(all(target_os = "macos", feature = "bluetooth"))] + &$enum_type::Mac(ref mac) => mac.$function_name(), + #[cfg(not(any(all(target_os = "linux", feature = "bluetooth"), + all(target_os = "android", feature = "bluetooth"), + all(target_os = "macos", feature = "bluetooth"))))] + &$enum_type::Empty(ref empty) => empty.$function_name(), + #[cfg(feature = "bluetooth-test")] + &$enum_type::Mock(ref fake) => fake.$function_name(), + } + }; + + ($enum_value: expr, $enum_type: ident, $function_name: ident, $value: expr) => { + match $enum_value { + #[cfg(all(target_os = "linux", feature = "bluetooth"))] + &$enum_type::Bluez(ref bluez) => bluez.$function_name($value), + #[cfg(all(target_os = "android", feature = "bluetooth"))] + &$enum_type::Android(ref android) => android.$function_name($value), + #[cfg(all(target_os = "macos", feature = "bluetooth"))] + &$enum_type::Mac(ref mac) => mac.$function_name($value), + #[cfg(not(any(all(target_os = "linux", feature = "bluetooth"), + all(target_os = "android", feature = "bluetooth"), + all(target_os = "macos", feature = "bluetooth"))))] + &$enum_type::Empty(ref empty) => empty.$function_name($value), + #[cfg(feature = "bluetooth-test")] + &$enum_type::Mock(ref fake) => fake.$function_name($value), + } + }; + + (@with_bluez_offset, $enum_value: expr, $enum_type: ident, $function_name: ident, $value: expr) => { + match $enum_value { + #[cfg(all(target_os = "linux", feature = "bluetooth"))] + &$enum_type::Bluez(ref bluez) => bluez.$function_name($value, None), + #[cfg(all(target_os = "android", feature = "bluetooth"))] + &$enum_type::Android(ref android) => android.$function_name($value), + #[cfg(all(target_os = "macos", feature = "bluetooth"))] + &$enum_type::Mac(ref mac) => mac.$function_name($value), + #[cfg(not(any(all(target_os = "linux", feature = "bluetooth"), + all(target_os = "android", feature = "bluetooth"), + all(target_os = "macos", feature = "bluetooth"))))] + &$enum_type::Empty(ref empty) => empty.$function_name($value), + #[cfg(feature = "bluetooth-test")] + &$enum_type::Mock(ref fake) => fake.$function_name($value), + } + }; +); + +#[cfg(feature = "bluetooth-test")] +macro_rules! get_inner_and_call_test_func { + ($enum_value: expr, $enum_type: ident, $function_name: ident, $value: expr) => { + match $enum_value { + &$enum_type::Mock(ref fake) => fake.$function_name($value), + _ => Err(Box::from( + "Error! Test functions are not supported on real devices!", + )), + } + }; + + ($enum_value: expr, $enum_type: ident, $function_name: ident) => { + match $enum_value { + &$enum_type::Mock(ref fake) => fake.$function_name(), + _ => Err(Box::from( + "Error! Test functions are not supported on real devices!", + )), + } + }; +} + +pub(crate) use get_inner_and_call; +#[cfg(feature = "bluetooth-test")] +pub(crate) use get_inner_and_call_test_func; diff --git a/components/bluetooth/test.rs b/components/bluetooth/test.rs index 25e4d9cbb7d..bf3b19b82cc 100644 --- a/components/bluetooth/test.rs +++ b/components/bluetooth/test.rs @@ -8,12 +8,12 @@ use std::collections::{HashMap, HashSet}; use std::error::Error; use std::string::String; -use device::bluetooth::{ +use uuid::Uuid; + +use crate::bluetooth::{ BluetoothAdapter, BluetoothDevice, BluetoothGATTCharacteristic, BluetoothGATTDescriptor, BluetoothGATTService, }; -use uuid::Uuid; - use crate::BluetoothManager; thread_local!(pub static CACHED_IDS: RefCell> = RefCell::new(HashSet::new())); @@ -152,7 +152,7 @@ fn create_device( name: String, address: String, ) -> Result> { - let device = BluetoothDevice::create_mock_device(adapter.clone(), generate_id().to_string())?; + let device = adapter.create_mock_device(generate_id().to_string())?; device.set_name(Some(name))?; device.set_address(address)?; device.set_connectable(true)?; -- cgit v1.2.3