Skip to content

Commit

Permalink
Merge pull request loopnlearn#13 from marionbarker/rk-dev-01
Browse files Browse the repository at this point in the history
Incorporate sraka1 Improvements
  • Loading branch information
randallknutson authored Jan 11, 2022
2 parents eeb25f7 + fa2b50e commit bb90c19
Show file tree
Hide file tree
Showing 6 changed files with 232 additions and 74 deletions.
109 changes: 95 additions & 14 deletions OmniBLE/Bluetooth/BluetoothManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,14 @@ class BluetoothManager: NSObject {
}
private let lockedStayConnected: Locked<Bool> = Locked(true)

private var isPermanentlyDisconnecting: Bool = false

weak var delegate: BluetoothManagerDelegate?

private let log = OSLog(category: "BluetoothManager")

private let concurrentReconnectSemaphore = DispatchSemaphore(value: 1)

/// Isolated to `managerQueue`
private var manager: CBCentralManager! = nil

Expand Down Expand Up @@ -125,20 +129,68 @@ class BluetoothManager: NSObject {
}
}

func disconnect() {
// This is a actually `permanentDisconnect` - we do not plan on connecting to this device anymore
func permanentDisconnect() {
dispatchPrecondition(condition: .notOnQueue(managerQueue))

log.debug("permanentDisconnect called")

// TODO: This could also be async?
managerQueue.sync {
if manager.isScanning {
log.debug("permanentDisconnect - running stopScan")
manager.stopScan()
}

if let peripheral = peripheral {
isPermanentlyDisconnecting = true
log.debug("permanentDisconnect - running cancelPeripheralConnection")
manager.cancelPeripheralConnection(peripheral)
}
}
}

func reconnectPeripheral() {
dispatchPrecondition(condition: .notOnQueue(managerQueue))

// Make sure only one reconnect loop is happening concurrently
log.debug("reconnectPeripheral concurrency semaphore check")
concurrentReconnectSemaphore.wait()
log.debug("reconnectPeripheral concurrency semaphore check - is free, continuing")

guard manager.state == .poweredOn else {
log.debug("reconnectPeripheral error - manager.state != .poweredOn")
concurrentReconnectSemaphore.signal()
return
}

let currentState = peripheral?.state ?? .disconnected
guard currentState != .connected else {
if let _ = peripheral {
log.debug("reconnectPeripheral error - peripheral is already connected %@", peripheral!)
}
concurrentReconnectSemaphore.signal()
return
}

// Possible states are disconnected, disconnecting, connected and connecting
// We guard against connected earlier and in case of connecting we only need to wait for the semaphore
if currentState == .disconnected || currentState == .disconnecting {
if let _ = peripheral {
log.debug("reconnectPeripheral running managerQueue_scanForPeripheral for peripheral %@", peripheral!)
}
managerQueue.sync {
log.debug("reconnectPeripheral - in managerQueue.sync")
self.managerQueue_scanForPeripheral()
log.debug("reconnectPeripheral - finished managerQueue.sync")
}
}

// Release reconnect loop for other callers
log.debug("reconnectPeripheral concurrency semaphore signaling")
concurrentReconnectSemaphore.signal()
}

private func managerQueue_scanForPeripheral() {
dispatchPrecondition(condition: .onQueue(managerQueue))

Expand All @@ -151,11 +203,10 @@ class BluetoothManager: NSObject {
return
}

// if let peripheralID = peripheralIdentifier, let peripheral = manager.retrievePeripherals(withIdentifiers: [peripheralID]).first {
// log.debug("Re-connecting to known peripheral %{public}@", peripheral.identifier.uuidString)
// self.peripheral = peripheral
// self.manager.connect(peripheral)
// } else
guard currentState != .connecting else {
return
}

if let peripheral = manager.retrieveConnectedPeripherals(withServices: [
OmnipodServiceUUID.advertisement.cbUUID,
OmnipodServiceUUID.service.cbUUID
Expand All @@ -166,18 +217,27 @@ class BluetoothManager: NSObject {
self.peripheral = peripheral
self.manager.connect(peripheral)
} else {
log.debug("Scanning for peripherals")
manager.scanForPeripherals(withServices: [OmnipodServiceUUID.advertisement.cbUUID],
// TEST: There might be a race condition where PeripheralManager considers the device already connected, although it isn't
// TODO: Investigate more
// Related https://github.com/randallknutson/OmniBLE/pull/10#pullrequestreview-837692407
if let peripheralID = peripheralIdentifier, let peripheral = manager.retrievePeripherals(withIdentifiers: [peripheralID]).first {
log.debug("Re-connecting to known peripheral %{public}@", peripheral.identifier.uuidString)
self.peripheral = peripheral
self.manager.connect(peripheral)
} else {
log.debug("Scanning for peripherals")
manager.scanForPeripherals(withServices: [OmnipodServiceUUID.advertisement.cbUUID],
options: nil
)
)
}
}
}

/**

Persistent connections don't seem to work with the transmitter shutoff: The OS won't re-wake the
app unless it's scanning.

The sleep gives the transmitter time to shut down, but keeps the app running.

*/
Expand Down Expand Up @@ -231,12 +291,14 @@ extension BluetoothManager: CBCentralManagerDelegate {

func centralManager(_ central: CBCentralManager, willRestoreState dict: [String : Any]) {
dispatchPrecondition(condition: .onQueue(managerQueue))
log.info("%{public}@: %{public}@", #function, dict)

if let peripherals = dict[CBCentralManagerRestoredStatePeripheralsKey] as? [CBPeripheral] {
for peripheral in peripherals {
if delegate == nil || delegate!.bluetoothManager(self, shouldConnectPeripheral: peripheral, advertisementData: nil) {
log.default("Restoring peripheral from state: %{public}@", peripheral.identifier.uuidString)
self.peripheral = peripheral
// TODO: Maybe connect to peripheral if its state is disconnected?
}
}
}
Expand All @@ -249,6 +311,7 @@ extension BluetoothManager: CBCentralManagerDelegate {
if delegate == nil || delegate!.bluetoothManager(self, shouldConnectPeripheral: peripheral, advertisementData: advertisementData) {
self.peripheral = peripheral

log.debug("connecting to peripheral %@", peripheral)
central.connect(peripheral, options: nil)
}
}
Expand Down Expand Up @@ -279,6 +342,17 @@ extension BluetoothManager: CBCentralManagerDelegate {
}
}

// Make sure if permanent disconnect is requested, we are actually permanently clearing the peripheral
if isPermanentlyDisconnecting {
log.debug("isPermanentlyDisconnecting is true - nullifying peripheral")
// nullify the peripheral if we don't want it anymore
// TODO: check why stayConnected is never set?
self.stayConnected = false
self.peripheral = nil // this should also nullify peripheralManager and peripheralIdentifier
log.debug("isPermanentlyDisconnecting done - setting isPermanentlyDisconnecting to false")
self.isPermanentlyDisconnecting = false
}

if stayConnected {
scanAfterDelay()
}
Expand All @@ -287,6 +361,8 @@ extension BluetoothManager: CBCentralManagerDelegate {
func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) {
dispatchPrecondition(condition: .onQueue(managerQueue))

peripheralManager?.centralManager(central, didFailToConnect: peripheral, error: error)

log.error("%{public}@: %{public}@", #function, String(describing: error))
if let error = error, let peripheralManager = peripheralManager {
self.delegate?.bluetoothManager(self, peripheralManager: peripheralManager, isReadyWithError: error)
Expand All @@ -301,7 +377,7 @@ extension BluetoothManager: CBCentralManagerDelegate {

extension BluetoothManager: PeripheralManagerDelegate {
func peripheralManager(_ manager: PeripheralManager, didReadRSSI RSSI: NSNumber, error: Error?) {

}

func peripheralManagerDidUpdateName(_ manager: PeripheralManager) {
Expand All @@ -312,10 +388,15 @@ extension BluetoothManager: PeripheralManagerDelegate {
self.delegate?.bluetoothManager(self, didCompleteConfiguration: manager)
}

// throws?
func reconnectLatestPeripheral() {
reconnectPeripheral()
}

func peripheralManager(_ manager: PeripheralManager, didUpdateNotificationStateFor characteristic: CBCharacteristic) {

}

func peripheralManager(_ manager: PeripheralManager, didUpdateValueFor characteristic: CBCharacteristic) {

}
Expand Down
12 changes: 6 additions & 6 deletions OmniBLE/Bluetooth/Pair/LTKExchanger.swift
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,10 @@ class LTKExchanger {

log.debug("Reading sps1")
let podSps1 = try manager.readMessage(true)
guard let podSps1 = podSps1 else {
guard let _ = podSps1 else {
throw BluetoothErrors.PairingException("Could not read SPS1")
}
try processSps1FromPod(podSps1)
try processSps1FromPod(podSps1!)
// now we have all the data to generate: confPod, confPdm, ltk and noncePrefix

log.debug("Sending sps2")
Expand All @@ -74,10 +74,10 @@ class LTKExchanger {
try throwOnSendError(sps2.message, LTKExchanger.SPS2)

let podSps2 = try manager.readMessage()
guard let podSps2 = podSps2 else {
guard let _ = podSps2 else {
throw BluetoothErrors.PairingException("Could not read SPS2")
}
try validatePodSps2(podSps2)
try validatePodSps2(podSps2!)
// No exception throwing after this point. It is possible that the pod saved the LTK

seq += 1
Expand All @@ -95,10 +95,10 @@ class LTKExchanger {
}

let p0 = try manager.readMessage()
guard let p0 = p0 else {
guard let _ = p0 else {
throw BluetoothErrors.PairingException("Could not read P0")
}
try validateP0(p0)
try validateP0(p0!)

guard keyExchange.ltk.count == 16 else {
throw BluetoothErrors.InvalidLTKKey("Invalid Key, got \(String(data: keyExchange.ltk, encoding: .utf8) ?? "")")
Expand Down
4 changes: 2 additions & 2 deletions OmniBLE/Bluetooth/PeripheralManager+Omnipod.swift
Original file line number Diff line number Diff line change
Expand Up @@ -89,11 +89,11 @@ extension PeripheralManager {
var expected: UInt8 = 0
let firstPacket = try readData(sequence: expected, timeout: 5)

guard let firstPacket = firstPacket else {
guard let _ = firstPacket else {
return nil
}

let joiner = try PayloadJoiner(firstPacket: firstPacket)
let joiner = try PayloadJoiner(firstPacket: firstPacket!)

for _ in 1...joiner.fullFragments {
expected += 1
Expand Down
Loading

0 comments on commit bb90c19

Please sign in to comment.