Auto merge of #10632 - szeged:bluetooth-ipc, r=jdm

WebBluetooth impementation

Update the current WebBluetooth implementation.

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="35" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/10632)
<!-- Reviewable:end -->
This commit is contained in:
bors-servo 2016-05-04 08:06:06 -07:00
commit 944a8dc25a
36 changed files with 2178 additions and 125 deletions

View file

@ -34,6 +34,7 @@ matrix:
- python-virtualenv
- xorg-dev
- ccache
- libdbus-glib-1-dev
branches:
only:

View file

@ -36,6 +36,7 @@ use msg::constellation_msg::{PipelineNamespace, PipelineNamespaceId, NavigationD
use msg::constellation_msg::{SubpageId, WindowSizeData, WindowSizeType};
use msg::constellation_msg::{self, ConstellationChan, PanicMsg};
use msg::webdriver_msg;
use net_traits::bluetooth_thread::BluetoothMethodMsg;
use net_traits::image_cache_thread::ImageCacheThread;
use net_traits::storage_thread::{StorageThread, StorageThreadMsg};
use net_traits::{self, ResourceThread};
@ -123,6 +124,9 @@ pub struct Constellation<LTF, STF> {
/// A channel through which messages can be sent to the developer tools.
devtools_chan: Option<Sender<DevtoolsControlMsg>>,
/// A channel through which messages can be sent to the bluetooth thread.
bluetooth_thread: IpcSender<BluetoothMethodMsg>,
/// A channel through which messages can be sent to the storage thread.
storage_thread: StorageThread,
@ -197,6 +201,8 @@ pub struct InitialConstellationState {
pub compositor_proxy: Box<CompositorProxy + Send>,
/// A channel to the developer tools, if applicable.
pub devtools_chan: Option<Sender<DevtoolsControlMsg>>,
/// A channel to the bluetooth thread.
pub bluetooth_thread: IpcSender<BluetoothMethodMsg>,
/// A channel to the image cache thread.
pub image_cache_thread: ImageCacheThread,
/// A channel to the font cache thread.
@ -338,6 +344,7 @@ impl<LTF: LayoutThreadFactory, STF: ScriptThreadFactory> Constellation<LTF, STF>
panic_receiver: panic_receiver,
compositor_proxy: state.compositor_proxy,
devtools_chan: state.devtools_chan,
bluetooth_thread: state.bluetooth_thread,
resource_thread: state.resource_thread,
image_cache_thread: state.image_cache_thread,
font_cache_thread: state.font_cache_thread,
@ -424,6 +431,7 @@ impl<LTF: LayoutThreadFactory, STF: ScriptThreadFactory> Constellation<LTF, STF>
scheduler_chan: self.scheduler_chan.clone(),
compositor_proxy: self.compositor_proxy.clone_compositor_proxy(),
devtools_chan: self.devtools_chan.clone(),
bluetooth_thread: self.bluetooth_thread.clone(),
image_cache_thread: self.image_cache_thread.clone(),
font_cache_thread: self.font_cache_thread.clone(),
resource_thread: self.resource_thread.clone(),
@ -840,6 +848,9 @@ impl<LTF: LayoutThreadFactory, STF: ScriptThreadFactory> Constellation<LTF, STF>
if let Err(e) = self.storage_thread.send(StorageThreadMsg::Exit) {
warn!("Exit storage thread failed ({})", e);
}
if let Err(e) = self.bluetooth_thread.send(BluetoothMethodMsg::Exit) {
warn!("Exit bluetooth thread failed ({})", e);
}
self.font_cache_thread.exit();
self.compositor_proxy.send(ToCompositorMsg::ShutdownComplete);
}

View file

@ -18,6 +18,7 @@ use msg::constellation_msg::{ConstellationChan, PanicMsg, FrameId, PipelineId, S
use msg::constellation_msg::{LoadData, WindowSizeData};
use msg::constellation_msg::{PipelineNamespaceId};
use net_traits::ResourceThread;
use net_traits::bluetooth_thread::BluetoothMethodMsg;
use net_traits::image_cache_thread::ImageCacheThread;
use net_traits::storage_thread::StorageThread;
use profile_traits::mem as profile_mem;
@ -92,6 +93,8 @@ pub struct InitialPipelineState {
pub compositor_proxy: Box<CompositorProxy + 'static + Send>,
/// A channel to the developer tools, if applicable.
pub devtools_chan: Option<Sender<DevtoolsControlMsg>>,
/// A channel to the bluetooth thread.
pub bluetooth_thread: IpcSender<BluetoothMethodMsg>,
/// A channel to the image cache thread.
pub image_cache_thread: ImageCacheThread,
/// A channel to the font cache thread.
@ -214,6 +217,7 @@ impl Pipeline {
constellation_chan: state.constellation_chan,
scheduler_chan: state.scheduler_chan,
devtools_chan: script_to_devtools_chan,
bluetooth_thread: state.bluetooth_thread,
image_cache_thread: state.image_cache_thread,
font_cache_thread: state.font_cache_thread.clone(),
resource_thread: state.resource_thread,
@ -390,6 +394,7 @@ pub struct UnprivilegedPipelineContent {
scheduler_chan: IpcSender<TimerEventRequest>,
devtools_chan: Option<IpcSender<ScriptToDevtoolsControlMsg>>,
script_to_compositor_chan: IpcSender<ScriptToCompositorMsg>,
bluetooth_thread: IpcSender<BluetoothMethodMsg>,
image_cache_thread: ImageCacheThread,
font_cache_thread: FontCacheThread,
resource_thread: ResourceThread,
@ -430,6 +435,7 @@ impl UnprivilegedPipelineContent {
layout_to_constellation_chan: self.layout_to_constellation_chan.clone(),
scheduler_chan: self.scheduler_chan.clone(),
panic_chan: self.panic_chan.clone(),
bluetooth_thread: self.bluetooth_thread.clone(),
resource_thread: self.resource_thread,
storage_thread: self.storage_thread.clone(),
image_cache_thread: self.image_cache_thread.clone(),

View file

@ -17,6 +17,8 @@ plugins = {path = "../plugins"}
msg = {path = "../msg"}
ipc-channel = {git = "https://github.com/servo/ipc-channel"}
webrender_traits = {git = "https://github.com/servo/webrender_traits"}
device = {git = "https://github.com/servo/devices"}
bitflags = "0.6.0"
cookie = { version = "0.2.4", features = [ "serialize-rustc" ] }
flate2 = "0.2.0"
hyper = { version = "0.9", features = [ "serde-serialization" ] }

View file

@ -0,0 +1,649 @@
/* 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 device::bluetooth::BluetoothAdapter;
use device::bluetooth::BluetoothDevice;
use device::bluetooth::BluetoothGATTCharacteristic;
use device::bluetooth::BluetoothGATTDescriptor;
use device::bluetooth::BluetoothGATTService;
use ipc_channel::ipc::{self, IpcReceiver, IpcSender};
use net_traits::bluetooth_scanfilter::{BluetoothScanfilter, BluetoothScanfilterSequence, RequestDeviceoptions};
use net_traits::bluetooth_thread::{BluetoothCharacteristicMsg, BluetoothCharacteristicsMsg};
use net_traits::bluetooth_thread::{BluetoothDescriptorMsg, BluetoothDescriptorsMsg};
use net_traits::bluetooth_thread::{BluetoothDeviceMsg, BluetoothMethodMsg};
use net_traits::bluetooth_thread::{BluetoothResult, BluetoothServiceMsg, BluetoothServicesMsg};
use std::borrow::ToOwned;
use std::collections::HashMap;
use std::string::String;
use util::thread::spawn_named;
const ADAPTER_ERROR: &'static str = "No adapter found";
const DEVICE_ERROR: &'static str = "No device found";
const DEVICE_MATCH_ERROR: &'static str = "No device found, that matches the given options";
const PRIMARY_SERVICE_ERROR: &'static str = "No primary service found";
const CHARACTERISTIC_ERROR: &'static str = "No characteristic found";
const DESCRIPTOR_ERROR: &'static str = "No descriptor found";
const VALUE_ERROR: &'static str = "No characteristic or descriptor found with that id";
bitflags! {
flags Flags: u32 {
const BROADCAST = 0b000000001,
const READ = 0b000000010,
const WRITE_WITHOUT_RESPONSE = 0b000000100,
const WRITE = 0b000001000,
const NOTIFY = 0b000010000,
const INDICATE = 0b000100000,
const AUTHENTICATED_SIGNED_WRITES = 0b001000000,
const RELIABLE_WRITE = 0b010000000,
const WRITABLE_AUXILIARIES = 0b100000000,
}
}
macro_rules! return_if_cached(
($cache:expr, $key:expr) => (
if $cache.contains_key($key) {
return $cache.get($key);
}
);
);
pub trait BluetoothThreadFactory {
fn new() -> Self;
}
impl BluetoothThreadFactory for IpcSender<BluetoothMethodMsg> {
fn new() -> IpcSender<BluetoothMethodMsg> {
let (sender, receiver) = ipc::channel().unwrap();
let adapter = BluetoothAdapter::init().ok();
spawn_named("BluetoothThread".to_owned(), move || {
BluetoothManager::new(receiver, adapter).start();
});
sender
}
}
fn matches_filter(device: &BluetoothDevice, filter: &BluetoothScanfilter) -> bool {
if filter.is_empty_or_invalid() {
return false;
}
if !filter.get_name().is_empty() {
if device.get_name().ok() != Some(filter.get_name().to_string()) {
return false;
}
}
if !filter.get_name_prefix().is_empty() {
if let Ok(device_name) = device.get_name() {
if !device_name.starts_with(filter.get_name_prefix()) {
return false;
}
} else {
return false;
}
}
if !filter.get_services().is_empty() {
if let Ok(device_uuids) = device.get_uuids() {
for service in filter.get_services() {
if device_uuids.iter().find(|x| x == &service).is_none() {
return false;
}
}
}
}
return true;
}
fn matches_filters(device: &BluetoothDevice, filters: &BluetoothScanfilterSequence) -> bool {
if filters.has_empty_or_invalid_filter() {
return false;
}
return filters.iter().any(|f| matches_filter(device, f))
}
pub struct BluetoothManager {
receiver: IpcReceiver<BluetoothMethodMsg>,
adapter: Option<BluetoothAdapter>,
service_to_device: HashMap<String, String>,
characteristic_to_service: HashMap<String, String>,
descriptor_to_characteristic: HashMap<String, String>,
cached_devices: HashMap<String, BluetoothDevice>,
cached_services: HashMap<String, BluetoothGATTService>,
cached_characteristics: HashMap<String, BluetoothGATTCharacteristic>,
cached_descriptors: HashMap<String, BluetoothGATTDescriptor>,
}
impl BluetoothManager {
pub fn new (receiver: IpcReceiver<BluetoothMethodMsg>, adapter: Option<BluetoothAdapter>) -> BluetoothManager {
BluetoothManager {
receiver: receiver,
adapter: adapter,
service_to_device: HashMap::new(),
characteristic_to_service: HashMap::new(),
descriptor_to_characteristic: HashMap::new(),
cached_devices: HashMap::new(),
cached_services: HashMap::new(),
cached_characteristics: HashMap::new(),
cached_descriptors: HashMap::new(),
}
}
fn start(&mut self) {
while let Ok(msg) = self.receiver.recv() {
match msg {
BluetoothMethodMsg::RequestDevice(options, sender) => {
self.request_device(options, sender)
},
BluetoothMethodMsg::GATTServerConnect(device_id, sender) => {
self.gatt_server_connect(device_id, sender)
},
BluetoothMethodMsg::GATTServerDisconnect(device_id, sender) => {
self.gatt_server_disconnect(device_id, sender)
},
BluetoothMethodMsg::GetPrimaryService(device_id, uuid, sender) => {
self.get_primary_service(device_id, uuid, sender)
},
BluetoothMethodMsg::GetPrimaryServices(device_id, uuid, sender) => {
self.get_primary_services(device_id, uuid, sender)
},
BluetoothMethodMsg::GetCharacteristic(service_id, uuid, sender) => {
self.get_characteristic(service_id, uuid, sender)
},
BluetoothMethodMsg::GetCharacteristics(service_id, uuid, sender) => {
self.get_characteristics(service_id, uuid, sender)
},
BluetoothMethodMsg::GetDescriptor(characteristic_id, uuid, sender) => {
self.get_descriptor(characteristic_id, uuid, sender)
},
BluetoothMethodMsg::GetDescriptors(characteristic_id, uuid, sender) => {
self.get_descriptors(characteristic_id, uuid, sender)
},
BluetoothMethodMsg::ReadValue(id, sender) => {
self.read_value(id, sender)
},
BluetoothMethodMsg::WriteValue(id, value, sender) => {
self.write_value(id, value, sender)
},
BluetoothMethodMsg::Exit => {
break
},
}
}
}
// Adapter
fn get_or_create_adapter(&mut self) -> Option<BluetoothAdapter> {
let adapter_valid = self.adapter.as_ref().map_or(false, |a| a.get_address().is_ok());
if !adapter_valid {
self.adapter = BluetoothAdapter::init().ok();
}
self.adapter.clone()
}
// Device
fn get_and_cache_devices(&mut self, adapter: &mut BluetoothAdapter) -> Vec<BluetoothDevice> {
let devices = adapter.get_devices().unwrap_or(vec!());
for device in &devices {
if let Ok(address) = device.get_address() {
self.cached_devices.insert(address, device.clone());
}
}
self.cached_devices.iter().map(|(_, d)| d.clone()).collect()
}
fn get_device(&mut self, adapter: &mut BluetoothAdapter, device_id: &str) -> Option<&BluetoothDevice> {
return_if_cached!(self.cached_devices, device_id);
self.get_and_cache_devices(adapter);
return_if_cached!(self.cached_devices, device_id);
None
}
// Service
fn get_and_cache_gatt_services(&mut self,
adapter: &mut BluetoothAdapter,
device_id: &str)
-> Vec<BluetoothGATTService> {
let services = match self.get_device(adapter, device_id) {
Some(d) => d.get_gatt_services().unwrap_or(vec!()),
None => vec!(),
};
for service in &services {
self.cached_services.insert(service.get_object_path(), service.clone());
self.service_to_device.insert(service.get_object_path(), device_id.to_owned());
}
services
}
fn get_gatt_service(&mut self, adapter: &mut BluetoothAdapter, service_id: &str) -> Option<&BluetoothGATTService> {
return_if_cached!(self.cached_services, service_id);
let device_id = match self.service_to_device.get(service_id) {
Some(d) => d.clone(),
None => return None,
};
self.get_and_cache_gatt_services(adapter, &device_id);
return_if_cached!(self.cached_services, service_id);
None
}
fn get_gatt_services_by_uuid(&mut self,
adapter: &mut BluetoothAdapter,
device_id: &str,
service_uuid: &str)
-> Vec<BluetoothGATTService> {
let services = self.get_and_cache_gatt_services(adapter, device_id);
services.into_iter().filter(|s| s.get_uuid().ok() == Some(service_uuid.to_string())).collect()
}
// Characteristic
fn get_and_cache_gatt_characteristics(&mut self,
adapter: &mut BluetoothAdapter,
service_id: &str)
-> Vec<BluetoothGATTCharacteristic> {
let characteristics = match self.get_gatt_service(adapter, service_id) {
Some(s) => s.get_gatt_characteristics().unwrap_or(vec!()),
None => vec!(),
};
for characteristic in &characteristics {
self.cached_characteristics.insert(characteristic.get_object_path(), characteristic.clone());
self.characteristic_to_service.insert(characteristic.get_object_path(), service_id.to_owned());
}
characteristics
}
fn get_gatt_characteristic(&mut self,
adapter: &mut BluetoothAdapter,
characteristic_id: &str)
-> Option<&BluetoothGATTCharacteristic> {
return_if_cached!(self.cached_characteristics, characteristic_id);
let service_id = match self.characteristic_to_service.get(characteristic_id) {
Some(s) => s.clone(),
None => return None,
};
self.get_and_cache_gatt_characteristics(adapter, &service_id);
return_if_cached!(self.cached_characteristics, characteristic_id);
None
}
fn get_gatt_characteristics_by_uuid(&mut self,
adapter: &mut BluetoothAdapter,
service_id: &str,
characteristic_uuid: &str)
-> Vec<BluetoothGATTCharacteristic> {
let characteristics = self.get_and_cache_gatt_characteristics(adapter, service_id);
characteristics.into_iter()
.filter(|c| c.get_uuid().ok() == Some(characteristic_uuid.to_string()))
.collect()
}
fn get_characteristic_properties(&self, characteristic: &BluetoothGATTCharacteristic) -> Flags {
let mut props: Flags = Flags::empty();
let flags = characteristic.get_flags().unwrap_or(vec!());
for flag in flags {
match flag.as_ref() {
"broadcast" => props.insert(BROADCAST),
"read" => props.insert(READ),
"write_without_response" => props.insert(WRITE_WITHOUT_RESPONSE),
"write" => props.insert(WRITE),
"notify" => props.insert(NOTIFY),
"indicate" => props.insert(INDICATE),
"authenticated_signed_writes" => props.insert(AUTHENTICATED_SIGNED_WRITES),
"reliable_write" => props.insert(RELIABLE_WRITE),
"writable_auxiliaries" => props.insert(WRITABLE_AUXILIARIES),
_ => (),
}
}
props
}
// Descriptor
fn get_and_cache_gatt_descriptors(&mut self,
adapter: &mut BluetoothAdapter,
characteristic_id: &str)
-> Vec<BluetoothGATTDescriptor> {
let descriptors = match self.get_gatt_characteristic(adapter, characteristic_id) {
Some(c) => c.get_gatt_descriptors().unwrap_or(vec!()),
None => vec!(),
};
for descriptor in &descriptors {
self.cached_descriptors.insert(descriptor.get_object_path(), descriptor.clone());
self.descriptor_to_characteristic.insert(descriptor.get_object_path(), characteristic_id.to_owned());
}
descriptors
}
fn get_gatt_descriptor(&mut self,
adapter: &mut BluetoothAdapter,
descriptor_id: &str)
-> Option<&BluetoothGATTDescriptor> {
return_if_cached!(self.cached_descriptors, descriptor_id);
let characteristic_id = match self.descriptor_to_characteristic.get(descriptor_id) {
Some(c) => c.clone(),
None => return None,
};
self.get_and_cache_gatt_descriptors(adapter, &characteristic_id);
return_if_cached!(self.cached_descriptors, descriptor_id);
None
}
fn get_gatt_descriptors_by_uuid(&mut self,
adapter: &mut BluetoothAdapter,
characteristic_id: &str,
descriptor_uuid: &str)
-> Vec<BluetoothGATTDescriptor> {
let descriptors = self.get_and_cache_gatt_descriptors(adapter, characteristic_id);
descriptors.into_iter()
.filter(|d| d.get_uuid().ok() == Some(descriptor_uuid.to_string()))
.collect()
}
// Methods
fn request_device(&mut self,
options: RequestDeviceoptions,
sender: IpcSender<BluetoothResult<BluetoothDeviceMsg>>) {
let mut adapter = match self.get_or_create_adapter() {
Some(a) => a,
None => return drop(sender.send(Err(String::from(ADAPTER_ERROR)))),
};
let devices = self.get_and_cache_devices(&mut adapter);
if devices.is_empty() {
return drop(sender.send(Err(String::from(DEVICE_ERROR))));
}
let matched_devices: Vec<BluetoothDevice> = devices.into_iter()
.filter(|d| matches_filters(d, options.get_filters()))
.collect();
for device in matched_devices {
if let Ok(address) = device.get_address() {
let message = Ok(BluetoothDeviceMsg {
id: address,
name: device.get_name().ok(),
device_class: device.get_class().ok(),
vendor_id_source: device.get_vendor_id_source().ok(),
vendor_id: device.get_vendor_id().ok(),
product_id: device.get_product_id().ok(),
product_version: device.get_device_id().ok(),
appearance: device.get_appearance().ok(),
tx_power: device.get_tx_power().ok().map(|p| p as i8),
rssi: device.get_rssi().ok().map(|p| p as i8),
});
return drop(sender.send(message));
}
}
return drop(sender.send(Err(String::from(DEVICE_MATCH_ERROR))));
}
fn gatt_server_connect(&mut self, device_id: String, sender: IpcSender<BluetoothResult<bool>>) {
let mut adapter = match self.get_or_create_adapter() {
Some(a) => a,
None => return drop(sender.send(Err(String::from(ADAPTER_ERROR)))),
};
let connected = match self.get_device(&mut adapter, &device_id) {
Some(d) => {
if d.is_connected().unwrap_or(false) {
true
} else {
d.connect().is_ok()
}
},
None => return drop(sender.send(Err(String::from(DEVICE_ERROR)))),
};
let _ = sender.send(Ok(connected));
}
fn gatt_server_disconnect(&mut self, device_id: String, sender: IpcSender<BluetoothResult<bool>>) {
let mut adapter = match self.get_or_create_adapter() {
Some(a) => a,
None => return drop(sender.send(Err(String::from(ADAPTER_ERROR)))),
};
let connected = match self.get_device(&mut adapter, &device_id) {
Some(d) => {
if d.is_connected().unwrap_or(false) {
d.disconnect().is_ok()
} else {
false
}
},
None => return drop(sender.send(Err(String::from(DEVICE_ERROR)))),
};
let _ = sender.send(Ok(connected));
}
fn get_primary_service(&mut self,
device_id: String,
uuid: String,
sender: IpcSender<BluetoothResult<BluetoothServiceMsg>>) {
let mut adapter = match self.get_or_create_adapter() {
Some(a) => a,
None => return drop(sender.send(Err(String::from(ADAPTER_ERROR)))),
};
let services = self.get_gatt_services_by_uuid(&mut adapter, &device_id, &uuid);
if services.is_empty() {
return drop(sender.send(Err(String::from(PRIMARY_SERVICE_ERROR))));
}
for service in services {
if service.is_primary().unwrap_or(false) {
if let Ok(uuid) = service.get_uuid() {
return drop(sender.send(Ok(BluetoothServiceMsg {
uuid: uuid,
is_primary: true,
instance_id: service.get_object_path(),
})));
}
}
}
return drop(sender.send(Err(String::from(PRIMARY_SERVICE_ERROR))));
}
fn get_primary_services(&mut self,
device_id: String,
uuid: Option<String>,
sender: IpcSender<BluetoothResult<BluetoothServicesMsg>>) {
let mut adapter = match self.get_or_create_adapter() {
Some(a) => a,
None => return drop(sender.send(Err(String::from(ADAPTER_ERROR)))),
};
let services = match uuid {
Some(id) => self.get_gatt_services_by_uuid(&mut adapter, &device_id, &id),
None => self.get_and_cache_gatt_services(&mut adapter, &device_id),
};
if services.is_empty() {
return drop(sender.send(Err(String::from(PRIMARY_SERVICE_ERROR))));
}
let mut services_vec = vec!();
for service in services {
if service.is_primary().unwrap_or(false) {
if let Ok(uuid) = service.get_uuid() {
services_vec.push(BluetoothServiceMsg {
uuid: uuid,
is_primary: true,
instance_id: service.get_object_path(),
});
}
}
}
if services_vec.is_empty() {
return drop(sender.send(Err(String::from(PRIMARY_SERVICE_ERROR))));
}
let _ = sender.send(Ok(services_vec));
}
fn get_characteristic(&mut self,
service_id: String,
uuid: String,
sender: IpcSender<BluetoothResult<BluetoothCharacteristicMsg>>) {
let mut adapter = match self.get_or_create_adapter() {
Some(a) => a,
None => return drop(sender.send(Err(String::from(ADAPTER_ERROR)))),
};
let characteristics = self.get_gatt_characteristics_by_uuid(&mut adapter, &service_id, &uuid);
if characteristics.is_empty() {
return drop(sender.send(Err(String::from(CHARACTERISTIC_ERROR))));
}
for characteristic in characteristics {
if let Ok(uuid) = characteristic.get_uuid() {
let properties = self.get_characteristic_properties(&characteristic);
let message = Ok(BluetoothCharacteristicMsg {
uuid: uuid,
instance_id: characteristic.get_object_path(),
broadcast: properties.contains(BROADCAST),
read: properties.contains(READ),
write_without_response: properties.contains(WRITE_WITHOUT_RESPONSE),
write: properties.contains(WRITE),
notify: properties.contains(NOTIFY),
indicate: properties.contains(INDICATE),
authenticated_signed_writes: properties.contains(AUTHENTICATED_SIGNED_WRITES),
reliable_write: properties.contains(RELIABLE_WRITE),
writable_auxiliaries: properties.contains(WRITABLE_AUXILIARIES),
});
return drop(sender.send(message));
}
}
return drop(sender.send(Err(String::from(CHARACTERISTIC_ERROR))));
}
fn get_characteristics(&mut self,
service_id: String,
uuid: Option<String>,
sender: IpcSender<BluetoothResult<BluetoothCharacteristicsMsg>>) {
let mut adapter = match self.get_or_create_adapter() {
Some(a) => a,
None => return drop(sender.send(Err(String::from(ADAPTER_ERROR)))),
};
let characteristics = match uuid {
Some(id) => self.get_gatt_characteristics_by_uuid(&mut adapter, &service_id, &id),
None => self.get_and_cache_gatt_characteristics(&mut adapter, &service_id),
};
if characteristics.is_empty() {
return drop(sender.send(Err(String::from(CHARACTERISTIC_ERROR))));
}
let mut characteristics_vec = vec!();
for characteristic in characteristics {
if let Ok(uuid) = characteristic.get_uuid() {
let properties = self.get_characteristic_properties(&characteristic);
characteristics_vec.push(
BluetoothCharacteristicMsg {
uuid: uuid,
instance_id: characteristic.get_object_path(),
broadcast: properties.contains(BROADCAST),
read: properties.contains(READ),
write_without_response: properties.contains(WRITE_WITHOUT_RESPONSE),
write: properties.contains(WRITE),
notify: properties.contains(NOTIFY),
indicate: properties.contains(INDICATE),
authenticated_signed_writes: properties.contains(AUTHENTICATED_SIGNED_WRITES),
reliable_write: properties.contains(RELIABLE_WRITE),
writable_auxiliaries: properties.contains(WRITABLE_AUXILIARIES),
});
}
}
if characteristics_vec.is_empty() {
return drop(sender.send(Err(String::from(CHARACTERISTIC_ERROR))));
}
let _ = sender.send(Ok(characteristics_vec));
}
fn get_descriptor(&mut self,
characteristic_id: String,
uuid: String,
sender: IpcSender<BluetoothResult<BluetoothDescriptorMsg>>) {
let mut adapter = match self.get_or_create_adapter() {
Some(a) => a,
None => return drop(sender.send(Err(String::from(ADAPTER_ERROR)))),
};
let descriptors = self.get_gatt_descriptors_by_uuid(&mut adapter, &characteristic_id, &uuid);
if descriptors.is_empty() {
return drop(sender.send(Err(String::from(DESCRIPTOR_ERROR))));
}
for descriptor in descriptors {
if let Ok(uuid) = descriptor.get_uuid() {
return drop(sender.send(Ok(BluetoothDescriptorMsg {
uuid: uuid,
instance_id: descriptor.get_object_path(),
})));
}
}
return drop(sender.send(Err(String::from(DESCRIPTOR_ERROR))));
}
fn get_descriptors(&mut self,
characteristic_id: String,
uuid: Option<String>,
sender: IpcSender<BluetoothResult<BluetoothDescriptorsMsg>>) {
let mut adapter = match self.get_or_create_adapter() {
Some(a) => a,
None => return drop(sender.send(Err(String::from(ADAPTER_ERROR)))),
};
let descriptors = match uuid {
Some(id) => self.get_gatt_descriptors_by_uuid(&mut adapter, &characteristic_id, &id),
None => self.get_and_cache_gatt_descriptors(&mut adapter, &characteristic_id),
};
if descriptors.is_empty() {
return drop(sender.send(Err(String::from(DESCRIPTOR_ERROR))));
}
let mut descriptors_vec = vec!();
for descriptor in descriptors {
if let Ok(uuid) = descriptor.get_uuid() {
descriptors_vec.push(BluetoothDescriptorMsg {
uuid: uuid,
instance_id: descriptor.get_object_path(),
});
}
}
if descriptors_vec.is_empty() {
return drop(sender.send(Err(String::from(DESCRIPTOR_ERROR))));
}
let _ = sender.send(Ok(descriptors_vec));
}
fn read_value(&mut self, id: String, sender: IpcSender<BluetoothResult<Vec<u8>>>) {
let mut adapter = match self.get_or_create_adapter() {
Some(a) => a,
None => return drop(sender.send(Err(String::from(ADAPTER_ERROR)))),
};
let mut value = self.get_gatt_characteristic(&mut adapter, &id)
.map(|c| c.read_value().unwrap_or(vec![]));
if value.is_none() {
value = self.get_gatt_descriptor(&mut adapter, &id)
.map(|d| d.read_value().unwrap_or(vec![]));
}
let _ = sender.send(value.ok_or(String::from(VALUE_ERROR)));
}
fn write_value(&mut self, id: String, value: Vec<u8>, sender: IpcSender<BluetoothResult<bool>>) {
let mut adapter = match self.get_or_create_adapter() {
Some(a) => a,
None => return drop(sender.send(Err(String::from(ADAPTER_ERROR)))),
};
let mut result = self.get_gatt_characteristic(&mut adapter, &id)
.map(|c| c.write_value(value.clone()));
if result.is_none() {
result = self.get_gatt_descriptor(&mut adapter, &id)
.map(|d| d.write_value(value.clone()));
}
let message = match result {
Some(v) => match v {
Ok(_) => Ok(true),
Err(e) => return drop(sender.send(Err(e.to_string()))),
},
None => return drop(sender.send(Err(String::from(VALUE_ERROR)))),
};
let _ = sender.send(message);
}
}

View file

@ -11,8 +11,11 @@
#![deny(unsafe_code)]
#[macro_use]
extern crate bitflags;
extern crate brotli;
extern crate cookie as cookie_rs;
extern crate device;
extern crate devtools_traits;
extern crate flate2;
extern crate hyper;
@ -39,6 +42,7 @@ extern crate webrender_traits;
extern crate websocket;
pub mod about_loader;
pub mod bluetooth_thread;
pub mod chrome_loader;
pub mod cookie;
pub mod cookie_storage;

View file

@ -0,0 +1,93 @@
/* 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::slice::Iter;
// A device name can never be longer than 29 bytes. An adv packet is at most
// 31 bytes long. The length and identifier of the length field take 2 bytes.
// That leaves 29 bytes for the name.
const MAX_NAME_LENGTH: usize = 29;
#[derive(Deserialize, Serialize)]
pub struct ServiceUUIDSequence(Vec<String>);
impl ServiceUUIDSequence {
pub fn new(vec: Vec<String>) -> ServiceUUIDSequence {
ServiceUUIDSequence(vec)
}
}
#[derive(Deserialize, Serialize)]
pub struct BluetoothScanfilter {
name: String,
name_prefix: String,
services: ServiceUUIDSequence,
}
impl BluetoothScanfilter {
pub fn new(name: String, name_prefix: String, services: Vec<String>) -> BluetoothScanfilter {
BluetoothScanfilter {
name: name,
name_prefix: name_prefix,
services: ServiceUUIDSequence::new(services),
}
}
pub fn get_name(&self) -> &str {
&self.name
}
pub fn get_name_prefix(&self) -> &str {
&self.name_prefix
}
pub fn get_services(&self) -> &[String] {
&self.services.0
}
pub fn is_empty_or_invalid(&self) -> bool {
(self.name.is_empty() && self.name_prefix.is_empty() && self.get_services().is_empty()) ||
self.name.len() > MAX_NAME_LENGTH ||
self.name_prefix.len() > MAX_NAME_LENGTH
}
}
#[derive(Deserialize, Serialize)]
pub struct BluetoothScanfilterSequence(Vec<BluetoothScanfilter>);
impl BluetoothScanfilterSequence {
pub fn new(vec: Vec<BluetoothScanfilter>) -> BluetoothScanfilterSequence {
BluetoothScanfilterSequence(vec)
}
pub fn has_empty_or_invalid_filter(&self) -> bool {
self.0.is_empty() ||
self.0.iter().any(BluetoothScanfilter::is_empty_or_invalid)
}
pub fn iter(&self) -> Iter<BluetoothScanfilter> {
self.0.iter()
}
}
#[derive(Deserialize, Serialize)]
pub struct RequestDeviceoptions {
filters: BluetoothScanfilterSequence,
optional_services: ServiceUUIDSequence,
}
impl RequestDeviceoptions {
pub fn new(filters: BluetoothScanfilterSequence,
services: ServiceUUIDSequence)
-> RequestDeviceoptions {
RequestDeviceoptions {
filters: filters,
optional_services: services,
}
}
pub fn get_filters(&self) -> &BluetoothScanfilterSequence {
&self.filters
}
}

View file

@ -0,0 +1,75 @@
/* 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 bluetooth_scanfilter::RequestDeviceoptions;
use ipc_channel::ipc::IpcSender;
#[derive(Deserialize, Serialize)]
pub struct BluetoothDeviceMsg {
// Bluetooth Device properties
pub id: String,
pub name: Option<String>,
pub device_class: Option<u32>,
pub vendor_id_source: Option<String>,
pub vendor_id: Option<u32>,
pub product_id: Option<u32>,
pub product_version: Option<u32>,
// Advertisiong Data properties
pub appearance: Option<u16>,
pub tx_power: Option<i8>,
pub rssi: Option<i8>,
}
#[derive(Deserialize, Serialize)]
pub struct BluetoothServiceMsg {
pub uuid: String,
pub is_primary: bool,
pub instance_id: String,
}
#[derive(Deserialize, Serialize)]
pub struct BluetoothCharacteristicMsg {
// Characteristic
pub uuid: String,
pub instance_id: String,
// Characteristic properties
pub broadcast: bool,
pub read: bool,
pub write_without_response: bool,
pub write: bool,
pub notify: bool,
pub indicate: bool,
pub authenticated_signed_writes: bool,
pub reliable_write: bool,
pub writable_auxiliaries: bool,
}
#[derive(Deserialize, Serialize)]
pub struct BluetoothDescriptorMsg {
pub uuid: String,
pub instance_id: String,
}
pub type BluetoothServicesMsg = Vec<BluetoothServiceMsg>;
pub type BluetoothCharacteristicsMsg = Vec<BluetoothCharacteristicMsg>;
pub type BluetoothDescriptorsMsg = Vec<BluetoothDescriptorMsg>;
pub type BluetoothResult<T> = Result<T, String>;
#[derive(Deserialize, Serialize)]
pub enum BluetoothMethodMsg {
RequestDevice(RequestDeviceoptions, IpcSender<BluetoothResult<BluetoothDeviceMsg>>),
GATTServerConnect(String, IpcSender<BluetoothResult<bool>>),
GATTServerDisconnect(String, IpcSender<BluetoothResult<bool>>),
GetPrimaryService(String, String, IpcSender<BluetoothResult<BluetoothServiceMsg>>),
GetPrimaryServices(String, Option<String>, IpcSender<BluetoothResult<BluetoothServicesMsg>>),
GetCharacteristic(String, String, IpcSender<BluetoothResult<BluetoothCharacteristicMsg>>),
GetCharacteristics(String, Option<String>, IpcSender<BluetoothResult<BluetoothCharacteristicsMsg>>),
GetDescriptor(String, String, IpcSender<BluetoothResult<BluetoothDescriptorMsg>>),
GetDescriptors(String, Option<String>, IpcSender<BluetoothResult<BluetoothDescriptorsMsg>>),
ReadValue(String, IpcSender<BluetoothResult<Vec<u8>>>),
WriteValue(String, Vec<u8>, IpcSender<BluetoothResult<bool>>),
Exit,
}

View file

@ -40,6 +40,8 @@ use std::thread;
use url::Url;
use websocket::header;
pub mod bluetooth_scanfilter;
pub mod bluetooth_thread;
pub mod hosts;
pub mod image_cache_thread;
pub mod net_error_list;

View file

@ -2,12 +2,38 @@
* 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 core::clone::Clone;
use dom::bindings::codegen::Bindings::BluetoothBinding;
use dom::bindings::codegen::Bindings::BluetoothBinding::BluetoothMethods;
use dom::bindings::codegen::Bindings::BluetoothBinding::RequestDeviceOptions;
use dom::bindings::codegen::Bindings::BluetoothBinding::{BluetoothScanFilter, BluetoothMethods};
use dom::bindings::codegen::Bindings::BluetoothDeviceBinding::VendorIDSource;
use dom::bindings::error::Error::Type;
use dom::bindings::error::Fallible;
use dom::bindings::global::GlobalRef;
use dom::bindings::js::Root;
use dom::bindings::reflector::{Reflector, reflect_dom_object};
use dom::bindings::reflector::{Reflectable, Reflector, reflect_dom_object};
use dom::bluetoothadvertisingdata::BluetoothAdvertisingData;
use dom::bluetoothdevice::BluetoothDevice;
use dom::bluetoothuuid::BluetoothUUID;
use ipc_channel::ipc::{self, IpcSender};
use net_traits::bluetooth_scanfilter::{BluetoothScanfilter, BluetoothScanfilterSequence};
use net_traits::bluetooth_scanfilter::{RequestDeviceoptions, ServiceUUIDSequence};
use net_traits::bluetooth_thread::BluetoothMethodMsg;
use util::str::DOMString;
const FILTER_EMPTY_ERROR: &'static str = "'filters' member must be non - empty to find any devices.";
const FILTER_ERROR: &'static str = "A filter must restrict the devices in some way.";
const FILTER_NAME_TOO_LONG_ERROR: &'static str = "A 'name' or 'namePrefix' can't be longer then 29 bytes.";
// 248 is the maximum number of UTF-8 code units in a Bluetooth Device Name.
const MAX_DEVICE_NAME_LENGTH: usize = 248;
// A device name can never be longer than 29 bytes.
// An advertising packet is at most 31 bytes long.
// The length and identifier of the length field take 2 bytes.
// That leaves 29 bytes for the name.
const MAX_FILTER_NAME_LENGTH: usize = 29;
const NAME_PREFIX_ERROR: &'static str = "'namePrefix', if present, must be non - empty.";
const NAME_TOO_LONG_ERROR: &'static str = "A device name can't be longer than 248 bytes.";
const SERVICE_ERROR: &'static str = "'services', if present, must contain at least one service.";
// https://webbluetoothcg.github.io/web-bluetooth/#bluetooth
#[dom_struct]
@ -27,13 +53,113 @@ impl Bluetooth {
global,
BluetoothBinding::Wrap)
}
fn get_bluetooth_thread(&self) -> IpcSender<BluetoothMethodMsg> {
let global_root = self.global();
let global_ref = global_root.r();
global_ref.as_window().bluetooth_thread()
}
}
fn canonicalize_filter(filter: &BluetoothScanFilter, global: GlobalRef) -> Fallible<BluetoothScanfilter> {
if filter.services.is_none() && filter.name.is_none() && filter.namePrefix.is_none() {
return Err(Type(FILTER_ERROR.to_owned()));
}
let mut services_vec = vec!();
if let Some(ref services) = filter.services {
if services.is_empty() {
return Err(Type(SERVICE_ERROR.to_owned()));
}
for service in services {
services_vec.push(try!(BluetoothUUID::GetService(global, service.clone())).to_string());
}
}
let mut name = String::new();
if let Some(ref filter_name) = filter.name {
//NOTE: DOMString::len() gives back the size in bytes
if filter_name.len() > MAX_DEVICE_NAME_LENGTH {
return Err(Type(NAME_TOO_LONG_ERROR.to_owned()));
}
if filter_name.len() > MAX_FILTER_NAME_LENGTH {
return Err(Type(FILTER_NAME_TOO_LONG_ERROR.to_owned()));
}
name = filter_name.to_string();
}
let mut name_prefix = String::new();
if let Some(ref filter_name_prefix) = filter.namePrefix {
if filter_name_prefix.is_empty() {
return Err(Type(NAME_PREFIX_ERROR.to_owned()));
}
if filter_name_prefix.len() > MAX_DEVICE_NAME_LENGTH {
return Err(Type(NAME_TOO_LONG_ERROR.to_owned()));
}
if filter_name_prefix.len() > MAX_FILTER_NAME_LENGTH {
return Err(Type(FILTER_NAME_TOO_LONG_ERROR.to_owned()));
}
name_prefix = filter_name_prefix.to_string();
}
Ok(BluetoothScanfilter::new(name, name_prefix, services_vec))
}
fn convert_request_device_options(options: &RequestDeviceOptions,
global: GlobalRef)
-> Fallible<RequestDeviceoptions> {
if options.filters.is_empty() {
return Err(Type(FILTER_EMPTY_ERROR.to_owned()));
}
let mut filters = vec!();
for filter in &options.filters {
filters.push(try!(canonicalize_filter(&filter, global)));
}
let mut optional_services = vec!();
if let Some(ref opt_services) = options.optionalServices {
for opt_service in opt_services {
optional_services.push(try!(BluetoothUUID::GetService(global, opt_service.clone())).to_string());
}
}
Ok(RequestDeviceoptions::new(BluetoothScanfilterSequence::new(filters),
ServiceUUIDSequence::new(optional_services)))
}
impl BluetoothMethods for Bluetooth {
// https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetooth-requestdevice
fn RequestDevice(&self) -> Option<Root<BluetoothDevice>> {
//UNIMPLEMENTED
None
fn RequestDevice(&self, option: &RequestDeviceOptions) -> Fallible<Root<BluetoothDevice>> {
let (sender, receiver) = ipc::channel().unwrap();
let option = try!(convert_request_device_options(option, self.global().r()));
self.get_bluetooth_thread().send(BluetoothMethodMsg::RequestDevice(option, sender)).unwrap();
let device = receiver.recv().unwrap();
match device {
Ok(device) => {
let ad_data = BluetoothAdvertisingData::new(self.global().r(),
device.appearance,
device.tx_power,
device.rssi);
let vendor_id_source = device.vendor_id_source.map(|vid| match vid.as_str() {
"bluetooth" => VendorIDSource::Bluetooth,
"usb" => VendorIDSource::Usb,
_ => VendorIDSource::Unknown,
});
Ok(BluetoothDevice::new(self.global().r(),
DOMString::from(device.id),
device.name.map(DOMString::from),
&ad_data,
device.device_class,
vendor_id_source,
device.vendor_id,
device.product_id,
device.product_version))
},
Err(error) => {
Err(Type(error))
},
}
}
}

View file

@ -12,13 +12,16 @@ use dom::bindings::reflector::{Reflector, reflect_dom_object};
#[dom_struct]
pub struct BluetoothAdvertisingData {
reflector_: Reflector,
appearance: u16,
txPower: i8,
rssi: i8,
appearance: Option<u16>,
txPower: Option<i8>,
rssi: Option<i8>,
}
impl BluetoothAdvertisingData {
pub fn new_inherited(appearance: u16, txPower: i8, rssi: i8) -> BluetoothAdvertisingData {
pub fn new_inherited(appearance: Option<u16>,
txPower: Option<i8>,
rssi: Option<i8>)
-> BluetoothAdvertisingData {
BluetoothAdvertisingData {
reflector_: Reflector::new(),
appearance: appearance,
@ -27,7 +30,11 @@ impl BluetoothAdvertisingData {
}
}
pub fn new(global: GlobalRef, appearance: u16, txPower: i8, rssi: i8) -> Root<BluetoothAdvertisingData> {
pub fn new(global: GlobalRef,
appearance: Option<u16>,
txPower: Option<i8>,
rssi: Option<i8>)
-> Root<BluetoothAdvertisingData> {
reflect_dom_object(box BluetoothAdvertisingData::new_inherited(appearance,
txPower,
rssi),
@ -39,16 +46,16 @@ impl BluetoothAdvertisingData {
impl BluetoothAdvertisingDataMethods for BluetoothAdvertisingData {
// https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothadvertisingdata-appearance
fn GetAppearance(&self) -> Option<u16> {
Some(self.appearance)
self.appearance
}
// https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothadvertisingdata-txpower
fn GetTxPower(&self) -> Option<i8> {
Some(self.txPower)
self.txPower
}
// https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothadvertisingdata-rssi
fn GetRssi(&self) -> Option<i8> {
Some(self.rssi)
self.rssi
}
}

View file

@ -5,8 +5,8 @@
use dom::bindings::codegen::Bindings::BluetoothDeviceBinding;
use dom::bindings::codegen::Bindings::BluetoothDeviceBinding::{BluetoothDeviceMethods, VendorIDSource};
use dom::bindings::global::GlobalRef;
use dom::bindings::js::{JS, Root, MutHeap};
use dom::bindings::reflector::{Reflector, reflect_dom_object};
use dom::bindings::js::{JS, Root, MutHeap, MutNullableHeap};
use dom::bindings::reflector::{Reflectable, Reflector, reflect_dom_object};
use dom::bluetoothadvertisingdata::BluetoothAdvertisingData;
use dom::bluetoothremotegattserver::BluetoothRemoteGATTServer;
use util::str::DOMString;
@ -16,26 +16,25 @@ use util::str::DOMString;
pub struct BluetoothDevice {
reflector_: Reflector,
id: DOMString,
name: DOMString,
name: Option<DOMString>,
adData: MutHeap<JS<BluetoothAdvertisingData>>,
deviceClass: u32,
vendorIDSource: VendorIDSource,
vendorID: u32,
productID: u32,
productVersion: u32,
gatt: MutHeap<JS<BluetoothRemoteGATTServer>>,
deviceClass: Option<u32>,
vendorIDSource: Option<VendorIDSource>,
vendorID: Option<u32>,
productID: Option<u32>,
productVersion: Option<u32>,
gatt: MutNullableHeap<JS<BluetoothRemoteGATTServer>>,
}
impl BluetoothDevice {
pub fn new_inherited(id: DOMString,
name: DOMString,
name: Option<DOMString>,
adData: &BluetoothAdvertisingData,
deviceClass: u32,
vendorIDSource: VendorIDSource,
vendorID: u32,
productID: u32,
productVersion: u32,
gatt: &BluetoothRemoteGATTServer)
deviceClass: Option<u32>,
vendorIDSource: Option<VendorIDSource>,
vendorID: Option<u32>,
productID: Option<u32>,
productVersion: Option<u32>)
-> BluetoothDevice {
BluetoothDevice {
reflector_: Reflector::new(),
@ -47,21 +46,20 @@ impl BluetoothDevice {
vendorID: vendorID,
productID: productID,
productVersion: productVersion,
gatt: MutHeap::new(gatt),
gatt: Default::default(),
}
}
pub fn new(global: GlobalRef,
id: DOMString,
name: DOMString,
adData: &BluetoothAdvertisingData,
deviceClass: u32,
vendorIDSource: VendorIDSource,
vendorID: u32,
productID: u32,
productVersion: u32,
gatt: &BluetoothRemoteGATTServer)
-> Root<BluetoothDevice> {
id: DOMString,
name: Option<DOMString>,
adData: &BluetoothAdvertisingData,
deviceClass: Option<u32>,
vendorIDSource: Option<VendorIDSource>,
vendorID: Option<u32>,
productID: Option<u32>,
productVersion: Option<u32>)
-> Root<BluetoothDevice> {
reflect_dom_object(box BluetoothDevice::new_inherited(id,
name,
adData,
@ -69,8 +67,7 @@ impl BluetoothDevice {
vendorIDSource,
vendorID,
productID,
productVersion,
gatt),
productVersion),
global,
BluetoothDeviceBinding::Wrap)
}
@ -85,7 +82,7 @@ impl BluetoothDeviceMethods for BluetoothDevice {
// https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothdevice-name
fn GetName(&self) -> Option<DOMString> {
Some(self.name.clone())
self.name.clone()
}
// https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothdevice-addata
@ -95,31 +92,31 @@ impl BluetoothDeviceMethods for BluetoothDevice {
// https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothdevice-deviceclass
fn GetDeviceClass(&self) -> Option<u32> {
Some(self.deviceClass)
self.deviceClass
}
// https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothdevice-vendoridsource
fn GetVendorIDSource(&self) -> Option<VendorIDSource> {
Some(self.vendorIDSource)
self.vendorIDSource
}
// https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothdevice-vendorid
fn GetVendorID(&self) -> Option<u32> {
Some(self.vendorID)
self.vendorID
}
// https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothdevice-productid
fn GetProductID(&self) -> Option<u32> {
Some(self.productID)
self.productID
}
// https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothdevice-productversion
fn GetProductVersion(&self) -> Option<u32> {
Some(self.productVersion)
self.productVersion
}
// https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothdevice-gatt
fn Gatt(&self) -> Root<BluetoothRemoteGATTServer> {
self.gatt.get()
self.gatt.or_init(|| BluetoothRemoteGATTServer::new(self.global().r(), self))
}
}

View file

@ -2,16 +2,25 @@
* 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 dom::bindings::cell::DOMRefCell;
use dom::bindings::codegen::Bindings::BluetoothDeviceBinding::BluetoothDeviceMethods;
use dom::bindings::codegen::Bindings::BluetoothRemoteGATTCharacteristicBinding;
use dom::bindings::codegen::Bindings::BluetoothRemoteGATTCharacteristicBinding::
BluetoothRemoteGATTCharacteristicMethods;
use dom::bindings::codegen::Bindings::BluetoothRemoteGATTServerBinding::BluetoothRemoteGATTServerMethods;
use dom::bindings::codegen::Bindings::BluetoothRemoteGATTServiceBinding::BluetoothRemoteGATTServiceMethods;
use dom::bindings::error::Error::{Network, Type};
use dom::bindings::error::{Fallible, ErrorResult};
use dom::bindings::global::GlobalRef;
use dom::bindings::js::{JS, MutHeap, Root};
use dom::bindings::reflector::{Reflector, reflect_dom_object};
use dom::bindings::reflector::{Reflectable, Reflector, reflect_dom_object};
use dom::bindings::str::ByteString;
use dom::bluetoothcharacteristicproperties::BluetoothCharacteristicProperties;
use dom::bluetoothremotegattdescriptor::BluetoothRemoteGATTDescriptor;
use dom::bluetoothremotegattservice::BluetoothRemoteGATTService;
use dom::bluetoothuuid::{BluetoothDescriptorUUID, BluetoothUUID};
use ipc_channel::ipc::{self, IpcSender};
use net_traits::bluetooth_thread::BluetoothMethodMsg;
use util::str::DOMString;
// https://webbluetoothcg.github.io/web-bluetooth/#bluetoothremotegattcharacteristic
@ -21,33 +30,48 @@ pub struct BluetoothRemoteGATTCharacteristic {
service: MutHeap<JS<BluetoothRemoteGATTService>>,
uuid: DOMString,
properties: MutHeap<JS<BluetoothCharacteristicProperties>>,
value: Option<ByteString>,
value: DOMRefCell<Option<ByteString>>,
instanceID: String,
}
impl BluetoothRemoteGATTCharacteristic {
pub fn new_inherited(service: &BluetoothRemoteGATTService,
uuid: DOMString,
properties: &BluetoothCharacteristicProperties)
properties: &BluetoothCharacteristicProperties,
instanceID: String)
-> BluetoothRemoteGATTCharacteristic {
BluetoothRemoteGATTCharacteristic {
reflector_: Reflector::new(),
service: MutHeap::new(service),
uuid: uuid,
properties: MutHeap::new(properties),
value: None,
value: DOMRefCell::new(None),
instanceID: instanceID,
}
}
pub fn new(global: GlobalRef,
service: &BluetoothRemoteGATTService,
uuid: DOMString,
properties: &BluetoothCharacteristicProperties)
properties: &BluetoothCharacteristicProperties,
instanceID: String)
-> Root<BluetoothRemoteGATTCharacteristic> {
reflect_dom_object(box BluetoothRemoteGATTCharacteristic::new_inherited(service,
uuid,
properties),
global,
BluetoothRemoteGATTCharacteristicBinding::Wrap)
properties,
instanceID),
global,
BluetoothRemoteGATTCharacteristicBinding::Wrap)
}
fn get_bluetooth_thread(&self) -> IpcSender<BluetoothMethodMsg> {
let global_root = self.global();
let global_ref = global_root.r();
global_ref.as_window().bluetooth_thread()
}
pub fn get_instance_id(&self) -> String {
self.instanceID.clone()
}
}
@ -69,19 +93,89 @@ impl BluetoothRemoteGATTCharacteristicMethods for BluetoothRemoteGATTCharacteris
}
// https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattcharacteristic-getdescriptor
fn GetDescriptor(&self) -> Option<Root<BluetoothRemoteGATTDescriptor>> {
//UNIMPLEMENTED
None
fn GetDescriptor(&self, descriptor: BluetoothDescriptorUUID) -> Fallible<Root<BluetoothRemoteGATTDescriptor>> {
let uuid = try!(BluetoothUUID::GetDescriptor(self.global().r(), descriptor)).to_string();
let (sender, receiver) = ipc::channel().unwrap();
self.get_bluetooth_thread().send(
BluetoothMethodMsg::GetDescriptor(self.get_instance_id(), uuid, sender)).unwrap();
let descriptor = receiver.recv().unwrap();
match descriptor {
Ok(descriptor) => {
Ok(BluetoothRemoteGATTDescriptor::new(self.global().r(),
self,
DOMString::from(descriptor.uuid),
descriptor.instance_id))
},
Err(error) => {
Err(Type(error))
},
}
}
// https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattcharacteristic-getdescriptors
fn GetDescriptors(&self,
descriptor: Option<BluetoothDescriptorUUID>)
-> Fallible<Vec<Root<BluetoothRemoteGATTDescriptor>>> {
let mut uuid: Option<String> = None;
if let Some(d) = descriptor {
uuid = Some(try!(BluetoothUUID::GetDescriptor(self.global().r(), d)).to_string())
};
let (sender, receiver) = ipc::channel().unwrap();
self.get_bluetooth_thread().send(
BluetoothMethodMsg::GetDescriptors(self.get_instance_id(), uuid, sender)).unwrap();
let descriptors_vec = receiver.recv().unwrap();
match descriptors_vec {
Ok(descriptor_vec) => {
Ok(descriptor_vec.into_iter()
.map(|desc| BluetoothRemoteGATTDescriptor::new(self.global().r(),
self,
DOMString::from(desc.uuid),
desc.instance_id))
.collect())
},
Err(error) => {
Err(Type(error))
},
}
}
// https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattcharacteristic-value
fn GetValue(&self) -> Option<ByteString> {
self.value.clone()
self.value.borrow().clone()
}
// https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattcharacteristic-readvalue
fn ReadValue(&self) -> ByteString {
//UNIMPLEMENTED
ByteString::new(vec!())
fn ReadValue(&self) -> Fallible<ByteString> {
let (sender, receiver) = ipc::channel().unwrap();
if !self.Service().Device().Gatt().Connected() {
return Err(Network)
}
self.get_bluetooth_thread().send(
BluetoothMethodMsg::ReadValue(self.get_instance_id(), sender)).unwrap();
let result = receiver.recv().unwrap();
let value = match result {
Ok(val) => {
ByteString::new(val)
},
Err(error) => {
return Err(Type(error))
},
};
*self.value.borrow_mut() = Some(value.clone());
Ok(value)
}
// https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattcharacteristic-writevalue
fn WriteValue(&self, value: Vec<u8>) -> ErrorResult {
let (sender, receiver) = ipc::channel().unwrap();
self.get_bluetooth_thread().send(
BluetoothMethodMsg::WriteValue(self.get_instance_id(), value, sender)).unwrap();
let result = receiver.recv().unwrap();
match result {
Ok(_) => Ok(()),
Err(error) => {
Err(Type(error))
},
}
}
}

View file

@ -2,13 +2,23 @@
* 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 dom::bindings::cell::DOMRefCell;
use dom::bindings::codegen::Bindings::BluetoothDeviceBinding::BluetoothDeviceMethods;
use dom::bindings::codegen::Bindings::BluetoothRemoteGATTCharacteristicBinding::
BluetoothRemoteGATTCharacteristicMethods;
use dom::bindings::codegen::Bindings::BluetoothRemoteGATTDescriptorBinding;
use dom::bindings::codegen::Bindings::BluetoothRemoteGATTDescriptorBinding::BluetoothRemoteGATTDescriptorMethods;
use dom::bindings::codegen::Bindings::BluetoothRemoteGATTServerBinding::BluetoothRemoteGATTServerMethods;
use dom::bindings::codegen::Bindings::BluetoothRemoteGATTServiceBinding::BluetoothRemoteGATTServiceMethods;
use dom::bindings::error::Error::{Type, Network};
use dom::bindings::error::{Fallible, ErrorResult};
use dom::bindings::global::GlobalRef;
use dom::bindings::js::{JS, MutHeap, Root};
use dom::bindings::reflector::{Reflector, reflect_dom_object};
use dom::bindings::reflector::{Reflectable, Reflector, reflect_dom_object};
use dom::bindings::str::ByteString;
use dom::bluetoothremotegattcharacteristic::BluetoothRemoteGATTCharacteristic;
use ipc_channel::ipc::{self, IpcSender};
use net_traits::bluetooth_thread::BluetoothMethodMsg;
use util::str::DOMString;
// http://webbluetoothcg.github.io/web-bluetooth/#bluetoothremotegattdescriptor
@ -17,30 +27,45 @@ pub struct BluetoothRemoteGATTDescriptor {
reflector_: Reflector,
characteristic: MutHeap<JS<BluetoothRemoteGATTCharacteristic>>,
uuid: DOMString,
value: Option<ByteString>,
value: DOMRefCell<Option<ByteString>>,
instanceID: String,
}
impl BluetoothRemoteGATTDescriptor {
pub fn new_inherited(characteristic: &BluetoothRemoteGATTCharacteristic,
uuid: DOMString)
uuid: DOMString,
instanceID: String)
-> BluetoothRemoteGATTDescriptor {
BluetoothRemoteGATTDescriptor {
reflector_: Reflector::new(),
characteristic: MutHeap::new(characteristic),
uuid: uuid,
value: None,
value: DOMRefCell::new(None),
instanceID: instanceID,
}
}
pub fn new(global: GlobalRef,
characteristic: &BluetoothRemoteGATTCharacteristic,
uuid: DOMString)
uuid: DOMString,
instanceID: String)
-> Root<BluetoothRemoteGATTDescriptor>{
reflect_dom_object(box BluetoothRemoteGATTDescriptor::new_inherited(characteristic,
uuid),
uuid,
instanceID),
global,
BluetoothRemoteGATTDescriptorBinding::Wrap)
}
fn get_bluetooth_thread(&self) -> IpcSender<BluetoothMethodMsg> {
let global_root = self.global();
let global_ref = global_root.r();
global_ref.as_window().bluetooth_thread()
}
pub fn get_instance_id(&self) -> String {
self.instanceID.clone()
}
}
impl BluetoothRemoteGATTDescriptorMethods for BluetoothRemoteGATTDescriptor {
@ -57,12 +82,41 @@ impl BluetoothRemoteGATTDescriptorMethods for BluetoothRemoteGATTDescriptor {
// https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattdescriptor-value
fn GetValue(&self) -> Option<ByteString> {
self.value.clone()
self.value.borrow().clone()
}
// https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattdescriptor-readvalue
fn ReadValue(&self) -> ByteString {
//UNIMPLEMENTED
ByteString::new(vec!())
fn ReadValue(&self) -> Fallible<ByteString> {
let (sender, receiver) = ipc::channel().unwrap();
if !self.Characteristic().Service().Device().Gatt().Connected() {
return Err(Network)
}
self.get_bluetooth_thread().send(
BluetoothMethodMsg::ReadValue(self.get_instance_id(), sender)).unwrap();
let result = receiver.recv().unwrap();
let value = match result {
Ok(val) => {
ByteString::new(val)
},
Err(error) => {
return Err(Type(error))
},
};
*self.value.borrow_mut() = Some(value.clone());
Ok(value)
}
// https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattdescriptor-writevalue
fn WriteValue(&self, value: Vec<u8>) -> ErrorResult {
let (sender, receiver) = ipc::channel().unwrap();
self.get_bluetooth_thread().send(
BluetoothMethodMsg::WriteValue(self.get_instance_id(), value, sender)).unwrap();
let result = receiver.recv().unwrap();
match result {
Ok(_) => Ok(()),
Err(error) => {
Err(Type(error))
},
}
}
}

View file

@ -2,14 +2,21 @@
* 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 dom::bindings::codegen::Bindings::BluetoothDeviceBinding::BluetoothDeviceMethods;
use dom::bindings::codegen::Bindings::BluetoothRemoteGATTServerBinding;
use dom::bindings::codegen::Bindings::BluetoothRemoteGATTServerBinding::BluetoothRemoteGATTServerMethods;
use dom::bindings::error::Error::Type;
use dom::bindings::error::{Fallible, ErrorResult};
use dom::bindings::global::GlobalRef;
use dom::bindings::js::{JS, MutHeap, Root};
use dom::bindings::reflector::{Reflector, reflect_dom_object};
use dom::bindings::reflector::{Reflectable, Reflector, reflect_dom_object};
use dom::bluetoothdevice::BluetoothDevice;
use dom::bluetoothremotegattservice::BluetoothRemoteGATTService;
use dom::bluetoothuuid::{BluetoothServiceUUID, BluetoothUUID};
use ipc_channel::ipc::{self, IpcSender};
use net_traits::bluetooth_thread::BluetoothMethodMsg;
use std::cell::Cell;
use util::str::DOMString;
// https://webbluetoothcg.github.io/web-bluetooth/#bluetoothremotegattserver
#[dom_struct]
@ -20,21 +27,25 @@ pub struct BluetoothRemoteGATTServer {
}
impl BluetoothRemoteGATTServer {
pub fn new_inherited(device: &BluetoothDevice, is_connected: bool) -> BluetoothRemoteGATTServer {
pub fn new_inherited(device: &BluetoothDevice) -> BluetoothRemoteGATTServer {
BluetoothRemoteGATTServer {
reflector_: Reflector::new(),
device: MutHeap::new(device),
connected: Cell::new(is_connected),
connected: Cell::new(false),
}
}
pub fn new(global: GlobalRef, device: &BluetoothDevice, connected: bool) -> Root<BluetoothRemoteGATTServer> {
reflect_dom_object(box BluetoothRemoteGATTServer::new_inherited(
device,
connected),
pub fn new(global: GlobalRef, device: &BluetoothDevice) -> Root<BluetoothRemoteGATTServer> {
reflect_dom_object(box BluetoothRemoteGATTServer::new_inherited(device),
global,
BluetoothRemoteGATTServerBinding::Wrap)
}
fn get_bluetooth_thread(&self) -> IpcSender<BluetoothMethodMsg> {
let global_root = self.global();
let global_ref = global_root.r();
global_ref.as_window().bluetooth_thread()
}
}
impl BluetoothRemoteGATTServerMethods for BluetoothRemoteGATTServer {
@ -50,21 +61,85 @@ impl BluetoothRemoteGATTServerMethods for BluetoothRemoteGATTServer {
}
// https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattserver-connect
fn Connect(&self) -> Root<BluetoothRemoteGATTServer> {
if !self.connected.get() {
self.connected.set(true);
fn Connect(&self) -> Fallible<Root<BluetoothRemoteGATTServer>> {
let (sender, receiver) = ipc::channel().unwrap();
self.get_bluetooth_thread().send(
BluetoothMethodMsg::GATTServerConnect(String::from(self.Device().Id()), sender)).unwrap();
let server = receiver.recv().unwrap();
match server {
Ok(connected) => {
self.connected.set(connected);
Ok(Root::from_ref(self))
},
Err(error) => {
Err(Type(error))
},
}
Root::from_ref(self)
}
// https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattserver-disconnect
fn Disconnect(&self) {
self.connected.set(false);
fn Disconnect(&self) -> ErrorResult {
let (sender, receiver) = ipc::channel().unwrap();
self.get_bluetooth_thread().send(
BluetoothMethodMsg::GATTServerDisconnect(String::from(self.Device().Id()), sender)).unwrap();
let server = receiver.recv().unwrap();
match server {
Ok(connected) => {
self.connected.set(connected);
Ok(())
},
Err(error) => {
Err(Type(error))
},
}
}
// https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattserver-getprimaryservice
fn GetPrimaryService(&self) -> Option<Root<BluetoothRemoteGATTService>> {
//UNIMPLEMENTED
None
fn GetPrimaryService(&self, service: BluetoothServiceUUID) -> Fallible<Root<BluetoothRemoteGATTService>> {
let uuid = try!(BluetoothUUID::GetService(self.global().r(), service)).to_string();
let (sender, receiver) = ipc::channel().unwrap();
self.get_bluetooth_thread().send(
BluetoothMethodMsg::GetPrimaryService(String::from(self.Device().Id()), uuid, sender)).unwrap();
let service = receiver.recv().unwrap();
match service {
Ok(service) => {
Ok(BluetoothRemoteGATTService::new(self.global().r(),
&self.device.get(),
DOMString::from(service.uuid),
service.is_primary,
service.instance_id))
},
Err(error) => {
Err(Type(error))
},
}
}
// https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattserver-getprimaryservices
fn GetPrimaryServices(&self,
service: Option<BluetoothServiceUUID>)
-> Fallible<Vec<Root<BluetoothRemoteGATTService>>> {
let mut uuid: Option<String> = None;
if let Some(s) = service {
uuid = Some(try!(BluetoothUUID::GetService(self.global().r(), s)).to_string())
};
let (sender, receiver) = ipc::channel().unwrap();
self.get_bluetooth_thread().send(
BluetoothMethodMsg::GetPrimaryServices(String::from(self.Device().Id()), uuid, sender)).unwrap();
let services_vec = receiver.recv().unwrap();
match services_vec {
Ok(service_vec) => {
Ok(service_vec.into_iter()
.map(|service| BluetoothRemoteGATTService::new(self.global().r(),
&self.device.get(),
DOMString::from(service.uuid),
service.is_primary,
service.instance_id))
.collect())
},
Err(error) => {
Err(Type(error))
},
}
}
}

View file

@ -4,11 +4,17 @@
use dom::bindings::codegen::Bindings::BluetoothRemoteGATTServiceBinding;
use dom::bindings::codegen::Bindings::BluetoothRemoteGATTServiceBinding::BluetoothRemoteGATTServiceMethods;
use dom::bindings::error::Error::Type;
use dom::bindings::error::Fallible;
use dom::bindings::global::GlobalRef;
use dom::bindings::js::{JS, MutHeap, Root};
use dom::bindings::reflector::{Reflector, reflect_dom_object};
use dom::bindings::reflector::{Reflectable, Reflector, reflect_dom_object};
use dom::bluetoothcharacteristicproperties::BluetoothCharacteristicProperties;
use dom::bluetoothdevice::BluetoothDevice;
use dom::bluetoothremotegattcharacteristic::BluetoothRemoteGATTCharacteristic;
use dom::bluetoothuuid::{BluetoothCharacteristicUUID, BluetoothUUID};
use ipc_channel::ipc::{self, IpcSender};
use net_traits::bluetooth_thread::BluetoothMethodMsg;
use util::str::DOMString;
// https://webbluetoothcg.github.io/web-bluetooth/#bluetoothremotegattservice
@ -18,32 +24,47 @@ pub struct BluetoothRemoteGATTService {
device: MutHeap<JS<BluetoothDevice>>,
uuid: DOMString,
isPrimary: bool,
instanceID: String,
}
impl BluetoothRemoteGATTService {
pub fn new_inherited(device: &BluetoothDevice,
uuid: DOMString,
isPrimary: bool)
isPrimary: bool,
instanceID: String)
-> BluetoothRemoteGATTService {
BluetoothRemoteGATTService {
reflector_: Reflector::new(),
device: MutHeap::new(device),
uuid: uuid,
isPrimary: isPrimary,
instanceID: instanceID,
}
}
pub fn new(global: GlobalRef,
device: &BluetoothDevice,
uuid: DOMString,
isPrimary: bool)
isPrimary: bool,
instanceID: String)
-> Root<BluetoothRemoteGATTService> {
reflect_dom_object(box BluetoothRemoteGATTService::new_inherited(device,
uuid,
isPrimary),
isPrimary,
instanceID),
global,
BluetoothRemoteGATTServiceBinding::Wrap)
}
fn get_bluetooth_thread(&self) -> IpcSender<BluetoothMethodMsg> {
let global_root = self.global();
let global_ref = global_root.r();
global_ref.as_window().bluetooth_thread()
}
pub fn get_instance_id(&self) -> String {
self.instanceID.clone()
}
}
impl BluetoothRemoteGATTServiceMethods for BluetoothRemoteGATTService {
@ -63,8 +84,75 @@ impl BluetoothRemoteGATTServiceMethods for BluetoothRemoteGATTService {
}
// https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattservice-getcharacteristic
fn GetCharacteristic(&self) -> Option<Root<BluetoothRemoteGATTCharacteristic>> {
// UNIMPLEMENTED
None
fn GetCharacteristic(&self,
characteristic: BluetoothCharacteristicUUID)
-> Fallible<Root<BluetoothRemoteGATTCharacteristic>> {
let uuid = try!(BluetoothUUID::GetCharacteristic(self.global().r(), characteristic)).to_string();
let (sender, receiver) = ipc::channel().unwrap();
self.get_bluetooth_thread().send(
BluetoothMethodMsg::GetCharacteristic(self.get_instance_id(), uuid, sender)).unwrap();
let characteristic = receiver.recv().unwrap();
match characteristic {
Ok(characteristic) => {
let properties = BluetoothCharacteristicProperties::new(self.global().r(),
characteristic.broadcast,
characteristic.read,
characteristic.write_without_response,
characteristic.write,
characteristic.notify,
characteristic.indicate,
characteristic.authenticated_signed_writes,
characteristic.reliable_write,
characteristic.writable_auxiliaries);
Ok(BluetoothRemoteGATTCharacteristic::new(self.global().r(),
self,
DOMString::from(characteristic.uuid),
&properties,
characteristic.instance_id))
},
Err(error) => {
Err(Type(error))
},
}
}
// https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattservice-getcharacteristics
fn GetCharacteristics(&self,
characteristic: Option<BluetoothCharacteristicUUID>)
-> Fallible<Vec<Root<BluetoothRemoteGATTCharacteristic>>> {
let mut uuid: Option<String> = None;
if let Some(c) = characteristic {
uuid = Some(try!(BluetoothUUID::GetCharacteristic(self.global().r(), c)).to_string())
};
let mut characteristics = vec!();
let (sender, receiver) = ipc::channel().unwrap();
self.get_bluetooth_thread().send(
BluetoothMethodMsg::GetCharacteristics(self.get_instance_id(), uuid, sender)).unwrap();
let characteristics_vec = receiver.recv().unwrap();
match characteristics_vec {
Ok(characteristic_vec) => {
for characteristic in characteristic_vec {
let properties = BluetoothCharacteristicProperties::new(self.global().r(),
characteristic.broadcast,
characteristic.read,
characteristic.write_without_response,
characteristic.write,
characteristic.notify,
characteristic.indicate,
characteristic.authenticated_signed_writes,
characteristic.reliable_write,
characteristic.writable_auxiliaries);
characteristics.push(BluetoothRemoteGATTCharacteristic::new(self.global().r(),
self,
DOMString::from(characteristic.uuid),
&properties,
characteristic.instance_id));
}
Ok(characteristics)
},
Err(error) => {
Err(Type(error))
},
}
}
}

View file

@ -11,6 +11,9 @@ use regex::Regex;
use util::str::DOMString;
pub type UUID = DOMString;
pub type BluetoothServiceUUID = StringOrUnsignedLong;
pub type BluetoothCharacteristicUUID = StringOrUnsignedLong;
pub type BluetoothDescriptorUUID = StringOrUnsignedLong;
// https://webbluetoothcg.github.io/web-bluetooth/#bluetoothuuid
#[dom_struct]
@ -18,27 +21,246 @@ pub struct BluetoothUUID {
reflector_: Reflector,
}
const BLUETOOTH_ASSIGNED_SERVICES: &'static [(&'static str, u32)] = &[
//TODO(zakorgy) create all the services
//https://developer.bluetooth.org/gatt/services/Pages/ServicesHome.aspx
const BLUETOOTH_ASSIGNED_SERVICES: &'static [(&'static str, u32)] = &[
("org.bluetooth.service.alert_notification", 0x1811_u32),
("org.bluetooth.service.automation_io", 0x1815_u32),
("org.bluetooth.service.battery_service", 0x180f_u32)
("org.bluetooth.service.battery_service", 0x180f_u32),
("org.bluetooth.service.blood_pressure", 0x1810_u32),
("org.bluetooth.service.body_composition", 0x181b_u32),
("org.bluetooth.service.bond_management", 0x181e_u32),
("org.bluetooth.service.continuous_glucose_monitoring", 0x181f_u32),
("org.bluetooth.service.current_time", 0x1805_u32),
("org.bluetooth.service.cycling_power", 0x1818_u32),
("org.bluetooth.service.cycling_speed_and_cadence", 0x1816_u32),
("org.bluetooth.service.device_information", 0x180a_u32),
("org.bluetooth.service.environmental_sensing", 0x181a_u32),
("org.bluetooth.service.generic_access", 0x1800_u32),
("org.bluetooth.service.generic_attribute", 0x1801_u32),
("org.bluetooth.service.glucose", 0x1808_u32),
("org.bluetooth.service.health_thermometer", 0x1809_u32),
("org.bluetooth.service.heart_rate", 0x180d_u32),
("org.bluetooth.service.http_proxy", 0x1823_u32),
("org.bluetooth.service.human_interface_device", 0x1812_u32),
("org.bluetooth.service.immediate_alert", 0x1802_u32),
("org.bluetooth.service.indoor_positioning", 0x1821_u32),
("org.bluetooth.service.internet_protocol_support", 0x1820_u32),
("org.bluetooth.service.link_loss", 0x1803_u32),
("org.bluetooth.service.location_and_navigation", 0x1819_u32),
("org.bluetooth.service.next_dst_change", 0x1807_u32),
("org.bluetooth.service.object_transfer", 0x1825_u32),
("org.bluetooth.service.phone_alert_status", 0x180e_u32),
("org.bluetooth.service.pulse_oximeter", 0x1822_u32),
("org.bluetooth.service.reference_time_update", 0x1806_u32),
("org.bluetooth.service.running_speed_and_cadence", 0x1814_u32),
("org.bluetooth.service.scan_parameters", 0x1813_u32),
("org.bluetooth.service.transport_discovery", 0x1824),
("org.bluetooth.service.tx_power", 0x1804_u32),
("org.bluetooth.service.user_data", 0x181c_u32),
("org.bluetooth.service.weight_scale", 0x181d_u32),
];
const BLUETOOTH_ASSIGNED_CHARCTERISTICS: &'static [(&'static str, u32)] = &[
//TODO(zakorgy) create all the characteristics
//https://developer.bluetooth.org/gatt/services/Pages/ServicesHome.aspx
const BLUETOOTH_ASSIGNED_CHARCTERISTICS: &'static [(&'static str, u32)] = &[
("org.bluetooth.characteristic.aerobic_heart_rate_lower_limit", 0x2a7e_u32),
("org.bluetooth.characteristic.aerobic_heart_rate_upper_limit", 0x2a84_u32),
("org.bluetooth.characteristic.battery_level", 0x2a19_u32)
("org.bluetooth.characteristic.aerobic_threshold", 0x2a7f_u32),
("org.bluetooth.characteristic.age", 0x2a80_u32),
("org.bluetooth.characteristic.aggregate", 0x2a5a_u32),
("org.bluetooth.characteristic.alert_category_id", 0x2a43_u32),
("org.bluetooth.characteristic.alert_category_id_bit_mask", 0x2a42_u32),
("org.bluetooth.characteristic.alert_level", 0x2a06_u32),
("org.bluetooth.characteristic.alert_notification_control_point", 0x2a44_u32),
("org.bluetooth.characteristic.alert_status", 0x2a3f_u32),
("org.bluetooth.characteristic.altitude", 0x2ab3_u32),
("org.bluetooth.characteristic.anaerobic_heart_rate_lower_limit", 0x2a81_u32),
("org.bluetooth.characteristic.anaerobic_heart_rate_upper_limit", 0x2a82_u32),
("org.bluetooth.characteristic.anaerobic_threshold", 0x2a83_u32),
("org.bluetooth.characteristic.analog", 0x2a58_u32),
("org.bluetooth.characteristic.apparent_wind_direction", 0x2a73_u32),
("org.bluetooth.characteristic.apparent_wind_speed", 0x2a72_u32),
("org.bluetooth.characteristic.gap.appearance", 0x2a01_u32),
("org.bluetooth.characteristic.barometric_pressure_trend", 0x2aa3_u32),
("org.bluetooth.characteristic.battery_level", 0x2a19_u32),
("org.bluetooth.characteristic.blood_pressure_feature", 0x2a49_u32),
("org.bluetooth.characteristic.blood_pressure_measurement", 0x2a35_u32),
("org.bluetooth.characteristic.body_composition_feature", 0x2a9b_u32),
("org.bluetooth.characteristic.body_composition_measurement", 0x2a9c_u32),
("org.bluetooth.characteristic.body_sensor_location", 0x2a38_u32),
("org.bluetooth.characteristic.bond_management_control_point", 0x2aa4_u32),
("org.bluetooth.characteristic.bond_management_feature", 0x2aa5_u32),
("org.bluetooth.characteristic.boot_keyboard_input_report", 0x2a22_u32),
("org.bluetooth.characteristic.boot_keyboard_output_report", 0x2a32_u32),
("org.bluetooth.characteristic.boot_mouse_input_report", 0x2a33_u32),
("org.bluetooth.characteristic.gap.central_address_resolution_support", 0x2aa6_u32),
("org.bluetooth.characteristic.cgm_feature", 0x2aa8_u32),
("org.bluetooth.characteristic.cgm_measurement", 0x2aa7_u32),
("org.bluetooth.characteristic.cgm_session_run_time", 0x2aab_u32),
("org.bluetooth.characteristic.cgm_session_start_time", 0x2aaa_u32),
("org.bluetooth.characteristic.cgm_specific_ops_control_point", 0x2aac_u32),
("org.bluetooth.characteristic.cgm_status", 0x2aa9_u32),
("org.bluetooth.characteristic.csc_feature", 0x2a5c_u32),
("org.bluetooth.characteristic.csc_measurement", 0x2a5b_u32),
("org.bluetooth.characteristic.current_time", 0x2a2b_u32),
("org.bluetooth.characteristic.cycling_power_control_point", 0x2a66_u32),
("org.bluetooth.characteristic.cycling_power_feature", 0x2a65_u32),
("org.bluetooth.characteristic.cycling_power_measurement", 0x2a63_u32),
("org.bluetooth.characteristic.cycling_power_vector", 0x2a64_u32),
("org.bluetooth.characteristic.database_change_increment", 0x2a99_u32),
("org.bluetooth.characteristic.date_of_birth", 0x2a85_u32),
("org.bluetooth.characteristic.date_of_threshold_assessment", 0x2a86_u32),
("org.bluetooth.characteristic.date_time", 0x2a08_u32),
("org.bluetooth.characteristic.day_date_time", 0x2a0a_u32),
("org.bluetooth.characteristic.day_of_week", 0x2a09_u32),
("org.bluetooth.characteristic.descriptor_value_changed", 0x2a7d_u32),
("org.bluetooth.characteristic.gap.device_name", 0x2a00_u32),
("org.bluetooth.characteristic.dew_point", 0x2a7b_u32),
("org.bluetooth.characteristic.digital", 0x2a56_u32),
("org.bluetooth.characteristic.dst_offset", 0x2a0d_u32),
("org.bluetooth.characteristic.elevation", 0x2a6c_u32),
("org.bluetooth.characteristic.email_address", 0x2a87_u32),
("org.bluetooth.characteristic.exact_time_256", 0x2a0c_u32),
("org.bluetooth.characteristic.fat_burn_heart_rate_lower_limit", 0x2a88_u32),
("org.bluetooth.characteristic.fat_burn_heart_rate_upper_limit", 0x2a89_u32),
("org.bluetooth.characteristic.firmware_revision_string", 0x2a26_u32),
("org.bluetooth.characteristic.first_name", 0x2a8a_u32),
("org.bluetooth.characteristic.five_zone_heart_rate_limits", 0x2a8b_u32),
("org.bluetooth.characteristic.floor_number", 0x2ab2_u32),
("org.bluetooth.characteristic.gender", 0x2a8c_u32),
("org.bluetooth.characteristic.glucose_feature", 0x2a51_u32),
("org.bluetooth.characteristic.glucose_measurement", 0x2a18_u32),
("org.bluetooth.characteristic.glucose_measurement_context", 0x2a34_u32),
("org.bluetooth.characteristic.gust_factor", 0x2a74_u32),
("org.bluetooth.characteristic.hardware_revision_string", 0x2a27_u32),
("org.bluetooth.characteristic.heart_rate_control_point", 0x2a39_u32),
("org.bluetooth.characteristic.heart_rate_max", 0x2a8d_u32),
("org.bluetooth.characteristic.heart_rate_measurement", 0x2a37_u32),
("org.bluetooth.characteristic.heat_index", 0x2a7a_u32),
("org.bluetooth.characteristic.height", 0x2a8e_u32),
("org.bluetooth.characteristic.hid_control_point", 0x2a4c_u32),
("org.bluetooth.characteristic.hid_information", 0x2a4a_u32),
("org.bluetooth.characteristic.hip_circumference", 0x2a8f_u32),
("org.bluetooth.characteristic.http_control_point", 0x2aba_u32),
("org.bluetooth.characteristic.http_entity_body", 0x2ab9_u32),
("org.bluetooth.characteristic.http_headers", 0x2ab7_u32),
("org.bluetooth.characteristic.http_status_code", 0x2ab8_u32),
("org.bluetooth.characteristic.https_security", 0x2abb_u32),
("org.bluetooth.characteristic.humidity", 0x2a6f_u32),
("org.bluetooth.characteristic.ieee_11073-20601_regulatory_certification_data_list", 0x2a2a_u32),
("org.bluetooth.characteristic.indoor_positioning_configuration", 0x2aad_u32),
("org.bluetooth.characteristic.intermediate_cuff_pressure", 0x2a36_u32),
("org.bluetooth.characteristic.intermediate_temperature", 0x2a1e_u32),
("org.bluetooth.characteristic.irradiance", 0x2a77_u32),
("org.bluetooth.characteristic.language", 0x2aa2_u32),
("org.bluetooth.characteristic.last_name", 0x2a90_u32),
("org.bluetooth.characteristic.latitude", 0x2aae_u32),
("org.bluetooth.characteristic.ln_control_point", 0x2a6b_u32),
("org.bluetooth.characteristic.ln_feature", 0x2a6a_u32),
("org.bluetooth.characteristic.local_east_coordinate.xml", 0x2ab1_u32),
("org.bluetooth.characteristic.local_north_coordinate", 0x2ab0_u32),
("org.bluetooth.characteristic.local_time_information", 0x2a0f_u32),
("org.bluetooth.characteristic.location_and_speed", 0x2a67_u32),
("org.bluetooth.characteristic.location_name", 0x2ab5_u32),
("org.bluetooth.characteristic.longitude", 0x2aaf_u32),
("org.bluetooth.characteristic.magnetic_declination", 0x2a2c_u32),
("org.bluetooth.characteristic.magnetic_flux_density_2d", 0x2aa0_u32),
("org.bluetooth.characteristic.magnetic_flux_density_3d", 0x2aa1_u32),
("org.bluetooth.characteristic.manufacturer_name_string", 0x2a29_u32),
("org.bluetooth.characteristic.maximum_recommended_heart_rate", 0x2a91_u32),
("org.bluetooth.characteristic.measurement_interval", 0x2a21_u32),
("org.bluetooth.characteristic.model_number_string", 0x2a24_u32),
("org.bluetooth.characteristic.navigation", 0x2a68_u32),
("org.bluetooth.characteristic.new_alert", 0x2a46_u32),
("org.bluetooth.characteristic.object_action_control_point", 0x2ac5_u32),
("org.bluetooth.characteristic.object_changed", 0x2ac8_u32),
("org.bluetooth.characteristic.object_first_created", 0x2ac1_u32),
("org.bluetooth.characteristic.object_id", 0x2ac3_u32),
("org.bluetooth.characteristic.object_last_modified", 0x2ac2_u32),
("org.bluetooth.characteristic.object_list_control_point", 0x2ac6_u32),
("org.bluetooth.characteristic.object_list_filter", 0x2ac7_u32),
("org.bluetooth.characteristic.object_name", 0x2abe_u32),
("org.bluetooth.characteristic.object_properties", 0x2ac4_u32),
("org.bluetooth.characteristic.object_size", 0x2ac0_u32),
("org.bluetooth.characteristic.object_type", 0x2abf_u32),
("org.bluetooth.characteristic.ots_feature", 0x2abd_u32),
("org.bluetooth.characteristic.gap.peripheral_preferred_connection_parameters", 0x2a04_u32),
("org.bluetooth.characteristic.gap.peripheral_privacy_flag", 0x2a02_u32),
("org.bluetooth.characteristic.plx_continuous_measurement", 0x2a5f_u32),
("org.bluetooth.characteristic.plx_features", 0x2a60_u32),
("org.bluetooth.characteristic.plx_spot_check_measurement", 0x2a5e_u32),
("org.bluetooth.characteristic.pnp_id", 0x2a50_u32),
("org.bluetooth.characteristic.pollen_concentration", 0x2a75_u32),
("org.bluetooth.characteristic.position_quality", 0x2a69_u32),
("org.bluetooth.characteristic.pressure", 0x2a6d_u32),
("org.bluetooth.characteristic.protocol_mode", 0x2a4e_u32),
("org.bluetooth.characteristic.rainfall", 0x2a78_u32),
("org.bluetooth.characteristic.gap.reconnection_address", 0x2a03_u32),
("org.bluetooth.characteristic.record_access_control_point", 0x2a52_u32),
("org.bluetooth.characteristic.reference_time_information", 0x2a14_u32),
("org.bluetooth.characteristic.report", 0x2a4d_u32),
("org.bluetooth.characteristic.report_map", 0x2a4b_u32),
("org.bluetooth.characteristic.resting_heart_rate", 0x2a92_u32),
("org.bluetooth.characteristic.ringer_control_point", 0x2a40_u32),
("org.bluetooth.characteristic.ringer_setting", 0x2a41_u32),
("org.bluetooth.characteristic.rsc_feature", 0x2a54_u32),
("org.bluetooth.characteristic.rsc_measurement", 0x2a53_u32),
("org.bluetooth.characteristic.sc_control_point", 0x2a55_u32),
("org.bluetooth.characteristic.scan_interval_window", 0x2a4f_u32),
("org.bluetooth.characteristic.scan_refresh", 0x2a31_u32),
("org.bluetooth.characteristic.sensor_location", 0x2a5d_u32),
("org.bluetooth.characteristic.serial_number_string", 0x2a25_u32),
("org.bluetooth.characteristic.gatt.service_changed", 0x2a05_u32),
("org.bluetooth.characteristic.software_revision_string", 0x2a28_u32),
("org.bluetooth.characteristic.sport_type_for_aerobic_and_anaerobic_thresholds", 0x2a93_u32),
("org.bluetooth.characteristic.supported_new_alert_category", 0x2a47_u32),
("org.bluetooth.characteristic.supported_unread_alert_category", 0x2a48_u32),
("org.bluetooth.characteristic.system_id", 0x2a23_u32),
("org.bluetooth.characteristic.tds_control_point", 0x2abc_u32),
("org.bluetooth.characteristic.temperature", 0x2a6e_u32),
("org.bluetooth.characteristic.temperature_measurement", 0x2a1c_u32),
("org.bluetooth.characteristic.temperature_type", 0x2a1d_u32),
("org.bluetooth.characteristic.three_zone_heart_rate_limits", 0x2a94_u32),
("org.bluetooth.characteristic.time_accuracy", 0x2a12_u32),
("org.bluetooth.characteristic.time_source", 0x2a13_u32),
("org.bluetooth.characteristic.time_update_control_point", 0x2a16_u32),
("org.bluetooth.characteristic.time_update_state", 0x2a17_u32),
("org.bluetooth.characteristic.time_with_dst", 0x2a11_u32),
("org.bluetooth.characteristic.time_zone", 0x2a0e_u32),
("org.bluetooth.characteristic.true_wind_direction", 0x2a71_u32),
("org.bluetooth.characteristic.true_wind_speed", 0x2a70_u32),
("org.bluetooth.characteristic.two_zone_heart_rate_limit", 0x2a95_u32),
("org.bluetooth.characteristic.tx_power_level", 0x2a07_u32),
("org.bluetooth.characteristic.uncertainty", 0x2ab4_u32),
("org.bluetooth.characteristic.unread_alert_status", 0x2a45_u32),
("org.bluetooth.characteristic.uri", 0x2ab6_u32),
("org.bluetooth.characteristic.user_control_point", 0x2a9f_u32),
("org.bluetooth.characteristic.user_index", 0x2a9a_u32),
("org.bluetooth.characteristic.uv_index", 0x2a76_u32),
("org.bluetooth.characteristic.vo2_max", 0x2a96_u32),
("org.bluetooth.characteristic.waist_circumference", 0x2a97_u32),
("org.bluetooth.characteristic.weight", 0x2a98_u32),
("org.bluetooth.characteristic.weight_measurement", 0x2a9d_u32),
("org.bluetooth.characteristic.weight_scale_feature", 0x2a9e_u32),
("org.bluetooth.characteristic.wind_chill", 0x2a79_u32),
];
const BLUETOOTH_ASSIGNED_DESCRIPTORS: &'static [(&'static str, u32)] = &[
//TODO(zakorgy) create all the descriptors
//https://developer.bluetooth.org/gatt/services/Pages/ServicesHome.aspx
const BLUETOOTH_ASSIGNED_DESCRIPTORS: &'static [(&'static str, u32)] = &[
("org.bluetooth.descriptor.gatt.characteristic_extended_properties", 0x2900_u32),
("org.bluetooth.descriptor.gatt.characteristic_user_description", 0x2901_u32)
("org.bluetooth.descriptor.gatt.characteristic_user_description", 0x2901_u32),
("org.bluetooth.descriptor.gatt.client_characteristic_configuration", 0x2902_u32),
("org.bluetooth.descriptor.gatt.server_characteristic_configuration", 0x2903_u32),
("org.bluetooth.descriptor.gatt.characteristic_presentation_format", 0x2904_u32),
("org.bluetooth.descriptor.gatt.characteristic_aggregate_format", 0x2905_u32),
("org.bluetooth.descriptor.valid_range", 0x2906_u32),
("org.bluetooth.descriptor.external_report_reference", 0x2907_u32),
("org.bluetooth.descriptor.report_reference", 0x2908_u32),
("org.bluetooth.descriptor.number_of_digitals", 0x2909_u32),
("org.bluetooth.descriptor.value_trigger_setting", 0x290a_u32),
("org.bluetooth.descriptor.es_configuration", 0x290b_u32),
("org.bluetooth.descriptor.es_measurement", 0x290c_u32),
("org.bluetooth.descriptor.es_trigger_setting", 0x290d_u32),
("org.bluetooth.descriptor.time_trigger_setting", 0x290e_u32),
];
const BASE_UUID: &'static str = "-0000-1000-8000-00805f9b34fb";
@ -55,9 +277,7 @@ impl BluetoothUUID {
}
// https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothuuid-getservice
pub fn GetService(globalref: GlobalRef,
name: StringOrUnsignedLong)
-> Fallible<UUID> {
pub fn GetService(globalref: GlobalRef, name: BluetoothServiceUUID) -> Fallible<UUID> {
BluetoothUUID::resolve_uuid_name(globalref,
name,
BLUETOOTH_ASSIGNED_SERVICES,
@ -65,9 +285,7 @@ impl BluetoothUUID {
}
// https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothuuid-getcharacteristic
pub fn GetCharacteristic(globalref: GlobalRef,
name: StringOrUnsignedLong)
-> Fallible<UUID> {
pub fn GetCharacteristic(globalref: GlobalRef, name: BluetoothCharacteristicUUID) -> Fallible<UUID> {
BluetoothUUID::resolve_uuid_name(globalref,
name,
BLUETOOTH_ASSIGNED_CHARCTERISTICS,
@ -75,9 +293,7 @@ impl BluetoothUUID {
}
// https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothuuid-getdescriptor
pub fn GetDescriptor(globalref: GlobalRef,
name: StringOrUnsignedLong)
-> Fallible<UUID> {
pub fn GetDescriptor(globalref: GlobalRef, name: BluetoothDescriptorUUID) -> Fallible<UUID> {
BluetoothUUID::resolve_uuid_name(globalref,
name,
BLUETOOTH_ASSIGNED_DESCRIPTORS,
@ -92,7 +308,7 @@ impl BluetoothUUID {
-> Fallible<DOMString> {
match name {
// Step 1
StringOrUnsignedLong::UnsignedLong(unsigned32) =>{
StringOrUnsignedLong::UnsignedLong(unsigned32) => {
Ok(BluetoothUUID::CanonicalUUID(globalref, unsigned32))
},
StringOrUnsignedLong::String(dstring) => {
@ -103,8 +319,7 @@ impl BluetoothUUID {
} else {
// Step 3
let concatenated = format!("{}.{}", prefix, dstring);
let is_in_table = assigned_numbers_table.iter()
.find(|p| p.0 == concatenated);
let is_in_table = assigned_numbers_table.iter().find(|p| p.0 == concatenated);
match is_in_table {
Some(&(_, alias)) => Ok(BluetoothUUID::CanonicalUUID(globalref, alias)),
None => Err(Syntax),
@ -114,3 +329,12 @@ impl BluetoothUUID {
}
}
}
impl Clone for StringOrUnsignedLong {
fn clone(&self) -> StringOrUnsignedLong {
match self {
&StringOrUnsignedLong::String(ref s) => StringOrUnsignedLong::String(s.clone()),
&StringOrUnsignedLong::UnsignedLong(ul) => StringOrUnsignedLong::UnsignedLong(ul),
}
}
}

View file

@ -4,10 +4,22 @@
// https://webbluetoothcg.github.io/web-bluetooth/#bluetooth
dictionary BluetoothScanFilter {
sequence<BluetoothServiceUUID> services;
DOMString name;
DOMString namePrefix;
};
dictionary RequestDeviceOptions {
required sequence<BluetoothScanFilter> filters;
sequence<BluetoothServiceUUID> optionalServices /*= []*/;
};
[Pref="dom.bluetooth.enabled"]
interface Bluetooth {
// Promise<BluetoothDevice> requestDevice(RequestDeviceOptions options);
BluetoothDevice? requestDevice(/*RequestDeviceOptions options*/);
[Throws]
BluetoothDevice requestDevice(RequestDeviceOptions options);
};
// Bluetooth implements EventTarget;

View file

@ -7,7 +7,8 @@
// Allocation authorities for Vendor IDs:
enum VendorIDSource {
"bluetooth",
"usb"
"usb",
"unknown"
};
[Pref="dom.bluetooth.enabled"]

View file

@ -10,12 +10,18 @@ interface BluetoothRemoteGATTCharacteristic {
readonly attribute DOMString uuid;
readonly attribute BluetoothCharacteristicProperties properties;
readonly attribute ByteString? value;
BluetoothRemoteGATTDescriptor? getDescriptor(/*BluetoothDescriptorUUID descriptor*/);
[Throws]
BluetoothRemoteGATTDescriptor getDescriptor(BluetoothDescriptorUUID descriptor);
[Throws]
sequence<BluetoothRemoteGATTDescriptor> getDescriptors(optional BluetoothDescriptorUUID descriptor);
//Promise<BluetoothRemoteGATTDescriptor> getDescriptor(BluetoothDescriptorUUID descriptor);
//Promise<sequence<BluetoothRemoteGATTDescriptor>>
//getDescriptors(optional BluetoothDescriptorUUID descriptor);
//Promise<DataView> readValue();
[Throws]
ByteString readValue();
//Promise<DataView> readValue();
[Throws]
void writeValue(sequence<octet> value);
//Promise<void> writeValue(BufferSource value);
//Promise<void> startNotifications();
//Promise<void> stopNotifications();

View file

@ -9,8 +9,10 @@ interface BluetoothRemoteGATTDescriptor {
readonly attribute BluetoothRemoteGATTCharacteristic characteristic;
readonly attribute DOMString uuid;
readonly attribute ByteString? value;
[Throws]
ByteString readValue();
//Promise<DataView> readValue();
[Throws]
void writeValue(sequence<octet> value);
//Promise<void> writeValue(BufferSource value);
};

View file

@ -8,9 +8,14 @@
interface BluetoothRemoteGATTServer {
readonly attribute BluetoothDevice device;
readonly attribute boolean connected;
[Throws]
BluetoothRemoteGATTServer connect();
[Throws]
void disconnect();
BluetoothRemoteGATTService? getPrimaryService();
[Throws]
BluetoothRemoteGATTService getPrimaryService(BluetoothServiceUUID service);
[Throws]
sequence<BluetoothRemoteGATTService> getPrimaryServices(optional BluetoothServiceUUID service);
//Promise<BluetoothRemoteGATTService> getPrimaryService(BluetoothServiceUUID service);
//Promise<sequence<BluetoothRemoteGATTService>>getPrimaryServices(optional BluetoothServiceUUID service);
//Promise<BluetoothRemoteGATTServer> connect();

View file

@ -9,7 +9,11 @@ interface BluetoothRemoteGATTService {
readonly attribute BluetoothDevice device;
readonly attribute DOMString uuid;
readonly attribute boolean isPrimary;
BluetoothRemoteGATTCharacteristic? getCharacteristic(/*DOMString characteristic*/);
[Throws]
BluetoothRemoteGATTCharacteristic getCharacteristic(BluetoothCharacteristicUUID characteristic);
[Throws]
sequence<BluetoothRemoteGATTCharacteristic> getCharacteristics
(optional BluetoothCharacteristicUUID characteristic);
//Promise<BluetoothRemoteGATTCharacteristic>getCharacteristic(BluetoothCharacteristicUUID characteristic);
//Promise<sequence<BluetoothRemoteGATTCharacteristic>>
//getCharacteristics(optional BluetoothCharacteristicUUID characteristic);

View file

@ -47,6 +47,7 @@ use msg::constellation_msg::{ConstellationChan, LoadData, PipelineId, SubpageId}
use msg::constellation_msg::{WindowSizeData, WindowSizeType};
use msg::webdriver_msg::{WebDriverJSError, WebDriverJSResult};
use net_traits::ResourceThread;
use net_traits::bluetooth_thread::BluetoothMethodMsg;
use net_traits::image_cache_thread::{ImageCacheChan, ImageCacheThread};
use net_traits::storage_thread::{StorageThread, StorageType};
use num_traits::ToPrimitive;
@ -212,6 +213,10 @@ pub struct Window {
#[ignore_heap_size_of = "channels are hard"]
resource_thread: Arc<ResourceThread>,
/// A handle for communicating messages to the bluetooth thread.
#[ignore_heap_size_of = "channels are hard"]
bluetooth_thread: IpcSender<BluetoothMethodMsg>,
/// A handle for communicating messages to the storage thread.
#[ignore_heap_size_of = "channels are hard"]
storage_thread: StorageThread,
@ -334,6 +339,10 @@ impl Window {
&*self.page
}
pub fn bluetooth_thread(&self) -> IpcSender<BluetoothMethodMsg> {
self.bluetooth_thread.clone()
}
pub fn storage_thread(&self) -> StorageThread {
self.storage_thread.clone()
}
@ -1407,6 +1416,7 @@ impl Window {
compositor: IpcSender<ScriptToCompositorMsg>,
image_cache_thread: ImageCacheThread,
resource_thread: Arc<ResourceThread>,
bluetooth_thread: IpcSender<BluetoothMethodMsg>,
storage_thread: StorageThread,
mem_profiler_chan: mem::ProfilerChan,
devtools_chan: Option<IpcSender<ScriptToDevtoolsControlMsg>>,
@ -1462,6 +1472,7 @@ impl Window {
dom_static: GlobalStaticData::new(),
js_runtime: DOMRefCell::new(Some(runtime.clone())),
resource_thread: resource_thread,
bluetooth_thread: bluetooth_thread,
storage_thread: storage_thread,
constellation_chan: constellation_chan,
page_clip_rect: Cell::new(MAX_RECT),

View file

@ -64,6 +64,7 @@ use msg::constellation_msg::{PipelineId, PipelineNamespace};
use msg::constellation_msg::{SubpageId, WindowSizeData, WindowSizeType};
use msg::webdriver_msg::WebDriverScriptCommand;
use net_traits::LoadData as NetLoadData;
use net_traits::bluetooth_thread::BluetoothMethodMsg;
use net_traits::image_cache_thread::{ImageCacheChan, ImageCacheResult, ImageCacheThread};
use net_traits::storage_thread::StorageThread;
use net_traits::{AsyncResponseTarget, ControlMsg, LoadConsumer, LoadContext, Metadata, ResourceThread};
@ -310,6 +311,8 @@ pub struct ScriptThread {
/// A handle to the resource thread. This is an `Arc` to avoid running out of file descriptors if
/// there are many iframes.
resource_thread: Arc<ResourceThread>,
/// A handle to the bluetooth thread.
bluetooth_thread: IpcSender<BluetoothMethodMsg>,
/// A handle to the storage thread.
storage_thread: StorageThread,
@ -554,6 +557,7 @@ impl ScriptThread {
image_cache_port: image_cache_port,
resource_thread: Arc::new(state.resource_thread),
bluetooth_thread: state.bluetooth_thread,
storage_thread: state.storage_thread,
port: port,
@ -1496,6 +1500,7 @@ impl ScriptThread {
self.compositor.borrow_mut().clone(),
self.image_cache_thread.clone(),
self.resource_thread.clone(),
self.bluetooth_thread.clone(),
self.storage_thread.clone(),
self.mem_profiler_chan.clone(),
self.devtools_chan.clone(),

View file

@ -46,6 +46,7 @@ use msg::constellation_msg::{Key, KeyModifiers, KeyState, LoadData};
use msg::constellation_msg::{PipelineNamespaceId, SubpageId};
use msg::webdriver_msg::WebDriverScriptCommand;
use net_traits::ResourceThread;
use net_traits::bluetooth_thread::BluetoothMethodMsg;
use net_traits::image_cache_thread::ImageCacheThread;
use net_traits::response::HttpsState;
use net_traits::storage_thread::StorageThread;
@ -320,6 +321,8 @@ pub struct InitialScriptState {
pub scheduler_chan: IpcSender<TimerEventRequest>,
/// A channel to the resource manager thread.
pub resource_thread: ResourceThread,
/// A channel to the bluetooth thread.
pub bluetooth_thread: IpcSender<BluetoothMethodMsg>,
/// A channel to the storage thread.
pub storage_thread: StorageThread,
/// A channel to the image cache thread.

View file

@ -167,6 +167,15 @@ name = "block"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "blurz"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"dbus 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "brotli"
version = "0.3.20"
@ -414,6 +423,15 @@ dependencies = [
"winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "dbus"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)",
"pkg-config 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "debug_unreachable"
version = "0.1.1"
@ -430,6 +448,14 @@ dependencies = [
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "device"
version = "0.0.1"
source = "git+https://github.com/servo/devices#eeac5abfd548b63dcc48873261609922351551e1"
dependencies = [
"blurz 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "devtools"
version = "0.0.1"
@ -1305,8 +1331,10 @@ dependencies = [
name = "net"
version = "0.0.1"
dependencies = [
"bitflags 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
"brotli 0.3.20 (git+https://github.com/ende76/brotli-rs)",
"cookie 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
"device 0.0.1 (git+https://github.com/servo/devices)",
"devtools_traits 0.0.1",
"flate2 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
"hyper 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",

View file

@ -71,9 +71,11 @@ use compositing::{CompositorProxy, CompositorThread, Constellation};
use gaol::sandbox::{ChildSandbox, ChildSandboxMethods};
use gfx::font_cache_thread::FontCacheThread;
use ipc_channel::ipc::{self, IpcSender};
use net::bluetooth_thread::BluetoothThreadFactory;
use net::image_cache_thread::new_image_cache_thread;
use net::resource_thread::new_resource_thread;
use net::storage_thread::StorageThreadFactory;
use net_traits::bluetooth_thread::BluetoothMethodMsg;
use net_traits::storage_thread::StorageThread;
use profile::mem as profile_mem;
use profile::time as profile_time;
@ -208,6 +210,7 @@ fn create_constellation(opts: opts::Opts,
devtools_chan: Option<Sender<devtools_traits::DevtoolsControlMsg>>,
supports_clipboard: bool,
webrender_api_sender: Option<webrender_traits::RenderApiSender>) -> Sender<ConstellationMsg> {
let bluetooth_thread: IpcSender<BluetoothMethodMsg> = BluetoothThreadFactory::new();
let resource_thread = new_resource_thread(opts.user_agent.clone(), devtools_chan.clone());
let image_cache_thread = new_image_cache_thread(resource_thread.clone(),
webrender_api_sender.as_ref().map(|wr| wr.create_api()));
@ -218,6 +221,7 @@ fn create_constellation(opts: opts::Opts,
let initial_state = InitialConstellationState {
compositor_proxy: compositor_proxy,
devtools_chan: devtools_chan,
bluetooth_thread: bluetooth_thread,
image_cache_thread: image_cache_thread,
font_cache_thread: font_cache_thread,
resource_thread: resource_thread,

28
ports/cef/Cargo.lock generated
View file

@ -153,6 +153,15 @@ name = "block"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "blurz"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"dbus 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "brotli"
version = "0.3.20"
@ -385,6 +394,15 @@ dependencies = [
"winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "dbus"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)",
"pkg-config 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "debug_unreachable"
version = "0.1.1"
@ -401,6 +419,14 @@ dependencies = [
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "device"
version = "0.0.1"
source = "git+https://github.com/servo/devices#eeac5abfd548b63dcc48873261609922351551e1"
dependencies = [
"blurz 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "devtools"
version = "0.0.1"
@ -1219,8 +1245,10 @@ dependencies = [
name = "net"
version = "0.0.1"
dependencies = [
"bitflags 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
"brotli 0.3.20 (git+https://github.com/ende76/brotli-rs)",
"cookie 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
"device 0.0.1 (git+https://github.com/servo/devices)",
"devtools_traits 0.0.1",
"flate2 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
"hyper 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",

28
ports/gonk/Cargo.lock generated
View file

@ -146,6 +146,15 @@ name = "block"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "blurz"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"dbus 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "brotli"
version = "0.3.20"
@ -378,6 +387,15 @@ dependencies = [
"winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "dbus"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)",
"pkg-config 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "debug_unreachable"
version = "0.1.1"
@ -394,6 +412,14 @@ dependencies = [
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "device"
version = "0.0.1"
source = "git+https://github.com/servo/devices#eeac5abfd548b63dcc48873261609922351551e1"
dependencies = [
"blurz 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "devtools"
version = "0.0.1"
@ -1202,8 +1228,10 @@ dependencies = [
name = "net"
version = "0.0.1"
dependencies = [
"bitflags 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
"brotli 0.3.20 (git+https://github.com/ende76/brotli-rs)",
"cookie 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
"device 0.0.1 (git+https://github.com/servo/devices)",
"devtools_traits 0.0.1",
"flate2 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
"hyper 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",

View file

@ -0,0 +1,61 @@
<!DOCTYPE html>
<html>
<title>Battery Level</title>
<body>
<input id="name" type="text" placeholder="Device Name">
<input id="namePrefix" type="text" placeholder="Device Name Prefix">
<button type="button" onclick="onButtonClick()">Get Bluetooth Device's Battery Level</button>
<pre id="log"></pre>
<script>
function onButtonClick() {
clear();
var options = {filters: [{services: ['battery_service']}], optinalServices: []};
var filterName = document.getElementById('name').value;
if (filterName)
options.filters.push({name: filterName});
var filterNamePrefix = document.getElementById('namePrefix').value;
if (filterNamePrefix)
options.filters.push({namePrefix: filterNamePrefix});
try {
log('Requesting Bluetooth Device...');
var bluetooth = window.navigator.bluetooth;
var device = bluetooth.requestDevice(options);
log('Connecting to GATT Server on device...');
var server = device.gatt.connect();
log('Getting Battery Service...');
var service = server.getPrimaryService('battery_service');
log('Getting Battery Level Characteristic...');
var characteristic = service.getCharacteristic('battery_level');
log('Reading Battery Level...');
var value = AsciiToDecimal(characteristic.readValue());
log('> Battery Level is ' + value + '%');
} catch(err) {
log(err);
}
}
function clear() {
document.getElementById("log").textContent = "";
}
function log(line) {
document.getElementById("log").textContent += line + '\n';
}
function AsciiToDecimal(bytestr) {
var result = [];
for(i = 0; i < bytestr.length; i++) {
result[i] = bytestr[i].charCodeAt(0) ;
}
return result;
}
</script>
</body>
</html>

View file

@ -0,0 +1,69 @@
<!DOCTYPE html>
<html>
<title>Characteristic info</title>
<body>
<input id="service" type="text" autofocus placeholder="Bluetooth Service">
<input id="characteristic" type="text" autofocus placeholder="Bluetooth Characteristic">
<button type="button" onclick="onButtonClick()">Get Characteristic Info</button>
<pre id="log"></pre>
<script>
function onButtonClick() {
clear();
var serviceUuid = document.getElementById('service').value;
if (serviceUuid.startsWith('0x'))
serviceUuid = parseInt(serviceUuid, 16);
var characteristicUuid = document.getElementById('characteristic').value;
if (characteristicUuid.startsWith('0x'))
characteristicUuid = parseInt(characteristicUuid, 16);
try {
log('Requesting Bluetooth Device...');
var device = window.navigator.bluetooth.requestDevice({filters: [{services: [serviceUuid]}]});
log('Connecting to GATTserver on device...');
var server = device.gatt.connect();
log('Getting Primary Service...');
var primaryService = server.getPrimaryService(serviceUuid);
log('Getting Characteristic...');
var characteristic = primaryService.getCharacteristic(characteristicUuid);
log('Characteristic found!');
log('> Characteristic service: ' + characteristic.service.uuid);
log('> Characteristic UUID: ' + characteristic.uuid);
log('> Broadcast: ' + characteristic.properties.broadcast);
log('> Read: ' + characteristic.properties.read);
log('> Write w/o response: ' + characteristic.properties.writeWithoutResponse);
log('> Write: ' + characteristic.properties.write);
log('> Notify: ' + characteristic.properties.notify);
log('> Indicate: ' + characteristic.properties.indicate);
log('> Signed Write: ' + characteristic.properties.authenticatedSignedWrites);
log('> Queued Write: ' + characteristic.properties.reliableWrite);
log('> Writable Auxiliaries: ' + characteristic.properties.writableAuxiliaries);
characteristic.readValue();
log('> Characteristic value: ' + AsciiToDecimal(characteristic.value));
} catch(err) {
log(err);
}
}
function clear() {
document.getElementById("log").textContent = "";
}
function log(line) {
document.getElementById("log").textContent += line + '\n';
}
function AsciiToDecimal(bytestr) {
var result = [];
for(i = 0; i < bytestr.length; i++) {
result[i] = bytestr[i].charCodeAt(0) ;
}
return result;
}
</script>
</body>
</html>

View file

@ -0,0 +1,68 @@
<!DOCTYPE html>
<html>
<title>Descriptor info</title>
<body>
<input id="service" type="text" autofocus placeholder="Bluetooth Service">
<input id="characteristic" type="text" autofocus placeholder="Bluetooth Characteristic">
<input id="descriptor" type="text" autofocus placeholder="Bluetooth Descriptor">
<button type="button" onclick="onButtonClick()">Get Descriptor Info</button>
<pre id="log"></pre>
<script>
function onButtonClick() {
clear();
var serviceUuid = document.getElementById('service').value;
if (serviceUuid.startsWith('0x'))
serviceUuid = parseInt(serviceUuid, 16);
var characteristicUuid = document.getElementById('characteristic').value;
if (characteristicUuid.startsWith('0x'))
characteristicUuid = parseInt(characteristicUuid, 16);
var descriptorUuid = document.getElementById('descriptor').value;
if (descriptorUuid.startsWith('0x'))
descriptorUuid = parseInt(descriptorUuid, 16);
try {
log('Requesting Bluetooth Device...');
var device = window.navigator.bluetooth.requestDevice({filters: [{services: [serviceUuid]}]});
log('Connecting to GATTserver on device...');
var server = device.gatt.connect();
log('Getting Primary Service...');
var primaryService = server.getPrimaryService(serviceUuid);
log('Getting Characteristic...');
var characteristic = primaryService.getCharacteristic(characteristicUuid);
log('Getting Descriptor...');
var descriptor = characteristic.getDescriptor(descriptorUuid);
log('Descriptor found!');
log('> Descriptor characteristic: ' + descriptor.characteristic.uuid);
log('> Descriptor UUID: ' + descriptor.uuid);
descriptor.readValue();
log('> Descriptor value: ' + AsciiToDecimal(descriptor.value));
} catch(err) {
log(err);
}
}
function clear() {
document.getElementById("log").textContent = "";
}
function log(line) {
document.getElementById("log").textContent += line + '\n';
}
function AsciiToDecimal(bytestr) {
var result = [];
for(i = 0; i < bytestr.length; i++) {
result[i] = bytestr[i].charCodeAt(0) ;
}
return result;
}
</script>
</body>
</html>

View file

@ -0,0 +1,94 @@
<!DOCTYPE html>
<html>
<title>Device Disconnect</title>
<body>
<input id="service" type="text" autofocus placeholder="Bluetooth Service">
<input id="name" type="text" placeholder="Device Name">
<input id="namePrefix" type="text" placeholder="Device Name Prefix">
<button type="button" onclick="onScanButtonClick()">Scan()</button>
<button type="button" onclick="onDisconnectButtonClick()">Disconnect()</button>
<button type="button" onclick="onReconnectButtonClick()">Reconnect()</button>
<pre id="log"></pre>
<script>
var bluetoothDevice;
function onScanButtonClick() {
clear();
var options = {filters: []};
var filterService = document.getElementById('service').value;
if (filterService.startsWith('0x'))
filterService = parseInt(filterService, 16);
if (filterService)
options.filters.push({services: [filterService]});
var filterName = document.getElementById('name').value;
if (filterName)
options.filters.push({name: filterName});
var filterNamePrefix = document.getElementById('namePrefix').value;
if (filterNamePrefix)
options.filters.push({namePrefix: filterNamePrefix});
bluetoothDevice = null;
try {
log('Requesting Bluetooth Device...');
bluetoothDevice = window.navigator.bluetooth.requestDevice(options);
connect();
} catch(err) {
log(err);
}
}
function onDisconnectButtonClick() {
clear();
if (!bluetoothDevice)
return;
try {
log('Disconnecting from Bluetooth Device...');
if (bluetoothDevice.gatt.connected) {
bluetoothDevice.gatt.disconnect();
log('> Bluetooth Device connected: ' + bluetoothDevice.gatt.connected);
} else {
log('> Bluetooth Device is already disconnected');
}
} catch(err) {
log(err);
}
}
function onReconnectButtonClick() {
clear();
if (!bluetoothDevice)
log('> There is no connected Bluetooth Device instance')
if (bluetoothDevice.gatt.connected) {
log('> Bluetooth Device is already connected');
return;
} else {
connect();
}
}
function connect() {
try {
log('Connecting to Bluetooth Device...');
bluetoothDevice.gatt.connect();
log('> Bluetooth Device connected: ' + bluetoothDevice.gatt.connected);
} catch(err) {
log(err);
}
}
function clear() {
document.getElementById("log").textContent = "";
}
function log(line) {
document.getElementById("log").textContent += line + '\n';
}
</script>
</body>
</html>

View file

@ -0,0 +1,59 @@
<!DOCTYPE html>
<html>
<title>Device Info</title>
<body>
<input id="service" type="text" autofocus placeholder="Bluetooth Service">
<input id="name" type="text" placeholder="Device Name">
<input id="namePrefix" type="text" placeholder="Device Name Prefix">
<button type="button" onclick="onButtonClick()">Get Bluetooth Device Info</button>
<pre id="log"></pre>
<script>
function onButtonClick() {
clear();
var options = {filters: [], optinalServices: []};
var filterService = document.getElementById('service').value;
if (filterService.startsWith('0x'))
filterService = parseInt(filterService, 16);
if (filterService)
options.filters.push({services: [filterService]});
var filterName = document.getElementById('name').value;
if (filterName)
options.filters.push({name: filterName});
var filterNamePrefix = document.getElementById('namePrefix').value;
if (filterNamePrefix)
options.filters.push({namePrefix: filterNamePrefix});
try {
log('Requesting Bluetooth Device...');
var device = window.navigator.bluetooth.requestDevice(options);
log('Found a device!');
log('> Name: ' + device.name);
log('> Id: ' + device.id);
log('> Device Class: ' + device.deviceClass);
log('> Vendor Id Source: ' + device.vendorIDSource);
log('> Vendor Id: ' + device.vendorID);
log('> Product Id: ' + device.productID);
log('> Product Version: ' + device.productVersion);
log('> Appearance: ' + device.adData.appearance);
log('> Tx Power: ' + device.adData.txPower + ' dBm');
log('> RSSI: ' + device.adData.rssi + ' dBm');
} catch(err) {
log(err);
}
}
function clear() {
document.getElementById("log").textContent = "";
}
function log(line) {
document.getElementById("log").textContent += line + '\n';
}
</script>
</body>
</html>

View file

@ -0,0 +1,57 @@
<!DOCTYPE html>
<html>
<title>Primary Service info</title>
<body>
<input id="service" type="text" autofocus placeholder="Bluetooth Service">
<input id="name" type="text" placeholder="Device Name">
<input id="namePrefix" type="text" placeholder="Device Name Prefix">
<button type="button" onclick="onButtonClick()">Get Primary Service Info</button>
<pre id="log"></pre>
<script>
function onButtonClick() {
clear();
var options = {filters: [], optinalServices: []};
var filterService = document.getElementById('service').value;
if (filterService.startsWith('0x'))
filterService = parseInt(filterService, 16);
if (filterService)
options.filters.push({services: [filterService]});
var filterName = document.getElementById('name').value;
if (filterName)
options.filters.push({name: filterName});
var filterNamePrefix = document.getElementById('namePrefix').value;
if (filterNamePrefix)
options.filters.push({namePrefix: filterNamePrefix});
try {
log('Requesting Bluetooth Device...');
var device = window.navigator.bluetooth.requestDevice(options);
log('Connecting to GATTserver on device...');
var server = device.gatt.connect();
log('Getting Primary Service...');
var primaryService = server.getPrimaryService(filterService);
log('Primary Service found on device: ' + primaryService.device.name);
log('> UUID: ' + primaryService.uuid);
log('> Is primary: ' + primaryService.isPrimary);
} catch(err) {
log(err);
}
}
function clear() {
document.getElementById("log").textContent = "";
}
function log(line) {
document.getElementById("log").textContent += line + '\n';
}
</script>
</body>
</html>