Use ImportKeysResult to notify sessions listeners

This commit is contained in:
Valere 2021-11-29 14:41:37 +01:00
parent ee6eec041a
commit 24dc52e4f6
11 changed files with 118 additions and 29 deletions

View file

@ -134,6 +134,7 @@ internal class DefaultCryptoService @Inject constructor(
private val crossSigningService: CrossSigningService, private val crossSigningService: CrossSigningService,
private val verificationService: RustVerificationService, private val verificationService: RustVerificationService,
private val keysBackupService: RustKeyBackupService, private val keysBackupService: RustKeyBackupService,
private val megolmSessionImportManager: MegolmSessionImportManager,
private val olmMachineProvider: OlmMachineProvider private val olmMachineProvider: OlmMachineProvider
) : CryptoService { ) : CryptoService {
@ -152,9 +153,6 @@ internal class DefaultCryptoService @Inject constructor(
private val outgoingRequestsLock: Mutex = Mutex() private val outgoingRequestsLock: Mutex = Mutex()
private val roomKeyShareLocks: ConcurrentHashMap<String, Mutex> = ConcurrentHashMap() private val roomKeyShareLocks: ConcurrentHashMap<String, Mutex> = ConcurrentHashMap()
// TODO does this need to be concurrent?
private val newSessionListeners = ArrayList<NewSessionListener>()
fun onStateEvent(roomId: String, event: Event) { fun onStateEvent(roomId: String, event: Event) {
when (event.getClearType()) { when (event.getClearType()) {
EventType.STATE_ROOM_ENCRYPTION -> onRoomEncryptionEvent(roomId, event) EventType.STATE_ROOM_ENCRYPTION -> onRoomEncryptionEvent(roomId, event)
@ -675,17 +673,7 @@ internal class DefaultCryptoService @Inject constructor(
roomId: String, roomId: String,
sessionId: String, sessionId: String,
) { ) {
// The sender key is actually unused since it's unimportant for megolm megolmSessionImportManager.dispatchNewSession(roomId, sessionId)
// Our events don't contain the info so pass an empty string until we
// change the listener definition
val senderKey = ""
newSessionListeners.forEach {
try {
it.onNewSession(roomId, senderKey, sessionId)
} catch (e: Throwable) {
}
}
} }
suspend fun receiveSyncChanges( suspend fun receiveSyncChanges(
@ -886,7 +874,9 @@ internal class DefaultCryptoService @Inject constructor(
override suspend fun importRoomKeys(roomKeysAsArray: ByteArray, override suspend fun importRoomKeys(roomKeysAsArray: ByteArray,
password: String, password: String,
progressListener: ProgressListener?): ImportRoomKeysResult { progressListener: ProgressListener?): ImportRoomKeysResult {
val result = olmMachine.importKeys(roomKeysAsArray, password, progressListener) val result = olmMachine.importKeys(roomKeysAsArray, password, progressListener).also {
megolmSessionImportManager.dispatchKeyImportResults(it)
}
keysBackupService.maybeBackupKeys() keysBackupService.maybeBackupKeys()
return result return result
@ -1030,11 +1020,11 @@ internal class DefaultCryptoService @Inject constructor(
} }
override fun addNewSessionListener(newSessionListener: NewSessionListener) { override fun addNewSessionListener(newSessionListener: NewSessionListener) {
if (!newSessionListeners.contains(newSessionListener)) newSessionListeners.add(newSessionListener) megolmSessionImportManager.addListener(newSessionListener)
} }
override fun removeSessionListener(listener: NewSessionListener) { override fun removeSessionListener(listener: NewSessionListener) {
newSessionListeners.remove(listener) megolmSessionImportManager.removeListener(listener)
} }
/* ========================================================================================== /* ==========================================================================================
* DEBUG INFO * DEBUG INFO

View file

@ -44,6 +44,7 @@ import timber.log.Timber
import java.util.concurrent.Executors import java.util.concurrent.Executors
import javax.inject.Inject import javax.inject.Inject
@Deprecated("rust")
@SessionScope @SessionScope
internal class IncomingGossipingRequestManager @Inject constructor( internal class IncomingGossipingRequestManager @Inject constructor(
@SessionId private val sessionId: String, @SessionId private val sessionId: String,

View file

@ -0,0 +1,77 @@
/*
* Copyright 2021 The Matrix.org Foundation C.I.C.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.matrix.android.sdk.internal.crypto
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import org.matrix.android.sdk.api.MatrixCoroutineDispatchers
import org.matrix.android.sdk.api.extensions.tryOrNull
import org.matrix.android.sdk.internal.crypto.model.ImportRoomKeysResult
import org.matrix.android.sdk.internal.session.SessionScope
import javax.inject.Inject
/**
* Helper that allows listeners to be notified when a new megolm session
* has been added to the crypto layer (could be via room keys or forward keys via sync
* or after importing keys from key backup or manual import).
* Can be used to refresh display when the keys are received after the message
*/
@SessionScope
internal class MegolmSessionImportManager @Inject constructor(
private val coroutineDispatchers: MatrixCoroutineDispatchers,
private val cryptoCoroutineScope: CoroutineScope
) {
private val newSessionsListeners = mutableListOf<NewSessionListener>()
fun addListener(listener: NewSessionListener) {
synchronized(newSessionsListeners) {
if (!newSessionsListeners.contains(listener)) {
newSessionsListeners.add(listener)
}
}
}
fun removeListener(listener: NewSessionListener) {
synchronized(newSessionsListeners) {
newSessionsListeners.remove(listener)
}
}
fun dispatchNewSession(roomId: String?, sessionId: String) {
val copy = synchronized(newSessionsListeners) {
newSessionsListeners.toList()
}
cryptoCoroutineScope.launch(coroutineDispatchers.computation) {
copy.forEach {
tryOrNull("Failed to dispatch new session import") {
it.onNewSession(roomId, sessionId)
}
}
}
}
fun dispatchKeyImportResults(result: ImportRoomKeysResult) {
result.importedSessionInfo.forEach { (roomId, senderToSessionIdMap) ->
senderToSessionIdMap.values.forEach { sessionList ->
sessionList.forEach { sessionId ->
dispatchNewSession(roomId, sessionId)
}
}
}
}
}

View file

@ -16,5 +16,5 @@
package org.matrix.android.sdk.internal.crypto package org.matrix.android.sdk.internal.crypto
interface NewSessionListener { interface NewSessionListener {
fun onNewSession(roomId: String?, senderKey: String, sessionId: String) fun onNewSession(roomId: String?, sessionId: String)
} }

View file

@ -510,8 +510,7 @@ internal class OlmMachine(
val result = inner.importKeys(decodedKeys, passphrase, rustListener) val result = inner.importKeys(decodedKeys, passphrase, rustListener)
// TODO do we want to remove the cast here? ImportRoomKeysResult.fromOlm(result)
ImportRoomKeysResult(result.total.toInt(), result.imported.toInt())
} }
@Throws(CryptoStoreException::class) @Throws(CryptoStoreException::class)
@ -520,13 +519,14 @@ internal class OlmMachine(
listener: ProgressListener? listener: ProgressListener?
): ImportRoomKeysResult = ): ImportRoomKeysResult =
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
val adapter = MoshiProvider.providesMoshi().adapter(List::class.java) val adapter = MoshiProvider.providesMoshi().adapter(List::class.java)
// If the key backup is too big we take the risk of causing OOM // If the key backup is too big we take the risk of causing OOM
// when serializing to json
// so let's chunk to avoid it // so let's chunk to avoid it
var totalImported = 0L var totalImported = 0L
var accTotal = 0L var accTotal = 0L
val details = mutableMapOf<String, Map<String, List<String>>>()
keys.chunked(500) keys.chunked(500)
.forEach { keysSlice -> .forEach { keysSlice ->
val encodedKeys = adapter.toJson(keysSlice) val encodedKeys = adapter.toJson(keysSlice)
@ -540,9 +540,10 @@ internal class OlmMachine(
inner.importDecryptedKeys(encodedKeys, rustListener).let { inner.importDecryptedKeys(encodedKeys, rustListener).let {
totalImported += it.imported totalImported += it.imported
accTotal += it.total accTotal += it.total
details.putAll(it.keys)
} }
} }
ImportRoomKeysResult(totalImported.toInt(), accTotal.toInt()) ImportRoomKeysResult(totalImported.toInt(), accTotal.toInt(), details)
} }
@Throws(CryptoStoreException::class) @Throws(CryptoStoreException::class)

View file

@ -72,11 +72,11 @@ internal class RoomDecryptorProvider @Inject constructor(
val alg = when (algorithm) { val alg = when (algorithm) {
MXCRYPTO_ALGORITHM_MEGOLM -> megolmDecryptionFactory.create().apply { MXCRYPTO_ALGORITHM_MEGOLM -> megolmDecryptionFactory.create().apply {
this.newSessionListener = object : NewSessionListener { this.newSessionListener = object : NewSessionListener {
override fun onNewSession(roomId: String?, senderKey: String, sessionId: String) { override fun onNewSession(roomId: String?, sessionId: String) {
// PR reviewer: the parameter has been renamed so is now in conflict with the parameter of getOrCreateRoomDecryptor // PR reviewer: the parameter has been renamed so is now in conflict with the parameter of getOrCreateRoomDecryptor
newSessionListeners.forEach { newSessionListeners.forEach {
try { try {
it.onNewSession(roomId, senderKey, sessionId) it.onNewSession(roomId, sessionId)
} catch (e: Throwable) { } catch (e: Throwable) {
} }
} }

View file

@ -103,6 +103,6 @@ internal class MegolmSessionDataImporter @Inject constructor(private val olmDevi
Timber.v("## importMegolmSessionsData : sessions import " + (t1 - t0) + " ms (" + megolmSessionsData.size + " sessions)") Timber.v("## importMegolmSessionsData : sessions import " + (t1 - t0) + " ms (" + megolmSessionsData.size + " sessions)")
return ImportRoomKeysResult(totalNumbersOfKeys, totalNumbersOfImportedKeys) return ImportRoomKeysResult(totalNumbersOfKeys, totalNumbersOfImportedKeys, emptyMap())
} }
} }

View file

@ -317,7 +317,7 @@ internal class MXMegolmDecryption(private val userId: String,
*/ */
override fun onNewSession(senderKey: String, sessionId: String) { override fun onNewSession(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(null, sessionId)
} }
override fun hasKeysForKeyRequest(request: IncomingRoomKeyRequest): Boolean { override fun hasKeysForKeyRequest(request: IncomingRoomKeyRequest): Boolean {

View file

@ -39,6 +39,7 @@ import org.matrix.android.sdk.api.session.crypto.keysbackup.KeysBackupState
import org.matrix.android.sdk.api.session.crypto.keysbackup.KeysBackupStateListener import org.matrix.android.sdk.api.session.crypto.keysbackup.KeysBackupStateListener
import org.matrix.android.sdk.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM_BACKUP import org.matrix.android.sdk.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM_BACKUP
import org.matrix.android.sdk.internal.crypto.MegolmSessionData import org.matrix.android.sdk.internal.crypto.MegolmSessionData
import org.matrix.android.sdk.internal.crypto.MegolmSessionImportManager
import org.matrix.android.sdk.internal.crypto.OlmMachineProvider import org.matrix.android.sdk.internal.crypto.OlmMachineProvider
import org.matrix.android.sdk.internal.crypto.RequestSender import org.matrix.android.sdk.internal.crypto.RequestSender
import org.matrix.android.sdk.internal.crypto.keysbackup.model.KeysBackupVersionTrust import org.matrix.android.sdk.internal.crypto.keysbackup.model.KeysBackupVersionTrust
@ -74,6 +75,7 @@ internal class RustKeyBackupService @Inject constructor(
olmMachineProvider: OlmMachineProvider, olmMachineProvider: OlmMachineProvider,
private val sender: RequestSender, private val sender: RequestSender,
private val coroutineDispatchers: MatrixCoroutineDispatchers, private val coroutineDispatchers: MatrixCoroutineDispatchers,
private val megolmSessionImportManager: MegolmSessionImportManager,
private val cryptoCoroutineScope: CoroutineScope, private val cryptoCoroutineScope: CoroutineScope,
) : KeysBackupService { ) : KeysBackupService {
companion object { companion object {
@ -545,7 +547,9 @@ internal class RustKeyBackupService @Inject constructor(
null null
} }
val result = olmMachine.importDecryptedKeys(sessionsData, progressListener) val result = olmMachine.importDecryptedKeys(sessionsData, progressListener).also {
megolmSessionImportManager.dispatchKeyImportResults(it)
}
// Do not back up the key if it comes from a backup recovery // Do not back up the key if it comes from a backup recovery
if (backUp) { if (backUp) {

View file

@ -16,5 +16,21 @@
package org.matrix.android.sdk.internal.crypto.model package org.matrix.android.sdk.internal.crypto.model
import uniffi.olm.KeysImportResult
data class ImportRoomKeysResult(val totalNumberOfKeys: Int, data class ImportRoomKeysResult(val totalNumberOfKeys: Int,
val successfullyNumberOfImportedKeys: Int) val successfullyNumberOfImportedKeys: Int,
/**It's a map from room id to a map of the sender key to a list of session*/
val importedSessionInfo: Map<String, Map<String, List<String>>>
) {
companion object {
fun fromOlm(result: KeysImportResult): ImportRoomKeysResult {
return ImportRoomKeysResult(
result.total.toInt(),
result.imported.toInt(),
result.keys
)
}
}
}

View file

@ -40,7 +40,7 @@ internal class TimelineEventDecryptor @Inject constructor(
) { ) {
private val newSessionListener = object : NewSessionListener { private val newSessionListener = object : NewSessionListener {
override fun onNewSession(roomId: String?, senderKey: String, sessionId: String) { override fun onNewSession(roomId: String?, sessionId: String) {
synchronized(unknownSessionsFailure) { synchronized(unknownSessionsFailure) {
unknownSessionsFailure[sessionId] unknownSessionsFailure[sessionId]
?.toList() ?.toList()