WIP
This commit is contained in:
parent
6d8000b957
commit
52d9adad70
|
@ -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
|
||||||
}
|
}
|
|
@ -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
|
|
||||||
}
|
}
|
|
@ -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>
|
|
||||||
|
|
||||||
}
|
}
|
|
@ -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,10 +596,10 @@ 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>) {
|
||||||
// wait that the crypto is really started
|
// wait that the crypto is really started
|
||||||
if (!isStarted()) {
|
if (!isStarted()) {
|
||||||
Timber.v("## encryptEventContent() : wait after e2e init")
|
Timber.v("## encryptEventContent() : wait after e2e init")
|
||||||
|
@ -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) {
|
||||||
|
|
|
@ -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(),
|
||||||
|
|
|
@ -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
|
|
|
@ -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()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -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()
|
||||||
|
}
|
||||||
|
}
|
|
@ -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.
|
||||||
*
|
*
|
||||||
|
|
|
@ -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
|
||||||
|
@ -55,7 +55,7 @@ internal class RealmCryptoStore(private val enableFileEncryption: Boolean = fals
|
||||||
// Cache for InboundGroupSession, to release them properly
|
// Cache for InboundGroupSession, to release them properly
|
||||||
private val inboundGroupSessionToRelease = HashMap<String, MXOlmInboundGroupSession2>()
|
private val inboundGroupSessionToRelease = HashMap<String, MXOlmInboundGroupSession2>()
|
||||||
|
|
||||||
/* ==========================================================================================
|
/* ==========================================================================================
|
||||||
* Other data
|
* Other data
|
||||||
* ========================================================================================== */
|
* ========================================================================================== */
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
||||||
|
|
|
@ -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>)
|
||||||
|
|
||||||
}
|
}
|
|
@ -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,
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -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
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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()
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -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()
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -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)
|
||||||
|
@ -93,9 +117,9 @@ internal class DefaultSendService(private val roomId: String,
|
||||||
private fun saveLocalEcho(event: Event) {
|
private fun saveLocalEcho(event: Event) {
|
||||||
monarchy.tryTransactionAsync { realm ->
|
monarchy.tryTransactionAsync { realm ->
|
||||||
val roomEntity = RoomEntity.where(realm, roomId = roomId).findFirst()
|
val roomEntity = RoomEntity.where(realm, roomId = roomId).findFirst()
|
||||||
?: return@tryTransactionAsync
|
?: return@tryTransactionAsync
|
||||||
val liveChunk = ChunkEntity.findLastLiveChunkFromRoom(realm, roomId = roomId)
|
val liveChunk = ChunkEntity.findLastLiveChunkFromRoom(realm, roomId = roomId)
|
||||||
?: return@tryTransactionAsync
|
?: return@tryTransactionAsync
|
||||||
|
|
||||||
roomEntity.addSendingEvent(event, liveChunk.forwardsStateIndex ?: 0)
|
roomEntity.addSendingEvent(event, liveChunk.forwardsStateIndex ?: 0)
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
|
|
@ -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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -63,9 +63,9 @@ internal class RoomSyncHandler(private val monarchy: Monarchy,
|
||||||
|
|
||||||
private fun handleRoomSync(realm: Realm, handlingStrategy: HandlingStrategy) {
|
private fun handleRoomSync(realm: Realm, handlingStrategy: HandlingStrategy) {
|
||||||
val rooms = when (handlingStrategy) {
|
val rooms = when (handlingStrategy) {
|
||||||
is HandlingStrategy.JOINED -> handlingStrategy.data.map { handleJoinedRoom(realm, it.key, it.value) }
|
is HandlingStrategy.JOINED -> handlingStrategy.data.map { handleJoinedRoom(realm, it.key, it.value) }
|
||||||
is HandlingStrategy.INVITED -> handlingStrategy.data.map { handleInvitedRoom(realm, it.key, it.value) }
|
is HandlingStrategy.INVITED -> handlingStrategy.data.map { handleInvitedRoom(realm, it.key, it.value) }
|
||||||
is HandlingStrategy.LEFT -> handlingStrategy.data.map { handleLeftRoom(it.key, it.value) }
|
is HandlingStrategy.LEFT -> handlingStrategy.data.map { handleLeftRoom(it.key, it.value) }
|
||||||
}
|
}
|
||||||
realm.insertOrUpdate(rooms)
|
realm.insertOrUpdate(rooms)
|
||||||
}
|
}
|
||||||
|
@ -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()) {
|
||||||
|
|
Loading…
Reference in New Issue