BLE iOS - Failed to encrypt the connection, the connection has timed out unexpectedly - ionic-framework

I'm using #capacitor-community/bluetooth-le to connect my Ionic 6 APP to a specific BLE device.
In Android everything works fine.
In iOS, first time connects to BLE successfully but next times (after pairing) gives the following error when try to connects: "Failed to encrypt the connection, the connection has timed out unexpectedly."
I have been tried a lot of different approachs: connects after scan. Connects when is scanning. But nothing works. What is strange is in first time everything works fine (after pairing).
Any help?

Unfortunately this is kind of a known issue in iOS. What I think is happening is that because you have already paired/bonded with the device at the OS level, the connection is being re-established at the OS level when the remote BLE device is discoverable. When you are attempting to reconnect to the remote device from your app, a connection is already in place at the OS level which is why it is failing at the app level. Alternatively, maybe the stored keys from the bonding process is causing the rebonding/pairing process to clash. You can confirm the issue by doing the following:-
Go to the iOS settings, Bluetooth, then unpair the device if it exists.
Attempt to reconnect to the device from your iOS app.
Disconnect the device from your iOS app (do not unpair from the Bluetooth settings this time).
Try to connect to the remote device from your Android app (or any other device that you can use apart from the iOS device). If the connection doesn't succeed, it means that the iOS device is still connected to the remote device.
If the connection succeeds from the Android device, try to disconnect and reconnect from the iOS device. If the connection succeeds, you'll know that the issue is with the OS level connection, and if the connection doesn't succeed, you'll know that the issue is with the stored bonding/pairing keys clashing.
As for the solution, I don't think there's a simple and straight-forward one unfortunately. Below is one suggested solution which I found useful in the past (you may need to modify this for your ionic app):-
In some cases, like for HID devices, once a peripheral is bonded, iOS
will automatically connect to it whenever it sees the peripheral
advertising. This behavior occurs independently of any app, and a
peripheral can be connected to an iOS device, but not connected to the
app that originally established the bond. If a bonded peripheral
disconnects from an iOS device and then reconnects at the iOS level,
the app will need to retrieve the peripheral object
(retrieveConnectedPeripherals(with[Services/Identifiers]:) and
explicitly connect again through a CBCentralManager to establish an
app-level connection. To retrieve your device with this method, you
must specify either the Apple-assigned device identifier from the
previously-returned CBPeripheral object or at least one service it
contains.
iOS does not allow developer apps to clear a peripheral’s bonding
status from the cache. To clear a bond, the user must go to the
Bluetooth section of iOS Settings and explicitly “Forget” the
peripheral. It may be helpful to include this information in your
app’s UI if it’ll affect user experience, as most users will not know
this.
You can find more information about this in the links below:-
The ultimate guide to Apple's CoreBluetooth
CoreBluetooth iOS pairing issue
CoreBluetooth pairing/forgetting
Unable to reconnect after cancelling BLE peripheral

In my case after iPhone pairs with peripheral, never connects anymore. What is strange, in my iPhone 6s with iOS 15.5 everythings works fine.
The pugin has this code (it's possible to some is wrong?)
https://github.com/capacitor-community/bluetooth-le/blob/main/ios/Plugin/Plugin.swift
let CONNECTION_TIMEOUT: Double = 10
let DEFAULT_TIMEOUT: Double = 5
#objc func connect(_ call: CAPPluginCall) {
guard self.getDeviceManager(call) != nil else { return }
guard let device = self.getDevice(call, checkConnection: false) else { return }
let timeout = self.getTimeout(call, defaultTimeout: CONNECTION_TIMEOUT)
device.setOnConnected(timeout, {(success, message) -> Void in
if success {
// only resolve after service discovery
call.resolve()
} else {
call.reject(message)
}
})
self.deviceManager?.setOnDisconnected(device, {(_, _) -> Void in
let key = "disconnected|\(device.getId())"
self.notifyListeners(key, data: nil)
})
self.deviceManager?.connect(device, timeout, {(success, message) -> Void in
if success {
log("Connected to peripheral. Waiting for service discovery.")
} else {
call.reject(message)
}
})
}
I already tried another plugins, and the result is the same.

Related

How to connect to Ble device without rescan and manual device selection

I'm creating an Ionic react (TypeScript) app which uses the Community Bluetooth-le plugin.
When I try to connect to a device using requestDevice this shows the available devices and I can then pair/connect with that device and all is good.
await BleClient.initialize();
if (isAndroid) {
await BleClient.disconnect(devices.deviceId);
}
const device = await BleClient.requestDevice({
services: services ? services : [],
optionalServices: optionalServices ? optionalServices : [],
namePrefix: prefixFilter ? prefixFilter : "",
});
await BleClient.connect(device.deviceId, (deviceId) => onDisconnect(deviceId));
await BleClient.getServices(device.deviceId).then(
(services) => {
if (services[0]) {
//....
} else {
//....
}
}
)
However, if I save the device ID and then try to directly connect with that device using getDevices (rather than scanning and manually connecting) it always fails with the following console output:
Uncaught (in promise) Error: Device not found. Call "requestDevice", "requestLEScan" or "getDevices" first.
The code I use is this:
await BleClient.initialize();
if (isAndroid) {
await BleClient.disconnect(devices.deviceId);
}
await BleClient.getDevices(devices.deviceId);
await BleClient.connect(devices.deviceId, (deviceId) => onDisconnect(deviceId));
For clarification: I want to be able to search for available devices and connect to the device the first time the app is opened. Then, save the device ID and use this ID to connect to the device directly using getDevices from that point onwards. Likewise if the app is closed and re-opened I need to be able to take the stored device data and connect with that device directly without the whole scan and manual selection process.
I don't understand what I'm missing.
I assume Device-1 (app) is scanning and Device-2 is advertising. Try to bond the devices after first time connection. This allow you to connect it automatically without scanning next time.
Make sure the Device-2 is in connectable mode after getting disconnected from Device-1.
EDIT-1
For example I am using a generic app called nRF connect and a smart watch. In this app we can scan and connect with any BLE device. I am following below steps:
Scan
Connect
Bond (option available under 3 dots at top right corner, refer image)
After third step, device get bonded and later whenever the device is in vicinity you can connect with it, without advertising and scanning. PFA image.
This is the overview of process to be followed, in regards to code you can get many ready examples related to android or iOS. Hope this is helpful!!

How to disconnect from a joinOnce NEHotspotConfiguration?

I'm able to successfully connect to our IOT device's hotspot using NEHotspotConfiguration, however we need to be able to disconnect from the hotspot when they cancel the setup process.
Calling NEHotspotConfigurationManager.shared.removeConfiguration() only removes the config, but if the phone is currently connected to the hotspot this call has no effect and the device remains connected.
How can I disconnect from this hotspot? Seems like it should be simple!
NEHotspotConfigurationManager.shared.getConfiguredSSIDs { configuration in
print("configuration : \(configuration)")
NEHotspotConfigurationManager.shared.removeConfiguration(forSSID: configuration[0])
NEHotspotConfigurationManager.shared.removeConfiguration(forHS20DomainName: configuration[0])
}
I'm seeing the same behavior in iOS 15.2 when connecting to my IoT device: invoking removeConfiguration(forSSID:) does not disconnect an hotspot previously connected with joinOnce = true.
This is contrary to Apple's own documentation on joinOnce, which says:
To disconnect the device from a hotspot configured with joinOnce set
to true, call removeConfiguration(forSSID:)
So, it seems to be a bug in either: a) iOS 15.2, or b) Apple's documentation. I created a bug report here: https://feedbackassistant.apple.com/feedback/9954367, and I will reply when I hear back from them.
I also see this issue was discussed for earlier iOS versions (12.2, 12.3.1) in this developer forum thread without any resolution.

