Skip to content

Commit 09e8c10

Browse files
Merge pull request #5592 from vector-im/feature/aris/prevent_decryption_fom_suspend_functions
Avoid accessing realm instance from suspend functions
2 parents 5ac0555 + 5cfe218 commit 09e8c10

File tree

4 files changed

+62
-46
lines changed

4 files changed

+62
-46
lines changed

changelog.d/5592.bugfix

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Improve/fix crashes on messages decryption

matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/helper/ThreadSummaryHelper.kt

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import io.realm.Realm
2020
import io.realm.RealmQuery
2121
import io.realm.Sort
2222
import io.realm.kotlin.createObject
23+
import kotlinx.coroutines.runBlocking
2324
import org.matrix.android.sdk.api.session.crypto.CryptoService
2425
import org.matrix.android.sdk.api.session.crypto.MXCryptoError
2526
import org.matrix.android.sdk.api.session.crypto.model.OlmDecryptionResult
@@ -127,7 +128,7 @@ private fun EventEntity.toTimelineEventEntity(roomMemberContentsByUser: HashMap<
127128
return timelineEventEntity
128129
}
129130

130-
internal suspend fun ThreadSummaryEntity.Companion.createOrUpdate(
131+
internal fun ThreadSummaryEntity.Companion.createOrUpdate(
131132
threadSummaryType: ThreadSummaryUpdateType,
132133
realm: Realm,
133134
roomId: String,
@@ -153,10 +154,18 @@ internal suspend fun ThreadSummaryEntity.Companion.createOrUpdate(
153154
}
154155

155156
val rootThreadEventEntity = createEventEntity(roomId, rootThreadEvent, realm).also {
156-
decryptIfNeeded(cryptoService, it, roomId)
157+
try {
158+
decryptIfNeeded(cryptoService, it, roomId)
159+
} catch (e: InterruptedException) {
160+
Timber.i("Decryption got interrupted")
161+
}
157162
}
158163
val latestThreadEventEntity = createLatestEventEntity(roomId, rootThreadEvent, roomMemberContentsByUser, realm)?.also {
159-
decryptIfNeeded(cryptoService, it, roomId)
164+
try {
165+
decryptIfNeeded(cryptoService, it, roomId)
166+
} catch (e: InterruptedException) {
167+
Timber.i("Decryption got interrupted")
168+
}
160169
}
161170
val isUserParticipating = rootThreadEvent.unsignedData.relations.latestThread.isUserParticipating == true || rootThreadEvent.senderId == userId
162171
roomMemberContentsByUser.addSenderState(realm, roomId, rootThreadEvent.senderId)
@@ -204,14 +213,15 @@ internal suspend fun ThreadSummaryEntity.Companion.createOrUpdate(
204213
}
205214
}
206215

207-
private suspend fun decryptIfNeeded(cryptoService: CryptoService?, eventEntity: EventEntity, roomId: String) {
216+
private fun decryptIfNeeded(cryptoService: CryptoService?, eventEntity: EventEntity, roomId: String) {
208217
cryptoService ?: return
209218
val event = eventEntity.asDomain()
210219
if (event.isEncrypted() && event.mxDecryptionResult == null && event.eventId != null) {
211220
try {
212221
Timber.i("###THREADS ThreadSummaryHelper request decryption for eventId:${event.eventId}")
213222
// Event from sync does not have roomId, so add it to the event first
214-
val result = cryptoService.decryptEvent(event.copy(roomId = roomId), "")
223+
// note: runBlocking should be used here while we are in realm single thread executor, to avoid thread switching
224+
val result = runBlocking { cryptoService.decryptEvent(event.copy(roomId = roomId), "") }
215225
event.mxDecryptionResult = OlmDecryptionResult(
216226
payload = result.clearEvent,
217227
senderKey = result.senderCurve25519Key,

matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/TimelineEventDecryptor.kt

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -99,9 +99,7 @@ internal class TimelineEventDecryptor @Inject constructor(
9999
executor?.execute {
100100
Realm.getInstance(realmConfiguration).use { realm ->
101101
try {
102-
runBlocking {
103-
processDecryptRequest(request, realm)
104-
}
102+
processDecryptRequest(request, realm)
105103
} catch (e: InterruptedException) {
106104
Timber.i("Decryption got interrupted")
107105
}
@@ -121,7 +119,7 @@ internal class TimelineEventDecryptor @Inject constructor(
121119
}
122120
}
123121

124-
private suspend fun processDecryptRequest(request: DecryptionRequest, realm: Realm) {
122+
private fun processDecryptRequest(request: DecryptionRequest, realm: Realm) {
125123
val event = request.event
126124
val timelineId = request.timelineId
127125

@@ -132,7 +130,8 @@ internal class TimelineEventDecryptor @Inject constructor(
132130
return
133131
}
134132
try {
135-
val result = cryptoService.decryptEvent(request.event, timelineId)
133+
// note: runBlocking should be used here while we are in realm single thread executor, to avoid thread switching
134+
val result = runBlocking { cryptoService.decryptEvent(request.event, timelineId) }
136135
Timber.v("Successfully decrypted event ${event.eventId}")
137136
realm.executeTransaction {
138137
val eventId = event.eventId ?: return@executeTransaction

matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/handler/room/RoomSyncHandler.kt

Lines changed: 42 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -102,11 +102,11 @@ internal class RoomSyncHandler @Inject constructor(private val readReceiptHandle
102102
data class LEFT(val data: Map<String, RoomSync>) : HandlingStrategy()
103103
}
104104

105-
suspend fun handle(realm: Realm,
106-
roomsSyncResponse: RoomsSyncResponse,
107-
isInitialSync: Boolean,
108-
aggregator: SyncResponsePostTreatmentAggregator,
109-
reporter: ProgressReporter? = null) {
105+
fun handle(realm: Realm,
106+
roomsSyncResponse: RoomsSyncResponse,
107+
isInitialSync: Boolean,
108+
aggregator: SyncResponsePostTreatmentAggregator,
109+
reporter: ProgressReporter? = null) {
110110
handleRoomSync(realm, HandlingStrategy.JOINED(roomsSyncResponse.join), isInitialSync, aggregator, reporter)
111111
handleRoomSync(realm, HandlingStrategy.INVITED(roomsSyncResponse.invite), isInitialSync, aggregator, reporter)
112112
handleRoomSync(realm, HandlingStrategy.LEFT(roomsSyncResponse.leave), isInitialSync, aggregator, reporter)
@@ -120,11 +120,11 @@ internal class RoomSyncHandler @Inject constructor(private val readReceiptHandle
120120
}
121121
// PRIVATE METHODS *****************************************************************************
122122

123-
private suspend fun handleRoomSync(realm: Realm,
124-
handlingStrategy: HandlingStrategy,
125-
isInitialSync: Boolean,
126-
aggregator: SyncResponsePostTreatmentAggregator,
127-
reporter: ProgressReporter?) {
123+
private fun handleRoomSync(realm: Realm,
124+
handlingStrategy: HandlingStrategy,
125+
isInitialSync: Boolean,
126+
aggregator: SyncResponsePostTreatmentAggregator,
127+
reporter: ProgressReporter?) {
128128
val insertType = if (isInitialSync) {
129129
EventInsertType.INITIAL_SYNC
130130
} else {
@@ -157,11 +157,11 @@ internal class RoomSyncHandler @Inject constructor(private val readReceiptHandle
157157
realm.insertOrUpdate(rooms)
158158
}
159159

160-
private suspend fun insertJoinRoomsFromInitSync(realm: Realm,
161-
handlingStrategy: HandlingStrategy.JOINED,
162-
syncLocalTimeStampMillis: Long,
163-
aggregator: SyncResponsePostTreatmentAggregator,
164-
reporter: ProgressReporter?) {
160+
private fun insertJoinRoomsFromInitSync(realm: Realm,
161+
handlingStrategy: HandlingStrategy.JOINED,
162+
syncLocalTimeStampMillis: Long,
163+
aggregator: SyncResponsePostTreatmentAggregator,
164+
reporter: ProgressReporter?) {
165165
val bestChunkSize = computeBestChunkSize(
166166
listSize = handlingStrategy.data.keys.size,
167167
limit = (initialSyncStrategy as? InitialSyncStrategy.Optimized)?.maxRoomsToInsert ?: Int.MAX_VALUE
@@ -199,12 +199,12 @@ internal class RoomSyncHandler @Inject constructor(private val readReceiptHandle
199199
}
200200
}
201201

202-
private suspend fun handleJoinedRoom(realm: Realm,
203-
roomId: String,
204-
roomSync: RoomSync,
205-
insertType: EventInsertType,
206-
syncLocalTimestampMillis: Long,
207-
aggregator: SyncResponsePostTreatmentAggregator): RoomEntity {
202+
private fun handleJoinedRoom(realm: Realm,
203+
roomId: String,
204+
roomSync: RoomSync,
205+
insertType: EventInsertType,
206+
syncLocalTimestampMillis: Long,
207+
aggregator: SyncResponsePostTreatmentAggregator): RoomEntity {
208208
Timber.v("Handle join sync for room $roomId")
209209
val isInitialSync = insertType == EventInsertType.INITIAL_SYNC
210210

@@ -353,15 +353,15 @@ internal class RoomSyncHandler @Inject constructor(private val readReceiptHandle
353353
return roomEntity
354354
}
355355

356-
private suspend fun handleTimelineEvents(realm: Realm,
357-
roomId: String,
358-
roomEntity: RoomEntity,
359-
eventList: List<Event>,
360-
prevToken: String? = null,
361-
isLimited: Boolean = true,
362-
insertType: EventInsertType,
363-
syncLocalTimestampMillis: Long,
364-
aggregator: SyncResponsePostTreatmentAggregator): ChunkEntity {
356+
private fun handleTimelineEvents(realm: Realm,
357+
roomId: String,
358+
roomEntity: RoomEntity,
359+
eventList: List<Event>,
360+
prevToken: String? = null,
361+
isLimited: Boolean = true,
362+
insertType: EventInsertType,
363+
syncLocalTimestampMillis: Long,
364+
aggregator: SyncResponsePostTreatmentAggregator): ChunkEntity {
365365
val lastChunk = ChunkEntity.findLastForwardChunkOfRoom(realm, roomEntity.roomId)
366366
if (isLimited && lastChunk != null) {
367367
lastChunk.deleteOnCascade(deleteStateEvents = false, canDeleteRoot = true)
@@ -389,8 +389,10 @@ internal class RoomSyncHandler @Inject constructor(private val readReceiptHandle
389389
liveEventService.get().dispatchLiveEventReceived(event, roomId, isInitialSync)
390390

391391
if (event.isEncrypted() && !isInitialSync) {
392-
runBlocking {
392+
try {
393393
decryptIfNeeded(event, roomId)
394+
} catch (e: InterruptedException) {
395+
Timber.i("Decryption got interrupted")
394396
}
395397
}
396398
var contentToInject: String? = null
@@ -421,7 +423,8 @@ internal class RoomSyncHandler @Inject constructor(private val readReceiptHandle
421423
roomId = roomId,
422424
eventEntity = eventEntity,
423425
direction = PaginationDirection.FORWARDS,
424-
roomMemberContentsByUser = roomMemberContentsByUser)
426+
roomMemberContentsByUser = roomMemberContentsByUser
427+
)
425428
if (lightweightSettingsStorage.areThreadMessagesEnabled()) {
426429
eventEntity.rootThreadEventId?.let {
427430
// This is a thread event
@@ -437,7 +440,8 @@ internal class RoomSyncHandler @Inject constructor(private val readReceiptHandle
437440
threadEventEntity = eventEntity,
438441
roomMemberContentsByUser = roomMemberContentsByUser,
439442
userId = userId,
440-
roomEntity = roomEntity)
443+
roomEntity = roomEntity
444+
)
441445
}
442446
} ?: run {
443447
// This is a normal event or a root thread one
@@ -475,7 +479,8 @@ internal class RoomSyncHandler @Inject constructor(private val readReceiptHandle
475479
roomId = roomId,
476480
realm = realm,
477481
chunkEntity = chunkEntity,
478-
currentUserId = userId)
482+
currentUserId = userId
483+
)
479484
}
480485

481486
// posting new events to timeline if any is registered
@@ -505,10 +510,11 @@ internal class RoomSyncHandler @Inject constructor(private val readReceiptHandle
505510
}
506511
}
507512

508-
private suspend fun decryptIfNeeded(event: Event, roomId: String) {
513+
private fun decryptIfNeeded(event: Event, roomId: String) {
509514
try {
510515
// Event from sync does not have roomId, so add it to the event first
511-
val result = cryptoService.decryptEvent(event.copy(roomId = roomId), "")
516+
// note: runBlocking should be used here while we are in realm single thread executor, to avoid thread switching
517+
val result = runBlocking { cryptoService.decryptEvent(event.copy(roomId = roomId), "") }
512518
event.mxDecryptionResult = OlmDecryptionResult(
513519
payload = result.clearEvent,
514520
senderKey = result.senderCurve25519Key,

0 commit comments

Comments
 (0)