Skip to content

Commit 167d153

Browse files
committed
fix: clarify trezor pairing
1 parent 7947e1c commit 167d153

1 file changed

Lines changed: 62 additions & 63 deletions

File tree

app/src/main/java/to/bitkit/services/TrezorTransport.kt

Lines changed: 62 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -159,10 +159,8 @@ class TrezorTransport @Inject constructor(
159159
bluetoothManager.adapter
160160
}
161161

162-
// USB connections
163162
private val usbConnections = ConcurrentHashMap<String, UsbOpenDevice>()
164163

165-
// BLE connections
166164
private val bleConnections = ConcurrentHashMap<String, BleConnection>()
167165
private val discoveredBleDevices = ConcurrentHashMap<String, BluetoothDevice>()
168166

@@ -185,12 +183,9 @@ class TrezorTransport @Inject constructor(
185183
@Volatile var writeStatus: Int = BluetoothGatt.GATT_SUCCESS,
186184
)
187185

188-
// ==================== TrezorTransportCallback Implementation ====================
189-
190186
override fun enumerateDevices(): List<NativeDeviceInfo> {
191187
val devices = mutableListOf<NativeDeviceInfo>()
192188

193-
// Enumerate USB devices
194189
runCatching {
195190
usbManager.deviceList.values
196191
.filter { isTrezorDevice(it) }
@@ -210,7 +205,6 @@ class TrezorTransport @Inject constructor(
210205
Logger.error("USB enumerate failed", it, context = TAG)
211206
}
212207

213-
// Enumerate Bluetooth devices
214208
runCatching {
215209
enumerateBleDevices()
216210
}.onSuccess {
@@ -293,58 +287,65 @@ class TrezorTransport @Inject constructor(
293287
return bridgeTransport.callMessage(path, messageType, data)
294288
}
295289

296-
// For BLE/THP devices, the Rust side now handles THP protocol directly.
297-
// This callback returns null to let Rust use its built-in THP implementation.
298290
Logger.debug(
299-
"callMessage called for '$path', type='$messageType' - returning null (Rust handles THP)",
291+
"Delegating callMessage for '$path', type='$messageType' to core THP handling",
300292
context = TAG,
301293
)
302294
return null
303295
}
304296

305297
override fun getPairingCode(): String {
306-
// This is called by Rust during BLE THP pairing when the device
307-
// displays a 6-digit code that must be entered.
308-
//
309-
// We use a blocking approach with a latch. The UI observes needsPairingCode
310-
// and shows a dialog. When the user enters the code, submitPairingCode()
311-
// is called which releases the latch.
312298
TrezorDebugLog.log("PAIR", ">>> PAIRING CODE REQUESTED - Device requires re-pairing! <<<")
313-
Logger.info(">>> PAIRING CODE REQUESTED <<<", context = TAG)
314-
Logger.info("Look at your Trezor screen for a 6-digit code", context = TAG)
299+
Logger.info("Requested pairing code from user", context = TAG)
300+
Logger.info("Asked user to read the 6-digit code from Trezor screen", context = TAG)
315301

316302
val latch = CountDownLatch(1)
317303

318304
synchronized(pairingCodeLock) {
319-
submittedPairingCode = ""
305+
pairingCodeResult = null
320306
pairingCodeRequest = PairingCodeRequest(isRequested = true, latch = latch)
321307
_needsPairingCode.update { true }
322308
}
323309

324-
try {
325-
// Wait for user to enter the code (with timeout)
310+
val result = try {
326311
val received = latch.await(PAIRING_CODE_TIMEOUT_MS, TimeUnit.MILLISECONDS)
327-
328-
if (!received) {
329-
Logger.warn("Pairing code entry timed out", context = TAG)
330-
_needsPairingCode.update { false }
331-
return ""
312+
synchronized(pairingCodeLock) {
313+
val result = if (received) {
314+
pairingCodeResult ?: PairingCodeResult.Cancelled
315+
} else {
316+
PairingCodeResult.TimedOut
317+
}
318+
clearPairingCodeRequest()
319+
result
332320
}
333-
334-
val code = submittedPairingCode
335-
Logger.info("Pairing code received (len='${code.length}')", context = TAG)
336-
return code
337321
} catch (e: InterruptedException) {
338-
Logger.error("Pairing code wait interrupted", e, context = TAG)
339-
_needsPairingCode.update { false }
340-
return ""
322+
Thread.currentThread().interrupt()
323+
synchronized(pairingCodeLock) {
324+
clearPairingCodeRequest()
325+
}
326+
PairingCodeResult.Interrupted(e)
327+
}
328+
329+
return when (result) {
330+
is PairingCodeResult.Submitted -> {
331+
Logger.info("Received pairing code (len='${result.code.length}')", context = TAG)
332+
result.code
333+
}
334+
PairingCodeResult.Cancelled -> {
335+
Logger.info("Cancelled pairing code entry", context = TAG)
336+
""
337+
}
338+
PairingCodeResult.TimedOut -> {
339+
Logger.warn("Timed out waiting for pairing code entry", context = TAG)
340+
""
341+
}
342+
is PairingCodeResult.Interrupted -> {
343+
Logger.error("Interrupted pairing code wait", result.error, context = TAG)
344+
""
345+
}
341346
}
342347
}
343348

344-
/**
345-
* Pairing code request state for UI observation.
346-
* When getPairingCode() is called by Rust, we set this to true and wait.
347-
*/
348349
data class PairingCodeRequest(
349350
val isRequested: Boolean = false,
350351
val latch: CountDownLatch? = null,
@@ -354,10 +355,26 @@ class TrezorTransport @Inject constructor(
354355
private var pairingCodeRequest: PairingCodeRequest = PairingCodeRequest()
355356

356357
@Volatile
357-
private var submittedPairingCode: String = ""
358+
private var pairingCodeResult: PairingCodeResult? = null
358359

359360
private val pairingCodeLock = Object()
360361

362+
private sealed interface PairingCodeResult {
363+
data class Submitted(val code: String) : PairingCodeResult
364+
365+
data object Cancelled : PairingCodeResult
366+
367+
data object TimedOut : PairingCodeResult
368+
369+
data class Interrupted(val error: InterruptedException) : PairingCodeResult
370+
}
371+
372+
private fun clearPairingCodeRequest() {
373+
pairingCodeRequest = PairingCodeRequest()
374+
pairingCodeResult = null
375+
_needsPairingCode.update { false }
376+
}
377+
361378
/**
362379
* Flow to observe when a pairing code is needed.
363380
* UI should show a dialog when this is true.
@@ -371,18 +388,20 @@ class TrezorTransport @Inject constructor(
371388
*/
372389
fun submitPairingCode(code: String) {
373390
synchronized(pairingCodeLock) {
374-
Logger.info("Pairing code submitted (len='${code.length}')", context = TAG)
375-
submittedPairingCode = code
391+
Logger.info("Submitted pairing code (len='${code.length}')", context = TAG)
392+
pairingCodeResult = PairingCodeResult.Submitted(code)
376393
_needsPairingCode.update { false }
377394
pairingCodeRequest.latch?.countDown()
378395
}
379396
}
380397

381-
/**
382-
* Cancel pairing code entry (submit empty string).
383-
*/
384398
fun cancelPairingCode() {
385-
submitPairingCode("")
399+
synchronized(pairingCodeLock) {
400+
Logger.info("Cancelled pairing code entry", context = TAG)
401+
pairingCodeResult = PairingCodeResult.Cancelled
402+
_needsPairingCode.update { false }
403+
pairingCodeRequest.latch?.countDown()
404+
}
386405
}
387406

388407
@Suppress("TooGenericExceptionCaught")
@@ -411,7 +430,6 @@ class TrezorTransport @Inject constructor(
411430

412431
file.writeText(credentialJson)
413432

414-
// Immediately verify the file was written
415433
val verifyExists = file.exists()
416434
val verifySize = if (verifyExists) file.length() else 0
417435
TrezorDebugLog.log(
@@ -449,7 +467,6 @@ class TrezorTransport @Inject constructor(
449467
TrezorDebugLog.log("LOAD", "loadThpCredential for: $deviceId")
450468
TrezorDebugLog.log("LOAD", "File: ${file.absolutePath}, exists=$exists, size=$size")
451469

452-
// List all files in credential directory for debugging
453470
val allFiles = credentialDir.listFiles()?.map { "${it.name} (${it.length()}b)" } ?: emptyList()
454471
TrezorDebugLog.log("LOAD", "All credential files: $allFiles")
455472

@@ -496,8 +513,6 @@ class TrezorTransport @Inject constructor(
496513
return File(credentialDir, "$sanitizedId.json")
497514
}
498515

499-
// ==================== USB Methods ====================
500-
501516
/**
502517
* Request USB permission for a device and block until the user responds.
503518
* Returns true if permission was granted, false otherwise.
@@ -537,7 +552,6 @@ class TrezorTransport @Inject constructor(
537552
Logger.info("Requesting USB permission for '${device.deviceName}'", context = TAG)
538553
usbManager.requestPermission(device, permissionIntent)
539554

540-
// Block until user responds (up to 60 seconds)
541555
val responded = latch.await(USB_PERMISSION_TIMEOUT_MS, TimeUnit.MILLISECONDS)
542556
if (!responded) {
543557
Logger.warn("USB permission request timed out", context = TAG)
@@ -577,7 +591,6 @@ class TrezorTransport @Inject constructor(
577591
@Suppress("TooGenericExceptionCaught", "ReturnCount")
578592
private fun openUsbDevice(path: String): TrezorTransportWriteResult {
579593
return try {
580-
// Close existing connection if any
581594
closeUsbDevice(path)
582595

583596
val device = usbManager.deviceList[path]
@@ -726,8 +739,6 @@ class TrezorTransport @Inject constructor(
726739
}
727740
}
728741

729-
// ==================== Bluetooth Methods ====================
730-
731742
@SuppressLint("MissingPermission")
732743
private fun enumerateBleDevices(): List<NativeDeviceInfo> {
733744
if (bluetoothAdapter?.isEnabled != true) {
@@ -737,7 +748,6 @@ class TrezorTransport @Inject constructor(
737748

738749
val scanner = bluetoothAdapter?.bluetoothLeScanner ?: return emptyList()
739750

740-
// Start fresh scan
741751
discoveredBleDevices.clear()
742752

743753
val scanFilter = ScanFilter.Builder()
@@ -751,7 +761,6 @@ class TrezorTransport @Inject constructor(
751761
scanner.startScan(listOf(scanFilter), scanSettings, bleScanCallback)
752762
Logger.debug("BLE scan started", context = TAG)
753763

754-
// Wait for scan results
755764
Thread.sleep(SCAN_DURATION_MS)
756765

757766
scanner.stopScan(bleScanCallback)
@@ -828,10 +837,8 @@ class TrezorTransport @Inject constructor(
828837
val device = discoveredBleDevices[address]
829838
?: return TrezorTransportWriteResult(success = false, error = "Device not found: $path")
830839

831-
// Close existing connection
832840
closeBleDevice(path)
833841

834-
// Check if device needs bonding
835842
val bondError = waitForBonding(device, address)
836843
if (bondError != null) return bondError
837844

@@ -858,10 +865,8 @@ class TrezorTransport @Inject constructor(
858865
return TrezorTransportWriteResult(success = false, error = "Failed to connect")
859866
}
860867

861-
// Request high-priority BLE connection for faster, more reliable handshake
862868
gatt.requestConnectionPriority(BluetoothGatt.CONNECTION_PRIORITY_HIGH)
863869

864-
// Drain any stale notifications from a previous connection attempt
865870
val staleCount = updatedConnection.readQueue.size
866871
if (staleCount > 0) {
867872
updatedConnection.readQueue.clear()
@@ -953,7 +958,6 @@ class TrezorTransport @Inject constructor(
953958
}
954959

955960
return try {
956-
// Retry logic for transient GATT busy states
957961
var lastError = "Write initiation failed"
958962
for (attempt in 1..BLE_WRITE_RETRY_COUNT) {
959963
val writeLatch = CountDownLatch(1)
@@ -966,7 +970,6 @@ class TrezorTransport @Inject constructor(
966970
val success = connection.gatt.writeCharacteristic(writeChar)
967971

968972
if (!success) {
969-
// Get more diagnostic info
970973
val connState = connection.isConnected
971974
val charPropsHex = Integer.toHexString(writeChar.properties)
972975
Logger.warn(
@@ -1008,7 +1011,6 @@ class TrezorTransport @Inject constructor(
10081011
return TrezorTransportWriteResult(success = false, error = lastError)
10091012
}
10101013

1011-
// Success!
10121014
Logger.debug("BLE wrote '${data.size}' bytes to '$path' (attempt '$attempt')", context = TAG)
10131015

10141016
// Small delay between writes to avoid overwhelming the GATT
@@ -1105,7 +1107,6 @@ class TrezorTransport @Inject constructor(
11051107

11061108
gatt.setCharacteristicNotification(notifyChar, true)
11071109

1108-
// Also subscribe to PUSH characteristic
11091110
val pushChar = service.getCharacteristic(PUSH_CHAR_UUID)
11101111
if (pushChar != null) {
11111112
gatt.setCharacteristicNotification(pushChar, true)
@@ -1235,8 +1236,6 @@ class TrezorTransport @Inject constructor(
12351236
return result
12361237
}
12371238

1238-
// ==================== Utility Methods ====================
1239-
12401239
private fun isBleDevice(path: String): Boolean = path.startsWith("ble:")
12411240

12421241
private fun isTrezorDevice(device: UsbDevice): Boolean {

0 commit comments

Comments
 (0)