Merge pull request #4193 from vector-im/feature/bma/fix_logout_crash
Try to fix #4007
This commit is contained in:
commit
7338982030
|
@ -0,0 +1 @@
|
||||||
|
Fix random crash when user logs out just after the log in.
|
|
@ -379,7 +379,8 @@ internal interface IMXCryptoStore {
|
||||||
|
|
||||||
fun getOrAddOutgoingSecretShareRequest(secretName: String, recipients: Map<String, List<String>>): OutgoingSecretRequest?
|
fun getOrAddOutgoingSecretShareRequest(secretName: String, recipients: Map<String, List<String>>): OutgoingSecretRequest?
|
||||||
|
|
||||||
fun saveGossipingEvent(event: Event)
|
fun saveGossipingEvent(event: Event) = saveGossipingEvents(listOf(event))
|
||||||
|
|
||||||
fun saveGossipingEvents(events: List<Event>)
|
fun saveGossipingEvents(events: List<Event>)
|
||||||
|
|
||||||
fun updateGossipingRequestState(request: IncomingShareRequestCommon, state: GossipingRequestState) {
|
fun updateGossipingRequestState(request: IncomingShareRequestCommon, state: GossipingRequestState) {
|
||||||
|
|
|
@ -25,6 +25,7 @@ import io.realm.Realm
|
||||||
import io.realm.RealmConfiguration
|
import io.realm.RealmConfiguration
|
||||||
import io.realm.Sort
|
import io.realm.Sort
|
||||||
import io.realm.kotlin.where
|
import io.realm.kotlin.where
|
||||||
|
import org.matrix.android.sdk.api.extensions.tryOrNull
|
||||||
import org.matrix.android.sdk.api.session.crypto.crosssigning.MXCrossSigningInfo
|
import org.matrix.android.sdk.api.session.crypto.crosssigning.MXCrossSigningInfo
|
||||||
import org.matrix.android.sdk.api.session.events.model.Event
|
import org.matrix.android.sdk.api.session.events.model.Event
|
||||||
import org.matrix.android.sdk.api.session.room.send.SendState
|
import org.matrix.android.sdk.api.session.room.send.SendState
|
||||||
|
@ -100,6 +101,8 @@ import org.matrix.olm.OlmAccount
|
||||||
import org.matrix.olm.OlmException
|
import org.matrix.olm.OlmException
|
||||||
import org.matrix.olm.OlmOutboundGroupSession
|
import org.matrix.olm.OlmOutboundGroupSession
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
import java.util.concurrent.Executors
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import kotlin.collections.set
|
import kotlin.collections.set
|
||||||
|
|
||||||
|
@ -137,8 +140,11 @@ internal class RealmCryptoStore @Inject constructor(
|
||||||
newSessionListeners.remove(listener)
|
newSessionListeners.remove(listener)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val monarchyWriteAsyncExecutor = Executors.newSingleThreadExecutor()
|
||||||
|
|
||||||
private val monarchy = Monarchy.Builder()
|
private val monarchy = Monarchy.Builder()
|
||||||
.setRealmConfiguration(realmConfiguration)
|
.setRealmConfiguration(realmConfiguration)
|
||||||
|
.setWriteAsyncExecutor(monarchyWriteAsyncExecutor)
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
|
@ -199,6 +205,14 @@ internal class RealmCryptoStore @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun close() {
|
override fun close() {
|
||||||
|
// Ensure no async request will be run later
|
||||||
|
val tasks = monarchyWriteAsyncExecutor.shutdownNow()
|
||||||
|
Timber.w("Closing RealmCryptoStore, ${tasks.size} async task(s) cancelled")
|
||||||
|
tryOrNull("Interrupted") {
|
||||||
|
// Wait 1 minute max
|
||||||
|
monarchyWriteAsyncExecutor.awaitTermination(1, TimeUnit.MINUTES)
|
||||||
|
}
|
||||||
|
|
||||||
olmSessionsToRelease.forEach {
|
olmSessionsToRelease.forEach {
|
||||||
it.value.olmSession.releaseSession()
|
it.value.olmSession.releaseSession()
|
||||||
}
|
}
|
||||||
|
@ -1163,8 +1177,8 @@ internal class RealmCryptoStore @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun saveGossipingEvents(events: List<Event>) {
|
override fun saveGossipingEvents(events: List<Event>) {
|
||||||
val now = System.currentTimeMillis()
|
|
||||||
monarchy.writeAsync { realm ->
|
monarchy.writeAsync { realm ->
|
||||||
|
val now = System.currentTimeMillis()
|
||||||
events.forEach { event ->
|
events.forEach { event ->
|
||||||
val ageLocalTs = event.unsignedData?.age?.let { now - it } ?: now
|
val ageLocalTs = event.unsignedData?.age?.let { now - it } ?: now
|
||||||
val entity = GossipingEventEntity(
|
val entity = GossipingEventEntity(
|
||||||
|
@ -1182,23 +1196,6 @@ internal class RealmCryptoStore @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun saveGossipingEvent(event: Event) {
|
|
||||||
monarchy.writeAsync { realm ->
|
|
||||||
val now = System.currentTimeMillis()
|
|
||||||
val ageLocalTs = event.unsignedData?.age?.let { now - it } ?: now
|
|
||||||
val entity = GossipingEventEntity(
|
|
||||||
type = event.type,
|
|
||||||
sender = event.senderId,
|
|
||||||
ageLocalTs = ageLocalTs,
|
|
||||||
content = ContentMapper.map(event.content)
|
|
||||||
).apply {
|
|
||||||
sendState = SendState.SYNCED
|
|
||||||
decryptionResultJson = MoshiProvider.providesMoshi().adapter(OlmDecryptionResult::class.java).toJson(event.mxDecryptionResult)
|
|
||||||
decryptionErrorCode = event.mCryptoError?.name
|
|
||||||
}
|
|
||||||
realm.insertOrUpdate(entity)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// override fun getOutgoingRoomKeyRequestByState(states: Set<ShareRequestState>): OutgoingRoomKeyRequest? {
|
// override fun getOutgoingRoomKeyRequestByState(states: Set<ShareRequestState>): OutgoingRoomKeyRequest? {
|
||||||
// val statesIndex = states.map { it.ordinal }.toTypedArray()
|
// val statesIndex = states.map { it.ordinal }.toTypedArray()
|
||||||
// return doRealmQueryAndCopy(realmConfiguration) { realm ->
|
// return doRealmQueryAndCopy(realmConfiguration) { realm ->
|
||||||
|
|
|
@ -18,7 +18,7 @@ package org.matrix.android.sdk.internal.session.cleanup
|
||||||
|
|
||||||
import io.realm.Realm
|
import io.realm.Realm
|
||||||
import io.realm.RealmConfiguration
|
import io.realm.RealmConfiguration
|
||||||
import org.matrix.android.sdk.BuildConfig
|
import kotlinx.coroutines.delay
|
||||||
import org.matrix.android.sdk.internal.SessionManager
|
import org.matrix.android.sdk.internal.SessionManager
|
||||||
import org.matrix.android.sdk.internal.auth.SessionParamsStore
|
import org.matrix.android.sdk.internal.auth.SessionParamsStore
|
||||||
import org.matrix.android.sdk.internal.crypto.CryptoModule
|
import org.matrix.android.sdk.internal.crypto.CryptoModule
|
||||||
|
@ -51,37 +51,56 @@ internal class CleanupSession @Inject constructor(
|
||||||
@UserMd5 private val userMd5: String
|
@UserMd5 private val userMd5: String
|
||||||
) {
|
) {
|
||||||
suspend fun handle() {
|
suspend fun handle() {
|
||||||
|
val sessionRealmCount = Realm.getGlobalInstanceCount(realmSessionConfiguration)
|
||||||
|
val cryptoRealmCount = Realm.getGlobalInstanceCount(realmCryptoConfiguration)
|
||||||
|
Timber.d("Realm instance ($sessionRealmCount - $cryptoRealmCount)")
|
||||||
|
|
||||||
Timber.d("Cleanup: delete session params...")
|
Timber.d("Cleanup: delete session params...")
|
||||||
sessionParamsStore.delete(sessionId)
|
sessionParamsStore.delete(sessionId)
|
||||||
|
|
||||||
Timber.d("Cleanup: cancel pending works...")
|
Timber.d("Cleanup: cancel pending works...")
|
||||||
workManagerProvider.cancelAllWorks()
|
workManagerProvider.cancelAllWorks()
|
||||||
|
|
||||||
|
Timber.d("Cleanup: release session...")
|
||||||
|
sessionManager.releaseSession(sessionId)
|
||||||
|
|
||||||
Timber.d("Cleanup: clear session data...")
|
Timber.d("Cleanup: clear session data...")
|
||||||
clearSessionDataTask.execute(Unit)
|
clearSessionDataTask.execute(Unit)
|
||||||
|
|
||||||
Timber.d("Cleanup: clear crypto data...")
|
Timber.d("Cleanup: clear crypto data...")
|
||||||
clearCryptoDataTask.execute(Unit)
|
clearCryptoDataTask.execute(Unit)
|
||||||
|
|
||||||
Timber.d("Cleanup: clear file system")
|
|
||||||
sessionFiles.deleteRecursively()
|
|
||||||
sessionCache.deleteRecursively()
|
|
||||||
|
|
||||||
Timber.d("Cleanup: clear the database keys")
|
Timber.d("Cleanup: clear the database keys")
|
||||||
realmKeysUtils.clear(SessionModule.getKeyAlias(userMd5))
|
realmKeysUtils.clear(SessionModule.getKeyAlias(userMd5))
|
||||||
realmKeysUtils.clear(CryptoModule.getKeyAlias(userMd5))
|
realmKeysUtils.clear(CryptoModule.getKeyAlias(userMd5))
|
||||||
|
|
||||||
Timber.d("Cleanup: release session...")
|
// Wait for all the Realm instance to be released properly. Closing Realm instance is async.
|
||||||
sessionManager.releaseSession(sessionId)
|
// After that we can safely delete the Realm files
|
||||||
|
waitRealmRelease()
|
||||||
|
|
||||||
// Sanity check
|
Timber.d("Cleanup: clear file system")
|
||||||
if (BuildConfig.DEBUG) {
|
sessionFiles.deleteRecursively()
|
||||||
Realm.getGlobalInstanceCount(realmSessionConfiguration)
|
sessionCache.deleteRecursively()
|
||||||
.takeIf { it > 0 }
|
}
|
||||||
?.let { Timber.e("All realm instance for session has not been closed ($it)") }
|
|
||||||
Realm.getGlobalInstanceCount(realmCryptoConfiguration)
|
private suspend fun waitRealmRelease() {
|
||||||
.takeIf { it > 0 }
|
var timeToWaitMillis = MAX_TIME_TO_WAIT_MILLIS
|
||||||
?.let { Timber.e("All realm instance for crypto has not been closed ($it)") }
|
do {
|
||||||
}
|
val sessionRealmCount = Realm.getGlobalInstanceCount(realmSessionConfiguration)
|
||||||
|
val cryptoRealmCount = Realm.getGlobalInstanceCount(realmCryptoConfiguration)
|
||||||
|
Timber.d("Wait for all Realm instance to be closed ($sessionRealmCount - $cryptoRealmCount)")
|
||||||
|
if (sessionRealmCount > 0 || cryptoRealmCount > 0) {
|
||||||
|
Timber.d("Waiting ${TIME_TO_WAIT_MILLIS}ms")
|
||||||
|
delay(TIME_TO_WAIT_MILLIS)
|
||||||
|
timeToWaitMillis -= TIME_TO_WAIT_MILLIS
|
||||||
|
} else {
|
||||||
|
timeToWaitMillis = 0
|
||||||
|
}
|
||||||
|
} while (timeToWaitMillis > 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val MAX_TIME_TO_WAIT_MILLIS = 10_000L
|
||||||
|
private const val TIME_TO_WAIT_MILLIS = 10L
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue