diff --git a/.claude/settings.local.json b/.claude/settings.local.json index a59c4252..76093dbf 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -553,7 +553,8 @@ "Bash(\"/c/Users/dong/go/bin/go1.22.10.exe\" install golang.org/x/mobile/cmd/gobind@c31d5b91ecc32c0d598b8fe8457d244ca0b4e815)", "Bash(\"/c/Users/dong/go/bin/go1.22.10.exe\" mod tidy)", "Bash(adb devices:*)", - "Bash(adb logcat:*)" + "Bash(adb logcat:*)", + "Bash(git commit -m \"$\\(cat <<''EOF''\nfeat\\(android\\): add 5-minute polling timeout mechanism for keygen/sign\n\nImplements Electron''s checkAndTriggerKeygen\\(\\) polling fallback:\n- Adds polling every 2 seconds with 5-minute timeout\n- Triggers keygen/sign via synthetic session_started event on in_progress status\n- Handles gRPC stream disconnection when app goes to background\n- Shows timeout error in UI via existing error mechanism\n\n🤖 Generated with [Claude Code]\\(https://claude.com/claude-code\\)\n\nCo-Authored-By: Claude Opus 4.5 \nEOF\n\\)\")" ], "deny": [], "ask": [] diff --git a/backend/mpc-system/services/service-party-android/app/src/main/java/com/durian/tssparty/data/repository/TssRepository.kt b/backend/mpc-system/services/service-party-android/app/src/main/java/com/durian/tssparty/data/repository/TssRepository.kt index c44fc64a..faaa9ebf 100644 --- a/backend/mpc-system/services/service-party-android/app/src/main/java/com/durian/tssparty/data/repository/TssRepository.kt +++ b/backend/mpc-system/services/service-party-android/app/src/main/java/com/durian/tssparty/data/repository/TssRepository.kt @@ -63,6 +63,10 @@ class TssRepository @Inject constructor( // Called when 5-minute polling timeout is reached without session starting private var keygenTimeoutCallback: ((String) -> Unit)? = null + // Repository-level CoroutineScope for background tasks + // Uses SupervisorJob so individual task failures don't cancel other tasks + private val repositoryScope = CoroutineScope(SupervisorJob() + Dispatchers.IO) + companion object { // Polling interval for session status check (matching Electron's 2-second interval) private const val POLL_INTERVAL_MS = 2000L @@ -142,6 +146,19 @@ class TssRepository @Inject constructor( fun disconnect() { messageCollectionJob?.cancel() sessionEventJob?.cancel() + sessionStatusPollingJob?.cancel() + grpcClient.disconnect() + } + + /** + * Cleanup all resources when the repository is destroyed. + * Call this when the app is being destroyed to prevent memory leaks. + */ + fun cleanup() { + messageCollectionJob?.cancel() + sessionEventJob?.cancel() + sessionStatusPollingJob?.cancel() + repositoryScope.cancel() grpcClient.disconnect() } @@ -199,7 +216,7 @@ class TssRepository @Inject constructor( private fun startSessionEventSubscription() { sessionEventJob?.cancel() android.util.Log.d("TssRepository", "Starting session event subscription for partyId: $partyId") - sessionEventJob = CoroutineScope(Dispatchers.IO).launch { + sessionEventJob = repositoryScope.launch { grpcClient.subscribeSessionEvents(partyId).collect { event -> android.util.Log.d("TssRepository", "=== Session event received ===") android.util.Log.d("TssRepository", " eventType: ${event.eventType}") @@ -284,7 +301,7 @@ class TssRepository @Inject constructor( android.util.Log.d("TssRepository", "[POLLING] Starting session status polling for $sessionType session: $sessionId") - sessionStatusPollingJob = CoroutineScope(Dispatchers.IO).launch { + sessionStatusPollingJob = repositoryScope.launch { val startTime = System.currentTimeMillis() var pollCount = 0 @@ -1501,7 +1518,7 @@ class TssRepository @Inject constructor( currentMessageRoutingPartyIndex = partyIndex messageCollectionJob?.cancel() - messageCollectionJob = CoroutineScope(Dispatchers.IO).launch { + messageCollectionJob = repositoryScope.launch { // Collect outgoing messages from TSS and route via gRPC launch { tssNativeBridge.outgoingMessages.collect { message -> diff --git a/backend/mpc-system/services/service-party-android/app/src/main/java/com/durian/tssparty/presentation/viewmodel/MainViewModel.kt b/backend/mpc-system/services/service-party-android/app/src/main/java/com/durian/tssparty/presentation/viewmodel/MainViewModel.kt index 65c96121..6800fa59 100644 --- a/backend/mpc-system/services/service-party-android/app/src/main/java/com/durian/tssparty/presentation/viewmodel/MainViewModel.kt +++ b/backend/mpc-system/services/service-party-android/app/src/main/java/com/durian/tssparty/presentation/viewmodel/MainViewModel.kt @@ -1204,7 +1204,7 @@ class MainViewModel @Inject constructor( override fun onCleared() { super.onCleared() - repository.disconnect() + repository.cleanup() } }