跪拜 Guibai
← All articles
Android

Two BLE Provisioning Protocols for Android IoT Devices, Side by Side

By Android小渣渣 ·
Read original on juejin.cn ↗ Google Translate ↗ Alt translation

Most IoT SDKs hide these protocol details behind a library. Seeing the raw byte structures and state machines makes it easier to debug provisioning failures, support devices from different vendors in a single app, or build a custom provisioning flow when the vendor SDK is too heavy or unavailable.

Summary

IoT device provisioning on Android often means juggling multiple proprietary BLE protocols. This implementation walks through two of them: CoolKit uses an AES-encrypted handshake over GATT characteristics `bbb0`/`bbb1` with a phased command set for WiFi scanning, connection, and cloud registration. TanGe and similar IoT devices take a simpler route — a fixed 78-byte WiFi configuration packet written to characteristic `9999`, with single-byte ACKs or JSON responses coming back on `8888`.

Both flows share the same skeleton: scan for devices by name prefix, connect over BLE, deliver SSID and password, then poll a cloud endpoint for binding confirmation. The differences live in the wire format and the handshake. CoolKit demands a secret-key exchange before any WiFi commands move; TanGe just expects the 78-byte payload with a frame header and footer.

Timeout and retry logic is baked in at multiple levels — a 180-second total timeout, a separate 3-minute watchdog for TanGe devices, up to three automatic reconnection attempts, and a 5-second WiFi scan polling loop. Resource cleanup funnels through a single `resetData()` call that tears down scans, closes the GATT connection, and cancels all RxJava disposables.

Takeaways
CoolKit devices require an AES-encrypted handshake using a secret key fetched over HTTP before any WiFi commands are accepted.
TanGe/IoT devices use a simpler 78-byte fixed-length packet — frame header, 'wifisetup' string, SSID, password, frame tail — written to a single GATT characteristic.
Both protocols distinguish device types by BLE broadcast name prefix and use `BluetoothLeScanner` for discovery.
WiFi scan polling runs every 5 seconds until the device reports scan completion via a `0x06` command.
A 180-second total timeout and a separate 3-minute TanGe-specific timeout guard against stuck provisioning sessions.
All cleanup — stopping scans, closing GATT, canceling RxJava disposables — is centralized in one `resetData()` method.
Cloud binding follows the same pattern for both: call `preAdd` to get an `addToken`, send it to the device over BLE, then poll `pollAddResult` until the cloud returns a `device_id`.
Conclusions

Embedding two completely different BLE protocols in one app is common in Chinese IoT ecosystems where a single OEM ships devices built on different module vendors' stacks.

The CoolKit protocol's AES handshake adds a meaningful security layer — without the secret key fetched from the cloud, an attacker can't inject WiFi credentials even if they connect to the device over BLE.

TanGe's 78-byte fixed packet is easier to implement but sends the WiFi password in what appears to be plaintext (UTF-8 padded with null bytes), which is a weaker security posture if the BLE link itself isn't encrypted.

The static state machine approach in `BleScanUtils` keeps connection lifecycle manageable but can become brittle when adding a third or fourth protocol — each new device type risks bloating the state transitions.

Concepts & terms
BLE Provisioning
The process of sending WiFi credentials to a headless IoT device over Bluetooth Low Energy so it can connect to a network and register with a cloud service.
GATT Characteristic
A data channel in BLE's Generic Attribute Profile, identified by a UUID, that devices use to read, write, or receive notifications of data. Different characteristics serve different roles — one for commands, another for status reports.
AES/CBC/PKCS7Padding
A block cipher mode used in the CoolKit handshake. AES encrypts data in fixed-size blocks; CBC chains each block to the previous one; PKCS7 padding fills the last block to the required length.
addToken
A one-time session token generated by the IoT cloud when the app calls `preAdd`. The token is sent to the device over BLE, and the device presents it when registering with the cloud, linking the physical device to the user's account.
Source: juejin.cn ↗ Google Translate ↗ Backup ↗