servo: Merge #12260 - Timeout for GATTServer's connect, disconnect methods (from szeged:gattserverfunctions); r=jdm
authorzakorgy <zakorgy@inf.u-szeged.hu>
Tue, 05 Jul 2016 09:02:25 -0700
changeset 339212 04ad679bc57ca697b4cb05e5e571cf4c455c2503
parent 339211 08f7a9ed15e4028cd78c612507846c00b3776cc7
child 339213 4a254bb5384a6edf018572cae048d1789e17c258
push id31307
push usergszorc@mozilla.com
push dateSat, 04 Feb 2017 00:59:06 +0000
treeherdermozilla-central@94079d43835f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjdm
servo: Merge #12260 - Timeout for GATTServer's connect, disconnect methods (from szeged:gattserverfunctions); r=jdm <!-- Please describe your changes on the following line: --> Added 30 seconds maximum timeout as specified in the bluetooth 4.2 https://www.bluetooth.org/DocMan/handlers/DownloadDoc.ashx?doc_id=286439 (Vol. 3, page 480). With this the existing `bluetooth_device_disconnect.html` test will work correctly. It will not show the `connected:true` message, before the connection established, and will not show the `connected: false` message, before not disconnected from the GATT server. --- <!-- 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] These changes do not require tests because, there are no Web Bluetooth test API implementation yet. <!-- 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: 061cf054e7fe5019ef43c6fc13e49791fc25d99c
servo/components/net/bluetooth_thread.rs
--- a/servo/components/net/bluetooth_thread.rs
+++ b/servo/components/net/bluetooth_thread.rs
@@ -28,16 +28,21 @@ const ADAPTER_ERROR: &'static str = "No 
 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 INCLUDED_SERVICE_ERROR: &'static str = "No included 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";
 const SECURITY_ERROR: &'static str = "The operation is insecure";
+const NETWORK_ERROR: &'static str = "A network error occurred";
+// A transaction not completed within 30 seconds shall time out. Such a transaction shall be considered to have failed.
+// https://www.bluetooth.org/DocMan/handlers/DownloadDoc.ashx?doc_id=286439 (Vol. 3, page 480)
+const MAXIMUM_TRANSACTION_TIME: u8 = 30;
+const CONNECTION_TIMEOUT_MS: u64 = 1000;
 // The discovery session needs some time to find any nearby devices
 const DISCOVERY_TIMEOUT_MS: u64 = 1500;
 #[cfg(target_os = "linux")]
 const DIALOG_TITLE: &'static str = "Choose a device";
 #[cfg(target_os = "linux")]
 const DIALOG_COLUMN_ID: &'static str = "Id";
 #[cfg(target_os = "linux")]
 const DIALOG_COLUMN_NAME: &'static str = "Name";
@@ -467,45 +472,53 @@ impl BluetoothManager {
             }
         }
         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 = get_adapter_or_return_error!(self, sender);
 
-        let connected = match self.get_device(&mut adapter, &device_id) {
+        match self.get_device(&mut adapter, &device_id) {
             Some(d) => {
                 if d.is_connected().unwrap_or(false) {
-                    true
-                } else {
-                    d.connect().is_ok()
+                    return drop(sender.send(Ok(true)));
                 }
+                let _ = d.connect();
+                for _ in 0..MAXIMUM_TRANSACTION_TIME {
+                    match d.is_connected().unwrap_or(false) {
+                        true => return drop(sender.send(Ok(true))),
+                        false => thread::sleep(Duration::from_millis(CONNECTION_TIMEOUT_MS)),
+                    }
+                }
+                return drop(sender.send(Err(String::from(NETWORK_ERROR))));
             },
             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 = get_adapter_or_return_error!(self, sender);
 
-        let connected = match self.get_device(&mut adapter, &device_id) {
+        match self.get_device(&mut adapter, &device_id) {
             Some(d) => {
-                if d.is_connected().unwrap_or(false) {
-                    d.disconnect().is_ok()
-                } else {
-                    false
+                if !d.is_connected().unwrap_or(true) {
+                    return drop(sender.send(Ok(false)));
                 }
+                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 drop(sender.send(Ok(false))),
+                    }
+                }
+                return drop(sender.send(Err(String::from(NETWORK_ERROR))));
             },
             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 = get_adapter_or_return_error!(self, sender);
         if !self.allowed_services.get(&device_id).map_or(false, |s| s.contains(&uuid)) {