servo: Merge #14612 - Updating GATTServer's Connect/Disconnect calls (from szeged:connect-disconnect-update); r=jdm

<!-- Please describe your changes on the following line: -->
Added the missing [Step 5.2.3](https://github.com/servo/servo/compare/master...szeged:connect-disconnect-update#diff-1dbe29f87740f5aec93f37adbecace6cR213) to the `connect` function.
Updated the [disconnect](https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattserver-disconnect) function to its current state in the specification, including the `clean_up_disconnected_device` and the `garbage_collect_the connection` functions.
Added new tests for checking the invalid state of JS objects after disconnection.

---
<!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `__` with appropriate data: -->
- [x] `./mach build -d` does not report any errors
- [x] `./mach test-tidy` does not report any errors
- [x] There are tests for these changes

<!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. -->

Source-Repo: https://github.com/servo/servo
Source-Revision: 23590f683a06b67f666e83009513b67ed8225ce7
This commit is contained in:
Zakor Gyula 2016-12-22 06:51:16 -08:00
Родитель afbb39b71f
Коммит 49357c5565
7 изменённых файлов: 128 добавлений и 28 удалений

Просмотреть файл

@ -65,3 +65,4 @@ fetch
characteristicvaluechanged
fullscreenchange
fullscreenerror
gattserverdisconnected

Просмотреть файл

@ -247,6 +247,12 @@ impl BluetoothManager {
BluetoothRequest::Test(data_set_name, sender) => {
let _ = sender.send(self.test(data_set_name));
}
BluetoothRequest::SetRepresentedToNull(service_ids, characteristic_ids, descriptor_ids) => {
self.remove_ids_from_caches(service_ids, characteristic_ids, descriptor_ids)
}
BluetoothRequest::IsRepresentedDeviceNull(id, sender) => {
let _ = sender.send(!self.device_is_cached(&id));
}
BluetoothRequest::Exit => {
break
},
@ -273,6 +279,26 @@ impl BluetoothManager {
}
}
fn remove_ids_from_caches(&mut self,
service_ids: Vec<String>,
characteristic_ids: Vec<String>,
descriptor_ids: Vec<String>) {
for id in service_ids {
self.cached_services.remove(&id);
self.service_to_device.remove(&id);
}
for id in characteristic_ids {
self.cached_characteristics.remove(&id);
self.characteristic_to_service.remove(&id);
}
for id in descriptor_ids {
self.cached_descriptors.remove(&id);
self.descriptor_to_characteristic.remove(&id);
}
}
// Adapter
pub fn get_or_create_adapter(&mut self) -> Option<BluetoothAdapter> {
@ -613,20 +639,19 @@ impl BluetoothManager {
}
// https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattserver-disconnect
fn gatt_server_disconnect(&mut self, device_id: String) -> BluetoothResult<bool> {
fn gatt_server_disconnect(&mut self, device_id: String) -> BluetoothResult<()> {
let mut adapter = try!(self.get_adapter());
match self.get_device(&mut adapter, &device_id) {
Some(d) => {
// Step 2.
if !d.is_connected().unwrap_or(true) {
return Ok(false);
return Ok(());
}
let _ = d.disconnect();
for _ in 0..MAXIMUM_TRANSACTION_TIME {
match d.is_connected().unwrap_or(true) {
true => thread::sleep(Duration::from_millis(CONNECTION_TIMEOUT_MS)),
false => return Ok(false),
false => return Ok(()),
}
}
return Err(BluetoothError::Network);

Просмотреть файл

@ -222,21 +222,23 @@ fn create_heart_rate_service(device: &BluetoothDevice,
try!(create_characteristic_with_value(&heart_rate_service,
HEART_RATE_MEASUREMENT_CHARACTERISTIC_UUID.to_owned(),
vec![0]));
try!(heart_rate_measurement_characteristic.set_flags(vec![NOTIFY_FLAG.to_string()]));
try!(heart_rate_measurement_characteristic.set_flags(vec![NOTIFY_FLAG.to_string(),
READ_FLAG.to_string(),
WRITE_FLAG.to_string()]));
// Body Sensor Location Characteristic 1
let body_sensor_location_characteristic_1 =
try!(create_characteristic_with_value(&heart_rate_service,
BODY_SENSOR_LOCATION_CHARACTERISTIC_UUID.to_owned(),
vec![49]));
try!(body_sensor_location_characteristic_1.set_flags(vec![READ_FLAG.to_string()]));
try!(body_sensor_location_characteristic_1.set_flags(vec![READ_FLAG.to_string(), WRITE_FLAG.to_string()]));
// Body Sensor Location Characteristic 2
let body_sensor_location_characteristic_2 =
try!(create_characteristic_with_value(&heart_rate_service,
BODY_SENSOR_LOCATION_CHARACTERISTIC_UUID.to_owned(),
vec![50]));
try!(body_sensor_location_characteristic_2.set_flags(vec![READ_FLAG.to_string()]));
try!(body_sensor_location_characteristic_2.set_flags(vec![READ_FLAG.to_string(), WRITE_FLAG.to_string()]));
Ok(heart_rate_service)
}

Просмотреть файл

@ -85,12 +85,14 @@ pub type BluetoothResponseResult = Result<BluetoothResponse, BluetoothError>;
pub enum BluetoothRequest {
RequestDevice(RequestDeviceoptions, IpcSender<BluetoothResponseResult>),
GATTServerConnect(String, IpcSender<BluetoothResponseResult>),
GATTServerDisconnect(String, IpcSender<BluetoothResult<bool>>),
GATTServerDisconnect(String, IpcSender<BluetoothResult<()>>),
GetGATTChildren(String, Option<String>, bool, GATTType, IpcSender<BluetoothResponseResult>),
ReadValue(String, IpcSender<BluetoothResponseResult>),
WriteValue(String, Vec<u8>, IpcSender<BluetoothResponseResult>),
EnableNotification(String, bool, IpcSender<BluetoothResponseResult>),
WatchAdvertisements(String, IpcSender<BluetoothResponseResult>),
SetRepresentedToNull(Vec<String>, Vec<String>, Vec<String>),
IsRepresentedDeviceNull(String, IpcSender<bool>),
Test(String, IpcSender<BluetoothResult<()>>),
Exit,
}

Просмотреть файл

@ -104,6 +104,10 @@ impl Bluetooth {
self.global().as_window().bluetooth_thread()
}
pub fn get_device_map(&self) -> &DOMRefCell<HashMap<String, MutJS<BluetoothDevice>>> {
&self.device_instance_map
}
// https://webbluetoothcg.github.io/web-bluetooth/#request-bluetooth-devices
fn request_bluetooth_devices(&self,
p: &Rc<Promise>,

Просмотреть файл

@ -10,6 +10,8 @@ use dom::bindings::codegen::Bindings::BluetoothDeviceBinding::BluetoothDeviceMet
use dom::bindings::codegen::Bindings::BluetoothRemoteGATTServerBinding::BluetoothRemoteGATTServerMethods;
use dom::bindings::codegen::Bindings::EventHandlerBinding::EventHandlerNonNull;
use dom::bindings::error::Error;
use dom::bindings::error::ErrorResult;
use dom::bindings::inheritance::Castable;
use dom::bindings::js::{MutJS, MutNullableJS, Root};
use dom::bindings::reflector::{DomObject, reflect_dom_object};
use dom::bindings::str::DOMString;
@ -22,7 +24,7 @@ use dom::bluetoothremotegattservice::BluetoothRemoteGATTService;
use dom::eventtarget::EventTarget;
use dom::globalscope::GlobalScope;
use dom::promise::Promise;
use ipc_channel::ipc::IpcSender;
use ipc_channel::ipc::{self, IpcSender};
use js::jsapi::JSContext;
use std::cell::Cell;
use std::collections::HashMap;
@ -72,6 +74,10 @@ impl BluetoothDevice {
BluetoothDeviceBinding::Wrap)
}
fn get_context(&self) -> Root<Bluetooth> {
self.context.get()
}
pub fn get_or_create_service(&self,
service: &BluetoothServiceMsg,
server: &BluetoothRemoteGATTServer)
@ -119,6 +125,13 @@ impl BluetoothDevice {
return bt_characteristic;
}
pub fn is_represented_device_null(&self) -> bool {
let (sender, receiver) = ipc::channel().unwrap();
self.get_bluetooth_thread().send(
BluetoothRequest::IsRepresentedDeviceNull(self.Id().to_string(), sender)).unwrap();
receiver.recv().unwrap()
}
pub fn get_or_create_descriptor(&self,
descriptor: &BluetoothDescriptorMsg,
characteristic: &BluetoothRemoteGATTCharacteristic)
@ -139,6 +152,60 @@ impl BluetoothDevice {
fn get_bluetooth_thread(&self) -> IpcSender<BluetoothRequest> {
self.global().as_window().bluetooth_thread()
}
// https://webbluetoothcg.github.io/web-bluetooth/#clean-up-the-disconnected-device
#[allow(unrooted_must_root)]
pub fn clean_up_disconnected_device(&self) {
// Step 1.
self.Gatt().set_connected(false);
// TODO: Step 2: Implement activeAlgorithms internal slot for BluetoothRemoteGATTServer.
// Step 3: We don't need `context`, we get the attributeInstanceMap from the device.
// https://github.com/WebBluetoothCG/web-bluetooth/issues/330
// Step 4.
let mut service_map = self.attribute_instance_map.0.borrow_mut();
let service_ids = service_map.drain().map(|(id, _)| id).collect();
let mut characteristic_map = self.attribute_instance_map.1.borrow_mut();
let characteristic_ids = characteristic_map.drain().map(|(id, _)| id).collect();
let mut descriptor_map = self.attribute_instance_map.2.borrow_mut();
let descriptor_ids = descriptor_map.drain().map(|(id, _)| id).collect();
// Step 5, 6.4, 7.
// TODO: Step 6: Implement `active notification context set` for BluetoothRemoteGATTCharacteristic.
let _ = self.get_bluetooth_thread().send(
BluetoothRequest::SetRepresentedToNull(service_ids, characteristic_ids, descriptor_ids));
// Step 8.
self.upcast::<EventTarget>().fire_bubbling_event(atom!("gattserverdisconnected"));
}
// https://webbluetoothcg.github.io/web-bluetooth/#garbage-collect-the-connection
#[allow(unrooted_must_root)]
pub fn garbage_collect_the_connection(&self) -> ErrorResult {
// Step 1: TODO: Check if other systems using this device.
// Step 2.
let context = self.get_context();
for (id, device) in context.get_device_map().borrow().iter() {
// Step 2.1 - 2.2.
if id == &self.Id().to_string() {
if device.get().Gatt().Connected() {
return Ok(());
}
// TODO: Step 2.3: Implement activeAlgorithms internal slot for BluetoothRemoteGATTServer.
}
}
// Step 3.
let (sender, receiver) = ipc::channel().unwrap();
self.get_bluetooth_thread().send(
BluetoothRequest::GATTServerDisconnect(String::from(self.Id()), sender)).unwrap();
receiver.recv().unwrap().map_err(Error::from)
}
}
impl BluetoothDeviceMethods for BluetoothDevice {

Просмотреть файл

@ -15,7 +15,7 @@ use dom::bluetoothdevice::BluetoothDevice;
use dom::bluetoothuuid::{BluetoothServiceUUID, BluetoothUUID};
use dom::globalscope::GlobalScope;
use dom::promise::Promise;
use ipc_channel::ipc::{self, IpcSender};
use ipc_channel::ipc::IpcSender;
use js::jsapi::JSContext;
use std::cell::Cell;
use std::rc::Rc;
@ -46,6 +46,10 @@ impl BluetoothRemoteGATTServer {
fn get_bluetooth_thread(&self) -> IpcSender<BluetoothRequest> {
self.global().as_window().bluetooth_thread()
}
pub fn set_connected(&self, connected: bool) {
self.connected.set(connected);
}
}
impl BluetoothRemoteGATTServerMethods for BluetoothRemoteGATTServer {
@ -86,27 +90,14 @@ impl BluetoothRemoteGATTServerMethods for BluetoothRemoteGATTServer {
// Step 2.
if !self.Connected() {
return Ok(());
return Ok(())
}
let (sender, receiver) = ipc::channel().unwrap();
self.get_bluetooth_thread().send(
BluetoothRequest::GATTServerDisconnect(String::from(self.Device().Id()), sender)).unwrap();
let server = receiver.recv().unwrap();
// TODO: Step 3: Implement the `clean up the disconnected device` algorithm.
// Step 3.
self.Device().clean_up_disconnected_device();
// TODO: Step 4: Implement representedDevice internal slot for BluetoothDevice.
// TODO: Step 5: Implement the `garbage-collect the connection` algorithm.
match server {
Ok(connected) => {
self.connected.set(connected);
Ok(())
},
Err(error) => {
Err(Error::from(error))
},
}
// Step 4 - 5:
self.Device().garbage_collect_the_connection()
}
#[allow(unrooted_must_root)]
@ -134,6 +125,14 @@ impl AsyncBluetoothListener for BluetoothRemoteGATTServer {
match response {
// https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattserver-connect
BluetoothResponse::GATTServerConnect(connected) => {
// Step 5.2.3
if self.Device().is_represented_device_null() {
if let Err(e) = self.Device().garbage_collect_the_connection() {
return promise.reject_error(promise_cx, Error::from(e));
}
return promise.reject_error(promise_cx, Error::Network);
}
// Step 5.2.4.
self.connected.set(connected);