Skip to content

Commit

Permalink
fix(): do not connect after connection timeout (#80)
Browse files Browse the repository at this point in the history
(cherry picked from commit e969e5c085982852a0e4b48ad8a7df358627d25b)
  • Loading branch information
pwespi committed Mar 20, 2021
1 parent b290a06 commit 5d5cb42
Show file tree
Hide file tree
Showing 5 changed files with 65 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ class Device(
private const val STATE_CONNECTED = 2
private const val CLIENT_CHARACTERISTIC_CONFIG = "00002902-0000-1000-8000-00805f9b34fb"
private const val DEFAULT_TIMEOUT: Long = 5000
private const val CONNECT_TIMEOUT: Long = 7500
private const val CONNECTION_TIMEOUT: Long = 10000
private const val REQUEST_MTU = 512
}

Expand Down Expand Up @@ -159,7 +159,7 @@ class Device(
callbackMap["connect"] = callback
bluetoothGatt = device.connectGatt(context, false, gattCallback)
connectionState = STATE_CONNECTING
setTimeout("connect", "Connection timeout.", CONNECT_TIMEOUT)
setConnectionTimeout("connect", "Connection timeout.", bluetoothGatt)
}

fun isConnected(): Boolean {
Expand Down Expand Up @@ -294,4 +294,13 @@ class Device(
reject(key, message)
}, timeout)
}

private fun setConnectionTimeout(key: String, message: String, gatt: BluetoothGatt?, timeout: Long = CONNECTION_TIMEOUT) {
val handler = Handler()
timeoutMap[key] = handler
handler.postDelayed({
gatt?.disconnect()
reject(key, message)
}, timeout)
}
}
4 changes: 2 additions & 2 deletions ios/Plugin/Device.swift
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ class Device: NSObject, CBPeripheralDelegate {
func setOnConnected(_ callback: @escaping Callback) {
let key = "connect"
self.callbackMap[key] = callback
self.setTimeout(key, "Connection timeout", 7.5)
self.setTimeout(key, "Connection timeout", connectionTimeout)
}

func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
Expand Down Expand Up @@ -200,7 +200,7 @@ class Device: NSObject, CBPeripheralDelegate {
}
}

private func setTimeout(_ key: String, _ message: String, _ timeout: Double = 5) {
private func setTimeout(_ key: String, _ message: String, _ timeout: Double = defaultTimeout) {
let workItem = DispatchWorkItem {
self.reject(key, message)
}
Expand Down
19 changes: 17 additions & 2 deletions ios/Plugin/DeviceManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ import Foundation
import Capacitor
import CoreBluetooth

let defaultTimeout: Double = 5
let connectionTimeout: Double = 10

class DeviceManager: NSObject, CBCentralManagerDelegate {
typealias Callback = (_ success: Bool, _ message: String) -> Void
typealias StateReceiver = (_ enabled: Bool) -> Void
Expand Down Expand Up @@ -182,7 +185,7 @@ class DeviceManager: NSObject, CBCentralManagerDelegate {
self.callbackMap[key] = callback
print("Connecting to peripheral", device.getPeripheral())
self.centralManager.connect(device.getPeripheral(), options: nil)
self.setTimeout(key, "Connection timeout.", 7.5)
self.setConnectionTimeout(key, "Connection timeout.", device)
}

// didConnect
Expand Down Expand Up @@ -268,11 +271,23 @@ class DeviceManager: NSObject, CBCentralManagerDelegate {
}
}

private func setTimeout(_ key: String, _ message: String, _ timeout: Double = 5) {
private func setTimeout(_ key: String, _ message: String, _ timeout: Double = defaultTimeout) {
let workItem = DispatchWorkItem {
self.reject(key, message)
}
self.timeoutMap[key] = workItem
DispatchQueue.main.asyncAfter(deadline: .now() + timeout, execute: workItem)
}

private func setConnectionTimeout(_ key: String, _ message: String, _ device: Device, _ timeout: Double = connectionTimeout) {
let workItem = DispatchWorkItem {
// do not call onDisconnnected, which is triggered by cancelPeripheralConnection
let key = "onDisconnected|\(device.getId())"
self.callbackMap[key] = nil
self.centralManager.cancelPeripheralConnection(device.getPeripheral())
self.reject(key, message)
}
self.timeoutMap[key] = workItem
DispatchQueue.main.asyncAfter(deadline: .now() + timeout, execute: workItem)
}
}
13 changes: 13 additions & 0 deletions src/timeout.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
export async function runWithTimeout(
promise: Promise<unknown>,
time: number,
exception: symbol,
): Promise<unknown> {
let timer: NodeJS.Timeout;
return Promise.race([
promise,
new Promise((_, reject) => {
timer = setTimeout(() => reject(exception), time);
}),
]).finally(() => clearTimeout(timer));
}
23 changes: 22 additions & 1 deletion src/web.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,14 @@ import type {
ScanResultInternal,
WriteOptions,
} from './definitions';
import { runWithTimeout } from './timeout';

export class BluetoothLeWeb extends WebPlugin implements BluetoothLePlugin {
private deviceMap = new Map<string, BluetoothDevice>();
private discoverdDevices = new Map<string, boolean>();
private scan: BluetoothLEScan | null = null;
private requestBleDeviceOptions: RequestBleDeviceOptions | undefined;
private CONNECTION_TIMEOUT = 10000;

constructor() {
super({
Expand Down Expand Up @@ -115,7 +117,26 @@ export class BluetoothLeWeb extends WebPlugin implements BluetoothLePlugin {
const device = await this.getDevice(options.deviceId);
device.removeEventListener('gattserverdisconnected', this.onDisconnected);
device.addEventListener('gattserverdisconnected', this.onDisconnected);
await device.gatt?.connect();
const timeoutError = Symbol();
if (device.gatt === undefined) {
throw new Error('No gatt server available.');
}
try {
await runWithTimeout(
device.gatt.connect(),
this.CONNECTION_TIMEOUT,
timeoutError,
);
} catch (error) {
// cancel pending connect call, does not work yet in chromium because of a bug:
// https://bugs.chromium.org/p/chromium/issues/detail?id=684073
await device.gatt?.disconnect();
if (error === timeoutError) {
throw new Error('Connection timeout');
} else {
throw error;
}
}
}

private onDisconnected(event: Event) {
Expand Down

0 comments on commit 5d5cb42

Please sign in to comment.