-
Notifications
You must be signed in to change notification settings - Fork 767
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Updating encrypted room list message previews on key updates #5080
Conversation
@@ -90,6 +94,23 @@ internal class DefaultRoomService @Inject constructor( | |||
return roomSummaryDataSource.getRoomSummaries(queryParams, sortOrder) | |||
} | |||
|
|||
override fun refreshJoinedRoomSummaryPreviews(roomId: String?) { | |||
val roomSummaries = getRoomSummaries(roomSummaryQueryParams { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
due to nullability in the deeper crypto layers the room id isn't always guaranteed, I wasn't sure if we should skip those cases or attempt to refresh the entire room list
Matrix SDKIntegration Tests Results:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice fix!
Some though about it.
* | ||
* This is useful for refreshing summary content with encrypted messages after receiving new room keys | ||
*/ | ||
fun refreshJoinedRoomSummaryPreviews(roomId: String?) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
According to your video and following the implementation, the changes LGTM.
But I am wondering why we have to expose such API to the app. Can't this new decryption attempt be managed only SDK side? Since the SDK maintain the latestPreviewableEvent in the RoomSummary to me it would make more sense.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
moving to the sdk also makes sense to me, the tricky part is where~
in the timeline it's managed by the SDK DefaultTimeline
abstraction which acts as the datasource and holder for the listeners to allow the SDK to manage the crypto/pagination callbacks, from the client ViewModel
we simply call timeline.start()
currently the room summaries is just a data model which clients observe using RoomService.getRoomSummariesLive
, we could add a start/stop
to the RoomService
to internally register for crypto callbacks or create another abstraction on top of the summaries similar to Timeline
🤔 what do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
using the FlowSession
abstraction we can wrap and combine the room and crypto services
fun liveRoomSummaries(queryParams: RoomSummaryQueryParams, sortOrder: RoomSortOrder = RoomSortOrder.NONE): Flow<List<RoomSummary>> {
val refreshRoomSummariesOnCryptoSessionChange = object : NewSessionListener {
override fun onNewSession(roomId: String?, senderKey: String, sessionId: String) {
session.refreshJoinedRoomSummaryPreviews(roomId)
}
}
return session.getRoomSummariesLive(queryParams, sortOrder).asFlow()
.startWith(session.coroutineDispatchers.io) { session.getRoomSummaries(queryParams, sortOrder) }
.onStart { session.cryptoService().addNewSessionListener(refreshRoomSummariesOnCryptoSessionChange) }
.onCompletion { session.cryptoService().removeSessionListener(refreshRoomSummariesOnCryptoSessionChange) }
}
with the current keys being updated in background workers it makes it very difficult (and potentially easier to introduce memory leaks) to attach a key listener in the lower levels of the sync handler or dynamically spawning them when calling the room summary update
conceptually I like the idea of an abstraction on top of the Session api (similar to the SessionFlow
) which would interact and coordinate with the separate services to provide a source of truth
@@ -149,6 +157,7 @@ class HomeDetailViewModel @AssistedInject constructor( | |||
override fun onCleared() { | |||
super.onCleared() | |||
callManager.removeProtocolsCheckerListener(this) | |||
session.cryptoService().removeSessionListener(refreshRoomSummariesOnCryptoSessionChange) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the only change app side and it's clear that we listen for callback from the SDK to trigger a request to the SDK. So I think it confirms that it could be managed only by the SDK.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah I think it makes sense to keep it in the SDK too (my initial thought was to do it in app too, but it probably should be in the SDK).
I was thinking of letting the RoomSummary having an instance of TimelineEventDecryptor.
Then for every undecrypted lastMessage, we just request the TimelineEventDecryptor to decrypt it, it will then by itself listen to session and retry to decrypt.
Two points to check:
- When the TimelineEventDecryptor succesfully decrypt an event (and will update EventEntity), would this trigger an update to the LiveRoomSummaries?
- We have to encure that it's the same TimelineEventDecryptor that is used during the session lifetime (if not it won't remember all the event to decrypt). It should belong some how to the session
Does it make sense?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the TimelineEventDecrypter
is currently tied to the RoomDetailsActivity
via the Timeline
(and ViewModel), we could potentially lift it out and share with the RoomSummaryUpdater
but we would need to register/unregister the key watching somewhere~
When the TimelineEventDecryptor succesfully decrypt an event (and will update EventEntity), would this trigger an update to the LiveRoomSummaries?
as far as I'm aware yes (this PR currently only updates the preview part of the entity at gets the live updates)
@@ -76,7 +77,11 @@ internal class MegolmSessionDataImporter @Inject constructor(private val olmDevi | |||
outgoingGossipingRequestManager.cancelRoomKeyRequest(roomKeyRequestBody) | |||
|
|||
// Have another go at decrypting events sent with this session | |||
decrypting.onNewSession(megolmSessionData.senderKey!!, sessionId!!) | |||
when (decrypting) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For things like that I used the extension pattern. For example there is the IMXWithHeldExtension that is only supported by MXMegolmDecryption and not by MXOlmDecryption. So in code you can do
if (alg is IMXWithHeldExtension) {
alg.onRoomKeyWithHeldEvent(withHeldContent)
}
Maybe we can use also this principles for new session? Or let me know if you think it's too overengineered
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that could be quite nice as we're not using any of the other functions from the MXMegolmDecryption
maybe a bit of a hot take but I'm also wondering if we need the base interface? it seems that only fun decryptEvent(event: Event, timeline: String): MXEventDecryptionResult
is used by both olm and megolm
when talking to the roomDecryptorProvider.getOrCreateRoomDecryptor
we tend to already know which type of decrypter we need, in the case of MegolmSessionDataImporter
I wouldn't expect to see any olm decrypters popping up
@@ -327,9 +327,9 @@ internal class MXMegolmDecryption(private val userId: String, | |||
* @param senderKey the session sender key | |||
* @param sessionId the session id | |||
*/ | |||
override fun onNewSession(senderKey: String, sessionId: String) { | |||
fun onNewSession(roomId: String?, senderKey: String, sessionId: String) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please, can you update the documentation about the new param? Also, it would be great if we could have some documentation about the NewSessionListener interface (which would probably be the same as this method) :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
will do 👍 If my understanding is correct onNewSession
is triggered when we create a new megolm session for a given room, either by importing existing keys from cross signing/manual import or when receiving a toDevice room key share event
The change is to chain the existing room id from the key payload so that we can use it further down stream as a way know which rooms to attempt to re-decrypt
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the explanation, the link between the megolm session and the room was unclear to me!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
docs added! c679890
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the docs update! It would be perfect if you could also add the new param in the doc of that function to be up to date with your changes
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ah my bad, I thought you meant the interface, will update this method as well 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My comment was for both, the interface and the current method which already had documentation :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
updated both and provided context on why the value may be null 57bf044
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After rebase I am ok to merge
@BillCarsonFr thanks for taking a look, will update! hopefully something the rust sdk will provide for us for free in the future 🤞 |
…levant for Megolm sessions
- matches the same flow as the timeline by starting observing in the ViewModel init
683a551
to
eb72587
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just a small remark, SGTM otherwise 💯
@@ -327,9 +327,9 @@ internal class MXMegolmDecryption(private val userId: String, | |||
* @param senderKey the session sender key | |||
* @param sessionId the session id | |||
*/ | |||
override fun onNewSession(senderKey: String, sessionId: String) { | |||
fun onNewSession(roomId: String?, senderKey: String, sessionId: String) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the docs update! It would be perfect if you could also add the new param in the doc of that function to be up to date with your changes
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, but still a bit disappointed that we have to do changes app side. It will not be easy for other application using the SDK to do the same thing.
Since it fixes the issue, I do not want to block merging this PR.
c679890
to
57bf044
Compare
Fixes #4867 - Message previews not decrypting upon receiving crypto keys
Follows the example of the timeline and adds a
session.cryptoService().addNewSessionListener
which refreshes theRoomSummary.LatestPreviewContent
whenever we get a new key for the given room, the decryption will only be attempted if the current preview content doesn't contain an already decrypted jsonTimeline example
https://github.com/vector-im/element-android/blob/develop/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt#L159
https://github.com/vector-im/element-android/blob/develop/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimeline.kt#L121
https://github.com/vector-im/element-android/blob/develop/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimeline.kt#L235
https://github.com/vector-im/element-android/blob/develop/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/LoadTimelineStrategy.kt#L143
https://github.com/vector-im/element-android/blob/develop/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/TimelineEventDecryptor.kt#L67
onNewSession
function from the baseIMXDecrypting
interface as it's only used for Megolm sessions and allows us to better ensure a room id is available*before shows a new message coming in at the end, not the original message being decrypted