forked from GitHub-Mirror/riotX-android
Merge pull request #262 from vector-im/feature/update_timeline_when_new_key
Update timeline on new session
This commit is contained in:
commit
f21f4dbe91
@ -25,6 +25,7 @@ import im.vector.matrix.android.api.session.crypto.sas.SasVerificationService
|
||||
import im.vector.matrix.android.api.session.events.model.Content
|
||||
import im.vector.matrix.android.api.session.events.model.Event
|
||||
import im.vector.matrix.android.internal.crypto.MXEventDecryptionResult
|
||||
import im.vector.matrix.android.internal.crypto.NewSessionListener
|
||||
import im.vector.matrix.android.internal.crypto.model.ImportRoomKeysResult
|
||||
import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo
|
||||
import im.vector.matrix.android.internal.crypto.model.MXEncryptEventContentResult
|
||||
@ -107,4 +108,8 @@ interface CryptoService {
|
||||
|
||||
fun clearCryptoCache(callback: MatrixCallback<Unit>)
|
||||
|
||||
fun addNewSessionListener(newSessionListener: NewSessionListener)
|
||||
|
||||
fun removeSessionListener(listener: NewSessionListener)
|
||||
|
||||
}
|
@ -34,6 +34,7 @@ data class TimelineEvent(
|
||||
val isUniqueDisplayName: Boolean,
|
||||
val senderAvatar: String?,
|
||||
val sendState: SendState,
|
||||
val hasClearEventFlag: Boolean = false,
|
||||
val annotations: EventAnnotationsSummary? = null
|
||||
) {
|
||||
|
||||
|
@ -1063,6 +1063,13 @@ internal class CryptoManager @Inject constructor(
|
||||
.executeBy(taskExecutor)
|
||||
}
|
||||
|
||||
override fun addNewSessionListener(newSessionListener: NewSessionListener) {
|
||||
roomDecryptorProvider.addNewSessionListener(newSessionListener)
|
||||
}
|
||||
|
||||
override fun removeSessionListener(listener: NewSessionListener) {
|
||||
roomDecryptorProvider.removeSessionListener(listener)
|
||||
}
|
||||
/* ==========================================================================================
|
||||
* DEBUG INFO
|
||||
* ========================================================================================== */
|
||||
|
@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Copyright 2019 New Vector Ltd
|
||||
*
|
||||
* 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 im.vector.matrix.android.internal.crypto
|
||||
|
||||
|
||||
interface NewSessionListener {
|
||||
fun onNewSession(roomId: String?, senderKey: String, sessionId: String)
|
||||
}
|
@ -34,6 +34,16 @@ internal class RoomDecryptorProvider @Inject constructor(
|
||||
// A map from algorithm to MXDecrypting instance, for each room
|
||||
private val roomDecryptors: MutableMap<String /* room id */, MutableMap<String /* algorithm */, IMXDecrypting>> = HashMap()
|
||||
|
||||
private val newSessionListeners = ArrayList<NewSessionListener>()
|
||||
|
||||
fun addNewSessionListener(listener: NewSessionListener) {
|
||||
if (!newSessionListeners.contains(listener)) newSessionListeners.add(listener)
|
||||
}
|
||||
|
||||
fun removeSessionListener(listener: NewSessionListener) {
|
||||
newSessionListeners.remove(listener)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a decryptor for a given room and algorithm.
|
||||
* If we already have a decryptor for the given room and algorithm, return
|
||||
@ -64,7 +74,19 @@ internal class RoomDecryptorProvider @Inject constructor(
|
||||
val decryptingClass = MXCryptoAlgorithms.hasDecryptorClassForAlgorithm(algorithm)
|
||||
if (decryptingClass) {
|
||||
val alg = when (algorithm) {
|
||||
MXCRYPTO_ALGORITHM_MEGOLM -> megolmDecryptionFactory.create()
|
||||
MXCRYPTO_ALGORITHM_MEGOLM -> megolmDecryptionFactory.create().apply {
|
||||
this.newSessionListener = object : NewSessionListener {
|
||||
override fun onNewSession(rid: String?, senderKey: String, sessionId: String) {
|
||||
newSessionListeners.forEach {
|
||||
try {
|
||||
it.onNewSession(roomId, senderKey, sessionId)
|
||||
} catch (e: Throwable) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else -> olmDecryptionFactory.create()
|
||||
}
|
||||
if (roomId != null && !TextUtils.isEmpty(roomId)) {
|
||||
|
@ -55,6 +55,8 @@ internal class MXMegolmDecryption(private val credentials: Credentials,
|
||||
private val coroutineDispatchers: MatrixCoroutineDispatchers)
|
||||
: IMXDecrypting {
|
||||
|
||||
var newSessionListener: NewSessionListener? = null
|
||||
|
||||
/**
|
||||
* Events which we couldn't decrypt due to unknown sessions / indexes: map from
|
||||
* senderKey|sessionId to timelines to list of MatrixEvents.
|
||||
@ -203,7 +205,8 @@ internal class MXMegolmDecryption(private val credentials: Credentials,
|
||||
if (event.getClearType() == EventType.FORWARDED_ROOM_KEY) {
|
||||
Timber.v("## onRoomKeyEvent(), forward adding key : roomId " + roomKeyContent.roomId + " sessionId " + roomKeyContent.sessionId
|
||||
+ " sessionKey " + roomKeyContent.sessionKey) // from " + event);
|
||||
val forwardedRoomKeyContent = event.getClearContent().toModel<ForwardedRoomKeyContent>() ?: return
|
||||
val forwardedRoomKeyContent = event.getClearContent().toModel<ForwardedRoomKeyContent>()
|
||||
?: return
|
||||
forwardingCurve25519KeyChain = if (forwardedRoomKeyContent.forwardingCurve25519KeyChain == null) {
|
||||
ArrayList()
|
||||
} else {
|
||||
@ -275,43 +278,8 @@ internal class MXMegolmDecryption(private val credentials: Credentials,
|
||||
* @param sessionId the session id
|
||||
*/
|
||||
override fun onNewSession(senderKey: String, sessionId: String) {
|
||||
//TODO see how to handle this
|
||||
Timber.v("ON NEW SESSION $sessionId - $senderKey")
|
||||
/*val k = "$senderKey|$sessionId"
|
||||
|
||||
val pending = pendingEvents[k]
|
||||
|
||||
if (null != pending) {
|
||||
// Have another go at decrypting events sent with this session.
|
||||
pendingEvents.remove(k)
|
||||
|
||||
val timelineIds = pending.keys
|
||||
|
||||
for (timelineId in timelineIds) {
|
||||
val events = pending[timelineId]
|
||||
|
||||
for (event in events!!) {
|
||||
var result: MXEventDecryptionResult? = null
|
||||
|
||||
try {
|
||||
result = decryptEvent(event, timelineId)
|
||||
} catch (e: MXDecryptionException) {
|
||||
Timber.e(e, "## onNewSession() : Still can't decrypt " + event.eventId + ". Error")
|
||||
event.setCryptoError(e.cryptoError)
|
||||
}
|
||||
|
||||
if (null != result) {
|
||||
val fResut = result
|
||||
CryptoAsyncHelper.getUiHandler().post {
|
||||
event.setClearData(fResut)
|
||||
//mSession!!.onEventDecrypted(event)
|
||||
}
|
||||
Timber.v("## onNewSession() : successful re-decryption of " + event.eventId)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
newSessionListener?.onNewSession(null, senderKey, sessionId)
|
||||
}
|
||||
|
||||
override fun hasKeysForKeyRequest(request: IncomingRoomKeyRequest): Boolean {
|
||||
|
@ -18,6 +18,7 @@
|
||||
package im.vector.matrix.android.internal.crypto.store
|
||||
|
||||
import im.vector.matrix.android.internal.crypto.IncomingRoomKeyRequest
|
||||
import im.vector.matrix.android.internal.crypto.NewSessionListener
|
||||
import im.vector.matrix.android.internal.crypto.OutgoingRoomKeyRequest
|
||||
import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo
|
||||
import im.vector.matrix.android.internal.crypto.model.OlmInboundGroupSessionWrapper
|
||||
@ -376,4 +377,8 @@ internal interface IMXCryptoStore {
|
||||
* @return an IncomingRoomKeyRequest if it exists, else null
|
||||
*/
|
||||
fun getIncomingRoomKeyRequest(userId: String, deviceId: String, requestId: String): IncomingRoomKeyRequest?
|
||||
|
||||
fun addNewSessionListener(listener: NewSessionListener)
|
||||
|
||||
fun removeSessionListener(listener: NewSessionListener)
|
||||
}
|
||||
|
@ -19,6 +19,7 @@ package im.vector.matrix.android.internal.crypto.store.db
|
||||
import android.text.TextUtils
|
||||
import im.vector.matrix.android.api.auth.data.Credentials
|
||||
import im.vector.matrix.android.internal.crypto.IncomingRoomKeyRequest
|
||||
import im.vector.matrix.android.internal.crypto.NewSessionListener
|
||||
import im.vector.matrix.android.internal.crypto.OutgoingRoomKeyRequest
|
||||
import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo
|
||||
import im.vector.matrix.android.internal.crypto.model.OlmInboundGroupSessionWrapper
|
||||
@ -57,6 +58,17 @@ internal class RealmCryptoStore(private val enableFileEncryption: Boolean = fals
|
||||
// Cache for InboundGroupSession, to release them properly
|
||||
private val inboundGroupSessionToRelease = HashMap<String, OlmInboundGroupSessionWrapper>()
|
||||
|
||||
|
||||
private val newSessionListeners = ArrayList<NewSessionListener>()
|
||||
|
||||
override fun addNewSessionListener(listener: NewSessionListener) {
|
||||
if (!newSessionListeners.contains(listener)) newSessionListeners.add(listener)
|
||||
}
|
||||
|
||||
override fun removeSessionListener(listener: NewSessionListener) {
|
||||
newSessionListeners.remove(listener)
|
||||
}
|
||||
|
||||
/* ==========================================================================================
|
||||
* Other data
|
||||
* ========================================================================================== */
|
||||
@ -718,4 +730,5 @@ internal class RealmCryptoStore(private val enableFileEncryption: Boolean = fals
|
||||
}
|
||||
.toMutableList()
|
||||
}
|
||||
|
||||
}
|
@ -64,7 +64,7 @@ internal class RoomFactory @Inject constructor(private val context: Context,
|
||||
|
||||
fun create(roomId: String): Room {
|
||||
val timelineEventFactory = InMemoryTimelineEventFactory(SenderRoomMemberExtractor(), EventRelationExtractor(), cryptoService)
|
||||
val timelineService = DefaultTimelineService(roomId, monarchy, taskExecutor, timelineEventFactory, contextOfEventTask, paginationTask)
|
||||
val timelineService = DefaultTimelineService(roomId, monarchy, taskExecutor, timelineEventFactory, contextOfEventTask, cryptoService, paginationTask)
|
||||
val sendService = DefaultSendService(context, credentials, roomId, eventFactory, cryptoService, monarchy)
|
||||
val stateService = DefaultStateService(roomId, taskExecutor, sendStateTask)
|
||||
val roomMembersService = DefaultMembershipService(roomId, monarchy, taskExecutor, loadRoomMembersTask, inviteTask, joinRoomTask, leaveRoomTask)
|
||||
|
@ -20,11 +20,15 @@ import android.os.Handler
|
||||
import android.os.HandlerThread
|
||||
import android.os.Looper
|
||||
import im.vector.matrix.android.api.MatrixCallback
|
||||
import im.vector.matrix.android.api.session.crypto.CryptoService
|
||||
import im.vector.matrix.android.api.session.events.model.EventType
|
||||
import im.vector.matrix.android.api.session.events.model.toModel
|
||||
import im.vector.matrix.android.api.session.room.timeline.Timeline
|
||||
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
|
||||
import im.vector.matrix.android.api.util.CancelableBag
|
||||
import im.vector.matrix.android.api.util.addTo
|
||||
import im.vector.matrix.android.internal.crypto.NewSessionListener
|
||||
import im.vector.matrix.android.internal.crypto.model.event.EncryptedEventContent
|
||||
import im.vector.matrix.android.internal.database.mapper.asDomain
|
||||
import im.vector.matrix.android.internal.database.model.*
|
||||
import im.vector.matrix.android.internal.database.query.findIncludingEvent
|
||||
@ -56,6 +60,7 @@ internal class DefaultTimeline(
|
||||
private val contextOfEventTask: GetContextOfEventTask,
|
||||
private val timelineEventFactory: CacheableTimelineEventFactory,
|
||||
private val paginationTask: PaginationTask,
|
||||
private val cryptoService: CryptoService,
|
||||
private val allowedTypes: List<String>?
|
||||
) : Timeline {
|
||||
|
||||
@ -159,6 +164,33 @@ internal class DefaultTimeline(
|
||||
postSnapshot()
|
||||
}
|
||||
|
||||
private val newSessionListener = object : NewSessionListener {
|
||||
override fun onNewSession(roomId: String?, senderKey: String, sessionId: String) {
|
||||
if (roomId == this@DefaultTimeline.roomId) {
|
||||
Timber.v("New session id detected for this room")
|
||||
backgroundHandler.get()?.post {
|
||||
val realm = backgroundRealm.get()
|
||||
var hasChange = false
|
||||
builtEvents.forEachIndexed { index, timelineEvent ->
|
||||
if (timelineEvent.isEncrypted()) {
|
||||
val eventContent = timelineEvent.root.content.toModel<EncryptedEventContent>()
|
||||
if (eventContent?.sessionId == sessionId
|
||||
&& (timelineEvent.root.mClearEvent == null || timelineEvent.root.mCryptoError != null)) {
|
||||
//we need to rebuild this event
|
||||
EventEntity.where(realm, eventId = timelineEvent.root.eventId!!).findFirst()?.let {
|
||||
builtEvents[index] = timelineEventFactory.create(it, realm)
|
||||
hasChange = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (hasChange) postSnapshot()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Public methods ******************************************************************************
|
||||
|
||||
override fun paginate(direction: Timeline.Direction, count: Int) {
|
||||
@ -184,6 +216,7 @@ internal class DefaultTimeline(
|
||||
val handler = Handler(handlerThread.looper)
|
||||
this.backgroundHandlerThread.set(handlerThread)
|
||||
this.backgroundHandler.set(handler)
|
||||
cryptoService.addNewSessionListener(newSessionListener)
|
||||
handler.post {
|
||||
val realm = Realm.getInstance(realmConfiguration)
|
||||
backgroundRealm.set(realm)
|
||||
@ -211,6 +244,7 @@ internal class DefaultTimeline(
|
||||
|
||||
override fun dispose() {
|
||||
if (isStarted.compareAndSet(true, false)) {
|
||||
cryptoService.removeSessionListener(newSessionListener)
|
||||
Timber.v("Dispose timeline for roomId: $roomId and eventId: $initialEventId")
|
||||
backgroundHandler.get()?.post {
|
||||
cancelableBag.cancel()
|
||||
|
@ -19,6 +19,7 @@ package im.vector.matrix.android.internal.session.room.timeline
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MediatorLiveData
|
||||
import com.zhuinden.monarchy.Monarchy
|
||||
import im.vector.matrix.android.api.session.crypto.CryptoService
|
||||
import im.vector.matrix.android.api.session.room.timeline.Timeline
|
||||
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
|
||||
import im.vector.matrix.android.api.session.room.timeline.TimelineService
|
||||
@ -35,11 +36,20 @@ internal class DefaultTimelineService @Inject constructor(private val roomId: St
|
||||
private val taskExecutor: TaskExecutor,
|
||||
private val timelineEventFactory: CacheableTimelineEventFactory,
|
||||
private val contextOfEventTask: GetContextOfEventTask,
|
||||
private val cryptoService: CryptoService,
|
||||
private val paginationTask: PaginationTask
|
||||
) : TimelineService {
|
||||
|
||||
override fun createTimeline(eventId: String?, allowedTypes: List<String>?): Timeline {
|
||||
return DefaultTimeline(roomId, eventId, monarchy.realmConfiguration, taskExecutor, contextOfEventTask, timelineEventFactory, paginationTask, allowedTypes)
|
||||
return DefaultTimeline(roomId,
|
||||
eventId,
|
||||
monarchy.realmConfiguration,
|
||||
taskExecutor,
|
||||
contextOfEventTask,
|
||||
timelineEventFactory,
|
||||
paginationTask,
|
||||
cryptoService,
|
||||
allowedTypes)
|
||||
}
|
||||
|
||||
override fun getTimeLineEvent(eventId: String): TimelineEvent? {
|
||||
|
@ -69,6 +69,7 @@ internal class SimpleTimelineEventFactory @Inject constructor(private val roomMe
|
||||
isUniqueDisplayName,
|
||||
senderRoomMember?.avatarUrl,
|
||||
eventEntity.sendState,
|
||||
event.mClearEvent != null,
|
||||
relations
|
||||
)
|
||||
}
|
||||
@ -120,6 +121,7 @@ internal class InMemoryTimelineEventFactory @Inject constructor(private val room
|
||||
senderData.isUniqueDisplayName,
|
||||
senderData.senderAvatar,
|
||||
eventEntity.sendState,
|
||||
event.mClearEvent != null,
|
||||
relations
|
||||
)
|
||||
}
|
||||
@ -138,7 +140,7 @@ internal class InMemoryTimelineEventFactory @Inject constructor(private val room
|
||||
}
|
||||
event.setClearData(result)
|
||||
} catch (failure: Throwable) {
|
||||
Timber.e(failure, "Encrypted event: decryption failed")
|
||||
Timber.e("Encrypted event: decryption failed ${failure.localizedMessage}")
|
||||
if (failure is MXDecryptionException) {
|
||||
event.setCryptoError(failure.cryptoError)
|
||||
} else {
|
||||
|
Loading…
Reference in New Issue
Block a user