Skip to content

Commit f35551e

Browse files
committed
fix: clarify trezor pairing
1 parent 328259a commit f35551e

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
@@ -158,10 +158,8 @@ class TrezorTransport @Inject constructor(
158158
bluetoothManager.adapter
159159
}
160160

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

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

@@ -184,12 +182,9 @@ class TrezorTransport @Inject constructor(
184182
@Volatile var writeStatus: Int = BluetoothGatt.GATT_SUCCESS,
185183
)
186184

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

192-
// Enumerate USB devices
193188
runCatching {
194189
usbManager.deviceList.values
195190
.filter { isTrezorDevice(it) }
@@ -209,7 +204,6 @@ class TrezorTransport @Inject constructor(
209204
Logger.error("USB enumerate failed", it, context = TAG)
210205
}
211206

212-
// Enumerate Bluetooth devices
213207
runCatching {
214208
enumerateBleDevices()
215209
}.onSuccess {
@@ -272,58 +266,65 @@ class TrezorTransport @Inject constructor(
272266
messageType: UShort,
273267
data: ByteArray,
274268
): TrezorCallMessageResult? {
275-
// For BLE/THP devices, the Rust side now handles THP protocol directly.
276-
// This callback returns null to let Rust use its built-in THP implementation.
277269
Logger.debug(
278-
"callMessage called for '$path', type='$messageType' - returning null (Rust handles THP)",
270+
"Delegating callMessage for '$path', type='$messageType' to core THP handling",
279271
context = TAG,
280272
)
281273
return null
282274
}
283275

284276
override fun getPairingCode(): String {
285-
// This is called by Rust during BLE THP pairing when the device
286-
// displays a 6-digit code that must be entered.
287-
//
288-
// We use a blocking approach with a latch. The UI observes needsPairingCode
289-
// and shows a dialog. When the user enters the code, submitPairingCode()
290-
// is called which releases the latch.
291277
TrezorDebugLog.log("PAIR", ">>> PAIRING CODE REQUESTED - Device requires re-pairing! <<<")
292-
Logger.info(">>> PAIRING CODE REQUESTED <<<", context = TAG)
293-
Logger.info("Look at your Trezor screen for a 6-digit code", context = TAG)
278+
Logger.info("Requested pairing code from user", context = TAG)
279+
Logger.info("Asked user to read the 6-digit code from Trezor screen", context = TAG)
294280

295281
val latch = CountDownLatch(1)
296282

297283
synchronized(pairingCodeLock) {
298-
submittedPairingCode = ""
284+
pairingCodeResult = null
299285
pairingCodeRequest = PairingCodeRequest(isRequested = true, latch = latch)
300286
_needsPairingCode.update { true }
301287
}
302288

303-
try {
304-
// Wait for user to enter the code (with timeout)
289+
val result = try {
305290
val received = latch.await(PAIRING_CODE_TIMEOUT_MS, TimeUnit.MILLISECONDS)
306-
307-
if (!received) {
308-
Logger.warn("Pairing code entry timed out", context = TAG)
309-
_needsPairingCode.update { false }
310-
return ""
291+
synchronized(pairingCodeLock) {
292+
val result = if (received) {
293+
pairingCodeResult ?: PairingCodeResult.Cancelled
294+
} else {
295+
PairingCodeResult.TimedOut
296+
}
297+
clearPairingCodeRequest()
298+
result
311299
}
312-
313-
val code = submittedPairingCode
314-
Logger.info("Pairing code received (len='${code.length}')", context = TAG)
315-
return code
316300
} catch (e: InterruptedException) {
317-
Logger.error("Pairing code wait interrupted", e, context = TAG)
318-
_needsPairingCode.update { false }
319-
return ""
301+
Thread.currentThread().interrupt()
302+
synchronized(pairingCodeLock) {
303+
clearPairingCodeRequest()
304+
}
305+
PairingCodeResult.Interrupted(e)
306+
}
307+
308+
return when (result) {
309+
is PairingCodeResult.Submitted -> {
310+
Logger.info("Received pairing code (len='${result.code.length}')", context = TAG)
311+
result.code
312+
}
313+
PairingCodeResult.Cancelled -> {
314+
Logger.info("Cancelled pairing code entry", context = TAG)
315+
""
316+
}
317+
PairingCodeResult.TimedOut -> {
318+
Logger.warn("Timed out waiting for pairing code entry", context = TAG)
319+
""
320+
}
321+
is PairingCodeResult.Interrupted -> {
322+
Logger.error("Interrupted pairing code wait", result.error, context = TAG)
323+
""
324+
}
320325
}
321326
}
322327

323-
/**
324-
* Pairing code request state for UI observation.
325-
* When getPairingCode() is called by Rust, we set this to true and wait.
326-
*/
327328
data class PairingCodeRequest(
328329
val isRequested: Boolean = false,
329330
val latch: CountDownLatch? = null,
@@ -333,10 +334,26 @@ class TrezorTransport @Inject constructor(
333334
private var pairingCodeRequest: PairingCodeRequest = PairingCodeRequest()
334335

335336
@Volatile
336-
private var submittedPairingCode: String = ""
337+
private var pairingCodeResult: PairingCodeResult? = null
337338

338339
private val pairingCodeLock = Object()
339340

341+
private sealed interface PairingCodeResult {
342+
data class Submitted(val code: String) : PairingCodeResult
343+
344+
data object Cancelled : PairingCodeResult
345+
346+
data object TimedOut : PairingCodeResult
347+
348+
data class Interrupted(val error: InterruptedException) : PairingCodeResult
349+
}
350+
351+
private fun clearPairingCodeRequest() {
352+
pairingCodeRequest = PairingCodeRequest()
353+
pairingCodeResult = null
354+
_needsPairingCode.update { false }
355+
}
356+
340357
/**
341358
* Flow to observe when a pairing code is needed.
342359
* UI should show a dialog when this is true.
@@ -350,18 +367,20 @@ class TrezorTransport @Inject constructor(
350367
*/
351368
fun submitPairingCode(code: String) {
352369
synchronized(pairingCodeLock) {
353-
Logger.info("Pairing code submitted (len='${code.length}')", context = TAG)
354-
submittedPairingCode = code
370+
Logger.info("Submitted pairing code (len='${code.length}')", context = TAG)
371+
pairingCodeResult = PairingCodeResult.Submitted(code)
355372
_needsPairingCode.update { false }
356373
pairingCodeRequest.latch?.countDown()
357374
}
358375
}
359376

360-
/**
361-
* Cancel pairing code entry (submit empty string).
362-
*/
363377
fun cancelPairingCode() {
364-
submitPairingCode("")
378+
synchronized(pairingCodeLock) {
379+
Logger.info("Cancelled pairing code entry", context = TAG)
380+
pairingCodeResult = PairingCodeResult.Cancelled
381+
_needsPairingCode.update { false }
382+
pairingCodeRequest.latch?.countDown()
383+
}
365384
}
366385

367386
@Suppress("TooGenericExceptionCaught")
@@ -390,7 +409,6 @@ class TrezorTransport @Inject constructor(
390409

391410
file.writeText(credentialJson)
392411

393-
// Immediately verify the file was written
394412
val verifyExists = file.exists()
395413
val verifySize = if (verifyExists) file.length() else 0
396414
TrezorDebugLog.log(
@@ -428,7 +446,6 @@ class TrezorTransport @Inject constructor(
428446
TrezorDebugLog.log("LOAD", "loadThpCredential for: $deviceId")
429447
TrezorDebugLog.log("LOAD", "File: ${file.absolutePath}, exists=$exists, size=$size")
430448

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

@@ -475,8 +492,6 @@ class TrezorTransport @Inject constructor(
475492
return File(credentialDir, "$sanitizedId.json")
476493
}
477494

478-
// ==================== USB Methods ====================
479-
480495
/**
481496
* Request USB permission for a device and block until the user responds.
482497
* Returns true if permission was granted, false otherwise.
@@ -516,7 +531,6 @@ class TrezorTransport @Inject constructor(
516531
Logger.info("Requesting USB permission for '${device.deviceName}'", context = TAG)
517532
usbManager.requestPermission(device, permissionIntent)
518533

519-
// Block until user responds (up to 60 seconds)
520534
val responded = latch.await(USB_PERMISSION_TIMEOUT_MS, TimeUnit.MILLISECONDS)
521535
if (!responded) {
522536
Logger.warn("USB permission request timed out", context = TAG)
@@ -556,7 +570,6 @@ class TrezorTransport @Inject constructor(
556570
@Suppress("TooGenericExceptionCaught", "ReturnCount")
557571
private fun openUsbDevice(path: String): TrezorTransportWriteResult {
558572
return try {
559-
// Close existing connection if any
560573
closeUsbDevice(path)
561574

562575
val device = usbManager.deviceList[path]
@@ -705,8 +718,6 @@ class TrezorTransport @Inject constructor(
705718
}
706719
}
707720

708-
// ==================== Bluetooth Methods ====================
709-
710721
@SuppressLint("MissingPermission")
711722
private fun enumerateBleDevices(): List<NativeDeviceInfo> {
712723
if (bluetoothAdapter?.isEnabled != true) {
@@ -716,7 +727,6 @@ class TrezorTransport @Inject constructor(
716727

717728
val scanner = bluetoothAdapter?.bluetoothLeScanner ?: return emptyList()
718729

719-
// Start fresh scan
720730
discoveredBleDevices.clear()
721731

722732
val scanFilter = ScanFilter.Builder()
@@ -730,7 +740,6 @@ class TrezorTransport @Inject constructor(
730740
scanner.startScan(listOf(scanFilter), scanSettings, bleScanCallback)
731741
Logger.debug("BLE scan started", context = TAG)
732742

733-
// Wait for scan results
734743
Thread.sleep(SCAN_DURATION_MS)
735744

736745
scanner.stopScan(bleScanCallback)
@@ -807,10 +816,8 @@ class TrezorTransport @Inject constructor(
807816
val device = discoveredBleDevices[address]
808817
?: return TrezorTransportWriteResult(success = false, error = "Device not found: $path")
809818

810-
// Close existing connection
811819
closeBleDevice(path)
812820

813-
// Check if device needs bonding
814821
val bondError = waitForBonding(device, address)
815822
if (bondError != null) return bondError
816823

@@ -837,10 +844,8 @@ class TrezorTransport @Inject constructor(
837844
return TrezorTransportWriteResult(success = false, error = "Failed to connect")
838845
}
839846

840-
// Request high-priority BLE connection for faster, more reliable handshake
841847
gatt.requestConnectionPriority(BluetoothGatt.CONNECTION_PRIORITY_HIGH)
842848

843-
// Drain any stale notifications from a previous connection attempt
844849
val staleCount = updatedConnection.readQueue.size
845850
if (staleCount > 0) {
846851
updatedConnection.readQueue.clear()
@@ -932,7 +937,6 @@ class TrezorTransport @Inject constructor(
932937
}
933938

934939
return try {
935-
// Retry logic for transient GATT busy states
936940
var lastError = "Write initiation failed"
937941
for (attempt in 1..BLE_WRITE_RETRY_COUNT) {
938942
val writeLatch = CountDownLatch(1)
@@ -945,7 +949,6 @@ class TrezorTransport @Inject constructor(
945949
val success = connection.gatt.writeCharacteristic(writeChar)
946950

947951
if (!success) {
948-
// Get more diagnostic info
949952
val connState = connection.isConnected
950953
val charPropsHex = Integer.toHexString(writeChar.properties)
951954
Logger.warn(
@@ -987,7 +990,6 @@ class TrezorTransport @Inject constructor(
987990
return TrezorTransportWriteResult(success = false, error = lastError)
988991
}
989992

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

993995
// Small delay between writes to avoid overwhelming the GATT
@@ -1084,7 +1086,6 @@ class TrezorTransport @Inject constructor(
10841086

10851087
gatt.setCharacteristicNotification(notifyChar, true)
10861088

1087-
// Also subscribe to PUSH characteristic
10881089
val pushChar = service.getCharacteristic(PUSH_CHAR_UUID)
10891090
if (pushChar != null) {
10901091
gatt.setCharacteristicNotification(pushChar, true)
@@ -1214,8 +1215,6 @@ class TrezorTransport @Inject constructor(
12141215
return result
12151216
}
12161217

1217-
// ==================== Utility Methods ====================
1218-
12191218
private fun isBleDevice(path: String): Boolean = path.startsWith("ble:")
12201219

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

0 commit comments

Comments
 (0)