mirror of
https://github.com/servo/servo.git
synced 2025-08-04 13:10:20 +01:00
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:
parent
fddc4a430f
commit
6a804cd775
23 changed files with 3880 additions and 27 deletions
17
Cargo.lock
generated
17
Cargo.lock
generated
|
@ -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"
|
||||||
|
|
|
@ -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 }
|
||||||
|
|
55
components/bluetooth/README.md
Normal file
55
components/bluetooth/README.md
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
# Bluetooth Rust lib using macOS CoreBluetooth
|
||||||
|
|
||||||
|
[](https://travis-ci.org/akosthekiss/blurmac)
|
||||||
|
[](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).
|
408
components/bluetooth/adapter.rs
Normal file
408
components/bluetooth/adapter.rs
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
753
components/bluetooth/bluetooth.rs
Normal file
753
components/bluetooth/bluetooth.rs
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
377
components/bluetooth/empty.rs
Normal file
377
components/bluetooth/empty.rs
Normal 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))
|
||||||
|
}
|
||||||
|
}
|
|
@ -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()?;
|
||||||
|
|
98
components/bluetooth/macros.rs
Normal file
98
components/bluetooth/macros.rs
Normal 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;
|
|
@ -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
41
third_party/blurmac/Cargo.lock
generated
vendored
Normal 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
18
third_party/blurmac/Cargo.toml
vendored
Normal 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
27
third_party/blurmac/LICENSE.md
vendored
Normal 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
55
third_party/blurmac/README.md
vendored
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
# Bluetooth Rust lib using macOS CoreBluetooth
|
||||||
|
|
||||||
|
[](https://travis-ci.org/akosthekiss/blurmac)
|
||||||
|
[](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
212
third_party/blurmac/src/adapter.rs
vendored
Normal 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
443
third_party/blurmac/src/delegate.rs
vendored
Normal 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
280
third_party/blurmac/src/device.rs
vendored
Normal 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))
|
||||||
|
}
|
||||||
|
}
|
39
third_party/blurmac/src/discovery_session.rs
vendored
Normal file
39
third_party/blurmac/src/discovery_session.rs
vendored
Normal 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
490
third_party/blurmac/src/framework.rs
vendored
Normal 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;
|
||||||
|
}
|
225
third_party/blurmac/src/gatt_characteristic.rs
vendored
Normal file
225
third_party/blurmac/src/gatt_characteristic.rs
vendored
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
43
third_party/blurmac/src/gatt_descriptor.rs
vendored
Normal file
43
third_party/blurmac/src/gatt_descriptor.rs
vendored
Normal 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
128
third_party/blurmac/src/gatt_service.rs
vendored
Normal 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
28
third_party/blurmac/src/lib.rs
vendored
Normal 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
122
third_party/blurmac/src/utils.rs
vendored
Normal 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(())
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue