Skip to Content
HardwareTransportAndroid (Native)

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 build

Copy 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:

  1. Complete required bonding and report user rejection or timeout.
  2. Wait for the GATT connection to succeed.
  3. Discover the OneKey service and both required characteristics.
  4. Enable notifications and wait for descriptor/subscription completion.
  5. Resolve the bridge’s connect call 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.

PurposeUUID
Service00000001-0000-1000-8000-00805f9b34fb
Write00000002-0000-1000-8000-00805f9b34fb
Notify00000003-0000-1000-8000-00805f9b34fb

Bridge contract

MethodCompletion 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.

Last updated on