Merge pull request #5080 from vector-im/feature/adm/encrypted-last-message

Updating encrypted room list message previews on key updates
This commit is contained in:
Adam Brown 2022-04-01 17:48:05 +01:00 committed by GitHub
commit 3aac59a5ae
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 90 additions and 29 deletions

1
changelog.d/4867.bugfix Normal file
View File

@ -0,0 +1 @@
Fixes room summaries showing encrypted content after verifying device

View File

@ -140,7 +140,6 @@ interface CryptoService {
fun getLiveCryptoDeviceInfo(userIds: List<String>): LiveData<List<CryptoDeviceInfo>> fun getLiveCryptoDeviceInfo(userIds: List<String>): LiveData<List<CryptoDeviceInfo>>
fun addNewSessionListener(newSessionListener: NewSessionListener) fun addNewSessionListener(newSessionListener: NewSessionListener)
fun removeSessionListener(listener: NewSessionListener) fun removeSessionListener(listener: NewSessionListener)
fun getOutgoingRoomKeyRequests(): List<OutgoingRoomKeyRequest> fun getOutgoingRoomKeyRequests(): List<OutgoingRoomKeyRequest>

View File

@ -242,4 +242,12 @@ interface RoomService {
*/ */
fun getFlattenRoomSummaryChildrenOfLive(spaceId: String?, fun getFlattenRoomSummaryChildrenOfLive(spaceId: String?,
memberships: List<Membership> = Membership.activeMemberships()): LiveData<List<RoomSummary>> memberships: List<Membership> = Membership.activeMemberships()): LiveData<List<RoomSummary>>
/**
* Refreshes the RoomSummary LatestPreviewContent for the given @param roomId
* If the roomId is null, all rooms are updated
*
* This is useful for refreshing summary content with encrypted messages after receiving new room keys
*/
fun refreshJoinedRoomSummaryPreviews(roomId: String?)
} }

View File

@ -15,6 +15,15 @@
*/ */
package org.matrix.android.sdk.internal.crypto package org.matrix.android.sdk.internal.crypto
/**
* This listener notifies on new Megolm sessions being created
*/
interface NewSessionListener { interface NewSessionListener {
/**
* @param roomId the room id where the new Megolm session has been created for, may be null when importing from external sessions
* @param senderKey the sender key of the device which the Megolm session is shared with
* @param sessionId the session id of the Megolm session
*/
fun onNewSession(roomId: String?, senderKey: String, sessionId: String) fun onNewSession(roomId: String?, senderKey: String, sessionId: String)
} }

View File

@ -22,6 +22,7 @@ import org.matrix.android.sdk.internal.crypto.MXOlmDevice
import org.matrix.android.sdk.internal.crypto.MegolmSessionData import org.matrix.android.sdk.internal.crypto.MegolmSessionData
import org.matrix.android.sdk.internal.crypto.OutgoingGossipingRequestManager import org.matrix.android.sdk.internal.crypto.OutgoingGossipingRequestManager
import org.matrix.android.sdk.internal.crypto.RoomDecryptorProvider import org.matrix.android.sdk.internal.crypto.RoomDecryptorProvider
import org.matrix.android.sdk.internal.crypto.algorithms.megolm.MXMegolmDecryption
import org.matrix.android.sdk.internal.crypto.model.ImportRoomKeysResult import org.matrix.android.sdk.internal.crypto.model.ImportRoomKeysResult
import org.matrix.android.sdk.internal.crypto.model.rest.RoomKeyRequestBody import org.matrix.android.sdk.internal.crypto.model.rest.RoomKeyRequestBody
import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore
@ -76,7 +77,11 @@ internal class MegolmSessionDataImporter @Inject constructor(private val olmDevi
outgoingGossipingRequestManager.cancelRoomKeyRequest(roomKeyRequestBody) outgoingGossipingRequestManager.cancelRoomKeyRequest(roomKeyRequestBody)
// Have another go at decrypting events sent with this session // Have another go at decrypting events sent with this session
decrypting.onNewSession(megolmSessionData.senderKey!!, sessionId!!) when (decrypting) {
is MXMegolmDecryption -> {
decrypting.onNewSession(megolmSessionData.roomId, megolmSessionData.senderKey!!, sessionId!!)
}
}
} catch (e: Exception) { } catch (e: Exception) {
Timber.e(e, "## importRoomKeys() : onNewSession failed") Timber.e(e, "## importRoomKeys() : onNewSession failed")
} }

View File

