@@ -31,6 +31,7 @@ import kotlinx.collections.immutable.persistentListOf
3131import kotlinx.collections.immutable.toImmutableList
3232import kotlinx.coroutines.CoroutineDispatcher
3333import kotlinx.coroutines.CoroutineScope
34+ import kotlinx.coroutines.Job
3435import kotlinx.coroutines.SupervisorJob
3536import kotlinx.coroutines.delay
3637import kotlinx.coroutines.flow.MutableSharedFlow
@@ -96,6 +97,9 @@ class TrezorRepo @Inject constructor(
9697
9798 private val scope = CoroutineScope (SupervisorJob () + ioDispatcher)
9899
100+ @Volatile
101+ private var transportReconnectJob: Job ? = null
102+
99103 init {
100104 observeExternalDisconnects()
101105 observeTransportRestored()
@@ -675,7 +679,7 @@ class TrezorRepo @Inject constructor(
675679 */
676680 private fun observeTransportRestored () {
677681 trezorTransport.transportRestored.onEach {
678- retryAutoReconnect ()
682+ launchTransportReconnect ()
679683 }.launchIn(scope)
680684 }
681685
@@ -684,8 +688,17 @@ class TrezorRepo @Inject constructor(
684688 * e.g. the USB attach intent the OS app picker routes to the activity (attach is
685689 * not broadcast to receivers, unlike detach).
686690 */
687- fun onTransportRestored () {
688- scope.launch { retryAutoReconnect() }
691+ fun onTransportRestored () = launchTransportReconnect()
692+
693+ /* *
694+ * Serializes reconnect triggers into one in-flight retry loop. A Trezor
695+ * re-enumerates USB during its unlock flow, so a single replug delivers several
696+ * attach intents; letting each spawn its own loop staggers connect attempts for
697+ * many seconds, and every attempt restarts the device's PIN entry.
698+ */
699+ private fun launchTransportReconnect () {
700+ if (transportReconnectJob?.isActive == true ) return
701+ transportReconnectJob = scope.launch { retryAutoReconnect() }
689702 }
690703
691704 /* *
0 commit comments