Restore manual Client cleanup on session logout (#4333)

This is done to fix an issue where the Nodes that contain these dependencies are leaked and the `Client` and other SDK-related components can keep working in background for a while, until the caches holding the Nodes are flushed or the app is restarted.
This commit is contained in:
Jorge Martin Espinosa 2025-02-27 17:04:28 +01:00 committed by GitHub
parent 425d53a12b
commit 10557444dd
5 changed files with 41 additions and 6 deletions

View file

@ -19,6 +19,7 @@ import org.matrix.rustcomponents.sdk.ClientDelegate
import org.matrix.rustcomponents.sdk.ClientSessionDelegate import org.matrix.rustcomponents.sdk.ClientSessionDelegate
import org.matrix.rustcomponents.sdk.Session import org.matrix.rustcomponents.sdk.Session
import timber.log.Timber import timber.log.Timber
import java.lang.ref.WeakReference
import java.util.concurrent.atomic.AtomicBoolean import java.util.concurrent.atomic.AtomicBoolean
/** /**
@ -43,13 +44,20 @@ class RustClientSessionDelegate(
private val updateTokensDispatcher = coroutineDispatchers.io.limitedParallelism(1) private val updateTokensDispatcher = coroutineDispatchers.io.limitedParallelism(1)
// This Client needs to be set up as soon as possible so `didReceiveAuthError` can work properly. // This Client needs to be set up as soon as possible so `didReceiveAuthError` can work properly.
private var client: RustMatrixClient? = null private var client: WeakReference<RustMatrixClient> = WeakReference(null)
/** /**
* Sets the [ClientDelegate] for the [RustMatrixClient], and keeps a reference to the client so it can be used later. * Sets the [ClientDelegate] for the [RustMatrixClient], and keeps a reference to the client so it can be used later.
*/ */
fun bindClient(client: RustMatrixClient) { fun bindClient(client: RustMatrixClient) {
this.client = client this.client = WeakReference(client)
}
/**
* Clears the current client reference.
*/
fun clearCurrentClient() {
this.client.clear()
} }
override fun saveSessionInKeychain(session: Session) { override fun saveSessionInKeychain(session: Session) {
@ -81,7 +89,7 @@ class RustClientSessionDelegate(
clientLog.v("didReceiveAuthError -> do the cleanup") clientLog.v("didReceiveAuthError -> do the cleanup")
// TODO handle isSoftLogout parameter. // TODO handle isSoftLogout parameter.
appCoroutineScope.launch(updateTokensDispatcher) { appCoroutineScope.launch(updateTokensDispatcher) {
val currentClient = client val currentClient = client.get()
if (currentClient == null) { if (currentClient == null) {
clientLog.w("didReceiveAuthError -> no client, exiting") clientLog.w("didReceiveAuthError -> no client, exiting")
isLoggingOut.set(false) isLoggingOut.set(false)
@ -101,7 +109,7 @@ class RustClientSessionDelegate(
} else { } else {
clientLog.d("No session data found.") clientLog.d("No session data found.")
} }
client?.logout(userInitiated = false, ignoreSdkError = true) currentClient.logout(userInitiated = false, ignoreSdkError = true)
}.invokeOnCompletion { }.invokeOnCompletion {
if (it != null) { if (it != null) {
clientLog.e(it, "Failed to remove session data.") clientLog.e(it, "Failed to remove session data.")

View file

@ -482,6 +482,13 @@ class RustMatrixClient(
clientDelegateTaskHandle?.cancelAndDestroy() clientDelegateTaskHandle?.cancelAndDestroy()
notificationSettingsService.destroy() notificationSettingsService.destroy()
verificationService.destroy() verificationService.destroy()
sessionDelegate.clearCurrentClient()
innerRoomListService.destroy()
notificationService.destroy()
notificationProcessSetup.destroy()
encryptionService.destroy()
innerClient.destroy()
} }
override suspend fun getCacheSize(): Long { override suspend fun getCacheSize(): Long {

View file

@ -156,6 +156,10 @@ class RustMatrixAuthenticationService @Inject constructor(
) )
newMatrixClientObserver?.invoke(rustMatrixClientFactory.create(client)) newMatrixClientObserver?.invoke(rustMatrixClientFactory.create(client))
sessionStore.storeData(sessionData) sessionStore.storeData(sessionData)
// Clean up the strong reference held here since it's no longer necessary
currentClient = null
SessionId(sessionData.userId) SessionId(sessionData.userId)
}.mapFailure { failure -> }.mapFailure { failure ->
failure.mapAuthenticationException() failure.mapAuthenticationException()
@ -229,6 +233,10 @@ class RustMatrixAuthenticationService @Inject constructor(
pendingOidcAuthorizationData = null pendingOidcAuthorizationData = null
newMatrixClientObserver?.invoke(rustMatrixClientFactory.create(client)) newMatrixClientObserver?.invoke(rustMatrixClientFactory.create(client))
sessionStore.storeData(sessionData) sessionStore.storeData(sessionData)
// Clean up the strong reference held here since it's no longer necessary
currentClient = null
SessionId(sessionData.userId) SessionId(sessionData.userId)
}.mapFailure { failure -> }.mapFailure { failure ->
failure.mapAuthenticationException() failure.mapAuthenticationException()
@ -264,6 +272,10 @@ class RustMatrixAuthenticationService @Inject constructor(
) )
newMatrixClientObserver?.invoke(rustMatrixClientFactory.create(client)) newMatrixClientObserver?.invoke(rustMatrixClientFactory.create(client))
sessionStore.storeData(sessionData) sessionStore.storeData(sessionData)
// Clean up the strong reference held here since it's no longer necessary
currentClient = null
SessionId(sessionData.userId) SessionId(sessionData.userId)
}.mapFailure { }.mapFailure {
when (it) { when (it) {

View file

@ -186,11 +186,11 @@ internal class RustEncryptionService(
} }
override suspend fun deviceCurve25519(): String? { override suspend fun deviceCurve25519(): String? {
return service.curve25519Key() return runCatching { service.curve25519Key() }.getOrNull()
} }
override suspend fun deviceEd25519(): String? { override suspend fun deviceEd25519(): String? {
return service.ed25519Key() return runCatching { service.ed25519Key() }.getOrNull()
} }
override suspend fun startIdentityReset(): Result<IdentityResetHandle?> { override suspend fun startIdentityReset(): Result<IdentityResetHandle?> {
@ -219,4 +219,8 @@ internal class RustEncryptionService(
// requestFromHomeserverIfNeeded = true, // requestFromHomeserverIfNeeded = true,
) ?: error("User identity not found") ) ?: error("User identity not found")
} }
fun destroy() {
service.destroy()
}
} }

View file

@ -35,4 +35,8 @@ class RustNotificationService(
} }
} }
} }
fun destroy() {
notificationClient.destroy()
}
} }