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