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
This commit is contained in:
Martin Robinson 2024-01-09 10:13:41 +01:00 committed by GitHub
parent fddc4a430f
commit 6a804cd775
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
23 changed files with 3880 additions and 27 deletions

17
Cargo.lock generated
View file

@ -477,7 +477,10 @@ version = "0.0.1"
dependencies = [ dependencies = [
"bitflags 2.4.1", "bitflags 2.4.1",
"bluetooth_traits", "bluetooth_traits",
"device", "blurdroid",
"blurmac",
"blurmock",
"blurz",
"embedder_traits", "embedder_traits",
"ipc-channel", "ipc-channel",
"log", "log",
@ -505,7 +508,6 @@ checksum = "19b23557dd27704797128f9db2816416bef20dad62d4a9768714eeb65f07d296"
[[package]] [[package]]
name = "blurmac" name = "blurmac"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/servo/devices?rev=cb28c4725ffbfece99dab842d17d3e8c50774778#cb28c4725ffbfece99dab842d17d3e8c50774778"
dependencies = [ dependencies = [
"log", "log",
"objc", "objc",
@ -1322,17 +1324,6 @@ dependencies = [
"syn 1.0.103", "syn 1.0.103",
] ]
[[package]]
name = "device"
version = "0.0.1"
source = "git+https://github.com/servo/devices?rev=cb28c4725ffbfece99dab842d17d3e8c50774778#cb28c4725ffbfece99dab842d17d3e8c50774778"
dependencies = [
"blurdroid",
"blurmac",
"blurmock",
"blurz",
]
[[package]] [[package]]
name = "devtools" name = "devtools"
version = "0.0.1" version = "0.0.1"

View file

@ -13,7 +13,7 @@ path = "lib.rs"
[dependencies] [dependencies]
bitflags = { workspace = true } bitflags = { workspace = true }
bluetooth_traits = { 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 } embedder_traits = { workspace = true }
ipc-channel = { workspace = true } ipc-channel = { workspace = true }
log = { workspace = true } log = { workspace = true }
@ -22,4 +22,14 @@ servo_rand = { path = "../rand" }
uuid = { workspace = true } uuid = { workspace = true }
[features] [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 }

View file

@ -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).

View file

@ -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<BluetoothAdapterBluez>),
#[cfg(all(target_os = "android", feature = "bluetooth"))]
Android(Arc<BluetoothAdapterAndroid>),
#[cfg(all(target_os = "macos", feature = "bluetooth"))]
Mac(Arc<BluetoothAdapterMac>),
#[cfg(not(any(
all(target_os = "linux", feature = "bluetooth"),
all(target_os = "android", feature = "bluetooth"),
all(target_os = "macos", feature = "bluetooth")
)))]
Empty(Arc<BluetoothAdapterEmpty>),
#[cfg(feature = "bluetooth-test")]
Mock(Arc<FakeBluetoothAdapter>),
}
impl BluetoothAdapter {
#[cfg(all(target_os = "linux", feature = "bluetooth"))]
pub fn new() -> Result<BluetoothAdapter, Box<dyn Error>> {
let bluez_adapter = BluetoothAdapterBluez::init()?;
Ok(Self::Bluez(Arc::new(bluez_adapter)))
}
#[cfg(all(target_os = "android", feature = "bluetooth"))]
pub fn new() -> Result<BluetoothAdapter, Box<dyn Error>> {
let blurdroid_adapter = BluetoothAdapterAndroid::get_adapter()?;
Ok(Self::Android(Arc::new(blurdroid_adapter)))
}
#[cfg(all(target_os = "macos", feature = "bluetooth"))]
pub fn new() -> Result<BluetoothAdapter, Box<dyn Error>> {
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<BluetoothAdapter, Box<dyn Error>> {
let adapter = BluetoothAdapterEmpty::init()?;
Ok(Self::Empty(Arc::new(adapter)))
}
#[cfg(feature = "bluetooth-test")]
pub fn new_mock() -> Result<BluetoothAdapter, Box<dyn Error>> {
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<Vec<BluetoothDevice>, Box<dyn Error>> {
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<Option<BluetoothDevice>, Box<dyn Error>> {
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<BluetoothDevice, Box<dyn Error>> {
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<BluetoothDiscoverySession, Box<dyn Error>> {
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<String, Box<dyn Error>> {
get_inner_and_call!(self, BluetoothAdapter, get_address)
}
pub fn get_name(&self) -> Result<String, Box<dyn Error>> {
get_inner_and_call!(self, BluetoothAdapter, get_name)
}
pub fn get_alias(&self) -> Result<String, Box<dyn Error>> {
get_inner_and_call!(self, BluetoothAdapter, get_alias)
}
pub fn get_class(&self) -> Result<u32, Box<dyn Error>> {
get_inner_and_call!(self, BluetoothAdapter, get_class)
}
pub fn is_powered(&self) -> Result<bool, Box<dyn Error>> {
get_inner_and_call!(self, BluetoothAdapter, is_powered)
}
pub fn is_discoverable(&self) -> Result<bool, Box<dyn Error>> {
get_inner_and_call!(self, BluetoothAdapter, is_discoverable)
}
pub fn is_pairable(&self) -> Result<bool, Box<dyn Error>> {
get_inner_and_call!(self, BluetoothAdapter, is_pairable)
}
pub fn get_pairable_timeout(&self) -> Result<u32, Box<dyn Error>> {
get_inner_and_call!(self, BluetoothAdapter, get_pairable_timeout)
}
pub fn get_discoverable_timeout(&self) -> Result<u32, Box<dyn Error>> {
get_inner_and_call!(self, BluetoothAdapter, get_discoverable_timeout)
}
pub fn is_discovering(&self) -> Result<bool, Box<dyn Error>> {
get_inner_and_call!(self, BluetoothAdapter, is_discovering)
}
pub fn get_uuids(&self) -> Result<Vec<String>, Box<dyn Error>> {
get_inner_and_call!(self, BluetoothAdapter, get_uuids)
}
pub fn get_vendor_id_source(&self) -> Result<String, Box<dyn Error>> {
get_inner_and_call!(self, BluetoothAdapter, get_vendor_id_source)
}
pub fn get_vendor_id(&self) -> Result<u32, Box<dyn Error>> {
get_inner_and_call!(self, BluetoothAdapter, get_vendor_id)
}
pub fn get_product_id(&self) -> Result<u32, Box<dyn Error>> {
get_inner_and_call!(self, BluetoothAdapter, get_product_id)
}
pub fn get_device_id(&self) -> Result<u32, Box<dyn Error>> {
get_inner_and_call!(self, BluetoothAdapter, get_device_id)
}
pub fn get_modalias(&self) -> Result<(String, u32, u32, u32), Box<dyn Error>> {
get_inner_and_call!(self, BluetoothAdapter, get_modalias)
}
#[cfg(feature = "bluetooth-test")]
pub fn set_id(&self, id: String) -> Result<(), Box<dyn Error>> {
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<dyn Error>> {
get_inner_and_call_test_func!(self, BluetoothAdapter, set_address, address)
}
#[cfg(feature = "bluetooth-test")]
pub fn set_name(&self, name: String) -> Result<(), Box<dyn Error>> {
get_inner_and_call_test_func!(self, BluetoothAdapter, set_name, name)
}
#[cfg(feature = "bluetooth-test")]
pub fn set_alias(&self, alias: String) -> Result<(), Box<dyn Error>> {
get_inner_and_call_test_func!(self, BluetoothAdapter, set_alias, alias)
}
#[cfg(feature = "bluetooth-test")]
pub fn set_class(&self, class: u32) -> Result<(), Box<dyn Error>> {
get_inner_and_call_test_func!(self, BluetoothAdapter, set_class, class)
}
#[cfg(feature = "bluetooth-test")]
pub fn set_powered(&self, powered: bool) -> Result<(), Box<dyn Error>> {
get_inner_and_call_test_func!(self, BluetoothAdapter, set_powered, powered)
}
#[cfg(feature = "bluetooth-test")]
pub fn is_present(&self) -> Result<bool, Box<dyn Error>> {
get_inner_and_call_test_func!(self, BluetoothAdapter, is_present)
}
#[cfg(feature = "bluetooth-test")]
pub fn set_present(&self, present: bool) -> Result<(), Box<dyn Error>> {
get_inner_and_call_test_func!(self, BluetoothAdapter, set_present, present)
}
#[cfg(feature = "bluetooth-test")]
pub fn set_discoverable(&self, discoverable: bool) -> Result<(), Box<dyn Error>> {
get_inner_and_call_test_func!(self, BluetoothAdapter, set_discoverable, discoverable)
}
#[cfg(feature = "bluetooth-test")]
pub fn set_pairable(&self, pairable: bool) -> Result<(), Box<dyn Error>> {
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<dyn Error>> {
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<dyn Error>> {
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<dyn Error>> {
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<dyn Error>> {
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<dyn Error>> {
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<String>) -> Result<(), Box<dyn Error>> {
get_inner_and_call_test_func!(self, BluetoothAdapter, set_uuids, uuids)
}
#[cfg(feature = "bluetooth-test")]
pub fn set_modalias(&self, modalias: String) -> Result<(), Box<dyn Error>> {
get_inner_and_call_test_func!(self, BluetoothAdapter, set_modalias, modalias)
}
#[cfg(feature = "bluetooth-test")]
pub fn get_ad_datas(&self) -> Result<Vec<String>, Box<dyn Error>> {
get_inner_and_call_test_func!(self, BluetoothAdapter, get_ad_datas)
}
#[cfg(feature = "bluetooth-test")]
pub fn set_ad_datas(&self, ad_datas: Vec<String>) -> Result<(), Box<dyn Error>> {
get_inner_and_call_test_func!(self, BluetoothAdapter, set_ad_datas, ad_datas)
}
}

View file

@ -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<BluetoothDiscoverySessionBluez>),
#[cfg(all(target_os = "android", feature = "bluetooth"))]
Android(Arc<BluetoothDiscoverySessionAndroid>),
#[cfg(all(target_os = "macos", feature = "bluetooth"))]
Mac(Arc<BluetoothDiscoverySessionMac>),
#[cfg(not(any(
all(target_os = "linux", feature = "bluetooth"),
all(target_os = "android", feature = "bluetooth"),
all(target_os = "macos", feature = "bluetooth")
)))]
Empty(Arc<BluetoothDiscoverySessionEmpty>),
#[cfg(feature = "bluetooth-test")]
Mock(Arc<FakeBluetoothDiscoverySession>),
}
#[derive(Clone, Debug)]
pub enum BluetoothDevice {
#[cfg(all(target_os = "linux", feature = "bluetooth"))]
Bluez(Arc<BluetoothDeviceBluez>),
#[cfg(all(target_os = "android", feature = "bluetooth"))]
Android(Arc<BluetoothDeviceAndroid>),
#[cfg(all(target_os = "macos", feature = "bluetooth"))]
Mac(Arc<BluetoothDeviceMac>),
#[cfg(not(any(
all(target_os = "linux", feature = "bluetooth"),
all(target_os = "android", feature = "bluetooth"),
all(target_os = "macos", feature = "bluetooth")
)))]
Empty(Arc<BluetoothDeviceEmpty>),
#[cfg(feature = "bluetooth-test")]
Mock(Arc<FakeBluetoothDevice>),
}
#[derive(Clone, Debug)]
pub enum BluetoothGATTService {
#[cfg(all(target_os = "linux", feature = "bluetooth"))]
Bluez(Arc<BluetoothGATTServiceBluez>),
#[cfg(all(target_os = "android", feature = "bluetooth"))]
Android(Arc<BluetoothGATTServiceAndroid>),
#[cfg(all(target_os = "macos", feature = "bluetooth"))]
Mac(Arc<BluetoothGATTServiceMac>),
#[cfg(not(any(
all(target_os = "linux", feature = "bluetooth"),
all(target_os = "android", feature = "bluetooth"),
all(target_os = "macos", feature = "bluetooth")
)))]
Empty(Arc<BluetoothGATTServiceEmpty>),
#[cfg(feature = "bluetooth-test")]
Mock(Arc<FakeBluetoothGATTService>),
}
#[derive(Clone, Debug)]
pub enum BluetoothGATTCharacteristic {
#[cfg(all(target_os = "linux", feature = "bluetooth"))]
Bluez(Arc<BluetoothGATTCharacteristicBluez>),
#[cfg(all(target_os = "android", feature = "bluetooth"))]
Android(Arc<BluetoothGATTCharacteristicAndroid>),
#[cfg(all(target_os = "macos", feature = "bluetooth"))]
Mac(Arc<BluetoothGATTCharacteristicMac>),
#[cfg(not(any(
all(target_os = "linux", feature = "bluetooth"),
all(target_os = "android", feature = "bluetooth"),
all(target_os = "macos", feature = "bluetooth")
)))]
Empty(Arc<BluetoothGATTCharacteristicEmpty>),
#[cfg(feature = "bluetooth-test")]
Mock(Arc<FakeBluetoothGATTCharacteristic>),
}
#[derive(Clone, Debug)]
pub enum BluetoothGATTDescriptor {
#[cfg(all(target_os = "linux", feature = "bluetooth"))]
Bluez(Arc<BluetoothGATTDescriptorBluez>),
#[cfg(all(target_os = "android", feature = "bluetooth"))]
Android(Arc<BluetoothGATTDescriptorAndroid>),
#[cfg(all(target_os = "macos", feature = "bluetooth"))]
Mac(Arc<BluetoothGATTDescriptorMac>),
#[cfg(not(any(
all(target_os = "linux", feature = "bluetooth"),
all(target_os = "android", feature = "bluetooth"),
all(target_os = "macos", feature = "bluetooth")
)))]
Empty(Arc<BluetoothGATTDescriptorEmpty>),
#[cfg(feature = "bluetooth-test")]
Mock(Arc<FakeBluetoothGATTDescriptor>),
}
impl BluetoothDiscoverySession {
pub fn start_discovery(&self) -> Result<(), Box<dyn Error>> {
get_inner_and_call!(self, BluetoothDiscoverySession, start_discovery)
}
pub fn stop_discovery(&self) -> Result<(), Box<dyn Error>> {
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<String, Box<dyn Error>> {
get_inner_and_call!(self, BluetoothDevice, get_address)
}
#[cfg(feature = "bluetooth-test")]
pub fn set_address(&self, address: String) -> Result<(), Box<dyn Error>> {
get_inner_and_call_test_func!(self, BluetoothDevice, set_address, address)
}
pub fn get_name(&self) -> Result<String, Box<dyn Error>> {
get_inner_and_call!(self, BluetoothDevice, get_name)
}
#[cfg(feature = "bluetooth-test")]
pub fn set_name(&self, name: Option<String>) -> Result<(), Box<dyn Error>> {
get_inner_and_call_test_func!(self, BluetoothDevice, set_name, name)
}
pub fn get_icon(&self) -> Result<String, Box<dyn Error>> {
get_inner_and_call!(self, BluetoothDevice, get_icon)
}
#[cfg(feature = "bluetooth-test")]
pub fn set_icon(&self, icon: String) -> Result<(), Box<dyn Error>> {
get_inner_and_call_test_func!(self, BluetoothDevice, set_icon, icon)
}
pub fn get_class(&self) -> Result<u32, Box<dyn Error>> {
get_inner_and_call!(self, BluetoothDevice, get_class)
}
#[cfg(feature = "bluetooth-test")]
pub fn set_class(&self, class: u32) -> Result<(), Box<dyn Error>> {
get_inner_and_call_test_func!(self, BluetoothDevice, set_class, class)
}
pub fn get_appearance(&self) -> Result<u16, Box<dyn Error>> {
get_inner_and_call!(self, BluetoothDevice, get_appearance)
}
#[cfg(feature = "bluetooth-test")]
pub fn set_appearance(&self, appearance: u16) -> Result<(), Box<dyn Error>> {
get_inner_and_call_test_func!(self, BluetoothDevice, set_appearance, Some(appearance))
}
pub fn get_uuids(&self) -> Result<Vec<String>, Box<dyn Error>> {
get_inner_and_call!(self, BluetoothDevice, get_uuids)
}
#[cfg(feature = "bluetooth-test")]
pub fn set_uuids(&self, uuids: Vec<String>) -> Result<(), Box<dyn Error>> {
get_inner_and_call_test_func!(self, BluetoothDevice, set_uuids, uuids)
}
pub fn is_paired(&self) -> Result<bool, Box<dyn Error>> {
get_inner_and_call!(self, BluetoothDevice, is_paired)
}
#[cfg(feature = "bluetooth-test")]
pub fn set_paired(&self, paired: bool) -> Result<(), Box<dyn Error>> {
get_inner_and_call_test_func!(self, BluetoothDevice, set_paired, paired)
}
pub fn is_connected(&self) -> Result<bool, Box<dyn Error>> {
get_inner_and_call!(self, BluetoothDevice, is_connected)
}
#[cfg(feature = "bluetooth-test")]
pub fn set_connected(&self, connected: bool) -> Result<(), Box<dyn Error>> {
get_inner_and_call_test_func!(self, BluetoothDevice, set_connected, connected)
}
#[cfg(feature = "bluetooth-test")]
pub fn is_connectable(&self) -> Result<bool, Box<dyn Error>> {
get_inner_and_call_test_func!(self, BluetoothDevice, is_connectable)
}
#[cfg(feature = "bluetooth-test")]
pub fn set_connectable(&self, connectable: bool) -> Result<(), Box<dyn Error>> {
get_inner_and_call_test_func!(self, BluetoothDevice, set_connectable, connectable)
}
pub fn is_trusted(&self) -> Result<bool, Box<dyn Error>> {
get_inner_and_call!(self, BluetoothDevice, is_trusted)
}
#[cfg(feature = "bluetooth-test")]
pub fn set_trusted(&self, trusted: bool) -> Result<(), Box<dyn Error>> {
get_inner_and_call_test_func!(self, BluetoothDevice, set_trusted, trusted)
}
pub fn is_blocked(&self) -> Result<bool, Box<dyn Error>> {
get_inner_and_call!(self, BluetoothDevice, is_blocked)
}
#[cfg(feature = "bluetooth-test")]
pub fn set_blocked(&self, blocked: bool) -> Result<(), Box<dyn Error>> {
get_inner_and_call_test_func!(self, BluetoothDevice, set_blocked, blocked)
}
pub fn get_alias(&self) -> Result<String, Box<dyn Error>> {
get_inner_and_call!(self, BluetoothDevice, get_alias)
}
#[cfg(feature = "bluetooth-test")]
pub fn set_alias(&self, alias: String) -> Result<(), Box<dyn Error>> {
get_inner_and_call_test_func!(self, BluetoothDevice, set_alias, alias)
}
pub fn is_legacy_pairing(&self) -> Result<bool, Box<dyn Error>> {
get_inner_and_call!(self, BluetoothDevice, is_legacy_pairing)
}
#[cfg(feature = "bluetooth-test")]
pub fn set_legacy_pairing(&self, legacy_pairing: bool) -> Result<(), Box<dyn Error>> {
get_inner_and_call_test_func!(self, BluetoothDevice, set_legacy_pairing, legacy_pairing)
}
pub fn get_vendor_id_source(&self) -> Result<String, Box<dyn Error>> {
get_inner_and_call!(self, BluetoothDevice, get_vendor_id_source)
}
pub fn get_vendor_id(&self) -> Result<u32, Box<dyn Error>> {
get_inner_and_call!(self, BluetoothDevice, get_vendor_id)
}
pub fn get_product_id(&self) -> Result<u32, Box<dyn Error>> {
get_inner_and_call!(self, BluetoothDevice, get_product_id)
}
pub fn get_device_id(&self) -> Result<u32, Box<dyn Error>> {
get_inner_and_call!(self, BluetoothDevice, get_device_id)
}
pub fn get_modalias(&self) -> Result<(String, u32, u32, u32), Box<dyn Error>> {
get_inner_and_call!(self, BluetoothDevice, get_modalias)
}
#[cfg(feature = "bluetooth-test")]
pub fn set_modalias(&self, modalias: String) -> Result<(), Box<dyn Error>> {
get_inner_and_call_test_func!(self, BluetoothDevice, set_modalias, modalias)
}
pub fn get_rssi(&self) -> Result<i16, Box<dyn Error>> {
get_inner_and_call!(self, BluetoothDevice, get_rssi)
}
#[cfg(feature = "bluetooth-test")]
pub fn set_rssi(&self, rssi: i16) -> Result<(), Box<dyn Error>> {
get_inner_and_call_test_func!(self, BluetoothDevice, set_rssi, Some(rssi))
}
pub fn get_tx_power(&self) -> Result<i16, Box<dyn Error>> {
get_inner_and_call!(self, BluetoothDevice, get_tx_power)
}
#[cfg(feature = "bluetooth-test")]
pub fn set_tx_power(&self, tx_power: i16) -> Result<(), Box<dyn Error>> {
get_inner_and_call_test_func!(self, BluetoothDevice, set_tx_power, Some(tx_power))
}
pub fn get_manufacturer_data(&self) -> Result<HashMap<u16, Vec<u8>>, Box<dyn Error>> {
get_inner_and_call!(self, BluetoothDevice, get_manufacturer_data)
}
#[cfg(feature = "bluetooth-test")]
pub fn set_manufacturer_data(
&self,
manufacturer_data: HashMap<u16, Vec<u8>>,
) -> Result<(), Box<dyn Error>> {
get_inner_and_call_test_func!(
self,
BluetoothDevice,
set_manufacturer_data,
Some(manufacturer_data)
)
}
pub fn get_service_data(&self) -> Result<HashMap<String, Vec<u8>>, Box<dyn Error>> {
get_inner_and_call!(self, BluetoothDevice, get_service_data)
}
#[cfg(feature = "bluetooth-test")]
pub fn set_service_data(
&self,
service_data: HashMap<String, Vec<u8>>,
) -> Result<(), Box<dyn Error>> {
get_inner_and_call_test_func!(self, BluetoothDevice, set_service_data, Some(service_data))
}
pub fn get_gatt_services(&self) -> Result<Vec<BluetoothGATTService>, Box<dyn Error>> {
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<dyn Error>> {
get_inner_and_call!(self, BluetoothDevice, connect)
}
pub fn disconnect(&self) -> Result<(), Box<dyn Error>> {
get_inner_and_call!(self, BluetoothDevice, disconnect)
}
pub fn connect_profile(&self, uuid: String) -> Result<(), Box<dyn Error>> {
get_inner_and_call!(self, BluetoothDevice, connect_profile, uuid)
}
pub fn disconnect_profile(&self, uuid: String) -> Result<(), Box<dyn Error>> {
get_inner_and_call!(self, BluetoothDevice, disconnect_profile, uuid)
}
pub fn pair(&self) -> Result<(), Box<dyn Error>> {
get_inner_and_call!(self, BluetoothDevice, pair)
}
pub fn cancel_pairing(&self) -> Result<(), Box<dyn Error>> {
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<BluetoothGATTService, Box<dyn Error>> {
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<String, Box<dyn Error>> {
get_inner_and_call!(self, BluetoothGATTService, get_uuid)
}
#[cfg(feature = "bluetooth-test")]
pub fn set_uuid(&self, uuid: String) -> Result<(), Box<dyn Error>> {
get_inner_and_call_test_func!(self, BluetoothGATTService, set_uuid, uuid)
}
pub fn is_primary(&self) -> Result<bool, Box<dyn Error>> {
get_inner_and_call!(self, BluetoothGATTService, is_primary)
}
#[cfg(feature = "bluetooth-test")]
pub fn set_primary(&self, primary: bool) -> Result<(), Box<dyn Error>> {
get_inner_and_call_test_func!(self, BluetoothGATTService, set_is_primary, primary)
}
pub fn get_includes(
&self,
device: BluetoothDevice,
) -> Result<Vec<BluetoothGATTService>, Box<dyn Error>> {
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<Vec<BluetoothGATTCharacteristic>, Box<dyn Error>> {
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<BluetoothGATTCharacteristic, Box<dyn Error>> {
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<String, Box<dyn Error>> {
get_inner_and_call!(self, BluetoothGATTCharacteristic, get_uuid)
}
#[cfg(feature = "bluetooth-test")]
pub fn set_uuid(&self, uuid: String) -> Result<(), Box<dyn Error>> {
get_inner_and_call_test_func!(self, BluetoothGATTCharacteristic, set_uuid, uuid)
}
pub fn get_value(&self) -> Result<Vec<u8>, Box<dyn Error>> {
get_inner_and_call!(self, BluetoothGATTCharacteristic, get_value)
}
#[cfg(feature = "bluetooth-test")]
pub fn set_value(&self, value: Vec<u8>) -> Result<(), Box<dyn Error>> {
get_inner_and_call_test_func!(self, BluetoothGATTCharacteristic, set_value, Some(value))
}
pub fn is_notifying(&self) -> Result<bool, Box<dyn Error>> {
get_inner_and_call!(self, BluetoothGATTCharacteristic, is_notifying)
}
#[cfg(feature = "bluetooth-test")]
pub fn set_notifying(&self, notifying: bool) -> Result<(), Box<dyn Error>> {
get_inner_and_call_test_func!(self, BluetoothGATTCharacteristic, set_notifying, notifying)
}
pub fn get_flags(&self) -> Result<Vec<String>, Box<dyn Error>> {
get_inner_and_call!(self, BluetoothGATTCharacteristic, get_flags)
}
#[cfg(feature = "bluetooth-test")]
pub fn set_flags(&self, flags: Vec<String>) -> Result<(), Box<dyn Error>> {
get_inner_and_call_test_func!(self, BluetoothGATTCharacteristic, set_flags, flags)
}
pub fn get_gatt_descriptors(&self) -> Result<Vec<BluetoothGATTDescriptor>, Box<dyn Error>> {
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<Vec<u8>, Box<dyn Error>> {
get_inner_and_call!(@with_bluez_offset, self, BluetoothGATTCharacteristic, read_value)
}
pub fn write_value(&self, values: Vec<u8>) -> Result<(), Box<dyn Error>> {
get_inner_and_call!(@with_bluez_offset, self, BluetoothGATTCharacteristic, write_value, values)
}
pub fn start_notify(&self) -> Result<(), Box<dyn Error>> {
get_inner_and_call!(self, BluetoothGATTCharacteristic, start_notify)
}
pub fn stop_notify(&self) -> Result<(), Box<dyn Error>> {
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<BluetoothGATTDescriptor, Box<dyn Error>> {
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<String, Box<dyn Error>> {
get_inner_and_call!(self, BluetoothGATTDescriptor, get_uuid)
}
#[cfg(feature = "bluetooth-test")]
pub fn set_uuid(&self, uuid: String) -> Result<(), Box<dyn Error>> {
get_inner_and_call_test_func!(self, BluetoothGATTDescriptor, set_uuid, uuid)
}
pub fn get_value(&self) -> Result<Vec<u8>, Box<dyn Error>> {
get_inner_and_call!(self, BluetoothGATTDescriptor, get_value)
}
#[cfg(feature = "bluetooth-test")]
pub fn set_value(&self, value: Vec<u8>) -> Result<(), Box<dyn Error>> {
get_inner_and_call_test_func!(self, BluetoothGATTDescriptor, set_value, Some(value))
}
pub fn get_flags(&self) -> Result<Vec<String>, Box<dyn Error>> {
get_inner_and_call!(self, BluetoothGATTDescriptor, get_flags)
}
#[cfg(feature = "bluetooth-test")]
pub fn set_flags(&self, flags: Vec<String>) -> Result<(), Box<dyn Error>> {
get_inner_and_call_test_func!(self, BluetoothGATTDescriptor, set_flags, flags)
}
pub fn read_value(&self) -> Result<Vec<u8>, Box<dyn Error>> {
get_inner_and_call!(@with_bluez_offset, self, BluetoothGATTDescriptor, read_value)
}
pub fn write_value(&self, values: Vec<u8>) -> Result<(), Box<dyn Error>> {
get_inner_and_call!(@with_bluez_offset, self, BluetoothGATTDescriptor, write_value, values)
}
}

View file

@ -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<EmptyAdapter, Box<dyn Error>> {
Ok(EmptyAdapter::new())
}
fn new() -> EmptyAdapter {
EmptyAdapter {}
}
pub fn get_id(&self) -> String {
String::new()
}
pub fn get_device_list(&self) -> Result<Vec<String>, Box<dyn Error>> {
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn get_address(&self) -> Result<String, Box<dyn Error>> {
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn get_name(&self) -> Result<String, Box<dyn Error>> {
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn get_alias(&self) -> Result<String, Box<dyn Error>> {
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn set_alias(&self, _value: String) -> Result<(), Box<dyn Error>> {
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn get_class(&self) -> Result<u32, Box<dyn Error>> {
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn is_powered(&self) -> Result<bool, Box<dyn Error>> {
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn set_powered(&self, _value: bool) -> Result<(), Box<dyn Error>> {
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn is_discoverable(&self) -> Result<bool, Box<dyn Error>> {
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn set_discoverable(&self, _value: bool) -> Result<(), Box<dyn Error>> {
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn is_pairable(&self) -> Result<bool, Box<dyn Error>> {
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn set_pairable(&self, _value: bool) -> Result<(), Box<dyn Error>> {
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn get_pairable_timeout(&self) -> Result<u32, Box<dyn Error>> {
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn set_pairable_timeout(&self, _value: u32) -> Result<(), Box<dyn Error>> {
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn get_discoverable_timeout(&self) -> Result<u32, Box<dyn Error>> {
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn set_discoverable_timeout(&self, _value: u32) -> Result<(), Box<dyn Error>> {
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn is_discovering(&self) -> Result<bool, Box<dyn Error>> {
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn get_uuids(&self) -> Result<Vec<String>, Box<dyn Error>> {
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn get_vendor_id_source(&self) -> Result<String, Box<dyn Error>> {
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn get_vendor_id(&self) -> Result<u32, Box<dyn Error>> {
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn get_product_id(&self) -> Result<u32, Box<dyn Error>> {
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn get_device_id(&self) -> Result<u32, Box<dyn Error>> {
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn get_modalias(&self) -> Result<(String, u32, u32, u32), Box<dyn Error>> {
Err(Box::from(NOT_SUPPORTED_ERROR))
}
}
#[derive(Clone, Debug)]
pub struct BluetoothDiscoverySession {}
impl BluetoothDiscoverySession {
pub fn create_session(
_adapter: Arc<EmptyAdapter>,
) -> Result<BluetoothDiscoverySession, Box<dyn Error>> {
Ok(BluetoothDiscoverySession {})
}
pub fn start_discovery(&self) -> Result<(), Box<dyn Error>> {
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn stop_discovery(&self) -> Result<(), Box<dyn Error>> {
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<String, Box<dyn Error>> {
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn get_name(&self) -> Result<String, Box<dyn Error>> {
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn get_icon(&self) -> Result<String, Box<dyn Error>> {
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn get_class(&self) -> Result<u32, Box<dyn Error>> {
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn get_appearance(&self) -> Result<u16, Box<dyn Error>> {
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn get_uuids(&self) -> Result<Vec<String>, Box<dyn Error>> {
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn is_paired(&self) -> Result<bool, Box<dyn Error>> {
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn is_connected(&self) -> Result<bool, Box<dyn Error>> {
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn is_trusted(&self) -> Result<bool, Box<dyn Error>> {
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn is_blocked(&self) -> Result<bool, Box<dyn Error>> {
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn get_alias(&self) -> Result<String, Box<dyn Error>> {
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn set_alias(&self, _value: String) -> Result<(), Box<dyn Error>> {
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn is_legacy_pairing(&self) -> Result<bool, Box<dyn Error>> {
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn get_vendor_id_source(&self) -> Result<String, Box<dyn Error>> {
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn get_vendor_id(&self) -> Result<u32, Box<dyn Error>> {
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn get_product_id(&self) -> Result<u32, Box<dyn Error>> {
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn get_device_id(&self) -> Result<u32, Box<dyn Error>> {
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn get_modalias(&self) -> Result<(String, u32, u32, u32), Box<dyn Error>> {
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn get_rssi(&self) -> Result<i16, Box<dyn Error>> {
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn get_tx_power(&self) -> Result<i16, Box<dyn Error>> {
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn get_manufacturer_data(&self) -> Result<HashMap<u16, Vec<u8>>, Box<dyn Error>> {
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn get_service_data(&self) -> Result<HashMap<String, Vec<u8>>, Box<dyn Error>> {
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn get_gatt_services(&self) -> Result<Vec<String>, Box<dyn Error>> {
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn connect(&self) -> Result<(), Box<dyn Error>> {
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn disconnect(&self) -> Result<(), Box<dyn Error>> {
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn connect_profile(&self, _uuid: String) -> Result<(), Box<dyn Error>> {
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn disconnect_profile(&self, _uuid: String) -> Result<(), Box<dyn Error>> {
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn pair(&self) -> Result<(), Box<dyn Error>> {
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn cancel_pairing(&self) -> Result<(), Box<dyn Error>> {
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<String, Box<dyn Error>> {
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn is_primary(&self) -> Result<bool, Box<dyn Error>> {
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn get_includes(&self) -> Result<Vec<String>, Box<dyn Error>> {
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn get_gatt_characteristics(&self) -> Result<Vec<String>, Box<dyn Error>> {
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<String, Box<dyn Error>> {
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn get_value(&self) -> Result<Vec<u8>, Box<dyn Error>> {
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn is_notifying(&self) -> Result<bool, Box<dyn Error>> {
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn get_flags(&self) -> Result<Vec<String>, Box<dyn Error>> {
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn get_gatt_descriptors(&self) -> Result<Vec<String>, Box<dyn Error>> {
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn read_value(&self) -> Result<Vec<u8>, Box<dyn Error>> {
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn write_value(&self, _values: Vec<u8>) -> Result<(), Box<dyn Error>> {
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn start_notify(&self) -> Result<(), Box<dyn Error>> {
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn stop_notify(&self) -> Result<(), Box<dyn Error>> {
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<String, Box<dyn Error>> {
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn get_value(&self) -> Result<Vec<u8>, Box<dyn Error>> {
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn get_flags(&self) -> Result<Vec<String>, Box<dyn Error>> {
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn read_value(&self) -> Result<Vec<u8>, Box<dyn Error>> {
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn write_value(&self, _values: Vec<u8>) -> Result<(), Box<dyn Error>> {
Err(Box::from(NOT_SUPPORTED_ERROR))
}
}

View file

@ -2,6 +2,15 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * 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/. */ * 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; pub mod test;
use std::borrow::ToOwned; use std::borrow::ToOwned;
@ -20,16 +29,17 @@ use bluetooth_traits::{
BluetoothRequest, BluetoothResponse, BluetoothResponseResult, BluetoothResult, BluetoothRequest, BluetoothResponse, BluetoothResponseResult, BluetoothResult,
BluetoothServiceMsg, GATTType, BluetoothServiceMsg, GATTType,
}; };
use device::bluetooth::{
BluetoothAdapter, BluetoothDevice, BluetoothGATTCharacteristic, BluetoothGATTDescriptor,
BluetoothGATTService,
};
use embedder_traits::{EmbedderMsg, EmbedderProxy}; use embedder_traits::{EmbedderMsg, EmbedderProxy};
use ipc_channel::ipc::{self, IpcReceiver, IpcSender}; use ipc_channel::ipc::{self, IpcReceiver, IpcSender};
use log::warn; use log::warn;
use servo_config::pref; use servo_config::pref;
use servo_rand::{self, Rng}; 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. // 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) // https://www.bluetooth.org/DocMan/handlers/DownloadDoc.ashx?doc_id=286439 (Vol. 3, page 480)
const MAXIMUM_TRANSACTION_TIME: u8 = 30; const MAXIMUM_TRANSACTION_TIME: u8 = 30;
@ -67,9 +77,9 @@ impl BluetoothThreadFactory for IpcSender<BluetoothRequest> {
fn new(embedder_proxy: EmbedderProxy) -> IpcSender<BluetoothRequest> { fn new(embedder_proxy: EmbedderProxy) -> IpcSender<BluetoothRequest> {
let (sender, receiver) = ipc::channel().unwrap(); let (sender, receiver) = ipc::channel().unwrap();
let adapter = if pref!(dom.bluetooth.enabled) { let adapter = if pref!(dom.bluetooth.enabled) {
BluetoothAdapter::init() BluetoothAdapter::new()
} else { } else {
BluetoothAdapter::init_mock() BluetoothAdapter::new_mock()
} }
.ok(); .ok();
thread::Builder::new() thread::Builder::new()
@ -287,7 +297,7 @@ impl BluetoothManager {
self.cached_characteristics.clear(); self.cached_characteristics.clear();
self.cached_descriptors.clear(); self.cached_descriptors.clear();
self.allowed_services.clear(); self.allowed_services.clear();
self.adapter = BluetoothAdapter::init_mock().ok(); self.adapter = BluetoothAdapter::new_mock().ok();
match test::test(self, data_set_name) { match test::test(self, data_set_name) {
Ok(_) => return Ok(()), Ok(_) => return Ok(()),
Err(error) => Err(BluetoothError::Type(error.to_string())), Err(error) => Err(BluetoothError::Type(error.to_string())),
@ -324,7 +334,7 @@ impl BluetoothManager {
.as_ref() .as_ref()
.map_or(false, |a| a.get_address().is_ok()); .map_or(false, |a| a.get_address().is_ok());
if !adapter_valid { if !adapter_valid {
self.adapter = BluetoothAdapter::init().ok(); self.adapter = BluetoothAdapter::new().ok();
} }
let adapter = self.adapter.as_ref()?; let adapter = self.adapter.as_ref()?;

View file

@ -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;

View file

@ -8,12 +8,12 @@ use std::collections::{HashMap, HashSet};
use std::error::Error; use std::error::Error;
use std::string::String; use std::string::String;
use device::bluetooth::{ use uuid::Uuid;
use crate::bluetooth::{
BluetoothAdapter, BluetoothDevice, BluetoothGATTCharacteristic, BluetoothGATTDescriptor, BluetoothAdapter, BluetoothDevice, BluetoothGATTCharacteristic, BluetoothGATTDescriptor,
BluetoothGATTService, BluetoothGATTService,
}; };
use uuid::Uuid;
use crate::BluetoothManager; use crate::BluetoothManager;
thread_local!(pub static CACHED_IDS: RefCell<HashSet<Uuid>> = RefCell::new(HashSet::new())); thread_local!(pub static CACHED_IDS: RefCell<HashSet<Uuid>> = RefCell::new(HashSet::new()));
@ -152,7 +152,7 @@ fn create_device(
name: String, name: String,
address: String, address: String,
) -> Result<BluetoothDevice, Box<dyn Error>> { ) -> Result<BluetoothDevice, Box<dyn Error>> {
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_name(Some(name))?;
device.set_address(address)?; device.set_address(address)?;
device.set_connectable(true)?; device.set_connectable(true)?;

41
third_party/blurmac/Cargo.lock generated vendored Normal file
View file

@ -0,0 +1,41 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "blurmac"
version = "0.1.0"
dependencies = [
"log",
"objc",
]
[[package]]
name = "libc"
version = "0.2.151"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4"
[[package]]
name = "log"
version = "0.4.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
[[package]]
name = "malloc_buf"
version = "0.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb"
dependencies = [
"libc",
]
[[package]]
name = "objc"
version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1"
dependencies = [
"malloc_buf",
]

18
third_party/blurmac/Cargo.toml vendored Normal file
View file

@ -0,0 +1,18 @@
[package]
name = "blurmac"
description = "Bluetooth Rust lib using macOS CoreBluetooth"
version = "0.1.0"
readme = "README.md"
keywords = ["bluetooth", "ble", "macOS", "CoreBluetooth"]
repository = "https://github.com/akosthekiss/blurmac"
authors = ["Akos Kiss <akiss@inf.u-szeged.hu>"]
license = "BSD-3-Clause"
[lib]
name = "blurmac"
path = "src/lib.rs"
crate-type = ["rlib"]
[dependencies]
log = "0.4"
objc = "0.2"

27
third_party/blurmac/LICENSE.md vendored Normal file
View file

@ -0,0 +1,27 @@
Copyright (c) 2017 Akos Kiss.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

55
third_party/blurmac/README.md vendored Normal file
View file

@ -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).

212
third_party/blurmac/src/adapter.rs vendored Normal file
View file

@ -0,0 +1,212 @@
// Copyright (c) 2017 Akos Kiss.
//
// Licensed under the BSD 3-Clause License
// <LICENSE.md or https://opensource.org/licenses/BSD-3-Clause>.
// This file may not be copied, modified, or distributed except
// according to those terms.
use std::error::Error;
use std::os::raw::c_int;
use delegate::bm;
use framework::{cb, io, ns};
use objc::runtime::{Object, YES};
use utils::{nsx, NOT_SUPPORTED_ERROR};
#[derive(Clone, Debug)]
pub struct BluetoothAdapter {
pub(crate) manager: *mut Object,
pub(crate) delegate: *mut Object,
}
// TODO: implement std::fmt::Debug and/or std::fmt::Display instead of derive?
unsafe impl Send for BluetoothAdapter {}
unsafe impl Sync for BluetoothAdapter {}
impl BluetoothAdapter {
pub fn init() -> Result<BluetoothAdapter, Box<dyn Error>> {
trace!("BluetoothAdapter::init");
let delegate = bm::delegate();
let manager = cb::centralmanager(delegate);
let adapter = BluetoothAdapter {
manager: manager,
delegate: delegate,
};
// NOTE: start discovery at once, servo leaves close to no time to do a proper discovery
// in a BluetoothDiscoverySession
adapter.start_discovery().unwrap();
Ok(adapter)
}
pub fn get_id(&self) -> String {
trace!("BluetoothAdapter::get_id");
// NOTE: not aware of any better native ID than the address string
self.get_address().unwrap()
}
pub fn get_name(&self) -> Result<String, Box<dyn Error>> {
trace!("BluetoothAdapter::get_name");
let controller = io::bluetoothhostcontroller_defaultcontroller();
let name = io::bluetoothhostcontroller_nameasstring(controller);
Ok(nsx::string_to_string(name))
}
pub fn get_address(&self) -> Result<String, Box<dyn Error>> {
trace!("BluetoothAdapter::get_address");
let controller = io::bluetoothhostcontroller_defaultcontroller();
let address = io::bluetoothhostcontroller_addressasstring(controller);
Ok(nsx::string_to_string(address))
}
pub fn get_class(&self) -> Result<u32, Box<dyn Error>> {
trace!("BluetoothAdapter::get_class");
let controller = io::bluetoothhostcontroller_defaultcontroller();
let device_class = io::bluetoothhostcontroller_classofdevice(controller);
Ok(device_class)
}
pub fn is_powered(&self) -> Result<bool, Box<dyn Error>> {
trace!("BluetoothAdapter::is_powered");
// NOTE: might be also available through
// [[IOBluetoothHostController defaultController] powerState], but that's readonly, so keep
// it in sync with set_powered
Ok(io::bluetoothpreferencegetcontrollerpowerstate() == 1)
}
pub fn set_powered(&self, value: bool) -> Result<(), Box<dyn Error>> {
trace!("BluetoothAdapter::set_powered");
io::bluetoothpreferencesetcontrollerpowerstate(value as c_int);
// TODO: wait for change to happen? whether it really happened?
Ok(())
}
pub fn is_discoverable(&self) -> Result<bool, Box<dyn Error>> {
trace!("BluetoothAdapter::is_discoverable");
Ok(io::bluetoothpreferencegetdiscoverablestate() == 1)
}
pub fn set_discoverable(&self, value: bool) -> Result<(), Box<dyn Error>> {
trace!("BluetoothAdapter::set_discoverable");
io::bluetoothpreferencesetdiscoverablestate(value as c_int);
// TODO: wait for change to happen? whether it really happened?
Ok(())
}
pub fn get_device_list(&self) -> Result<Vec<String>, Box<dyn Error>> {
trace!("BluetoothAdapter::get_device_list");
let mut v = vec![];
let peripherals = bm::delegate_peripherals(self.delegate);
let keys = ns::dictionary_allkeys(peripherals);
for i in 0..ns::array_count(keys) {
v.push(nsx::string_to_string(ns::array_objectatindex(keys, i)));
}
Ok(v)
}
// Was in BluetoothDiscoverySession
fn start_discovery(&self) -> Result<(), Box<dyn Error>> {
trace!("BluetoothAdapter::start_discovery");
let options = ns::mutabledictionary();
// NOTE: If duplicates are not allowed then a peripheral will not show up again once
// connected and then disconnected.
ns::mutabledictionary_setobject_forkey(options, ns::number_withbool(YES), unsafe {
cb::CENTRALMANAGERSCANOPTIONALLOWDUPLICATESKEY
});
cb::centralmanager_scanforperipherals_options(self.manager, options);
Ok(())
}
fn stop_discovery(&self) -> Result<(), Box<dyn Error>> {
trace!("BluetoothAdapter::stop_discovery");
cb::centralmanager_stopscan(self.manager);
Ok(())
}
// Not supported
pub fn get_alias(&self) -> Result<String, Box<dyn Error>> {
warn!("BluetoothAdapter::get_alias not supported by BlurMac");
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn set_alias(&self, _value: String) -> Result<(), Box<dyn Error>> {
warn!("BluetoothAdapter::set_alias not supported by BlurMac");
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn is_pairable(&self) -> Result<bool, Box<dyn Error>> {
warn!("BluetoothAdapter::is_pairable not supported by BlurMac");
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn set_pairable(&self, _value: bool) -> Result<(), Box<dyn Error>> {
warn!("BluetoothAdapter::set_pairable not supported by BlurMac");
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn get_pairable_timeout(&self) -> Result<u32, Box<dyn Error>> {
warn!("BluetoothAdapter::get_pairable_timeout not supported by BlurMac");
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn set_pairable_timeout(&self, _value: u32) -> Result<(), Box<dyn Error>> {
warn!("BluetoothAdapter::set_pairable_timeout not supported by BlurMac");
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn get_discoverable_timeout(&self) -> Result<u32, Box<dyn Error>> {
warn!("BluetoothAdapter::get_discoverable_timeout not supported by BlurMac");
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn set_discoverable_timeout(&self, _value: u32) -> Result<(), Box<dyn Error>> {
warn!("BluetoothAdapter::set_discoverable_timeout not supported by BlurMac");
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn is_discovering(&self) -> Result<bool, Box<dyn Error>> {
warn!("BluetoothAdapter::is_discovering not supported by BlurMac");
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn get_uuids(&self) -> Result<Vec<String>, Box<dyn Error>> {
warn!("BluetoothAdapter::get_uuids not supported by BlurMac");
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn get_vendor_id_source(&self) -> Result<String, Box<dyn Error>> {
warn!("BluetoothAdapter::get_vendor_id_source not supported by BlurMac");
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn get_vendor_id(&self) -> Result<u32, Box<dyn Error>> {
warn!("BluetoothAdapter::get_vendor_id not supported by BlurMac");
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn get_product_id(&self) -> Result<u32, Box<dyn Error>> {
warn!("BluetoothAdapter::get_product_id not supported by BlurMac");
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn get_device_id(&self) -> Result<u32, Box<dyn Error>> {
warn!("BluetoothAdapter::get_device_id not supported by BlurMac");
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn get_modalias(&self) -> Result<(String, u32, u32, u32), Box<dyn Error>> {
warn!("BluetoothAdapter::get_modalias not supported by BlurMac");
Err(Box::from(NOT_SUPPORTED_ERROR))
}
}
impl Drop for BluetoothAdapter {
fn drop(&mut self) {
trace!("BluetoothAdapter::drop");
// NOTE: stop discovery only here instead of in BluetoothDiscoverySession
self.stop_discovery().unwrap();
}
}

443
third_party/blurmac/src/delegate.rs vendored Normal file
View file

@ -0,0 +1,443 @@
// Copyright (c) 2017 Akos Kiss.
//
// Licensed under the BSD 3-Clause License
// <LICENSE.md or https://opensource.org/licenses/BSD-3-Clause>.
// This file may not be copied, modified, or distributed except
// according to those terms.
use std::error::Error;
use std::sync::Once;
use framework::{cb, nil, ns};
use objc::declare::ClassDecl;
use objc::runtime::{Class, Object, Protocol, Sel};
use utils::{cbx, nsx, wait, NO_PERIPHERAL_FOUND};
pub mod bm {
use super::*;
// BlurMacDelegate : CBCentralManagerDelegate, CBPeripheralDelegate
const DELEGATE_PERIPHERALS_IVAR: &'static str = "_peripherals";
fn delegate_class() -> &'static Class {
trace!("delegate_class");
static REGISTER_DELEGATE_CLASS: Once = Once::new();
REGISTER_DELEGATE_CLASS.call_once(|| {
let mut decl = ClassDecl::new("BlurMacDelegate", Class::get("NSObject").unwrap()).unwrap();
decl.add_protocol(Protocol::get("CBCentralManagerDelegate").unwrap());
decl.add_ivar::<*mut Object>(DELEGATE_PERIPHERALS_IVAR); /* NSMutableDictionary<NSString*, BlurMacPeripheralData*>* */
unsafe {
decl.add_method(sel!(init), delegate_init as extern fn(&mut Object, Sel) -> *mut Object);
decl.add_method(sel!(centralManagerDidUpdateState:), delegate_centralmanagerdidupdatestate as extern fn(&mut Object, Sel, *mut Object));
// decl.add_method(sel!(centralManager:willRestoreState:), delegate_centralmanager_willrestorestate as extern fn(&mut Object, Sel, *mut Object, *mut Object));
decl.add_method(sel!(centralManager:didConnectPeripheral:), delegate_centralmanager_didconnectperipheral as extern fn(&mut Object, Sel, *mut Object, *mut Object));
decl.add_method(sel!(centralManager:didDisconnectPeripheral:error:), delegate_centralmanager_diddisconnectperipheral_error as extern fn(&mut Object, Sel, *mut Object, *mut Object, *mut Object));
// decl.add_method(sel!(centralManager:didFailToConnectPeripheral:error:), delegate_centralmanager_didfailtoconnectperipheral_error as extern fn(&mut Object, Sel, *mut Object, *mut Object, *mut Object));
decl.add_method(sel!(centralManager:didDiscoverPeripheral:advertisementData:RSSI:), delegate_centralmanager_diddiscoverperipheral_advertisementdata_rssi as extern fn(&mut Object, Sel, *mut Object, *mut Object, *mut Object, *mut Object));
decl.add_method(sel!(peripheral:didDiscoverServices:), delegate_peripheral_diddiscoverservices as extern fn(&mut Object, Sel, *mut Object, *mut Object));
decl.add_method(sel!(peripheral:didDiscoverIncludedServicesForService:error:), delegate_peripheral_diddiscoverincludedservicesforservice_error as extern fn(&mut Object, Sel, *mut Object, *mut Object, *mut Object));
decl.add_method(sel!(peripheral:didDiscoverCharacteristicsForService:error:), delegate_peripheral_diddiscovercharacteristicsforservice_error as extern fn(&mut Object, Sel, *mut Object, *mut Object, *mut Object));
decl.add_method(sel!(peripheral:didUpdateValueForCharacteristic:error:), delegate_peripheral_didupdatevalueforcharacteristic_error as extern fn(&mut Object, Sel, *mut Object, *mut Object, *mut Object));
decl.add_method(sel!(peripheral:didWriteValueForCharacteristic:error:), delegate_peripheral_didwritevalueforcharacteristic_error as extern fn(&mut Object, Sel, *mut Object, *mut Object, *mut Object));
decl.add_method(sel!(peripheral:didReadRSSI:error:), delegate_peripheral_didreadrssi_error as extern fn(&mut Object, Sel, *mut Object, *mut Object, *mut Object));
}
decl.register();
});
Class::get("BlurMacDelegate").unwrap()
}
extern "C" fn delegate_init(delegate: &mut Object, _cmd: Sel) -> *mut Object {
trace!("delegate_init");
unsafe {
delegate.set_ivar::<*mut Object>(DELEGATE_PERIPHERALS_IVAR, ns::mutabledictionary());
}
delegate
}
extern "C" fn delegate_centralmanagerdidupdatestate(
_delegate: &mut Object,
_cmd: Sel,
_central: *mut Object,
) {
trace!("delegate_centralmanagerdidupdatestate");
// NOTE: this is a no-op but kept because it is a required method of the protocol
}
// extern fn delegate_centralmanager_willrestorestate(_delegate: &mut Object, _cmd: Sel, _central: *mut Object, _dict: *mut Object) {
// trace!("delegate_centralmanager_willrestorestate");
// }
extern "C" fn delegate_centralmanager_didconnectperipheral(
delegate: &mut Object,
_cmd: Sel,
_central: *mut Object,
peripheral: *mut Object,
) {
trace!(
"delegate_centralmanager_didconnectperipheral {}",
cbx::peripheral_debug(peripheral)
);
cb::peripheral_setdelegate(peripheral, delegate);
cb::peripheral_discoverservices(peripheral);
}
extern "C" fn delegate_centralmanager_diddisconnectperipheral_error(
delegate: &mut Object,
_cmd: Sel,
_central: *mut Object,
peripheral: *mut Object,
_error: *mut Object,
) {
trace!(
"delegate_centralmanager_diddisconnectperipheral_error {}",
cbx::peripheral_debug(peripheral)
);
ns::mutabledictionary_removeobjectforkey(
delegate_peripherals(delegate),
ns::uuid_uuidstring(cb::peer_identifier(peripheral)),
);
}
// extern fn delegate_centralmanager_didfailtoconnectperipheral_error(_delegate: &mut Object, _cmd: Sel, _central: *mut Object, _peripheral: *mut Object, _error: *mut Object) {
// trace!("delegate_centralmanager_didfailtoconnectperipheral_error");
// }
extern "C" fn delegate_centralmanager_diddiscoverperipheral_advertisementdata_rssi(
delegate: &mut Object,
_cmd: Sel,
_central: *mut Object,
peripheral: *mut Object,
adv_data: *mut Object,
rssi: *mut Object,
) {
trace!(
"delegate_centralmanager_diddiscoverperipheral_advertisementdata_rssi {}",
cbx::peripheral_debug(peripheral)
);
let peripherals = delegate_peripherals(delegate);
let uuid_nsstring = ns::uuid_uuidstring(cb::peer_identifier(peripheral));
let mut data = ns::dictionary_objectforkey(peripherals, uuid_nsstring);
if data == nil {
data = ns::mutabledictionary();
ns::mutabledictionary_setobject_forkey(peripherals, data, uuid_nsstring);
}
ns::mutabledictionary_setobject_forkey(
data,
ns::object_copy(peripheral),
nsx::string_from_str(PERIPHERALDATA_PERIPHERALKEY),
);
ns::mutabledictionary_setobject_forkey(
data,
rssi,
nsx::string_from_str(PERIPHERALDATA_RSSIKEY),
);
let cbuuids_nsarray =
ns::dictionary_objectforkey(adv_data, unsafe { cb::ADVERTISEMENTDATASERVICEUUIDSKEY });
if cbuuids_nsarray != nil {
ns::mutabledictionary_setobject_forkey(
data,
cbuuids_nsarray,
nsx::string_from_str(PERIPHERALDATA_UUIDSKEY),
);
}
if ns::dictionary_objectforkey(data, nsx::string_from_str(PERIPHERALDATA_EVENTSKEY)) == nil
{
ns::mutabledictionary_setobject_forkey(
data,
ns::mutabledictionary(),
nsx::string_from_str(PERIPHERALDATA_EVENTSKEY),
);
}
}
extern "C" fn delegate_peripheral_diddiscoverservices(
delegate: &mut Object,
_cmd: Sel,
peripheral: *mut Object,
error: *mut Object,
) {
trace!(
"delegate_peripheral_diddiscoverservices {} {}",
cbx::peripheral_debug(peripheral),
if error != nil { "error" } else { "" }
);
if error == nil {
let services = cb::peripheral_services(peripheral);
for i in 0..ns::array_count(services) {
let s = ns::array_objectatindex(services, i);
cb::peripheral_discovercharacteristicsforservice(peripheral, s);
cb::peripheral_discoverincludedservicesforservice(peripheral, s);
}
// Notify BluetoothDevice::get_gatt_services that discovery was successful.
match bmx::peripheralevents(delegate, peripheral) {
Ok(events) => ns::mutabledictionary_setobject_forkey(
events,
wait::now(),
nsx::string_from_str(PERIPHERALEVENT_SERVICESDISCOVEREDKEY),
),
Err(_) => {},
}
}
}
extern "C" fn delegate_peripheral_diddiscoverincludedservicesforservice_error(
delegate: &mut Object,
_cmd: Sel,
peripheral: *mut Object,
service: *mut Object,
error: *mut Object,
) {
trace!(
"delegate_peripheral_diddiscoverincludedservicesforservice_error {} {} {}",
cbx::peripheral_debug(peripheral),
cbx::service_debug(service),
if error != nil { "error" } else { "" }
);
if error == nil {
let includes = cb::service_includedservices(service);
for i in 0..ns::array_count(includes) {
let s = ns::array_objectatindex(includes, i);
cb::peripheral_discovercharacteristicsforservice(peripheral, s);
}
// Notify BluetoothGATTService::get_includes that discovery was successful.
match bmx::peripheralevents(delegate, peripheral) {
Ok(events) => ns::mutabledictionary_setobject_forkey(
events,
wait::now(),
bmx::includedservicesdiscoveredkey(service),
),
Err(_) => {},
}
}
}
extern "C" fn delegate_peripheral_diddiscovercharacteristicsforservice_error(
delegate: &mut Object,
_cmd: Sel,
peripheral: *mut Object,
service: *mut Object,
error: *mut Object,
) {
trace!(
"delegate_peripheral_diddiscovercharacteristicsforservice_error {} {} {}",
cbx::peripheral_debug(peripheral),
cbx::service_debug(service),
if error != nil { "error" } else { "" }
);
if error == nil {
let chars = cb::service_characteristics(service);
for i in 0..ns::array_count(chars) {
let c = ns::array_objectatindex(chars, i);
cb::peripheral_discoverdescriptorsforcharacteristic(peripheral, c);
}
// Notify BluetoothGATTService::get_gatt_characteristics that discovery was successful.
match bmx::peripheralevents(delegate, peripheral) {
Ok(events) => ns::mutabledictionary_setobject_forkey(
events,
wait::now(),
bmx::characteristicsdiscoveredkey(service),
),
Err(_) => {},
}
}
}
extern "C" fn delegate_peripheral_didupdatevalueforcharacteristic_error(
delegate: &mut Object,
_cmd: Sel,
peripheral: *mut Object,
characteristic: *mut Object,
error: *mut Object,
) {
trace!(
"delegate_peripheral_didupdatevalueforcharacteristic_error {} {} {}",
cbx::peripheral_debug(peripheral),
cbx::characteristic_debug(characteristic),
if error != nil { "error" } else { "" }
);
if error == nil {
// Notify BluetoothGATTCharacteristic::read_value that read was successful.
match bmx::peripheralevents(delegate, peripheral) {
Ok(events) => ns::mutabledictionary_setobject_forkey(
events,
wait::now(),
bmx::valueupdatedkey(characteristic),
),
Err(_) => {},
}
}
}
extern "C" fn delegate_peripheral_didwritevalueforcharacteristic_error(
delegate: &mut Object,
_cmd: Sel,
peripheral: *mut Object,
characteristic: *mut Object,
error: *mut Object,
) {
trace!(
"delegate_peripheral_didwritevalueforcharacteristic_error {} {} {}",
cbx::peripheral_debug(peripheral),
cbx::characteristic_debug(characteristic),
if error != nil { "error" } else { "" }
);
if error == nil {
// Notify BluetoothGATTCharacteristic::write_value that write was successful.
match bmx::peripheralevents(delegate, peripheral) {
Ok(events) => ns::mutabledictionary_setobject_forkey(
events,
wait::now(),
bmx::valuewrittenkey(characteristic),
),
Err(_) => {},
}
}
}
// extern fn delegate_peripheral_didupdatenotificationstateforcharacteristic_error(_delegate: &mut Object, _cmd: Sel, _peripheral: *mut Object, _characteristic: *mut Object, _error: *mut Object) {
// trace!("delegate_peripheral_didupdatenotificationstateforcharacteristic_error");
// // TODO: this is where notifications should be handled...
// }
// extern fn delegate_peripheral_diddiscoverdescriptorsforcharacteristic_error(_delegate: &mut Object, _cmd: Sel, _peripheral: *mut Object, _characteristic: *mut Object, _error: *mut Object) {
// trace!("delegate_peripheral_diddiscoverdescriptorsforcharacteristic_error");
// }
// extern fn delegate_peripheral_didupdatevaluefordescriptor(_delegate: &mut Object, _cmd: Sel, _peripheral: *mut Object, _descriptor: *mut Object, _error: *mut Object) {
// trace!("delegate_peripheral_didupdatevaluefordescriptor");
// }
// extern fn delegate_peripheral_didwritevaluefordescriptor_error(_delegate: &mut Object, _cmd: Sel, _peripheral: *mut Object, _descriptor: *mut Object, _error: *mut Object) {
// trace!("delegate_peripheral_didwritevaluefordescriptor_error");
// }
extern "C" fn delegate_peripheral_didreadrssi_error(
delegate: &mut Object,
_cmd: Sel,
peripheral: *mut Object,
rssi: *mut Object,
error: *mut Object,
) {
trace!(
"delegate_peripheral_didreadrssi_error {}",
cbx::peripheral_debug(peripheral)
);
if error == nil {
let peripherals = delegate_peripherals(delegate);
let uuid_nsstring = ns::uuid_uuidstring(cb::peer_identifier(peripheral));
let data = ns::dictionary_objectforkey(peripherals, uuid_nsstring);
if data != nil {
ns::mutabledictionary_setobject_forkey(
data,
rssi,
nsx::string_from_str(PERIPHERALDATA_RSSIKEY),
);
}
}
}
pub fn delegate() -> *mut Object {
unsafe {
let mut delegate: *mut Object = msg_send![delegate_class(), alloc];
delegate = msg_send![delegate, init];
delegate
}
}
pub fn delegate_peripherals(delegate: *mut Object) -> *mut Object {
unsafe {
let peripherals: *mut Object =
*(&mut *delegate).get_ivar::<*mut Object>(DELEGATE_PERIPHERALS_IVAR);
peripherals
}
}
// "BlurMacPeripheralData" = NSMutableDictionary<NSString*, id>
pub const PERIPHERALDATA_PERIPHERALKEY: &'static str = "peripheral";
pub const PERIPHERALDATA_RSSIKEY: &'static str = "rssi";
pub const PERIPHERALDATA_UUIDSKEY: &'static str = "uuids";
pub const PERIPHERALDATA_EVENTSKEY: &'static str = "events";
pub const PERIPHERALEVENT_SERVICESDISCOVEREDKEY: &'static str = "services";
pub const PERIPHERALEVENT_INCLUDEDSERVICESDISCOVEREDKEYSUFFIX: &'static str = ":includes";
pub const PERIPHERALEVENT_CHARACTERISTICSDISCOVEREDKEYSUFFIX: &'static str = ":characteristics";
pub const PERIPHERALEVENT_VALUEUPDATEDKEYSUFFIX: &'static str = ":updated";
pub const PERIPHERALEVENT_VALUEWRITTENKEYSUFFIX: &'static str = ":written";
}
pub mod bmx {
use super::*;
pub fn peripheraldata(
delegate: *mut Object,
peripheral: *mut Object,
) -> Result<*mut Object, Box<dyn Error>> {
let peripherals = bm::delegate_peripherals(delegate);
let data = ns::dictionary_objectforkey(
peripherals,
ns::uuid_uuidstring(cb::peer_identifier(peripheral)),
);
if data == nil {
warn!("peripheraldata -> NOT FOUND");
return Err(Box::from(NO_PERIPHERAL_FOUND));
}
Ok(data)
}
pub fn peripheralevents(
delegate: *mut Object,
peripheral: *mut Object,
) -> Result<*mut Object, Box<dyn Error>> {
let data = peripheraldata(delegate, peripheral)?;
Ok(ns::dictionary_objectforkey(
data,
nsx::string_from_str(bm::PERIPHERALDATA_EVENTSKEY),
))
}
pub fn includedservicesdiscoveredkey(service: *mut Object) -> *mut Object {
suffixedkey(
service,
bm::PERIPHERALEVENT_INCLUDEDSERVICESDISCOVEREDKEYSUFFIX,
)
}
pub fn characteristicsdiscoveredkey(service: *mut Object) -> *mut Object {
suffixedkey(
service,
bm::PERIPHERALEVENT_CHARACTERISTICSDISCOVEREDKEYSUFFIX,
)
}
pub fn valueupdatedkey(characteristic: *mut Object) -> *mut Object {
suffixedkey(characteristic, bm::PERIPHERALEVENT_VALUEUPDATEDKEYSUFFIX)
}
pub fn valuewrittenkey(characteristic: *mut Object) -> *mut Object {
suffixedkey(characteristic, bm::PERIPHERALEVENT_VALUEWRITTENKEYSUFFIX)
}
fn suffixedkey(attribute: *mut Object, suffix: &str) -> *mut Object {
let key = format!(
"{}{}",
cbx::uuid_to_canonical_uuid_string(cb::attribute_uuid(attribute)),
suffix
);
nsx::string_from_str(key.as_str())
}
}

280
third_party/blurmac/src/device.rs vendored Normal file
View file

@ -0,0 +1,280 @@
// Copyright (c) 2017 Akos Kiss.
//
// Licensed under the BSD 3-Clause License
// <LICENSE.md or https://opensource.org/licenses/BSD-3-Clause>.
// This file may not be copied, modified, or distributed except
// according to those terms.
use std::collections::HashMap;
use std::error::Error;
use std::sync::Arc;
use adapter::BluetoothAdapter;
use delegate::{bm, bmx};
use framework::{cb, nil, ns};
use objc::runtime::Object;
use utils::{cbx, nsx, wait, NOT_SUPPORTED_ERROR, NO_PERIPHERAL_FOUND};
#[derive(Clone, Debug)]
pub struct BluetoothDevice {
pub(crate) adapter: Arc<BluetoothAdapter>,
pub(crate) peripheral: *mut Object,
}
// TODO: implement std::fmt::Debug and/or std::fmt::Display instead of derive?
impl BluetoothDevice {
pub fn new(adapter: Arc<BluetoothAdapter>, uuid: String) -> BluetoothDevice {
trace!("BluetoothDevice::new");
// NOTE: It can happen that there is no peripheral for the given UUID, in that case
// self.peripheral will be nil and all methods that return a Result will return
// Err(Box::from(NO_PERIPHERAL_FOUND)), while others will return some meaningless value.
let peripheral = Self::peripheral_by_uuid(adapter.delegate, &uuid);
if peripheral == nil {
warn!("BluetoothDevice::new found no peripheral for UUID {}", uuid);
}
BluetoothDevice {
adapter: adapter.clone(),
peripheral: peripheral,
}
}
fn peripheral_by_uuid(delegate: *mut Object, uuid: &String) -> *mut Object {
let peripherals = bm::delegate_peripherals(delegate);
let keys = ns::dictionary_allkeys(peripherals);
for i in 0..ns::array_count(keys) {
let uuid_nsstring = ns::array_objectatindex(keys, i);
if nsx::string_to_string(uuid_nsstring) == *uuid {
let data = ns::dictionary_objectforkey(peripherals, uuid_nsstring);
return ns::dictionary_objectforkey(
data,
nsx::string_from_str(bm::PERIPHERALDATA_PERIPHERALKEY),
);
}
}
nil
}
pub fn get_id(&self) -> String {
trace!("BluetoothDevice::get_id -> get_address");
self.get_address().unwrap_or(String::new())
}
pub fn get_address(&self) -> Result<String, Box<dyn Error>> {
trace!("BluetoothDevice::get_address");
if self.peripheral == nil {
return Err(Box::from(NO_PERIPHERAL_FOUND));
}
// NOTE: There is no better substitute for address than identifier.
let uuid_string =
nsx::string_to_string(ns::uuid_uuidstring(cb::peer_identifier(self.peripheral)));
debug!("BluetoothDevice::get_address -> {}", uuid_string);
Ok(uuid_string)
}
pub fn get_name(&self) -> Result<String, Box<dyn Error>> {
trace!("BluetoothDevice::get_name");
if self.peripheral == nil {
return Err(Box::from(NO_PERIPHERAL_FOUND));
}
let name_nsstring = cb::peripheral_name(self.peripheral);
let name = if name_nsstring != nil {
nsx::string_to_string(name_nsstring)
} else {
String::from("")
};
debug!("BluetoothDevice::get_name -> {}", name);
Ok(name)
}
pub fn get_uuids(&self) -> Result<Vec<String>, Box<dyn Error>> {
trace!("BluetoothDevice::get_uuids");
if self.peripheral == nil {
return Err(Box::from(NO_PERIPHERAL_FOUND));
}
let data = bmx::peripheraldata(self.adapter.delegate, self.peripheral)?;
let mut v = vec![];
let cbuuids_nsarray =
ns::dictionary_objectforkey(data, nsx::string_from_str(bm::PERIPHERALDATA_UUIDSKEY));
if cbuuids_nsarray != nil {
for i in 0..ns::array_count(cbuuids_nsarray) {
v.push(cbx::uuid_to_canonical_uuid_string(ns::array_objectatindex(
cbuuids_nsarray,
i,
)));
}
}
debug!("BluetoothDevice::get_uuids -> {:?}", v);
Ok(v)
}
pub fn connect(&self) -> Result<(), Box<dyn Error>> {
trace!("BluetoothDevice::connect");
if self.peripheral == nil {
return Err(Box::from(NO_PERIPHERAL_FOUND));
}
cb::centralmanager_connectperipheral(self.adapter.manager, self.peripheral);
Ok(())
}
pub fn disconnect(&self) -> Result<(), Box<dyn Error>> {
trace!("BluetoothDevice::disconnect");
if self.peripheral == nil {
return Err(Box::from(NO_PERIPHERAL_FOUND));
}
cb::centralmanager_cancelperipheralconnection(self.adapter.manager, self.peripheral);
Ok(())
}
pub fn is_connected(&self) -> Result<bool, Box<dyn Error>> {
trace!("BluetoothDevice::is_connected");
if self.peripheral == nil {
return Err(Box::from(NO_PERIPHERAL_FOUND));
}
let state = cb::peripheral_state(self.peripheral);
debug!("BluetoothDevice::is_connected -> {}", state);
Ok(state == cb::PERIPHERALSTATE_CONNECTED)
}
pub fn get_gatt_services(&self) -> Result<Vec<String>, Box<dyn Error>> {
trace!("BluetoothDevice::get_gatt_services");
if self.peripheral == nil {
return Err(Box::from(NO_PERIPHERAL_FOUND));
}
let events = bmx::peripheralevents(self.adapter.delegate, self.peripheral)?;
let key = nsx::string_from_str(bm::PERIPHERALEVENT_SERVICESDISCOVEREDKEY);
wait::wait_or_timeout(|| ns::dictionary_objectforkey(events, key) != nil)?;
let mut v = vec![];
let services = cb::peripheral_services(self.peripheral);
for i in 0..ns::array_count(services) {
let uuid_string = cbx::uuid_to_canonical_uuid_string(cb::attribute_uuid(
ns::array_objectatindex(services, i),
));
v.push(uuid_string);
}
debug!("BluetoothDevice::get_gatt_services -> {:?}", v);
Ok(v)
}
// Not supported
pub fn get_rssi(&self) -> Result<i16, Box<dyn Error>> {
warn!("BluetoothDevice::get_rssi not supported by BlurMac");
// TODO: Now available from peripheral data in BluetoothAdapter.
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn get_tx_power(&self) -> Result<i16, Box<dyn Error>> {
warn!("BluetoothDevice::get_tx_power not supported by BlurMac");
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn get_manufacturer_data(&self) -> Result<HashMap<u16, Vec<u8>>, Box<dyn Error>> {
warn!("BluetoothDevice::get_manufacturer_data not supported by BlurMac");
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn get_service_data(&self) -> Result<HashMap<String, Vec<u8>>, Box<dyn Error>> {
warn!("BluetoothDevice::get_service_data not supported by BlurMac");
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn get_icon(&self) -> Result<String, Box<dyn Error>> {
warn!("BluetoothDevice::get_icon not supported by BlurMac");
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn get_class(&self) -> Result<u32, Box<dyn Error>> {
warn!("BluetoothDevice::get_class not supported by BlurMac");
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn get_appearance(&self) -> Result<u16, Box<dyn Error>> {
warn!("BluetoothDevice::get_appearance not supported by BlurMac");
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn is_paired(&self) -> Result<bool, Box<dyn Error>> {
warn!("BluetoothDevice::is_paired not supported by BlurMac");
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn is_trusted(&self) -> Result<bool, Box<dyn Error>> {
warn!("BluetoothDevice::is_trusted not supported by BlurMac");
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn is_blocked(&self) -> Result<bool, Box<dyn Error>> {
warn!("BluetoothDevice::is_blocked not supported by BlurMac");
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn get_alias(&self) -> Result<String, Box<dyn Error>> {
warn!("BluetoothDevice::get_alias not supported by BlurMac");
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn set_alias(&self, _value: String) -> Result<(), Box<dyn Error>> {
warn!("BluetoothDevice::set_alias not supported by BlurMac");
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn is_legacy_pairing(&self) -> Result<bool, Box<dyn Error>> {
warn!("BluetoothDevice::is_legacy_pairing not supported by BlurMac");
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn get_vendor_id_source(&self) -> Result<String, Box<dyn Error>> {
warn!("BluetoothDevice::get_vendor_id_source not supported by BlurMac");
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn get_vendor_id(&self) -> Result<u32, Box<dyn Error>> {
warn!("BluetoothDevice::get_vendor_id not supported by BlurMac");
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn get_product_id(&self) -> Result<u32, Box<dyn Error>> {
warn!("BluetoothDevice::get_product_id not supported by BlurMac");
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn get_device_id(&self) -> Result<u32, Box<dyn Error>> {
warn!("BluetoothDevice::get_device_id not supported by BlurMac");
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn get_modalias(&self) -> Result<(String, u32, u32, u32), Box<dyn Error>> {
warn!("BluetoothDevice::get_modalias not supported by BlurMac");
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn connect_profile(&self, _uuid: String) -> Result<(), Box<dyn Error>> {
warn!("BluetoothDevice::connect_profile not supported by BlurMac");
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn disconnect_profile(&self, _uuid: String) -> Result<(), Box<dyn Error>> {
warn!("BluetoothDevice::disconnect_profile not supported by BlurMac");
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn pair(&self) -> Result<(), Box<dyn Error>> {
warn!("BluetoothDevice::pair not supported by BlurMac");
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn cancel_pairing(&self) -> Result<(), Box<dyn Error>> {
warn!("BluetoothDevice::cancel_pairing not supported by BlurMac");
Err(Box::from(NOT_SUPPORTED_ERROR))
}
}

View file

@ -0,0 +1,39 @@
// Copyright (c) 2017 Akos Kiss.
//
// Licensed under the BSD 3-Clause License
// <LICENSE.md or https://opensource.org/licenses/BSD-3-Clause>.
// This file may not be copied, modified, or distributed except
// according to those terms.
use std::error::Error;
use std::sync::Arc;
use adapter::BluetoothAdapter;
#[derive(Clone, Debug)]
pub struct BluetoothDiscoverySession {
// pub(crate) adapter: Arc<BluetoothAdapter>,
}
impl BluetoothDiscoverySession {
pub fn create_session(
_adapter: Arc<BluetoothAdapter>,
) -> Result<BluetoothDiscoverySession, Box<dyn Error>> {
trace!("BluetoothDiscoverySession::create_session");
Ok(BluetoothDiscoverySession {
// adapter: adapter.clone()
})
}
pub fn start_discovery(&self) -> Result<(), Box<dyn Error>> {
trace!("BluetoothDiscoverySession::start_discovery");
// NOTE: discovery is started by BluetoothAdapter::new to allow devices to pop up
Ok(())
}
pub fn stop_discovery(&self) -> Result<(), Box<dyn Error>> {
trace!("BluetoothDiscoverySession::stop_discovery");
// NOTE: discovery is only stopped when BluetoothAdapter is dropped
Ok(())
}
}

490
third_party/blurmac/src/framework.rs vendored Normal file
View file

@ -0,0 +1,490 @@
// Copyright (c) 2017 Akos Kiss.
//
// Licensed under the BSD 3-Clause License
// <LICENSE.md or https://opensource.org/licenses/BSD-3-Clause>.
// This file may not be copied, modified, or distributed except
// according to those terms.
use std::os::raw::{c_char, c_int, c_uint};
use objc::runtime::{Class, Object, BOOL};
#[allow(non_upper_case_globals)]
pub const nil: *mut Object = 0 as *mut Object;
pub mod ns {
use super::*;
// NSObject
pub fn object_copy(nsobject: *mut Object) -> *mut Object {
unsafe {
let copy: *mut Object = msg_send![nsobject, copy];
copy
}
}
// NSNumber
pub fn number_withbool(value: BOOL) -> *mut Object {
unsafe {
let nsnumber: *mut Object =
msg_send![Class::get("NSNumber").unwrap(), numberWithBool: value];
nsnumber
}
}
pub fn number_withunsignedlonglong(value: u64) -> *mut Object {
unsafe {
let nsnumber: *mut Object = msg_send![
Class::get("NSNumber").unwrap(),
numberWithUnsignedLongLong: value
];
nsnumber
}
}
pub fn number_unsignedlonglongvalue(nsnumber: *mut Object) -> u64 {
unsafe {
let value: u64 = msg_send![nsnumber, unsignedLongLongValue];
value
}
}
// NSString
pub fn string(cstring: *const c_char) -> *mut Object /* NSString* */ {
unsafe {
let nsstring: *mut Object = msg_send![
Class::get("NSString").unwrap(),
stringWithUTF8String: cstring
];
nsstring
}
}
pub fn string_utf8string(nsstring: *mut Object) -> *const c_char {
unsafe {
let utf8string: *const c_char = msg_send![nsstring, UTF8String];
utf8string
}
}
// NSArray
pub fn array_count(nsarray: *mut Object) -> c_uint {
unsafe {
let count: c_uint = msg_send![nsarray, count];
count
}
}
pub fn array_objectatindex(nsarray: *mut Object, index: c_uint) -> *mut Object {
unsafe {
let object: *mut Object = msg_send![nsarray, objectAtIndex: index];
object
}
}
// NSDictionary
pub fn dictionary_allkeys(nsdict: *mut Object) -> *mut Object /* NSArray* */ {
unsafe {
let keys: *mut Object = msg_send![nsdict, allKeys];
keys
}
}
pub fn dictionary_objectforkey(nsdict: *mut Object, key: *mut Object) -> *mut Object {
unsafe {
let object: *mut Object = msg_send![nsdict, objectForKey: key];
object
}
}
// NSMutableDictionary : NSDictionary
pub fn mutabledictionary() -> *mut Object {
unsafe {
let nsmutdict: *mut Object =
msg_send![Class::get("NSMutableDictionary").unwrap(), dictionaryWithCapacity:0];
nsmutdict
}
}
pub fn mutabledictionary_removeobjectforkey(nsmutdict: *mut Object, key: *mut Object) {
unsafe {
let () = msg_send![nsmutdict, removeObjectForKey: key];
}
}
pub fn mutabledictionary_setobject_forkey(
nsmutdict: *mut Object,
object: *mut Object,
key: *mut Object,
) {
unsafe {
let () = msg_send![nsmutdict, setObject:object forKey:key];
}
}
// NSData
pub fn data(bytes: *const u8, length: c_uint) -> *mut Object /* NSData* */ {
unsafe {
let data: *mut Object =
msg_send![Class::get("NSData").unwrap(), dataWithBytes:bytes length:length];
data
}
}
pub fn data_length(nsdata: *mut Object) -> c_uint {
unsafe {
let length: c_uint = msg_send![nsdata, length];
length
}
}
pub fn data_bytes(nsdata: *mut Object) -> *const u8 {
unsafe {
let bytes: *const u8 = msg_send![nsdata, bytes];
bytes
}
}
// NSUUID
pub fn uuid_uuidstring(nsuuid: *mut Object) -> *mut Object /* NSString* */ {
unsafe {
let uuidstring: *mut Object = msg_send![nsuuid, UUIDString];
uuidstring
}
}
}
pub mod io {
use super::*;
#[link(name = "IOBluetooth", kind = "framework")]
extern "C" {
pub fn IOBluetoothPreferenceGetControllerPowerState() -> c_int;
pub fn IOBluetoothPreferenceSetControllerPowerState(state: c_int);
pub fn IOBluetoothPreferenceGetDiscoverableState() -> c_int;
pub fn IOBluetoothPreferenceSetDiscoverableState(state: c_int);
}
// IOBluetoothHostController
pub fn bluetoothhostcontroller_defaultcontroller() -> *mut Object /* IOBluetoothHostController* */
{
unsafe {
let defaultcontroller: *mut Object = msg_send![
Class::get("IOBluetoothHostController").unwrap(),
defaultController
];
defaultcontroller
}
}
pub fn bluetoothhostcontroller_nameasstring(iobthc: *mut Object) -> *mut Object /* NSString* */
{
unsafe {
let name: *mut Object = msg_send![iobthc, nameAsString];
name
}
}
pub fn bluetoothhostcontroller_addressasstring(iobthc: *mut Object) -> *mut Object /* NSString* */
{
unsafe {
let address: *mut Object = msg_send![iobthc, addressAsString];
address
}
}
pub fn bluetoothhostcontroller_classofdevice(iobthc: *mut Object) -> u32 {
unsafe {
let classofdevice: u32 = msg_send![iobthc, classOfDevice];
classofdevice
}
}
// IOBluetoothPreference...
pub fn bluetoothpreferencegetcontrollerpowerstate() -> c_int {
unsafe { IOBluetoothPreferenceGetControllerPowerState() }
}
pub fn bluetoothpreferencesetcontrollerpowerstate(state: c_int) {
unsafe {
IOBluetoothPreferenceSetControllerPowerState(state);
}
}
pub fn bluetoothpreferencegetdiscoverablestate() -> c_int {
unsafe { IOBluetoothPreferenceGetDiscoverableState() }
}
pub fn bluetoothpreferencesetdiscoverablestate(state: c_int) {
unsafe {
IOBluetoothPreferenceSetDiscoverableState(state);
}
}
}
pub mod cb {
use super::*;
mod link {
use super::*;
#[link(name = "CoreBluetooth", kind = "framework")]
extern "C" {
pub static CBAdvertisementDataServiceUUIDsKey: *mut Object;
pub static CBCentralManagerScanOptionAllowDuplicatesKey: *mut Object;
}
}
// CBCentralManager
pub fn centralmanager(delegate: *mut Object, /*CBCentralManagerDelegate* */) -> *mut Object /*CBCentralManager* */
{
unsafe {
let cbcentralmanager: *mut Object =
msg_send![Class::get("CBCentralManager").unwrap(), alloc];
let () = msg_send![cbcentralmanager, initWithDelegate:delegate queue:nil];
cbcentralmanager
}
}
pub fn centralmanager_scanforperipherals_options(
cbcentralmanager: *mut Object,
options: *mut Object, /* NSDictionary<NSString*,id> */
) {
unsafe {
let () =
msg_send![cbcentralmanager, scanForPeripheralsWithServices:nil options:options];
}
}
pub fn centralmanager_stopscan(cbcentralmanager: *mut Object) {
unsafe {
let () = msg_send![cbcentralmanager, stopScan];
}
}
pub fn centralmanager_connectperipheral(
cbcentralmanager: *mut Object,
peripheral: *mut Object, /* CBPeripheral* */
) {
unsafe {
let () = msg_send![cbcentralmanager, connectPeripheral:peripheral options:nil];
}
}
pub fn centralmanager_cancelperipheralconnection(
cbcentralmanager: *mut Object,
peripheral: *mut Object, /* CBPeripheral* */
) {
unsafe {
let () = msg_send![cbcentralmanager, cancelPeripheralConnection: peripheral];
}
}
// CBPeer
pub fn peer_identifier(cbpeer: *mut Object) -> *mut Object /* NSUUID* */ {
unsafe {
let identifier: *mut Object = msg_send![cbpeer, identifier];
identifier
}
}
// CBPeripheral : CBPeer
pub fn peripheral_name(cbperipheral: *mut Object) -> *mut Object /* NSString* */ {
unsafe {
let name: *mut Object = msg_send![cbperipheral, name];
name
}
}
pub fn peripheral_state(cbperipheral: *mut Object) -> c_int {
unsafe {
let state: c_int = msg_send![cbperipheral, state];
state
}
}
pub fn peripheral_setdelegate(
cbperipheral: *mut Object,
delegate: *mut Object, /* CBPeripheralDelegate* */
) {
unsafe {
let () = msg_send![cbperipheral, setDelegate: delegate];
}
}
pub fn peripheral_discoverservices(cbperipheral: *mut Object) {
unsafe {
let () = msg_send![cbperipheral, discoverServices: nil];
}
}
pub fn peripheral_discoverincludedservicesforservice(
cbperipheral: *mut Object,
service: *mut Object, /* CBService* */
) {
unsafe {
let () = msg_send![cbperipheral, discoverIncludedServices:nil forService:service];
}
}
pub fn peripheral_services(cbperipheral: *mut Object) -> *mut Object /* NSArray<CBService*>* */
{
unsafe {
let services: *mut Object = msg_send![cbperipheral, services];
services
}
}
pub fn peripheral_discovercharacteristicsforservice(
cbperipheral: *mut Object,
service: *mut Object, /* CBService* */
) {
unsafe {
let () = msg_send![cbperipheral, discoverCharacteristics:nil forService:service];
}
}
pub fn peripheral_readvalueforcharacteristic(
cbperipheral: *mut Object,
characteristic: *mut Object, /* CBCharacteristic* */
) {
unsafe {
let () = msg_send![cbperipheral, readValueForCharacteristic: characteristic];
}
}
pub fn peripheral_writevalue_forcharacteristic(
cbperipheral: *mut Object,
value: *mut Object, /* NSData* */
characteristic: *mut Object, /* CBCharacteristic* */
) {
unsafe {
let () =
msg_send![cbperipheral, writeValue:value forCharacteristic:characteristic type:0];
// CBCharacteristicWriteWithResponse from CBPeripheral.h
}
}
pub fn peripheral_setnotifyvalue_forcharacteristic(
cbperipheral: *mut Object,
value: BOOL,
characteristic: *mut Object, /* CBCharacteristic* */
) {
unsafe {
let () = msg_send![cbperipheral, setNotifyValue:value forCharacteristic:characteristic];
}
}
pub fn peripheral_discoverdescriptorsforcharacteristic(
cbperipheral: *mut Object,
characteristic: *mut Object, /* CBCharacteristic* */
) {
unsafe {
let () = msg_send![
cbperipheral,
discoverDescriptorsForCharacteristic: characteristic
];
}
}
// CBPeripheralState = NSInteger from CBPeripheral.h
pub const PERIPHERALSTATE_CONNECTED: c_int = 2; // CBPeripheralStateConnected
// CBAttribute
pub fn attribute_uuid(cbattribute: *mut Object) -> *mut Object /* CBUUID* */ {
unsafe {
let uuid: *mut Object = msg_send![cbattribute, UUID];
uuid
}
}
// CBService : CBAttribute
// pub fn service_isprimary(cbservice: *mut Object) -> BOOL {
// unsafe {
// let isprimary: BOOL = msg_send![cbservice, isPrimary];
// isprimary
// }
// }
pub fn service_includedservices(cbservice: *mut Object) -> *mut Object /* NSArray<CBService*>* */
{
unsafe {
let includedservices: *mut Object = msg_send![cbservice, includedServices];
includedservices
}
}
pub fn service_characteristics(cbservice: *mut Object) -> *mut Object /* NSArray<CBCharacteristic*>* */
{
unsafe {
let characteristics: *mut Object = msg_send![cbservice, characteristics];
characteristics
}
}
// CBCharacteristic : CBAttribute
pub fn characteristic_isnotifying(cbcharacteristic: *mut Object) -> BOOL {
unsafe {
let isnotifying: BOOL = msg_send![cbcharacteristic, isNotifying];
isnotifying
}
}
pub fn characteristic_value(cbcharacteristic: *mut Object) -> *mut Object /* NSData* */ {
unsafe {
let value: *mut Object = msg_send![cbcharacteristic, value];
value
}
}
pub fn characteristic_properties(cbcharacteristic: *mut Object) -> c_uint {
unsafe {
let properties: c_uint = msg_send![cbcharacteristic, properties];
properties
}
}
// CBCharacteristicProperties = NSUInteger from CBCharacteristic.h
pub const CHARACTERISTICPROPERTY_BROADCAST: c_uint = 0x01; // CBCharacteristicPropertyBroadcast
pub const CHARACTERISTICPROPERTY_READ: c_uint = 0x02; // CBCharacteristicPropertyRead
pub const CHARACTERISTICPROPERTY_WRITEWITHOUTRESPONSE: c_uint = 0x04; // CBCharacteristicPropertyWriteWithoutResponse
pub const CHARACTERISTICPROPERTY_WRITE: c_uint = 0x08; // CBCharacteristicPropertyWrite
pub const CHARACTERISTICPROPERTY_NOTIFY: c_uint = 0x10; // CBCharacteristicPropertyNotify
pub const CHARACTERISTICPROPERTY_INDICATE: c_uint = 0x20; // CBCharacteristicPropertyIndicate
pub const CHARACTERISTICPROPERTY_AUTHENTICATEDSIGNEDWRITES: c_uint = 0x40; // CBCharacteristicPropertyAuthenticatedSignedWrites
// CBUUID
pub fn uuid_uuidstring(cbuuid: *mut Object) -> *mut Object /* NSString* */ {
unsafe {
let uuidstring: *mut Object = msg_send![cbuuid, UUIDString];
uuidstring
}
}
// CBCentralManagerScanOption...Key
// CBAdvertisementData...Key
pub use self::link::CBAdvertisementDataServiceUUIDsKey as ADVERTISEMENTDATASERVICEUUIDSKEY;
pub use self::link::CBCentralManagerScanOptionAllowDuplicatesKey as CENTRALMANAGERSCANOPTIONALLOWDUPLICATESKEY;
}

View file

@ -0,0 +1,225 @@
// Copyright (c) 2017 Akos Kiss.
//
// Licensed under the BSD 3-Clause License
// <LICENSE.md or https://opensource.org/licenses/BSD-3-Clause>.
// This file may not be copied, modified, or distributed except
// according to those terms.
use std::error::Error;
use std::os::raw::c_uint;
use std::slice;
use std::sync::Arc;
use delegate::bmx;
use framework::{cb, nil, ns};
use gatt_service::BluetoothGATTService;
use objc::runtime::{Object, NO, YES};
use utils::{cbx, wait, NOT_SUPPORTED_ERROR, NO_CHARACTERISTIC_FOUND};
#[derive(Clone, Debug)]
pub struct BluetoothGATTCharacteristic {
pub(crate) service: Arc<BluetoothGATTService>,
pub(crate) characteristic: *mut Object,
}
// TODO: implement std::fmt::Debug and/or std::fmt::Display instead of derive?
impl BluetoothGATTCharacteristic {
pub fn new(service: Arc<BluetoothGATTService>, uuid: String) -> BluetoothGATTCharacteristic {
// NOTE: It can happen that there is no characteristic for the given UUID, in that case
// self.characteristic will be nil and all methods that return a Result will return
// Err(Box::from(NO_CHARACTERISTIC_FOUND)), while others will return some meaningless value.
let characteristic = Self::characteristic_by_uuid(service.service, &uuid);
if characteristic == nil {
warn!(
"BluetoothGATTCharacteristic::new found no characteristic for UUID {}",
uuid
);
}
BluetoothGATTCharacteristic {
service: service.clone(),
characteristic: characteristic,
}
}
fn characteristic_by_uuid(service: *mut Object, uuid: &String) -> *mut Object {
if service != nil {
let chars = cb::service_characteristics(service);
for i in 0..ns::array_count(chars) {
let c = ns::array_objectatindex(chars, i);
if cbx::uuid_to_canonical_uuid_string(cb::attribute_uuid(c)) == *uuid {
return c;
}
}
}
nil
}
pub fn get_id(&self) -> String {
trace!("BluetoothGATTCharacteristic::get_id");
self.get_uuid().unwrap_or(String::new())
}
pub fn get_uuid(&self) -> Result<String, Box<dyn Error>> {
trace!("BluetoothGATTCharacteristic::get_uuid");
if self.characteristic == nil {
return Err(Box::from(NO_CHARACTERISTIC_FOUND));
}
let uuid_string =
cbx::uuid_to_canonical_uuid_string(cb::attribute_uuid(self.characteristic));
debug!("BluetoothGATTCharacteristic::get_uuid -> {}", uuid_string);
Ok(uuid_string)
}
pub fn get_value(&self) -> Result<Vec<u8>, Box<dyn Error>> {
trace!("BluetoothGATTCharacteristic::get_value");
if self.characteristic == nil {
return Err(Box::from(NO_CHARACTERISTIC_FOUND));
}
let value = cb::characteristic_value(self.characteristic);
let length = ns::data_length(value);
if length == 0 {
return Ok(vec![]);
}
let bytes = ns::data_bytes(value);
let v = unsafe { slice::from_raw_parts(bytes, length as usize).to_vec() };
debug!("BluetoothGATTCharacteristic::get_value -> {:?}", v);
Ok(v)
}
pub fn read_value(&self) -> Result<Vec<u8>, Box<dyn Error>> {
trace!("BluetoothGATTCharacteristic::read_value");
if self.characteristic == nil {
return Err(Box::from(NO_CHARACTERISTIC_FOUND));
}
let events = bmx::peripheralevents(
self.service.device.adapter.delegate,
self.service.device.peripheral,
)?;
let key = bmx::valueupdatedkey(self.characteristic);
let t = wait::get_timestamp();
cb::peripheral_readvalueforcharacteristic(
self.service.device.peripheral,
self.characteristic,
);
wait::wait_or_timeout(|| {
let nsnumber = ns::dictionary_objectforkey(events, key);
(nsnumber != nil) && (ns::number_unsignedlonglongvalue(nsnumber) >= t)
})?;
self.get_value()
}
pub fn write_value(&self, values: Vec<u8>) -> Result<(), Box<dyn Error>> {
trace!("BluetoothGATTCharacteristic::write_value");
if self.characteristic == nil {
return Err(Box::from(NO_CHARACTERISTIC_FOUND));
}
let events = bmx::peripheralevents(
self.service.device.adapter.delegate,
self.service.device.peripheral,
)?;
let key = bmx::valuewrittenkey(self.characteristic);
let t = wait::get_timestamp();
cb::peripheral_writevalue_forcharacteristic(
self.service.device.peripheral,
ns::data(values.as_ptr(), values.len() as c_uint),
self.characteristic,
);
wait::wait_or_timeout(|| {
let nsnumber = ns::dictionary_objectforkey(events, key);
(nsnumber != nil) && (ns::number_unsignedlonglongvalue(nsnumber) >= t)
})?;
Ok(())
}
pub fn is_notifying(&self) -> Result<bool, Box<dyn Error>> {
trace!("BluetoothGATTCharacteristic::is_notifying");
if self.characteristic == nil {
return Err(Box::from(NO_CHARACTERISTIC_FOUND));
}
let notifying = cb::characteristic_isnotifying(self.characteristic);
debug!("BluetoothGATTCharacteristic::is_notifying -> {}", notifying);
Ok(notifying != NO)
}
pub fn start_notify(&self) -> Result<(), Box<dyn Error>> {
trace!("BluetoothGATTCharacteristic::start_notify");
if self.characteristic == nil {
return Err(Box::from(NO_CHARACTERISTIC_FOUND));
}
cb::peripheral_setnotifyvalue_forcharacteristic(
self.service.device.peripheral,
YES,
self.characteristic,
);
Ok(())
}
pub fn stop_notify(&self) -> Result<(), Box<dyn Error>> {
trace!("BluetoothGATTCharacteristic::stop_notify");
if self.characteristic == nil {
return Err(Box::from(NO_CHARACTERISTIC_FOUND));
}
cb::peripheral_setnotifyvalue_forcharacteristic(
self.service.device.peripheral,
NO,
self.characteristic,
);
Ok(())
}
pub fn get_gatt_descriptors(&self) -> Result<Vec<String>, Box<dyn Error>> {
warn!("BluetoothGATTCharacteristic::get_gatt_descriptors");
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn get_flags(&self) -> Result<Vec<String>, Box<dyn Error>> {
trace!("BluetoothGATTCharacteristic::get_flags");
if self.characteristic == nil {
return Err(Box::from(NO_CHARACTERISTIC_FOUND));
}
let flags = cb::characteristic_properties(self.characteristic);
// NOTE: It is not documented anywhere what strings to return. Strings below were
// reverse-engineered from the sources of blurdroid.
let mut v = vec![];
if (flags & cb::CHARACTERISTICPROPERTY_BROADCAST) != 0 {
v.push(String::from("broadcast"));
}
if (flags & cb::CHARACTERISTICPROPERTY_READ) != 0 {
v.push(String::from("read"));
}
if (flags & cb::CHARACTERISTICPROPERTY_WRITEWITHOUTRESPONSE) != 0 {
v.push(String::from("write-without-response"));
}
if (flags & cb::CHARACTERISTICPROPERTY_WRITE) != 0 {
v.push(String::from("write"));
}
if (flags & cb::CHARACTERISTICPROPERTY_NOTIFY) != 0 {
v.push(String::from("notify"));
}
if (flags & cb::CHARACTERISTICPROPERTY_INDICATE) != 0 {
v.push(String::from("indicate"));
}
if (flags & cb::CHARACTERISTICPROPERTY_AUTHENTICATEDSIGNEDWRITES) != 0 {
v.push(String::from("authenticated-signed-writes"));
}
debug!("BluetoothGATTCharacteristic::get_flags -> {:?}", v);
Ok(v)
}
}

View file

@ -0,0 +1,43 @@
// Copyright (c) 2017 Akos Kiss.
//
// Licensed under the BSD 3-Clause License
// <LICENSE.md or https://opensource.org/licenses/BSD-3-Clause>.
// This file may not be copied, modified, or distributed except
// according to those terms.
use std::error::Error;
use utils::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<String, Box<dyn Error>> {
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn get_value(&self) -> Result<Vec<u8>, Box<dyn Error>> {
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn get_flags(&self) -> Result<Vec<String>, Box<dyn Error>> {
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn read_value(&self) -> Result<Vec<u8>, Box<dyn Error>> {
Err(Box::from(NOT_SUPPORTED_ERROR))
}
pub fn write_value(&self, _values: Vec<u8>) -> Result<(), Box<dyn Error>> {
Err(Box::from(NOT_SUPPORTED_ERROR))
}
}

128
third_party/blurmac/src/gatt_service.rs vendored Normal file
View file

@ -0,0 +1,128 @@
// Copyright (c) 2017 Akos Kiss.
//
// Licensed under the BSD 3-Clause License
// <LICENSE.md or https://opensource.org/licenses/BSD-3-Clause>.
// This file may not be copied, modified, or distributed except
// according to those terms.
use std::error::Error;
use std::sync::Arc;
use delegate::bmx;
use device::BluetoothDevice;
use framework::{cb, nil, ns};
use objc::runtime::Object;
use utils::{cbx, wait, NO_SERVICE_FOUND};
#[derive(Clone, Debug)]
pub struct BluetoothGATTService {
pub(crate) device: Arc<BluetoothDevice>,
pub(crate) service: *mut Object,
}
// TODO: implement std::fmt::Debug and/or std::fmt::Display instead of derive?
impl BluetoothGATTService {
pub fn new(device: Arc<BluetoothDevice>, uuid: String) -> BluetoothGATTService {
trace!("BluetoothGATTService::new");
// NOTE: It can happen that there is no service for the given UUID, in that case
// self.service will be nil and all methods that return a Result will return
// Err(Box::from(NO_SERVICE_FOUND)), while others will return some meaningless value.
let service = Self::service_by_uuid(device.peripheral, &uuid);
if service == nil {
warn!(
"BluetoothGATTService::new found no service for UUID {}",
uuid
);
}
BluetoothGATTService {
device: device.clone(),
service: service,
}
}
fn service_by_uuid(peripheral: *mut Object, uuid: &String) -> *mut Object {
if peripheral != nil {
// TODO: This function will most probably not find included services. Make it recursively
// descend into included services if first loop did not find what it was looking for.
let services = cb::peripheral_services(peripheral);
for i in 0..ns::array_count(services) {
let s = ns::array_objectatindex(services, i);
if cbx::uuid_to_canonical_uuid_string(cb::attribute_uuid(s)) == *uuid {
return s;
}
}
}
nil
}
pub fn get_id(&self) -> String {
trace!("BluetoothGATTService::get_id");
self.get_uuid().unwrap_or(String::new())
}
pub fn get_uuid(&self) -> Result<String, Box<dyn Error>> {
trace!("BluetoothGATTService::get_uuid");
if self.service == nil {
return Err(Box::from(NO_SERVICE_FOUND));
}
let uuid_string = cbx::uuid_to_canonical_uuid_string(cb::attribute_uuid(self.service));
debug!("BluetoothGATTService::get_uuid -> {}", uuid_string);
Ok(uuid_string)
}
pub fn is_primary(&self) -> Result<bool, Box<dyn Error>> {
trace!("BluetoothGATTService::is_primary");
if self.service == nil {
return Err(Box::from(NO_SERVICE_FOUND));
}
// let primary = cb::service_isprimary(self.service);
// debug!("BluetoothGATTService::is_primary -> {}", primary);
// Ok(primary != NO)
// FIXME: dirty hack. no idea why [CBService isPrimary] returns NO for a primary service.
Ok(true)
}
pub fn get_includes(&self) -> Result<Vec<String>, Box<dyn Error>> {
trace!("BluetoothGATTService::get_includes");
if self.service == nil {
return Err(Box::from(NO_SERVICE_FOUND));
}
let events = bmx::peripheralevents(self.device.adapter.delegate, self.device.peripheral)?;
let key = bmx::includedservicesdiscoveredkey(self.service);
wait::wait_or_timeout(|| ns::dictionary_objectforkey(events, key) != nil)?;
let mut v = vec![];
let includes = cb::service_includedservices(self.service);
for i in 0..ns::array_count(includes) {
v.push(cbx::uuid_to_canonical_uuid_string(cb::attribute_uuid(
ns::array_objectatindex(includes, i),
)));
}
Ok(v)
}
pub fn get_gatt_characteristics(&self) -> Result<Vec<String>, Box<dyn Error>> {
trace!("BluetoothGATTService::get_gatt_characteristics");
if self.service == nil {
return Err(Box::from(NO_SERVICE_FOUND));
}
let events = bmx::peripheralevents(self.device.adapter.delegate, self.device.peripheral)?;
let key = bmx::characteristicsdiscoveredkey(self.service);
wait::wait_or_timeout(|| ns::dictionary_objectforkey(events, key) != nil)?;
let mut v = vec![];
let chars = cb::service_characteristics(self.service);
for i in 0..ns::array_count(chars) {
v.push(cbx::uuid_to_canonical_uuid_string(cb::attribute_uuid(
ns::array_objectatindex(chars, i),
)));
}
Ok(v)
}
}

28
third_party/blurmac/src/lib.rs vendored Normal file
View file

@ -0,0 +1,28 @@
// Copyright (c) 2017 Akos Kiss.
//
// Licensed under the BSD 3-Clause License
// <LICENSE.md or https://opensource.org/licenses/BSD-3-Clause>.
// This file may not be copied, modified, or distributed except
// according to those terms.
#[macro_use]
extern crate log;
#[macro_use]
extern crate objc;
mod adapter;
mod delegate;
mod device;
mod discovery_session;
mod framework;
mod gatt_characteristic;
mod gatt_descriptor;
mod gatt_service;
mod utils;
pub use adapter::BluetoothAdapter;
pub use device::BluetoothDevice;
pub use discovery_session::BluetoothDiscoverySession;
pub use gatt_characteristic::BluetoothGATTCharacteristic;
pub use gatt_descriptor::BluetoothGATTDescriptor;
pub use gatt_service::BluetoothGATTService;

122
third_party/blurmac/src/utils.rs vendored Normal file
View file

@ -0,0 +1,122 @@
// Copyright (c) 2017 Akos Kiss.
//
// Licensed under the BSD 3-Clause License
// <LICENSE.md or https://opensource.org/licenses/BSD-3-Clause>.
// This file may not be copied, modified, or distributed except
// according to those terms.
use std::error::Error;
use std::ffi::{CStr, CString};
use std::sync::atomic::{AtomicUsize, Ordering};
use std::{thread, time};
use framework::{cb, nil, ns};
use objc::runtime::Object;
pub const NOT_SUPPORTED_ERROR: &'static str = "Error! Not supported by blurmac!";
pub const NO_PERIPHERAL_FOUND: &'static str = "Error! No peripheral found!";
pub const NO_SERVICE_FOUND: &'static str = "Error! No service found!";
pub const NO_CHARACTERISTIC_FOUND: &'static str = "Error! No characteristic found!";
pub mod nsx {
use super::*;
pub fn string_to_string(nsstring: *mut Object) -> String {
if nsstring == nil {
return String::from("nil");
}
unsafe {
String::from(
CStr::from_ptr(ns::string_utf8string(nsstring))
.to_str()
.unwrap(),
)
}
}
pub fn string_from_str(string: &str) -> *mut Object {
let cstring = CString::new(string).unwrap();
ns::string(cstring.as_ptr())
}
}
pub mod cbx {
use super::*;
pub fn uuid_to_canonical_uuid_string(cbuuid: *mut Object) -> String {
// NOTE: CoreBluetooth tends to return uppercase UUID strings, and only 4 character long if the
// UUID is short (16 bits). However, WebBluetooth mandates lowercase UUID strings. And Servo
// seems to compare strings, not the binary representation.
let uuid = nsx::string_to_string(cb::uuid_uuidstring(cbuuid));
let long = if uuid.len() == 4 {
format!("0000{}-0000-1000-8000-00805f9b34fb", uuid)
} else {
uuid
};
long.to_lowercase()
}
pub fn peripheral_debug(peripheral: *mut Object) -> String {
if peripheral == nil {
return String::from("nil");
}
let name = cb::peripheral_name(peripheral);
let uuid = ns::uuid_uuidstring(cb::peer_identifier(peripheral));
if name != nil {
format!(
"CBPeripheral({}, {})",
nsx::string_to_string(name),
nsx::string_to_string(uuid)
)
} else {
format!("CBPeripheral({})", nsx::string_to_string(uuid))
}
}
pub fn service_debug(service: *mut Object) -> String {
if service == nil {
return String::from("nil");
}
let uuid = cb::uuid_uuidstring(cb::attribute_uuid(service));
format!("CBService({})", nsx::string_to_string(uuid))
}
pub fn characteristic_debug(characteristic: *mut Object) -> String {
if characteristic == nil {
return String::from("nil");
}
let uuid = cb::uuid_uuidstring(cb::attribute_uuid(characteristic));
format!("CBCharacteristic({})", nsx::string_to_string(uuid))
}
}
pub mod wait {
use super::*;
pub type Timestamp = u64;
static TIMESTAMP: AtomicUsize = AtomicUsize::new(0);
pub fn get_timestamp() -> Timestamp {
TIMESTAMP.fetch_add(1, Ordering::SeqCst) as u64
}
pub fn now() -> *mut Object {
ns::number_withunsignedlonglong(get_timestamp())
}
pub fn wait_or_timeout<F>(mut f: F) -> Result<(), Box<dyn Error>>
where
F: FnMut() -> bool,
{
let now = time::Instant::now();
while !f() {
thread::sleep(time::Duration::from_secs(1));
if now.elapsed().as_secs() > 30 {
return Err(Box::from("timeout"));
}
}
Ok(())
}
}