From 7fecd469cce7fb54dff785d5a3ccb078ab73875d Mon Sep 17 00:00:00 2001 From: Blume1977 Date: Tue, 9 Jun 2026 12:07:59 +0200 Subject: [PATCH] fix(android): show pairing code simultaneously by unblocking serial queue MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Run the blocking Api.initDevice() (Noise pairing handshake) on its own thread so the serial bitbox_usb MethodChannel task queue stays free. Previously init occupied the single queue for the whole on-device confirmation wait, so the concurrent getChannelHash poll could not run until after the user confirmed on the BitBox — making the pairing code appear in the app only after device confirmation. iOS already dispatches init to a background queue and was unaffected. Other device operations remain serial, preserving noise nonce order. Also propagate the real initDevice() result instead of always returning true. --- CHANGELOG.md | 8 +++++ .../operations/InitBitBoxOperation.kt | 29 +++++++++++++++++-- 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 41cc7d8..c570f90 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,11 @@ +## 0.0.8 + +* Android: run the blocking `initBitBox` (Noise pairing handshake) off the serial + MethodChannel task queue so concurrent `getChannelHash` polls are serviced while + init waits for the on-device confirmation. The pairing code now appears in the app + and on the device simultaneously instead of only after confirming on the device + (iOS was already unaffected). `initBitBox` now also propagates the real init result. + ## 0.0.1 * TODO: Describe initial release. diff --git a/android/src/main/kotlin/com/cakewallet/bitbox_flutter/operations/InitBitBoxOperation.kt b/android/src/main/kotlin/com/cakewallet/bitbox_flutter/operations/InitBitBoxOperation.kt index 8cdfa46..b01265a 100644 --- a/android/src/main/kotlin/com/cakewallet/bitbox_flutter/operations/InitBitBoxOperation.kt +++ b/android/src/main/kotlin/com/cakewallet/bitbox_flutter/operations/InitBitBoxOperation.kt @@ -1,6 +1,8 @@ package com.cakewallet.bitbox_flutter.operations import android.content.Context +import android.os.Handler +import android.os.Looper import api.Api import com.cakewallet.bitbox_flutter.BitboxManager import io.flutter.plugin.common.MethodCall @@ -12,7 +14,30 @@ class InitBitBoxOperation(manager: BitboxManager) : UsbMethodCallOperation(manag methodCall: MethodCall, result: MethodChannel.Result ) { - Api.initDevice() - result.success(true) + // Api.initDevice() drives the Noise XX handshake and then blocks until the + // user confirms the pairing on the device. The channel hash (pairing code) is + // already set on the shared device object before that blocking wait, so the + // host can display it immediately. + // + // The "bitbox_usb" MethodChannel runs on a serial background task queue. If we + // called Api.initDevice() directly here, it would occupy that single queue for + // the whole blocking wait, so the concurrent getChannelHash poll could not run + // until init returned — i.e. only AFTER the device confirmation. That made the + // pairing code appear in the app only after the user confirmed on the BitBox, + // instead of simultaneously (Android-only; iOS dispatches init to a background + // queue and is unaffected). + // + // Running the blocking call on its own thread frees the serial queue, so + // getChannelHash is serviced while init is in flight and the code shows on app + // and device at the same time. All other device operations stay on the serial + // queue, preserving the single-cipher nonce ordering. + Thread { + val success = try { + Api.initDevice() + } catch (e: Throwable) { + false + } + Handler(Looper.getMainLooper()).post { result.success(success) } + }.apply { name = "bitbox-init" }.start() } }