@ -45,14 +45,6 @@ internal interface IMXDecrypting {
*/ */
fun onRoomKeyEvent(event: Event, defaultKeysBackupService: DefaultKeysBackupService) {} fun onRoomKeyEvent(event: Event, defaultKeysBackupService: DefaultKeysBackupService) {}
/**
* Check if the some messages can be decrypted with a new session
*
* @param senderKey the session sender key
* @param sessionId the session id
*/
fun onNewSession(senderKey: String, sessionId: String) {}
/** /**
* Determine if we have the keys necessary to respond to a room key request * Determine if we have the keys necessary to respond to a room key request
* *

View File

@ -318,19 +318,20 @@ internal class MXMegolmDecryption(private val userId: String,
outgoingGossipingRequestManager.cancelRoomKeyRequest(content) outgoingGossipingRequestManager.cancelRoomKeyRequest(content)
onNewSession(senderKey, roomKeyContent.sessionId) onNewSession(roomKeyContent.roomId, senderKey, roomKeyContent.sessionId)
} }
} }
/** /**
* Check if the some messages can be decrypted with a new session * Check if the some messages can be decrypted with a new session
* *
* @param roomId the room id where the new Megolm session has been created for, may be null when importing from external sessions
* @param senderKey the session sender key * @param senderKey the session sender key
* @param sessionId the session id * @param sessionId the session id
*/ */
override fun onNewSession(senderKey: String, sessionId: String) { fun onNewSession(roomId: String?, senderKey: String, sessionId: String) {
Timber.tag(loggerTag.value).v("ON NEW SESSION $sessionId - $senderKey") Timber.tag(loggerTag.value).v("ON NEW SESSION $sessionId - $senderKey")
newSessionListener?.onNewSession(null, senderKey, sessionId) newSessionListener?.onNewSession(roomId, senderKey, sessionId)
} }
override fun hasKeysForKeyRequest(request: IncomingRoomKeyRequest): Boolean { override fun hasKeysForKeyRequest(request: IncomingRoomKeyRequest): Boolean {

View File

@ -52,7 +52,7 @@ import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
internal class UpdateTrustWorker(context: Context, params: WorkerParameters, sessionManager: SessionManager) : internal class UpdateTrustWorker(context: Context, params: WorkerParameters, sessionManager: SessionManager) :
SessionSafeCoroutineWorker<UpdateTrustWorker.Params>(context, params, sessionManager, Params::class.java) { SessionSafeCoroutineWorker<UpdateTrustWorker.Params>(context, params, sessionManager, Params::class.java) {
@JsonClass(generateAdapter = true) @JsonClass(generateAdapter = true)
internal data class Params( internal data class Params(

View File

@ -49,6 +49,10 @@ internal fun RoomSummaryEntity.Companion.getOrCreate(realm: Realm, roomId: Strin
return where(realm, roomId).findFirst() ?: realm.createObject(roomId) return where(realm, roomId).findFirst() ?: realm.createObject(roomId)
} }
internal fun RoomSummaryEntity.Companion.getOrNull(realm: Realm, roomId: String): RoomSummaryEntity? {
return where(realm, roomId).findFirst()
}
internal fun RoomSummaryEntity.Companion.getDirectRooms(realm: Realm, internal fun RoomSummaryEntity.Companion.getDirectRooms(realm: Realm,
excludeRoomIds: Set<String>? = null): RealmResults<RoomSummaryEntity> { excludeRoomIds: Set<String>? = null): RealmResults<RoomSummaryEntity> {
return RoomSummaryEntity.where(realm) return RoomSummaryEntity.where(realm)

View File

@ -20,6 +20,7 @@ import androidx.lifecycle.LiveData
import androidx.lifecycle.Transformations import androidx.lifecycle.Transformations
import androidx.paging.PagedList import androidx.paging.PagedList
import com.zhuinden.monarchy.Monarchy import com.zhuinden.monarchy.Monarchy
import org.matrix.android.sdk.api.query.QueryStringValue
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.Room import org.matrix.android.sdk.api.session.room.Room
import org.matrix.android.sdk.api.session.room.RoomService import org.matrix.android.sdk.api.session.room.RoomService
@ -32,6 +33,7 @@ import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary
import org.matrix.android.sdk.api.session.room.model.RoomSummary import org.matrix.android.sdk.api.session.room.model.RoomSummary
import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams
import org.matrix.android.sdk.api.session.room.peeking.PeekResult import org.matrix.android.sdk.api.session.room.peeking.PeekResult
import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams
import org.matrix.android.sdk.api.session.room.summary.RoomAggregateNotificationCount import org.matrix.android.sdk.api.session.room.summary.RoomAggregateNotificationCount
import org.matrix.android.sdk.api.util.Optional import org.matrix.android.sdk.api.util.Optional
import org.matrix.android.sdk.api.util.toOptional import org.matrix.android.sdk.api.util.toOptional
@ -51,6 +53,7 @@ import org.matrix.android.sdk.internal.session.room.peeking.PeekRoomTask
import org.matrix.android.sdk.internal.session.room.peeking.ResolveRoomStateTask import org.matrix.android.sdk.internal.session.room.peeking.ResolveRoomStateTask
import org.matrix.android.sdk.internal.session.room.read.MarkAllRoomsReadTask import org.matrix.android.sdk.internal.session.room.read.MarkAllRoomsReadTask
import org.matrix.android.sdk.internal.session.room.summary.RoomSummaryDataSource import org.matrix.android.sdk.internal.session.room.summary.RoomSummaryDataSource
import org.matrix.android.sdk.internal.session.room.summary.RoomSummaryUpdater
import org.matrix.android.sdk.internal.session.user.accountdata.UpdateBreadcrumbsTask import org.matrix.android.sdk.internal.session.user.accountdata.UpdateBreadcrumbsTask
import org.matrix.android.sdk.internal.util.fetchCopied import org.matrix.android.sdk.internal.util.fetchCopied
import javax.inject.Inject import javax.inject.Inject
@ -69,6 +72,7 @@ internal class DefaultRoomService @Inject constructor(
private val roomSummaryDataSource: RoomSummaryDataSource, private val roomSummaryDataSource: RoomSummaryDataSource,
private val roomChangeMembershipStateDataSource: RoomChangeMembershipStateDataSource, private val roomChangeMembershipStateDataSource: RoomChangeMembershipStateDataSource,
private val leaveRoomTask: LeaveRoomTask, private val leaveRoomTask: LeaveRoomTask,
private val roomSummaryUpdater: RoomSummaryUpdater
) : RoomService { ) : RoomService {
override suspend fun createRoom(createRoomParams: CreateRoomParams): String { override suspend fun createRoom(createRoomParams: CreateRoomParams): String {
@ -92,6 +96,23 @@ internal class DefaultRoomService @Inject constructor(
return roomSummaryDataSource.getRoomSummaries(queryParams, sortOrder) return roomSummaryDataSource.getRoomSummaries(queryParams, sortOrder)
} }
override fun refreshJoinedRoomSummaryPreviews(roomId: String?) {
val roomSummaries = getRoomSummaries(roomSummaryQueryParams {
if (roomId != null) {
this.roomId = QueryStringValue.Equals(roomId)
}
memberships = listOf(Membership.JOIN)
})
if (roomSummaries.isNotEmpty()) {
monarchy.runTransactionSync { realm ->
roomSummaries.forEach {
roomSummaryUpdater.refreshLatestPreviewContent(realm, it.roomId)
}
}
}
}
override fun getRoomSummariesLive(queryParams: RoomSummaryQueryParams, override fun getRoomSummariesLive(queryParams: RoomSummaryQueryParams,
sortOrder: RoomSortOrder): LiveData<List<RoomSummary>> { sortOrder: RoomSortOrder): LiveData<List<RoomSummary>> {
return roomSummaryDataSource.getRoomSummariesLive(queryParams, sortOrder) return roomSummaryDataSource.getRoomSummariesLive(queryParams, sortOrder)

View File

@ -64,7 +64,6 @@ import org.matrix.android.sdk.internal.session.room.accountdata.RoomAccountDataD
import org.matrix.android.sdk.internal.session.room.membership.RoomDisplayNameResolver import org.matrix.android.sdk.internal.session.room.membership.RoomDisplayNameResolver
import org.matrix.android.sdk.internal.session.room.membership.RoomMemberHelper import org.matrix.android.sdk.internal.session.room.membership.RoomMemberHelper
import org.matrix.android.sdk.internal.session.room.relationship.RoomChildRelationInfo import org.matrix.android.sdk.internal.session.room.relationship.RoomChildRelationInfo
import org.matrix.android.sdk.internal.util.Normalizer
import timber.log.Timber import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
import kotlin.system.measureTimeMillis import kotlin.system.measureTimeMillis
@ -75,8 +74,16 @@ internal class RoomSummaryUpdater @Inject constructor(
private val roomAvatarResolver: RoomAvatarResolver, private val roomAvatarResolver: RoomAvatarResolver,
private val eventDecryptor: EventDecryptor, private val eventDecryptor: EventDecryptor,
private val crossSigningService: DefaultCrossSigningService, private val crossSigningService: DefaultCrossSigningService,
private val roomAccountDataDataSource: RoomAccountDataDataSource, private val roomAccountDataDataSource: RoomAccountDataDataSource
private val normalizer: Normalizer) { ) {
fun refreshLatestPreviewContent(realm: Realm, roomId: String) {
val roomSummaryEntity = RoomSummaryEntity.getOrNull(realm, roomId)
if (roomSummaryEntity != null) {
val latestPreviewableEvent = RoomSummaryEventsHelper.getLatestPreviewableEvent(realm, roomId)
latestPreviewableEvent?.attemptToDecrypt()
}
}
fun update(realm: Realm, fun update(realm: Realm,
roomId: String, roomId: String,
@ -128,6 +135,7 @@ internal class RoomSummaryUpdater @Inject constructor(
val lastActivityFromEvent = latestPreviewableEvent?.root?.originServerTs val lastActivityFromEvent = latestPreviewableEvent?.root?.originServerTs
if (lastActivityFromEvent != null) { if (lastActivityFromEvent != null) {
roomSummaryEntity.lastActivityTime = lastActivityFromEvent roomSummaryEntity.lastActivityTime = lastActivityFromEvent
latestPreviewableEvent.attemptToDecrypt()
} }
roomSummaryEntity.hasUnreadMessages = roomSummaryEntity.notificationCount > 0 || roomSummaryEntity.hasUnreadMessages = roomSummaryEntity.notificationCount > 0 ||
@ -161,18 +169,6 @@ internal class RoomSummaryUpdater @Inject constructor(
} }
roomSummaryEntity.updateHasFailedSending() roomSummaryEntity.updateHasFailedSending()
val root = latestPreviewableEvent?.root
if (root?.type == EventType.ENCRYPTED && root.decryptionResultJson == null) {
Timber.v("Should decrypt ${latestPreviewableEvent.eventId}")
// mmm i want to decrypt now or is it ok to do it async?
tryOrNull {
runBlocking {
eventDecryptor.decryptEvent(root.asDomain(), "")
}
}
?.let { root.setDecryptionResult(it) }
}
if (updateMembers) { if (updateMembers) {
val otherRoomMembers = RoomMemberHelper(realm, roomId) val otherRoomMembers = RoomMemberHelper(realm, roomId)
.queryActiveRoomMembersEvent() .queryActiveRoomMembersEvent()
@ -189,6 +185,22 @@ internal class RoomSummaryUpdater @Inject constructor(
} }
} }
private fun TimelineEventEntity.attemptToDecrypt() {
when (val root = this.root) {
null -> {
Timber.v("Decryption skipped due to missing root event $eventId")
}
else -> {
if (root.type == EventType.ENCRYPTED && root.decryptionResultJson == null) {
Timber.v("Should decrypt $eventId")
tryOrNull {
runBlocking { eventDecryptor.decryptEvent(root.asDomain(), "") }
}?.let { root.setDecryptionResult(it) }
}
}
}
}
private fun RoomSummaryEntity.updateHasFailedSending() { private fun RoomSummaryEntity.updateHasFailedSending() {
hasFailedSending = TimelineEventEntity.findAllInRoomWithSendStates(realm, roomId, SendState.HAS_FAILED_STATES).isNotEmpty() hasFailedSending = TimelineEventEntity.findAllInRoomWithSendStates(realm, roomId, SendState.HAS_FAILED_STATES).isNotEmpty()
} }

View File

@ -54,6 +54,7 @@ import org.matrix.android.sdk.api.session.room.model.Membership
import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams
import org.matrix.android.sdk.api.util.toMatrixItem import org.matrix.android.sdk.api.util.toMatrixItem
import org.matrix.android.sdk.flow.flow import org.matrix.android.sdk.flow.flow
import org.matrix.android.sdk.internal.crypto.NewSessionListener
import timber.log.Timber import timber.log.Timber
/** /**
@ -88,9 +89,16 @@ class HomeDetailViewModel @AssistedInject constructor(
} }
} }
private val refreshRoomSummariesOnCryptoSessionChange = object : NewSessionListener {
override fun onNewSession(roomId: String?, senderKey: String, sessionId: String) {
session.refreshJoinedRoomSummaryPreviews(roomId)
}
}
init { init {
observeSyncState() observeSyncState()
observeRoomGroupingMethod() observeRoomGroupingMethod()
session.cryptoService().addNewSessionListener(refreshRoomSummariesOnCryptoSessionChange)
observeRoomSummaries() observeRoomSummaries()
updatePstnSupportFlag() updatePstnSupportFlag()
observeDataStore() observeDataStore()
@ -150,6 +158,7 @@ class HomeDetailViewModel @AssistedInject constructor(
override fun onCleared() { override fun onCleared() {
super.onCleared() super.onCleared()
callManager.removeProtocolsCheckerListener(this) callManager.removeProtocolsCheckerListener(this)
session.cryptoService().removeSessionListener(refreshRoomSummariesOnCryptoSessionChange)
} }
override fun onPSTNSupportUpdated() { override fun onPSTNSupportUpdated() {