On this page
Android: Connect via Bluetooth
Run @onekeyfe/hd-common-connect-sdk in a WebView and bridge its low-level transport to native BLE. Start with the native Android example and the Low-Level Transport Plugin contract.
The native layer owns permissions, bonding, GATT connection, characteristic I/O and cleanup. The SDK owns protocol detection, framing and signing. Do not add a second V1/V2 decoder in the native bridge.
Permissions
For an app targeting Android 12 or newer, request BLUETOOTH_SCAN and BLUETOOTH_CONNECT at runtime. BLUETOOTH_ADVERTISE is unnecessary for a client that only scans and connects.
Use neverForLocation only if the app does not derive location from BLE scanning. Older Android versions need the applicable legacy Bluetooth and location permissions. Do not request Android 12-only permissions unconditionally on older devices. Follow the Android Bluetooth permission guide for your target SDK and location use.
Do not start scanning while permission is pending. Return a permission failure to the JS caller if the user declines, and allow a later user-initiated retry.
Build and load the JavaScript bundle
From the hardware-js-sdk checkout:
cd packages/connect-examples/native-android-example/web
yarn
yarn buildCopy web_dist/ to app/src/main/assets/web_dist/. Register the native bridge handlers before loading file:///android_asset/web_dist/index.html. Keep asset paths intact and load only your app’s trusted bundle into a WebView with access to the device bridge.
The demo supplies its dependencies and bridge implementation. Use its package/Gradle configuration as the starting point instead of combining snippets from different Nordic BLE library versions.
Complete the BLE connection before resolving
A fixed delay does not establish that bonding, GATT connection, discovery or notification subscription succeeded. Follow callbacks or the BLE library’s suspending completion APIs:
- Complete required bonding and report user rejection or timeout.
- Wait for the GATT connection to succeed.
- Discover the OneKey service and both required characteristics.
- Enable notifications and wait for descriptor/subscription completion.
- Resolve the bridge’s
connectcall only after the connection can send and receive.
Android exposes operation results through BluetoothGattCallback. A successful call to start an operation is not its completion. Bound each pending operation with a timeout; on failure close the connection and reject its pending bridge calls.
| Purpose | UUID |
|---|---|
| Service | 00000001-0000-1000-8000-00805f9b34fb |
| Write | 00000002-0000-1000-8000-00805f9b34fb |
| Notify | 00000003-0000-1000-8000-00805f9b34fb |
Bridge contract
| Method | Completion condition |
|---|---|
enumerate() | Return a deduplicated snapshot of candidates; stop the scan and complete the request once. |
connect(uuid) | Connection, characteristics and notifications are ready. |
send(uuid, data, options) | Validate hex, write bytes in order, honor withoutResponse, and propagate write failures. |
receive(uuid) | Return the next queued notification chunk as hex, including chunks received before a reader started waiting. |
disconnect(uuid) | Cancel subscriptions, close the selected connection, reject pending reads and discard old notification data. |
Keep queues and callbacks per connection. Reject a missing characteristic rather than treating a nullable write as a success. Send WebView bridge callbacks on the appropriate UI thread.
Expose the actual BLE write capacity through getProtocolV2PacketCapacity when implementing Protocol V2. Do not return the ATT MTU including its overhead. See the full plugin interface.
SDK and UI lifecycle
The bundle initializes env: 'lowlevel' with your plugin. Check the initialization result, discover a connection, then call getDeviceState(connectId) for the seed-lifetime deviceId.
Register UI event handling before protected calls. Reply only to input requests and copy their response correlation. REQUEST_BUTTON and device-only PIN prompts are notifications; a native “OK” button cannot confirm the operation on the hardware.
Pass useEmptyPassphrase: true for the standard wallet or the selected hidden wallet’s passphraseState on address and signing calls. Serialize calls per device.
Verify the integration
Test permission denial, bonding cancellation, discovery failure, notification failure, locked-device input, unplug/disconnect and reconnect on a physical device. Confirm an address on the device and inspect the SDK result. Finding a BLE advertisement alone is not an end-to-end pass.
Do not automatically replay signing or firmware updates after a timeout. See Disconnects and reconnection.