How iOS and Android Handle Connections with BLE Human Interface Devices
iOS and Android’s Bluetooth APIs allow apps to communicate with BLE devices. The API simplifies the way apps implement BLE and allows multiple apps to use BLE at the same time, but it also introduces many undocumented behaviors. Unfortunately, the only way to understand these behaviors is through experimentation. The behavior we’ll be focusing on today is how iOS and Android handle reconnecting to disconnected human interface devices.
The scenarios tested involved bonded devices and Human Interface Devices (HID). BLE bonding is a secure exchange of long-term encryption keys that allow two BLE devices to identify each other and encrypt their communication. On both mobile OSes, bonding is handled exclusively through the OS. Apps cannot delete bonds, and on iOS, apps have no way to ask the OS to initiate a bond or know if a device is bonded. Only the connected device can initiate bonding — this is typically done by having the app attempt to read an encrypted characteristic to trigger an insufficient authentication scenario.
Human Interface Devices
Human interface devices consist of devices that assist people in interacting with computers. Keyboards, mice, and game controllers are all examples of HIDs. HIDs are differentiated from other BLE devices by the presence of the HID GATT profile. iOS does not allow third-party apps to see or access HID GATT profiles. Upon their discovery, iOS automatically connects the HID profile to the OS to be used as an interface device. Other GATT profiles are accessible to the application like normal. Android also automatically connects the HID profile to the OS, but unlike on iOS, Android’s third-party apps are allowed to interact with the HID profile.
Bluetooth Connection Management
Software stacks for BLE consider a device disconnected if it’s not heard from for longer than the supervision timeout. After that time, iOS and Android both trigger the device disconnection callbacks in their native Bluetooth APIs. This enables app(s) using the device to take steps to handle the disconnection. Once a device disconnects, the app always needs to request a new connection through the Bluetooth API.
While the app needs to explicitly request a new connection before the app can use the device again, there are a couple of scenarios where iOS or Android will automatically form a new BLE connection when the device is next seen.
iOS will always automatically reconnect to a known HID device until the user manually forgets the device through the iOS settings menu.
Calling CBCentralManager’s cancelPeripheralConnection(_:) method to explicitly disconnect from an HID peripheral will not end the actual connection. This can be a source of confusion because the device might not advertise while connected, and thus won’t appear in scan results.
The only way to break the connection without forgetting the device is to turn off Bluetooth or turn off the HID device. Even then, the connection will be recreated automatically the next time both devices are on and in range of each other.
Android, on the other hand, exhibits much simpler behavior. The Android device will automatically reconnect to any device if the Android Settings app initiates the connection instead of a third-party app. Third-party apps will still need to request a connection to the automatically connected device before they’re able to interact with it.
On both iOS and Android, the reconnection behavior does not differ between bonded and unbonded devices.
How to Handle Disconnections
While the iOS and Android BLE stacks do have some undocumented auto-reconnection quirks at the OS level, your app must still follow the normal procedure to connect to a peripheral. These cases only reconnect the physical BLE connection; they don’t inform your app that the reconnection occurred. Your app should always request a new connection to a device after an unintended disconnection.