@@ -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