Skip to content

Commit

Permalink
Support for non-L2CAP fallback. (#35)
Browse files Browse the repository at this point in the history
 Support for non-L2CAP fallback as well as L2CAP on Android.

The holder may get part way through an L2CAP connection and then
fail to open the socket.  When it does this, it can fall back to the
old flow, and this change supports that.
  • Loading branch information
todd-spruceid authored Oct 1, 2024
1 parent 08a4be8 commit 160b07f
Show file tree
Hide file tree
Showing 2 changed files with 39 additions and 3 deletions.
1 change: 1 addition & 0 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ let package = Package(
],
dependencies: [
.package(url: "https://github.com/spruceid/mobile-sdk-rs.git", from: "0.0.32"),
// .package(path: "../mobile-sdk-rs"),
.package(url: "https://github.com/apple/swift-algorithms", from: "1.2.0")
],
targets: [
Expand Down
41 changes: 38 additions & 3 deletions Sources/MobileSdk/MDocReaderBLEPeripheral.swift
Original file line number Diff line number Diff line change
Expand Up @@ -113,18 +113,50 @@ class MDocReaderBLEPeripheral: NSObject {
/// L2CAP flow.
case .l2capRead: // We have a read on our L2CAP characteristic, start L2CAP flow.
machineState = .l2capAwaitChannelPublished
peripheralManager.publishL2CAPChannel(withEncryption: true)

// Setting the encryption flag here appears to break at least some Android devices; if `withEncryption`
// is set, when the Android device calls `.connect()` on the socket it throws a "resource not
// available" exception. The data is already encrypted, so I'm setting it to false, and making Android
// recover from the exception and fall back to the old flow if necessary as well. Support code for
// failover is in the next two states, below.

peripheralManager.publishL2CAPChannel(withEncryption: false)
update = true

case .l2capAwaitChannelPublished:
if machinePendingState == .l2capChannelPublished {
machineState = machinePendingState
} else if machinePendingState == .stateSubscribed {

// Failover case for Android; we could get this here, or in the published state. Android devices
// may be unable to connect() on an L2CAP socket (see the comment in .l2capRead, above), and if
// they do they "switch tracks" to the old flow. We need to notice that's happened and support
// it here.

print("Failover to non-L2CAP flow.")

if let psm = channelPSM {
peripheralManager.unpublishL2CAPChannel(psm)
}
machineState = machinePendingState
update = true
}

case .l2capChannelPublished:
if machinePendingState == .l2capStreamOpen {
machineState = machinePendingState
update = true
} else if machinePendingState == .stateSubscribed {

// See the comments in .l2capRead and .l2capAwaitChannelPublished, above.

print("Failover to non-L2CAP flow.")

if let psm = channelPSM {
peripheralManager.unpublishL2CAPChannel(psm)
}
machineState = machinePendingState
update = true
}

case .l2capStreamOpen: // An L2CAP stream is opened.
Expand Down Expand Up @@ -223,7 +255,7 @@ class MDocReaderBLEPeripheral: NSObject {
// 18013-5 doesn't require .indicate, but without it we don't seem to be able to propagate the PSM
// through to central.
l2capCharacteristic = CBMutableCharacteristic(type: readerL2CAPCharacteristicId,
properties: [.read, .indicate],
properties: [.read, .indicate, .notify],
value: nil,
permissions: [.readable])

Expand Down Expand Up @@ -472,7 +504,10 @@ extension MDocReaderBLEPeripheral: CBPeripheralManagerDelegate {
/// L2CAP Stream delegate functions.
extension MDocReaderBLEPeripheral: MDocReaderBLEPeriConnDelegate {
func streamOpen() {
machinePendingState = .l2capStreamOpen
// This sometimes gets hit multiple times.
if machinePendingState == .l2capChannelPublished {
machinePendingState = .l2capStreamOpen
}
}

func sentData(_ bytes: Int) {
Expand Down

0 comments on commit 160b07f

Please sign in to comment.