Core Bluetooth: Cannot get iPhone (the central) and Mac (the peripheral) to pair

I am developing an iPhone application and a Mac application that communicate with each other via Bluetooth LE. The iPhone is the central, and the Mac is the peripheral. I would like the iPhone application to be able to reconnect to the Mac application after a relaunch. Therefore, I save the UUID of the peripheral representing the Mac in NSUserDefaults, and then when the iPhone app launches, I call -[CBCentralManager retrievePeripheralsWithIdentifiers:], passing in the UUID. Although this method returns the peripheral, when I call -[CBCentralManager connectPeripheral:options:], the iPhone never reconnects to the Mac. According to this SO post, the problem is that the two devices need to be paired so that the UUID of the Mac is persistent. This brings me to my problem. I cannot for the life of me get the iPhone and the Mac to pair. According to this page of the Core Bluetooth Programming Guide, the way to require a paired connection is for the peripheral to set the characteristic's properties and permissions to the appropriate values. I quote:
You can ensure that only trusted devices have access to sensitive characteristic values by setting the appropriate characteristic properties and permissions. To continue the example above, to allow only trusted devices to retrieve a member’s email address, set the appropriate characteristic’s properties and permissions, like this:
emailCharacteristic = [[CBMutableCharacteristic alloc]
initWithType:emailCharacteristicUUID
properties:CBCharacteristicPropertyRead
| CBCharacteristicPropertyNotifyEncryptionRequired
value:nil permissions:CBAttributePermissionsReadEncryptionRequired];
However, this does not work. Even when I set the correct permissions and properties for the characteristic, the iPhone can still read/write to it without a pairing dialog being displayed. Elsewhere I am told that the way to initiate pairing is for the peripheral to reject a read/write request with an insufficient authentication error. According to this post,
To pair, you need to respond to a write request with an insufficient authentication error. For example, for an iOS peripheral you would write something like:
- (void)peripheralManager:(CBPeripheralManager *)peripheralManager didReceiveWriteRequests:(NSArray *)requests {
...
[peripheralManager respondToRequest:request withResult:CBATTErrorInsufficientAuthentication];
...
}
But this still doesn't work! When the Mac rejects the write request with an insufficient authentication error, the iPhone receives a -[CBPeripheralDelegate peripheral:didWriteValueForCharacteristic:error:] callback with an error that says, "Authentication is insufficient." No pairing dialog. I have no idea what to do and am considering dropping Core Bluetooth altogether if I cannot get this to work. If you have any idea how to get this to work, please let me know.
From Apple DTS:
"I have checked with other engineers here and we don’t believe that the pairing popup is possible between two iOS devices, but an iOS device and a BLE peripheral."
So, pairing popup happens only between a iOS/Mac app and a peripheral. rdar time.

