This commit is contained in:
Benoit Marty 2019-05-21 15:42:09 +02:00
parent 6d8000b957
commit 52d9adad70
24 changed files with 493 additions and 155 deletions

View file

@ -22,12 +22,15 @@ import im.vector.matrix.android.api.listeners.ProgressListener
import im.vector.matrix.android.api.session.crypto.keysbackup.KeysBackupService import im.vector.matrix.android.api.session.crypto.keysbackup.KeysBackupService
import im.vector.matrix.android.api.session.crypto.keyshare.RoomKeysRequestListener import im.vector.matrix.android.api.session.crypto.keyshare.RoomKeysRequestListener
import im.vector.matrix.android.api.session.crypto.sas.SasVerificationService 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.api.session.events.model.Event
import im.vector.matrix.android.api.session.room.Room
import im.vector.matrix.android.internal.crypto.MXEventDecryptionResult import im.vector.matrix.android.internal.crypto.MXEventDecryptionResult
import im.vector.matrix.android.internal.crypto.model.ImportRoomKeysResult 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.MXDeviceInfo
import im.vector.matrix.android.internal.crypto.model.rest.RoomKeyRequestBody import im.vector.matrix.android.internal.crypto.model.MXEncryptEventContentResult
import im.vector.matrix.android.internal.crypto.model.rest.DevicesListResponse import im.vector.matrix.android.internal.crypto.model.rest.DevicesListResponse
import im.vector.matrix.android.internal.crypto.model.rest.RoomKeyRequestBody
interface CryptoService { interface CryptoService {
@ -57,7 +60,7 @@ interface CryptoService {
fun getMyDevice(): MXDeviceInfo fun getMyDevice(): MXDeviceInfo
fun getGlobalBlacklistUnverifiedDevices() : Boolean fun getGlobalBlacklistUnverifiedDevices(): Boolean
fun setGlobalBlacklistUnverifiedDevices(block: Boolean) fun setGlobalBlacklistUnverifiedDevices(block: Boolean)
@ -83,6 +86,13 @@ interface CryptoService {
fun inboundGroupSessionsCount(onlyBackedUp: Boolean): Int fun inboundGroupSessionsCount(onlyBackedUp: Boolean): Int
fun isRoomEncrypted(roomId: String): Boolean
fun encryptEventContent(eventContent: Content,
eventType: String,
room: Room,
callback: MatrixCallback<MXEncryptEventContentResult>)
/* /*
fun start(isInitialSync: Boolean, aCallback: MatrixCallback<Unit>?) fun start(isInitialSync: Boolean, aCallback: MatrixCallback<Unit>?)
@ -92,14 +102,6 @@ interface CryptoService {
fun close() fun close()
fun encryptEventContent(eventContent: Content,
eventType: String,
room: Room,
callback: MatrixCallback<MXEncryptEventContentResult>)
fun onToDeviceEvent(event: Event)
fun onSyncCompleted(syncResponse: SyncResponse, fromToken: String?, isCatchingUp: Boolean)
fun getOlmDevice(): MXOlmDevice? fun getOlmDevice(): MXOlmDevice?
@ -118,4 +120,8 @@ interface CryptoService {
*/ */
fun decryptEvent(event: Event, timeline: String): MXEventDecryptionResult? fun decryptEvent(event: Event, timeline: String): MXEventDecryptionResult?
fun getEncryptionAlgorithm(roomId: String): String?
fun shouldEncryptForInvitedMembers(roomId: String): Boolean
} }

View file

@ -16,16 +16,11 @@
package im.vector.matrix.android.api.session.room.crypto package im.vector.matrix.android.api.session.room.crypto
import im.vector.matrix.android.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM
interface RoomCryptoService { interface RoomCryptoService {
// TODO fun isEncrypted(): Boolean
fun isEncrypted(): Boolean = false
// TODO fun encryptionAlgorithm(): String?
fun encryptionAlgorithm(): String? = MXCRYPTO_ALGORITHM_MEGOLM
// TODO fun shouldEncryptForInvitedMembers(): Boolean
fun shouldEncryptForInvitedMembers(): Boolean = false
} }

View file

@ -54,18 +54,4 @@ interface RoomMembersService {
*/ */
fun invite(userId: String, callback: MatrixCallback<Unit>) fun invite(userId: String, callback: MatrixCallback<Unit>)
/**
* Return all the roomMembers ids which are joined or invited to the room
*
* @return a roomMember id list of joined or invited members.
*/
fun getActiveRoomMemberIds(): List<String>
/**
* Return all the roomMembers ids which are joined to the room
*
* @return a roomMember id list of joined members.
*/
fun getJoinedRoomMemberIds(): List<String>
} }

View file

@ -35,8 +35,8 @@ import im.vector.matrix.android.api.session.events.model.Event
import im.vector.matrix.android.api.session.events.model.EventType 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.events.model.toModel
import im.vector.matrix.android.api.session.room.Room import im.vector.matrix.android.api.session.room.Room
import im.vector.matrix.android.api.session.room.RoomService import im.vector.matrix.android.api.session.room.model.RoomHistoryVisibility
import im.vector.matrix.android.api.session.room.model.Membership import im.vector.matrix.android.api.session.room.model.RoomHistoryVisibilityContent
import im.vector.matrix.android.internal.crypto.actions.EnsureOlmSessionsForDevicesAction import im.vector.matrix.android.internal.crypto.actions.EnsureOlmSessionsForDevicesAction
import im.vector.matrix.android.internal.crypto.actions.MegolmSessionDataImporter import im.vector.matrix.android.internal.crypto.actions.MegolmSessionDataImporter
import im.vector.matrix.android.internal.crypto.actions.SetDeviceVerificationAction import im.vector.matrix.android.internal.crypto.actions.SetDeviceVerificationAction
@ -97,8 +97,6 @@ internal class CryptoManager(
private val mIncomingRoomKeyRequestManager: IncomingRoomKeyRequestManager, private val mIncomingRoomKeyRequestManager: IncomingRoomKeyRequestManager,
// //
private val mOutgoingRoomKeyRequestManager: OutgoingRoomKeyRequestManager, private val mOutgoingRoomKeyRequestManager: OutgoingRoomKeyRequestManager,
// Room service
private val mRoomService: RoomService,
// Olm Manager // Olm Manager
private val mOlmManager: OlmManager, private val mOlmManager: OlmManager,
// Actions // Actions
@ -140,11 +138,23 @@ internal class CryptoManager(
// } // }
//} //}
fun onLiveEvent(roomId: String, event: Event) { fun onStateEvent(roomId: String, event: Event) {
if (event.type == EventType.ENCRYPTION) { if (event.type == EventType.ENCRYPTION) {
onRoomEncryptionEvent(roomId, event) // TODO Remove onRoomEncryptionEvent(roomId, event)
} else if (event.type == EventType.STATE_ROOM_MEMBER) { } else if (event.type == EventType.STATE_ROOM_MEMBER) {
onRoomMembershipEvent(roomId, event) onRoomMembershipEvent(roomId, event)
} else if (event.type == EventType.STATE_HISTORY_VISIBILITY) {
onRoomHistoryVisibilityEvent(roomId, event)
}
}
fun onLiveEvent(roomId: String, event: Event) {
if (event.type == EventType.ENCRYPTION) {
// TODO Remove onRoomEncryptionEvent(roomId, event)
} else if (event.type == EventType.STATE_ROOM_MEMBER) {
onRoomMembershipEvent(roomId, event)
} else if (event.type == EventType.STATE_HISTORY_VISIBILITY) {
onRoomHistoryVisibilityEvent(roomId, event)
} }
} }
@ -522,21 +532,15 @@ internal class CryptoManager(
* @param roomId the room id * @param roomId the room id
* @return true if the room is encrypted * @return true if the room is encrypted
*/ */
fun isRoomEncrypted(roomId: String?): Boolean { override fun isRoomEncrypted(roomId: String): Boolean {
var res = false var res: Boolean
if (null != roomId) {
synchronized(mRoomEncryptors) { synchronized(mRoomEncryptors) {
res = mRoomEncryptors.containsKey(roomId) res = mRoomEncryptors.containsKey(roomId)
}
if (!res) { if (!res) {
val room = mRoomService.getRoom(roomId) res = !mCryptoStore.getRoomAlgorithm(roomId).isNullOrBlank()
if (null != room) {
res = room.isEncrypted()
}
}
}
} }
return res return res
@ -564,6 +568,26 @@ internal class CryptoManager(
mEnsureOlmSessionsForDevicesAction.handle(devicesByUser, callback) mEnsureOlmSessionsForDevicesAction.handle(devicesByUser, callback)
} }
fun isEncryptionEnabledForInvitedUser(): Boolean {
return mCryptoConfig.mEnableEncryptionForInvitedMembers
}
override fun getEncryptionAlgorithm(roomId: String): String? {
return mCryptoStore.getRoomAlgorithm(roomId)
}
/**
* Determine whether we should encrypt messages for invited users in this room.
* <p>
* Check here whether the invited members are allowed to read messages in the room history
* from the point they were invited onwards.
*
* @return true if we should encrypt messages for invited users.
*/
override fun shouldEncryptForInvitedMembers(roomId: String): Boolean {
return mCryptoStore.shouldEncryptForInvitedMembers(roomId)
}
/** /**
* Encrypt an event content according to the configuration of the room. * Encrypt an event content according to the configuration of the room.
* *
@ -572,7 +596,7 @@ internal class CryptoManager(
* @param room the room the event will be sent. * @param room the room the event will be sent.
* @param callback the asynchronous callback * @param callback the asynchronous callback
*/ */
fun encryptEventContent(eventContent: Content, override fun encryptEventContent(eventContent: Content,
eventType: String, eventType: String,
room: Room, room: Room,
callback: MatrixCallback<MXEncryptEventContentResult>) { callback: MatrixCallback<MXEncryptEventContentResult>) {
@ -596,13 +620,15 @@ internal class CryptoManager(
} }
// Check whether the event content must be encrypted for the invited members. // Check whether the event content must be encrypted for the invited members.
val encryptForInvitedMembers = mCryptoConfig.mEnableEncryptionForInvitedMembers && room.shouldEncryptForInvitedMembers() val encryptForInvitedMembers = mCryptoConfig.mEnableEncryptionForInvitedMembers && shouldEncryptForInvitedMembers(room.roomId)
val userIds = if (encryptForInvitedMembers) { // TODO
room.getActiveRoomMemberIds() //val userIds = if (encryptForInvitedMembers) {
} else { // room.getActiveRoomMemberIds()
room.getJoinedRoomMemberIds() //} else {
} // room.getJoinedRoomMemberIds()
//}
val userIds = emptyList<String>()
// just as you are sending a secret message? // just as you are sending a secret message?
@ -749,22 +775,8 @@ internal class CryptoManager(
* *
* @param event the encryption event. * @param event the encryption event.
*/ */
private fun onRoomEncryptionEvent(roomId: String, event: Event) { fun onRoomEncryptionEvent(event: Event, userIds: List<String>) {
// TODO Parse the event setEncryptionInRoom(event.roomId!!, event.content!!["algorithm"] as String, true, userIds)
val eventContent = event.content // wireEventContent
val room = mRoomService.getRoom(roomId)!!
// Check whether the event content must be encrypted for the invited members.
val encryptForInvitedMembers = mCryptoConfig.mEnableEncryptionForInvitedMembers && room.shouldEncryptForInvitedMembers()
val userIds = if (encryptForInvitedMembers) {
room.getActiveRoomMemberIds()
} else {
room.getJoinedRoomMemberIds()
}
setEncryptionInRoom(roomId, eventContent!!["algorithm"] as String, true, userIds)
} }
/** /**
@ -785,6 +797,8 @@ internal class CryptoManager(
} }
val userId = event.stateKey!! val userId = event.stateKey!!
/* FIXME
val room = mRoomService.getRoom(roomId) val room = mRoomService.getRoom(roomId)
val roomMember = room?.getRoomMember(userId) val roomMember = room?.getRoomMember(userId)
@ -796,7 +810,7 @@ internal class CryptoManager(
// make sure we are tracking the deviceList for this user. // make sure we are tracking the deviceList for this user.
deviceListManager.startTrackingDeviceList(Arrays.asList(userId)) deviceListManager.startTrackingDeviceList(Arrays.asList(userId))
} else if (membership == Membership.INVITE } else if (membership == Membership.INVITE
&& room.shouldEncryptForInvitedMembers() && shouldEncryptForInvitedMembers(roomId)
&& mCryptoConfig.mEnableEncryptionForInvitedMembers) { && mCryptoConfig.mEnableEncryptionForInvitedMembers) {
// track the deviceList for this invited user. // track the deviceList for this invited user.
// Caution: there's a big edge case here in that federated servers do not // Caution: there's a big edge case here in that federated servers do not
@ -806,8 +820,18 @@ internal class CryptoManager(
deviceListManager.startTrackingDeviceList(Arrays.asList(userId)) deviceListManager.startTrackingDeviceList(Arrays.asList(userId))
} }
} }
*/
} }
private fun onRoomHistoryVisibilityEvent(roomId: String, event: Event) {
val eventContent = event.content.toModel<RoomHistoryVisibilityContent>()
eventContent?.historyVisibility?.let {
mCryptoStore.setShouldEncryptForInvitedMembers(roomId, it != RoomHistoryVisibility.JOINED)
}
}
/** /**
* Upload my user's device keys. * Upload my user's device keys.
* This method must called on getEncryptingThreadHandler() thread. * This method must called on getEncryptingThreadHandler() thread.
@ -996,6 +1020,7 @@ internal class CryptoManager(
* @param roomId the room id * @param roomId the room id
* @return true if the client should encrypt messages only for the verified devices. * @return true if the client should encrypt messages only for the verified devices.
*/ */
// TODO add this info in CryptoRoomEntity?
override fun isRoomBlacklistUnverifiedDevices(roomId: String?): Boolean { override fun isRoomBlacklistUnverifiedDevices(roomId: String?): Boolean {
return if (null != roomId) { return if (null != roomId) {
mCryptoStore.getRoomsListBlacklistUnverifiedDevices().contains(roomId) mCryptoStore.getRoomsListBlacklistUnverifiedDevices().contains(roomId)
@ -1011,13 +1036,6 @@ internal class CryptoManager(
* @param add true to add the room id to the list, false to remove it. * @param add true to add the room id to the list, false to remove it.
*/ */
private fun setRoomBlacklistUnverifiedDevices(roomId: String, add: Boolean) { private fun setRoomBlacklistUnverifiedDevices(roomId: String, add: Boolean) {
val room = mRoomService.getRoom(roomId)
// sanity check
if (null == room) {
return
}
val roomIds = mCryptoStore.getRoomsListBlacklistUnverifiedDevices().toMutableList() val roomIds = mCryptoStore.getRoomsListBlacklistUnverifiedDevices().toMutableList()
if (add) { if (add) {

View file

@ -80,7 +80,7 @@ internal class CryptoModule {
// CryptoService // CryptoService
scope(DefaultSession.SCOPE) { scope(DefaultSession.SCOPE) {
DefaultCryptoService(get()) as CryptoService get<CryptoManager>() as CryptoService
} }
// //
@ -187,7 +187,6 @@ internal class CryptoModule {
get(), get(),
get(), get(),
get(), get(),
get(),
// Actions // Actions
get(), get(),
get(), get(),

View file

@ -1,22 +0,0 @@
/*
* 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
import im.vector.matrix.android.api.session.crypto.CryptoService
internal class DefaultCryptoService(val cryptoManager: CryptoManager)
: CryptoService by cryptoManager

View file

@ -0,0 +1,93 @@
/*
* 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.live
import android.content.Context
import androidx.work.Worker
import androidx.work.WorkerParameters
import com.squareup.moshi.JsonClass
import com.zhuinden.monarchy.Monarchy
import im.vector.matrix.android.internal.crypto.CryptoManager
import im.vector.matrix.android.internal.database.mapper.asDomain
import im.vector.matrix.android.internal.database.model.EventEntity
import im.vector.matrix.android.internal.database.query.where
import im.vector.matrix.android.internal.di.MatrixKoinComponent
import im.vector.matrix.android.internal.session.room.members.LoadRoomMembersTask
import im.vector.matrix.android.internal.session.room.members.RoomMembers
import im.vector.matrix.android.internal.task.TaskExecutor
import im.vector.matrix.android.internal.task.TaskThread
import im.vector.matrix.android.internal.task.configureWith
import im.vector.matrix.android.internal.util.WorkerParamsFactory
import org.koin.standalone.inject
internal class EnableEncryptionWorker(context: Context,
workerParameters: WorkerParameters
) : Worker(context, workerParameters), MatrixKoinComponent {
private val monarchy by inject<Monarchy>()
private val cryptoManager by inject<CryptoManager>()
private val loadRoomMembersTask by inject<LoadRoomMembersTask>()
private val taskExecutor by inject<TaskExecutor>()
@JsonClass(generateAdapter = true)
internal class Params(
val eventIds: List<String>
)
override fun doWork(): Result {
val params = WorkerParamsFactory.fromData<EnableEncryptionWorker.Params>(inputData)
?: return Result.failure()
val events = monarchy.fetchAllMappedSync(
{ EventEntity.where(it, params.eventIds) },
{ it.asDomain() }
)
events.forEach {
val roomId = it.roomId!!
loadRoomMembersTask
.configureWith(LoadRoomMembersTask.Params(roomId))
.executeOn(TaskThread.CALLER)
.executeBy(taskExecutor)
var userIds: List<String> = emptyList()
monarchy.doWithRealm { realm ->
// Check whether the event content must be encrypted for the invited members.
val encryptForInvitedMembers = cryptoManager.isEncryptionEnabledForInvitedUser()
&& cryptoManager.shouldEncryptForInvitedMembers(roomId)
userIds = if (encryptForInvitedMembers) {
RoomMembers(realm, roomId).getActiveRoomMemberIds()
} else {
RoomMembers(realm, roomId).getJoinedRoomMemberIds()
}
}
cryptoManager.onRoomEncryptionEvent(it, userIds)
}
return Result.success()
}
}

View file

@ -0,0 +1,55 @@
/*
* 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.live
import androidx.work.ExistingWorkPolicy
import androidx.work.OneTimeWorkRequestBuilder
import androidx.work.WorkManager
import com.zhuinden.monarchy.Monarchy
import im.vector.matrix.android.api.session.events.model.EventType
import im.vector.matrix.android.internal.database.RealmLiveEntityObserver
import im.vector.matrix.android.internal.database.mapper.asDomain
import im.vector.matrix.android.internal.database.model.EventEntity
import im.vector.matrix.android.internal.database.query.where
import im.vector.matrix.android.internal.util.WorkerParamsFactory
import timber.log.Timber
private const val ENABLE_ENCRYPTION_EVENT_WORKER = "ENABLE_ENCRYPTION_EVENT_WORKER"
internal class RoomEncryptionEnabler(monarchy: Monarchy) : RealmLiveEntityObserver<EventEntity>(monarchy) {
override val query: Monarchy.Query<EventEntity>
get() = Monarchy.Query<EventEntity> { EventEntity.where(it, type = EventType.ENCRYPTION) }
override fun processChanges(inserted: List<EventEntity>, updated: List<EventEntity>, deleted: List<EventEntity>) {
Timber.v("RoomEncryption received")
val eventIds = inserted.mapNotNull { it.asDomain().eventId }
val workParam = EnableEncryptionWorker.Params(eventIds)
val workData = WorkerParamsFactory.toData(workParam)
val work = OneTimeWorkRequestBuilder<EnableEncryptionWorker>()
.setInputData(workData)
.build()
WorkManager.getInstance()
.beginUniqueWork(ENABLE_ENCRYPTION_EVENT_WORKER, ExistingWorkPolicy.APPEND, work)
.enqueue()
}
}

View file

@ -19,11 +19,11 @@ package im.vector.matrix.android.internal.crypto.store
import im.vector.matrix.android.internal.crypto.IncomingRoomKeyRequest import im.vector.matrix.android.internal.crypto.IncomingRoomKeyRequest
import im.vector.matrix.android.internal.crypto.OutgoingRoomKeyRequest import im.vector.matrix.android.internal.crypto.OutgoingRoomKeyRequest
import im.vector.matrix.android.internal.crypto.store.db.model.KeysBackupDataEntity
import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo
import im.vector.matrix.android.internal.crypto.model.MXOlmInboundGroupSession2 import im.vector.matrix.android.internal.crypto.model.MXOlmInboundGroupSession2
import im.vector.matrix.android.internal.crypto.model.MXOlmSession import im.vector.matrix.android.internal.crypto.model.MXOlmSession
import im.vector.matrix.android.internal.crypto.model.rest.RoomKeyRequestBody import im.vector.matrix.android.internal.crypto.model.rest.RoomKeyRequestBody
import im.vector.matrix.android.internal.crypto.store.db.model.KeysBackupDataEntity
import org.matrix.olm.OlmAccount import org.matrix.olm.OlmAccount
/** /**
@ -204,6 +204,10 @@ internal interface IMXCryptoStore {
*/ */
fun getRoomAlgorithm(roomId: String): String? fun getRoomAlgorithm(roomId: String): String?
fun shouldEncryptForInvitedMembers(roomId: String): Boolean
fun setShouldEncryptForInvitedMembers(roomId: String, shouldEncryptForInvitedMembers: Boolean)
/** /**
* Store a session between the logged-in user and another device. * Store a session between the logged-in user and another device.
* *

View file

@ -20,15 +20,15 @@ import android.text.TextUtils
import im.vector.matrix.android.api.auth.data.Credentials import im.vector.matrix.android.api.auth.data.Credentials
import im.vector.matrix.android.internal.crypto.IncomingRoomKeyRequest import im.vector.matrix.android.internal.crypto.IncomingRoomKeyRequest
import im.vector.matrix.android.internal.crypto.OutgoingRoomKeyRequest 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.MXOlmInboundGroupSession2
import im.vector.matrix.android.internal.crypto.model.MXOlmSession
import im.vector.matrix.android.internal.crypto.model.rest.RoomKeyRequestBody
import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore
import im.vector.matrix.android.internal.crypto.store.db.model.* import im.vector.matrix.android.internal.crypto.store.db.model.*
import im.vector.matrix.android.internal.crypto.store.db.query.delete import im.vector.matrix.android.internal.crypto.store.db.query.delete
import im.vector.matrix.android.internal.crypto.store.db.query.getById import im.vector.matrix.android.internal.crypto.store.db.query.getById
import im.vector.matrix.android.internal.crypto.store.db.query.getOrCreate import im.vector.matrix.android.internal.crypto.store.db.query.getOrCreate
import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo
import im.vector.matrix.android.internal.crypto.model.MXOlmInboundGroupSession2
import im.vector.matrix.android.internal.crypto.model.MXOlmSession
import im.vector.matrix.android.internal.crypto.model.rest.RoomKeyRequestBody
import io.realm.RealmConfiguration import io.realm.RealmConfiguration
import io.realm.Sort import io.realm.Sort
import io.realm.kotlin.where import io.realm.kotlin.where
@ -241,6 +241,19 @@ internal class RealmCryptoStore(private val enableFileEncryption: Boolean = fals
?.algorithm ?.algorithm
} }
override fun shouldEncryptForInvitedMembers(roomId: String): Boolean {
return doRealmQueryAndCopy(realmConfiguration) {
CryptoRoomEntity.getById(it, roomId)
}
?.shouldEncryptForInvitedMembers ?: false
}
override fun setShouldEncryptForInvitedMembers(roomId: String, shouldEncryptForInvitedMembers: Boolean) {
doRealmTransaction(realmConfiguration) {
CryptoRoomEntity.getOrCreate(it, roomId).shouldEncryptForInvitedMembers = shouldEncryptForInvitedMembers
}
}
override fun storeSession(session: MXOlmSession, deviceKey: String) { override fun storeSession(session: MXOlmSession, deviceKey: String) {
var sessionIdentifier: String? = null var sessionIdentifier: String? = null

View file

@ -22,6 +22,7 @@ import io.realm.annotations.PrimaryKey
internal open class CryptoRoomEntity( internal open class CryptoRoomEntity(
@PrimaryKey var roomId: String? = null, @PrimaryKey var roomId: String? = null,
var algorithm: String? = null, var algorithm: String? = null,
var shouldEncryptForInvitedMembers: Boolean? = null,
var blacklistUnverifiedDevices: Boolean = false) var blacklistUnverifiedDevices: Boolean = false)
: RealmObject() { : RealmObject() {

View file

@ -70,6 +70,9 @@ internal abstract class RealmLiveEntityObserver<T : RealmObject>(protected val m
processChanges(inserted, updated, deleted) processChanges(inserted, updated, deleted)
} }
/**
* Do quick treatment or delegate on a task
*/
protected abstract fun processChanges(inserted: List<T>, updated: List<T>, deleted: List<T>) protected abstract fun processChanges(inserted: List<T>, updated: List<T>, deleted: List<T>)
} }

View file

@ -30,6 +30,10 @@ internal fun EventEntity.Companion.where(realm: Realm, eventId: String): RealmQu
return realm.where<EventEntity>().equalTo(EventEntityFields.EVENT_ID, eventId) return realm.where<EventEntity>().equalTo(EventEntityFields.EVENT_ID, eventId)
} }
internal fun EventEntity.Companion.where(realm: Realm, eventIds: List<String>): RealmQuery<EventEntity> {
return realm.where<EventEntity>().`in`(EventEntityFields.EVENT_ID, eventIds.toTypedArray())
}
internal fun EventEntity.Companion.where(realm: Realm, internal fun EventEntity.Companion.where(realm: Realm,
roomId: String? = null, roomId: String? = null,
type: String? = null, type: String? = null,

View file

@ -48,6 +48,7 @@ object MoshiProvider {
return moshi return moshi
} }
// TODO Move
fun <T> getCanonicalJson(type: Class<T>, o: T): String { fun <T> getCanonicalJson(type: Class<T>, o: T): String {
val adapter = moshi.adapter<T>(type) val adapter = moshi.adapter<T>(type)

View file

@ -31,6 +31,7 @@ import im.vector.matrix.android.api.session.content.ContentUrlResolver
import im.vector.matrix.android.api.session.crypto.keysbackup.KeysBackupService import im.vector.matrix.android.api.session.crypto.keysbackup.KeysBackupService
import im.vector.matrix.android.api.session.crypto.keyshare.RoomKeysRequestListener import im.vector.matrix.android.api.session.crypto.keyshare.RoomKeysRequestListener
import im.vector.matrix.android.api.session.crypto.sas.SasVerificationService 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.api.session.events.model.Event
import im.vector.matrix.android.api.session.group.Group import im.vector.matrix.android.api.session.group.Group
import im.vector.matrix.android.api.session.group.GroupService import im.vector.matrix.android.api.session.group.GroupService
@ -44,13 +45,14 @@ import im.vector.matrix.android.api.session.sync.FilterService
import im.vector.matrix.android.api.session.user.UserService import im.vector.matrix.android.api.session.user.UserService
import im.vector.matrix.android.api.session.user.model.User import im.vector.matrix.android.api.session.user.model.User
import im.vector.matrix.android.api.util.MatrixCallbackDelegate import im.vector.matrix.android.api.util.MatrixCallbackDelegate
import im.vector.matrix.android.internal.crypto.CryptoManager
import im.vector.matrix.android.internal.crypto.CryptoModule import im.vector.matrix.android.internal.crypto.CryptoModule
import im.vector.matrix.android.internal.crypto.MXEventDecryptionResult import im.vector.matrix.android.internal.crypto.MXEventDecryptionResult
import im.vector.matrix.android.internal.crypto.model.ImportRoomKeysResult 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.MXDeviceInfo
import im.vector.matrix.android.internal.crypto.CryptoManager import im.vector.matrix.android.internal.crypto.model.MXEncryptEventContentResult
import im.vector.matrix.android.internal.crypto.model.rest.RoomKeyRequestBody
import im.vector.matrix.android.internal.crypto.model.rest.DevicesListResponse import im.vector.matrix.android.internal.crypto.model.rest.DevicesListResponse
import im.vector.matrix.android.internal.crypto.model.rest.RoomKeyRequestBody
import im.vector.matrix.android.internal.database.LiveEntityObserver import im.vector.matrix.android.internal.database.LiveEntityObserver
import im.vector.matrix.android.internal.di.MatrixKoinComponent import im.vector.matrix.android.internal.di.MatrixKoinComponent
import im.vector.matrix.android.internal.di.MatrixKoinHolder import im.vector.matrix.android.internal.di.MatrixKoinHolder
@ -321,6 +323,17 @@ internal class DefaultSession(override val sessionParams: SessionParams) : Sessi
cryptoService.setRoomBlacklistUnverifiedDevices(roomId) cryptoService.setRoomBlacklistUnverifiedDevices(roomId)
} }
override fun isRoomEncrypted(roomId: String): Boolean {
return cryptoService.isRoomEncrypted(roomId)
}
override fun encryptEventContent(eventContent: Content,
eventType: String,
room: Room,
callback: MatrixCallback<MXEncryptEventContentResult>) {
cryptoService.encryptEventContent(eventContent, eventType, room, callback)
}
override fun getDeviceInfo(userId: String, deviceId: String?): MXDeviceInfo? { override fun getDeviceInfo(userId: String, deviceId: String?): MXDeviceInfo? {
return cryptoService.getDeviceInfo(userId, deviceId) return cryptoService.getDeviceInfo(userId, deviceId)
} }
@ -341,6 +354,14 @@ internal class DefaultSession(override val sessionParams: SessionParams) : Sessi
return cryptoService.decryptEvent(event, timeline) return cryptoService.decryptEvent(event, timeline)
} }
override fun getEncryptionAlgorithm(roomId: String): String? {
return cryptoService.getEncryptionAlgorithm(roomId)
}
override fun shouldEncryptForInvitedMembers(roomId: String): Boolean {
return cryptoService.shouldEncryptForInvitedMembers(roomId)
}
// Private methods ***************************************************************************** // Private methods *****************************************************************************
private fun assertMainThread() { private fun assertMainThread() {

View file

@ -25,6 +25,7 @@ import im.vector.matrix.android.api.session.room.RoomService
import im.vector.matrix.android.api.session.signout.SignOutService import im.vector.matrix.android.api.session.signout.SignOutService
import im.vector.matrix.android.api.session.sync.FilterService import im.vector.matrix.android.api.session.sync.FilterService
import im.vector.matrix.android.api.session.user.UserService import im.vector.matrix.android.api.session.user.UserService
import im.vector.matrix.android.internal.crypto.live.RoomEncryptionEnabler
import im.vector.matrix.android.internal.database.LiveEntityObserver import im.vector.matrix.android.internal.database.LiveEntityObserver
import im.vector.matrix.android.internal.database.model.SessionRealmModule import im.vector.matrix.android.internal.database.model.SessionRealmModule
import im.vector.matrix.android.internal.session.cache.ClearCacheTask import im.vector.matrix.android.internal.session.cache.ClearCacheTask
@ -151,7 +152,8 @@ internal class SessionModule(private val sessionParams: SessionParams) {
val groupSummaryUpdater = GroupSummaryUpdater(get()) val groupSummaryUpdater = GroupSummaryUpdater(get())
val eventsPruner = EventsPruner(get()) val eventsPruner = EventsPruner(get())
val userEntityUpdater = UserEntityUpdater(get(), get(), get()) val userEntityUpdater = UserEntityUpdater(get(), get(), get())
listOf<LiveEntityObserver>(groupSummaryUpdater, eventsPruner, userEntityUpdater) val roomEncryptionEnabler = RoomEncryptionEnabler(get())
listOf<LiveEntityObserver>(groupSummaryUpdater, eventsPruner, userEntityUpdater, roomEncryptionEnabler)
} }

View file

@ -19,6 +19,7 @@ package im.vector.matrix.android.internal.session.room
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.Transformations import androidx.lifecycle.Transformations
import com.zhuinden.monarchy.Monarchy import com.zhuinden.monarchy.Monarchy
import im.vector.matrix.android.api.session.crypto.CryptoService
import im.vector.matrix.android.api.session.room.Room import im.vector.matrix.android.api.session.room.Room
import im.vector.matrix.android.api.session.room.members.RoomMembersService import im.vector.matrix.android.api.session.room.members.RoomMembersService
import im.vector.matrix.android.api.session.room.model.RoomSummary import im.vector.matrix.android.api.session.room.model.RoomSummary
@ -39,7 +40,8 @@ internal class DefaultRoom(
private val sendService: SendService, private val sendService: SendService,
private val stateService: StateService, private val stateService: StateService,
private val readService: ReadService, private val readService: ReadService,
private val roomMembersService: RoomMembersService private val roomMembersService: RoomMembersService,
private val cryptoService: CryptoService
) : Room, ) : Room,
TimelineService by timelineService, TimelineService by timelineService,
SendService by sendService, SendService by sendService,
@ -63,4 +65,16 @@ internal class DefaultRoom(
} }
} }
override fun isEncrypted(): Boolean {
return cryptoService.isRoomEncrypted(roomId)
}
override fun encryptionAlgorithm(): String? {
return cryptoService.getEncryptionAlgorithm(roomId)
}
override fun shouldEncryptForInvitedMembers(): Boolean {
return cryptoService.shouldEncryptForInvitedMembers(roomId)
}
} }

View file

@ -17,6 +17,7 @@
package im.vector.matrix.android.internal.session.room package im.vector.matrix.android.internal.session.room
import com.zhuinden.monarchy.Monarchy import com.zhuinden.monarchy.Monarchy
import im.vector.matrix.android.api.session.crypto.CryptoService
import im.vector.matrix.android.api.session.room.Room import im.vector.matrix.android.api.session.room.Room
import im.vector.matrix.android.internal.session.room.invite.InviteTask import im.vector.matrix.android.internal.session.room.invite.InviteTask
import im.vector.matrix.android.internal.session.room.members.DefaultRoomMembersService import im.vector.matrix.android.internal.session.room.members.DefaultRoomMembersService
@ -42,13 +43,14 @@ internal class RoomFactory(private val loadRoomMembersTask: LoadRoomMembersTask,
private val contextOfEventTask: GetContextOfEventTask, private val contextOfEventTask: GetContextOfEventTask,
private val setReadMarkersTask: SetReadMarkersTask, private val setReadMarkersTask: SetReadMarkersTask,
private val eventFactory: LocalEchoEventFactory, private val eventFactory: LocalEchoEventFactory,
private val cryptoService: CryptoService,
private val taskExecutor: TaskExecutor) { private val taskExecutor: TaskExecutor) {
fun instantiate(roomId: String): Room { fun instantiate(roomId: String): Room {
val roomMemberExtractor = SenderRoomMemberExtractor(roomId) val roomMemberExtractor = SenderRoomMemberExtractor(roomId)
val timelineEventFactory = TimelineEventFactory(roomMemberExtractor) val timelineEventFactory = TimelineEventFactory(roomMemberExtractor)
val timelineService = DefaultTimelineService(roomId, monarchy, taskExecutor, contextOfEventTask, timelineEventFactory, paginationTask) val timelineService = DefaultTimelineService(roomId, monarchy, taskExecutor, contextOfEventTask, timelineEventFactory, paginationTask)
val sendService = DefaultSendService(roomId, eventFactory, monarchy) val sendService = DefaultSendService(roomId, eventFactory, cryptoService, monarchy)
val stateService = DefaultStateService(roomId, sendStateTask, taskExecutor) val stateService = DefaultStateService(roomId, sendStateTask, taskExecutor)
val roomMembersService = DefaultRoomMembersService(roomId, monarchy, loadRoomMembersTask, inviteTask, taskExecutor) val roomMembersService = DefaultRoomMembersService(roomId, monarchy, loadRoomMembersTask, inviteTask, taskExecutor)
val readService = DefaultReadService(roomId, monarchy, setReadMarkersTask, taskExecutor) val readService = DefaultReadService(roomId, monarchy, setReadMarkersTask, taskExecutor)
@ -60,7 +62,8 @@ internal class RoomFactory(private val loadRoomMembersTask: LoadRoomMembersTask,
sendService, sendService,
stateService, stateService,
readService, readService,
roomMembersService roomMembersService,
cryptoService
) )
} }

View file

@ -71,7 +71,7 @@ class RoomModule {
} }
scope(DefaultSession.SCOPE) { scope(DefaultSession.SCOPE) {
RoomFactory(get(), get(), get(), get(), get(), get(), get(), get(), get()) RoomFactory(get(), get(), get(), get(), get(), get(), get(), get(), get(), get())
} }
scope(DefaultSession.SCOPE) { scope(DefaultSession.SCOPE) {

View file

@ -68,26 +68,4 @@ internal class DefaultRoomMembersService(private val roomId: String,
.dispatchTo(callback) .dispatchTo(callback)
.executeBy(taskExecutor) .executeBy(taskExecutor)
} }
override fun getActiveRoomMemberIds(): List<String> {
return getRoomMemberIdsFiltered { it.membership == Membership.JOIN || it.membership == Membership.INVITE }
}
override fun getJoinedRoomMemberIds(): List<String> {
return getRoomMemberIdsFiltered { it.membership == Membership.JOIN }
}
/* ==========================================================================================
* Private
* ========================================================================================== */
private fun getRoomMemberIdsFiltered(predicate: (RoomMember) -> Boolean): List<String> {
return monarchy.fetchAllCopiedSync { RoomMembers(it, roomId).queryRoomMembersEvent() }
.map { it.asDomain() }
.associateBy { it.stateKey!! }
.mapValues { it.value.content.toModel<RoomMember>()!! }
.filterValues { predicate(it) }
.keys
.toList()
}
} }

View file

@ -95,5 +95,38 @@ internal class RoomMembers(private val realm: Realm,
return getNumberOfJoinedMembers() + getNumberOfInvitedMembers() return getNumberOfJoinedMembers() + getNumberOfInvitedMembers()
} }
/**
* Return all the roomMembers ids which are joined or invited to the room
*
* @return a roomMember id list of joined or invited members.
*/
fun getActiveRoomMemberIds(): List<String> {
return getRoomMemberIdsFiltered { it.membership == Membership.JOIN || it.membership == Membership.INVITE }
}
/**
* Return all the roomMembers ids which are joined to the room
*
* @return a roomMember id list of joined members.
*/
fun getJoinedRoomMemberIds(): List<String> {
return getRoomMemberIdsFiltered { it.membership == Membership.JOIN }
}
/* ==========================================================================================
* Private
* ========================================================================================== */
private fun getRoomMemberIdsFiltered(predicate: (RoomMember) -> Boolean): List<String> {
return RoomMembers(realm, roomId)
.queryRoomMembersEvent()
.findAll()
.map { it.asDomain() }
.associateBy { it.stateKey!! }
.mapValues { it.value.content.toModel<RoomMember>()!! }
.filterValues { predicate(it) }
.keys
.toList()
}
} }

View file

@ -16,15 +16,10 @@
package im.vector.matrix.android.internal.session.room.send package im.vector.matrix.android.internal.session.room.send
import androidx.work.BackoffPolicy import androidx.work.*
import androidx.work.Constraints
import androidx.work.ExistingWorkPolicy
import androidx.work.NetworkType
import androidx.work.OneTimeWorkRequest
import androidx.work.OneTimeWorkRequestBuilder
import androidx.work.WorkManager
import com.zhuinden.monarchy.Monarchy import com.zhuinden.monarchy.Monarchy
import im.vector.matrix.android.api.session.content.ContentAttachmentData import im.vector.matrix.android.api.session.content.ContentAttachmentData
import im.vector.matrix.android.api.session.crypto.CryptoService
import im.vector.matrix.android.api.session.events.model.Event import im.vector.matrix.android.api.session.events.model.Event
import im.vector.matrix.android.api.session.room.send.SendService import im.vector.matrix.android.api.session.room.send.SendService
import im.vector.matrix.android.api.util.Cancelable import im.vector.matrix.android.api.util.Cancelable
@ -39,6 +34,7 @@ import im.vector.matrix.android.internal.session.content.UploadContentWorker
import im.vector.matrix.android.internal.util.CancelableWork import im.vector.matrix.android.internal.util.CancelableWork
import im.vector.matrix.android.internal.util.WorkerParamsFactory import im.vector.matrix.android.internal.util.WorkerParamsFactory
import im.vector.matrix.android.internal.util.tryTransactionAsync import im.vector.matrix.android.internal.util.tryTransactionAsync
import timber.log.Timber
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
private const val SEND_WORK = "SEND_WORK" private const val SEND_WORK = "SEND_WORK"
@ -51,6 +47,7 @@ private val WORK_CONSTRAINTS = Constraints.Builder()
internal class DefaultSendService(private val roomId: String, internal class DefaultSendService(private val roomId: String,
private val eventFactory: LocalEchoEventFactory, private val eventFactory: LocalEchoEventFactory,
private val cryptoService: CryptoService,
private val monarchy: Monarchy) private val monarchy: Monarchy)
: SendService { : SendService {
@ -59,6 +56,33 @@ internal class DefaultSendService(private val roomId: String,
val event = eventFactory.createTextEvent(roomId, msgType, text).also { val event = eventFactory.createTextEvent(roomId, msgType, text).also {
saveLocalEcho(it) saveLocalEcho(it)
} }
// Encrypted room handling
if (cryptoService.isRoomEncrypted(roomId)) {
Timber.v("Send event in encrypted room")
// Encrypt then send
val encryptWork = createEncryptEventWork(event)
val sendWork = OneTimeWorkRequestBuilder<SendEventWorker>()
.setConstraints(WORK_CONSTRAINTS)
.setBackoffCriteria(BackoffPolicy.LINEAR, BACKOFF_DELAY, TimeUnit.MILLISECONDS)
.build()
WorkManager.getInstance()
// Encrypt
.beginUniqueWork(buildWorkIdentifier(SEND_WORK), ExistingWorkPolicy.APPEND, encryptWork)
// then send
.then(sendWork)
.enqueue()
return CancelableWork(encryptWork.id)
} else {
return sendEvent(event)
}
}
private fun sendEvent(event: Event): Cancelable {
val sendWork = createSendEventWork(event) val sendWork = createSendEventWork(event)
WorkManager.getInstance() WorkManager.getInstance()
.beginUniqueWork(buildWorkIdentifier(SEND_WORK), ExistingWorkPolicy.APPEND, sendWork) .beginUniqueWork(buildWorkIdentifier(SEND_WORK), ExistingWorkPolicy.APPEND, sendWork)
@ -105,6 +129,18 @@ internal class DefaultSendService(private val roomId: String,
return "${roomId}_$identifier" return "${roomId}_$identifier"
} }
private fun createEncryptEventWork(event: Event): OneTimeWorkRequest {
// Same parameter
val sendContentWorkerParams = SendEventWorker.Params(roomId, event)
val sendWorkData = WorkerParamsFactory.toData(sendContentWorkerParams)
return OneTimeWorkRequestBuilder<EncryptEventWorker>()
.setConstraints(WORK_CONSTRAINTS)
.setInputData(sendWorkData)
.setBackoffCriteria(BackoffPolicy.LINEAR, BACKOFF_DELAY, TimeUnit.MILLISECONDS)
.build()
}
private fun createSendEventWork(event: Event): OneTimeWorkRequest { private fun createSendEventWork(event: Event): OneTimeWorkRequest {
val sendContentWorkerParams = SendEventWorker.Params(roomId, event) val sendContentWorkerParams = SendEventWorker.Params(roomId, event)
val sendWorkData = WorkerParamsFactory.toData(sendContentWorkerParams) val sendWorkData = WorkerParamsFactory.toData(sendContentWorkerParams)

View file

@ -0,0 +1,88 @@
/*
* 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.session.room.send
import android.content.Context
import androidx.work.Worker
import androidx.work.WorkerParameters
import com.squareup.moshi.JsonClass
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.Event
import im.vector.matrix.android.api.session.room.RoomService
import im.vector.matrix.android.internal.crypto.model.MXEncryptEventContentResult
import im.vector.matrix.android.internal.di.MatrixKoinComponent
import im.vector.matrix.android.internal.util.WorkerParamsFactory
import org.koin.standalone.inject
import java.util.concurrent.CountDownLatch
internal class EncryptEventWorker(context: Context, params: WorkerParameters)
: Worker(context, params), MatrixKoinComponent {
@JsonClass(generateAdapter = true)
internal data class Params(
val roomId: String,
val event: Event
)
private val crypto by inject<CryptoService>()
private val roomService by inject<RoomService>()
override fun doWork(): Result {
val params = WorkerParamsFactory.fromData<Params>(inputData)
?: return Result.failure()
val localEvent = params.event
if (localEvent.eventId == null) {
return Result.failure()
}
// TODO Better async handling
val latch = CountDownLatch(1)
var result: MXEncryptEventContentResult? = null
var error: Throwable? = null
crypto.encryptEventContent(localEvent.content!!, localEvent.type, roomService.getRoom(params.roomId)!!, object : MatrixCallback<MXEncryptEventContentResult> {
override fun onSuccess(data: MXEncryptEventContentResult) {
result = data
latch.countDown()
}
override fun onFailure(failure: Throwable) {
error = failure
latch.countDown()
}
})
latch.await()
// TODO Update local echo
if (error != null) {
return Result.failure() // TODO Pass error!!)
} else if (result != null) {
return Result.success(WorkerParamsFactory.toData(SendEventWorker.Params(params.roomId,
Event(type = result!!.mEventType,
content = result!!.mEventContent))))
} else {
return Result.failure()
}
}
}

View file

@ -91,9 +91,16 @@ internal class RoomSyncHandler(private val monarchy: Monarchy,
val numberOfStateEvents = roomSync.state?.events?.size ?: 0 val numberOfStateEvents = roomSync.state?.events?.size ?: 0
val stateIndexOffset = lastStateIndex + numberOfStateEvents val stateIndexOffset = lastStateIndex + numberOfStateEvents
// State event
if (roomSync.state != null && roomSync.state.events.isNotEmpty()) { if (roomSync.state != null && roomSync.state.events.isNotEmpty()) {
val untimelinedStateIndex = if (isInitialSync) Int.MIN_VALUE else stateIndexOffset val untimelinedStateIndex = if (isInitialSync) Int.MIN_VALUE else stateIndexOffset
roomEntity.addStateEvents(roomSync.state.events, filterDuplicates = true, stateIndex = untimelinedStateIndex) roomEntity.addStateEvents(roomSync.state.events, filterDuplicates = true, stateIndex = untimelinedStateIndex)
// Give info to crypto module
// TODO Remove
roomSync.state.events.forEach {
mCrypto.onStateEvent(roomId, it)
}
} }
if (roomSync.timeline != null && roomSync.timeline.events.isNotEmpty()) { if (roomSync.timeline != null && roomSync.timeline.events.isNotEmpty()) {