Apple Push Notifications - When Registering for Notifications no Callbacks are received at all

I've gone through all the steps as specified, am using a dev certificate with APN enabled and am calling registerForRemoteNotificationTypes: with the right flags in my app delegate.
Still, neither application:didRegisterForRemoteNotificationsWithDeviceToken: nor application:didFailToRegisterForRemoteNotificationsWithError: are being called. It looks like the registration request just gets stuck in transit.
What am I doing wrong?
This sounds silly, but it's bit me in the past: double-check your capitalization and parameter types. If a delegate method declaration isn't spot on your method won't get called, and you won't get any kind of warning.
Your best bet is to copy and paste the prototype from Apple's documentation.
Try your App on another device. If it works there...
I have the same Problem with one iPhone 3G (no jailbreak, no unlock). It somply dos not work with that phone. I tried updating to a newer iPhone OS. I tried a factory reset. It does show the confirmationscreen and it records the answer in the Settings. But neither application:didFailToRegisterForRemoteNotificationsWithError nor application:didRegisterForRemoteNotificationsWithDeviceToken are ever called.
The same App works on all other tested devices (ipods, iphones). This iPhone 3G does also not receive PNs for other apps.
PNs also did not work for one Jailbroken Classic iPhone.
It might be that your device is simply not behaving correctly. Try a different device.
I had this problem too and it was because of bad certificates, app id, provision profile...
Make sure that:
1, Correct certificates are installed on client AND server
2, Make sure that you have selected correct provision profile in your project settings. REMEMBER App ID in your provision profile has to be specific(not ending with *) and last part of this App ID has to match name of your application exactly and it is case sensitive. so iv your app is called myApp then your app id has to be SOMENUMBERScom.yourcompany.myApp
Just an idea...
I had the same problem and solved it by disconnecting from the cellular network. Seems to be some problem when both wireless and 3G are on concurrently.
I found out that PUSH notifications require port 5223 open in your network (if you are using WIFI), or otherwise, a cellular data connection.
I encountered the same problem in my home network, and had to open the port manually in the wireless router.

Unexpected error while trying to connect to iPhone

Your mobile device has encountered an unexpected error(0xE800002E)
Try disconnecting and powering off the device;then power the device on and reconnect it.
I received these message so many times. How can I fix it?
Although you can see this error when you connect in XCode, it is not actually an Xcode defect.
See this thread at Apple's discussion boards. You basically need to reset you phone. I'm not sure but Ithink there may be a link between seeing this error and using your phone for development but I have no concrete evidence.
I find that if I plug my device in and its set to automatically run iPhoto - then you must let iPhoto load and let it try to sync photos before you quit it. Then you should be able to use XCode to load an application on to your phone.
If you do get the error - unplug the device, turn it off and then back on, and that usually clears up the problem