Crypto: WIP cleaning

This commit is contained in:
ganfra 2019-06-02 20:34:19 +02:00
parent 3f7d20ec5b
commit 784d55c16c
10 changed files with 238 additions and 236 deletions

View File

@ -46,11 +46,11 @@ android {
debug { debug {
// Set to true to log privacy or sensible data, such as token // Set to true to log privacy or sensible data, such as token
// TODO Set to false // TODO Set to false
buildConfigField "boolean", "LOG_PRIVATE_DATA", "true" buildConfigField "boolean", "LOG_PRIVATE_DATA", "false"


// Set to BODY instead of NONE to enable logging // Set to BODY instead of NONE to enable logging
//TODO Revert BODY //TODO Revert BODY
buildConfigField "okhttp3.logging.HttpLoggingInterceptor.Level", "OKHTTP_LOGGING_LEVEL", "okhttp3.logging.HttpLoggingInterceptor.Level.BODY" buildConfigField "okhttp3.logging.HttpLoggingInterceptor.Level", "OKHTTP_LOGGING_LEVEL", "okhttp3.logging.HttpLoggingInterceptor.Level.HEADERS"
} }


release { release {

View File

@ -57,11 +57,8 @@ import im.vector.matrix.android.internal.crypto.model.rest.KeysUploadResponse
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.repository.WarnOnUnknownDeviceRepository import im.vector.matrix.android.internal.crypto.repository.WarnOnUnknownDeviceRepository
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.tasks.ClaimOneTimeKeysForUsersDeviceTask
import im.vector.matrix.android.internal.crypto.tasks.DeleteDeviceTask import im.vector.matrix.android.internal.crypto.tasks.DeleteDeviceTask
import im.vector.matrix.android.internal.crypto.tasks.GetDevicesTask import im.vector.matrix.android.internal.crypto.tasks.GetDevicesTask
import im.vector.matrix.android.internal.crypto.tasks.GetKeyChangesTask
import im.vector.matrix.android.internal.crypto.tasks.SendToDeviceTask
import im.vector.matrix.android.internal.crypto.tasks.SetDeviceNameTask import im.vector.matrix.android.internal.crypto.tasks.SetDeviceNameTask
import im.vector.matrix.android.internal.crypto.tasks.UploadKeysTask import im.vector.matrix.android.internal.crypto.tasks.UploadKeysTask
import im.vector.matrix.android.internal.crypto.verification.DefaultSasVerificationService import im.vector.matrix.android.internal.crypto.verification.DefaultSasVerificationService
@ -80,74 +77,71 @@ import timber.log.Timber
import java.util.* import java.util.*


/** /**
* A `MXCrypto` class instance manages the end-to-end crypto for a MXSession instance. * A `CryptoService` class instance manages the end-to-end crypto for a session.
* *
* *
* Messages posted by the user are automatically redirected to MXCrypto in order to be encrypted * Messages posted by the user are automatically redirected to CryptoService in order to be encrypted
* before sending. * before sending.
* In the other hand, received events goes through MXCrypto for decrypting. * In the other hand, received events goes through CryptoService for decrypting.
* MXCrypto maintains all necessary keys and their sharing with other devices required for the crypto. * CryptoService maintains all necessary keys and their sharing with other devices required for the crypto.
* Specially, it tracks all room membership changes events in order to do keys updates. * Specially, it tracks all room membership changes events in order to do keys updates.
*/ */
internal class CryptoManager( internal class CryptoManager(
// The credentials, // The credentials,
private val mCredentials: Credentials, private val credentials: Credentials,
private val mMyDeviceInfoHolder: MyDeviceInfoHolder, private val myDeviceInfoHolder: MyDeviceInfoHolder,
// the crypto store // the crypto store
private val mCryptoStore: IMXCryptoStore, private val cryptoStore: IMXCryptoStore,
// Olm device // Olm device
private val mOlmDevice: MXOlmDevice, private val olmDevice: MXOlmDevice,
// Set of parameters used to configure/customize the end-to-end crypto. // Set of parameters used to configure/customize the end-to-end crypto.
private val mCryptoConfig: MXCryptoConfig = MXCryptoConfig(), private val cryptoConfig: MXCryptoConfig = MXCryptoConfig(),
// Device list manager // Device list manager
private val deviceListManager: DeviceListManager, private val deviceListManager: DeviceListManager,
// The key backup service. // The key backup service.
private val mKeysBackup: KeysBackup, private val keysBackup: KeysBackup,
// //
private val mObjectSigner: ObjectSigner, private val objectSigner: ObjectSigner,
// //
private val mOneTimeKeysUploader: OneTimeKeysUploader, private val oneTimeKeysUploader: OneTimeKeysUploader,
// //
private val roomDecryptorProvider: RoomDecryptorProvider, private val roomDecryptorProvider: RoomDecryptorProvider,
// The SAS verification service. // The SAS verification service.
private val mSasVerificationService: DefaultSasVerificationService, private val sasVerificationService: DefaultSasVerificationService,
// //
private val mIncomingRoomKeyRequestManager: IncomingRoomKeyRequestManager, private val incomingRoomKeyRequestManager: IncomingRoomKeyRequestManager,
// //
private val mOutgoingRoomKeyRequestManager: OutgoingRoomKeyRequestManager, private val outgoingRoomKeyRequestManager: OutgoingRoomKeyRequestManager,
// Olm Manager // Olm Manager
private val mOlmManager: OlmManager, private val olmManager: OlmManager,
// Actions // Actions
private val mSetDeviceVerificationAction: SetDeviceVerificationAction, private val setDeviceVerificationAction: SetDeviceVerificationAction,
private val mMegolmSessionDataImporter: MegolmSessionDataImporter, private val megolmSessionDataImporter: MegolmSessionDataImporter,
private val mEnsureOlmSessionsForDevicesAction: EnsureOlmSessionsForDevicesAction, private val ensureOlmSessionsForDevicesAction: EnsureOlmSessionsForDevicesAction,
// Repository // Repository
private val mWarnOnUnknownDevicesRepository: WarnOnUnknownDeviceRepository, private val warnOnUnknownDevicesRepository: WarnOnUnknownDeviceRepository,
private val mMXMegolmEncryptionFactory: MXMegolmEncryptionFactory, private val megolmEncryptionFactory: MXMegolmEncryptionFactory,
private val mMXOlmEncryptionFactory: MXOlmEncryptionFactory, private val olmEncryptionFactory: MXOlmEncryptionFactory,
// Tasks // Tasks
private val mClaimOneTimeKeysForUsersDeviceTask: ClaimOneTimeKeysForUsersDeviceTask, private val deleteDeviceTask: DeleteDeviceTask,
private val mDeleteDeviceTask: DeleteDeviceTask, private val getDevicesTask: GetDevicesTask,
private val mGetDevicesTask: GetDevicesTask, private val setDeviceNameTask: SetDeviceNameTask,
private val mGetKeyChangesTask: GetKeyChangesTask, private val uploadKeysTask: UploadKeysTask,
private val mSendToDeviceTask: SendToDeviceTask,
private val mSetDeviceNameTask: SetDeviceNameTask,
private val mUploadKeysTask: UploadKeysTask,
private val loadRoomMembersTask: LoadRoomMembersTask, private val loadRoomMembersTask: LoadRoomMembersTask,
private val monarchy: Monarchy, private val monarchy: Monarchy,
private val coroutineDispatchers: MatrixCoroutineDispatchers, private val coroutineDispatchers: MatrixCoroutineDispatchers,
// TaskExecutor // TaskExecutor
private val mTaskExecutor: TaskExecutor private val taskExecutor: TaskExecutor
) : CryptoService { ) : CryptoService {


// MXEncrypting instance for each room. // MXEncrypting instance for each room.
private val mRoomEncryptors: MutableMap<String, IMXEncrypting> = HashMap() private val roomEncryptors: MutableMap<String, IMXEncrypting> = HashMap()


// the encryption is starting // the encryption is starting
private var mIsStarting: Boolean = false private var isStarting: Boolean = false


// tell if the crypto is started // tell if the crypto is started
private var mIsStarted: Boolean = false private var isStarted: Boolean = false


// TODO // TODO
//private val mNetworkListener = object : IMXNetworkEventListener { //private val mNetworkListener = object : IMXNetworkEventListener {
@ -179,36 +173,36 @@ internal class CryptoManager(
private val mInitializationCallbacks = ArrayList<MatrixCallback<Unit>>() private val mInitializationCallbacks = ArrayList<MatrixCallback<Unit>>()


override fun setDeviceName(deviceId: String, deviceName: String, callback: MatrixCallback<Unit>) { override fun setDeviceName(deviceId: String, deviceName: String, callback: MatrixCallback<Unit>) {
mSetDeviceNameTask setDeviceNameTask
.configureWith(SetDeviceNameTask.Params(deviceId, deviceName)) .configureWith(SetDeviceNameTask.Params(deviceId, deviceName))
.dispatchTo(callback) .dispatchTo(callback)
.executeBy(mTaskExecutor) .executeBy(taskExecutor)
} }


override fun deleteDevice(deviceId: String, accountPassword: String, callback: MatrixCallback<Unit>) { override fun deleteDevice(deviceId: String, accountPassword: String, callback: MatrixCallback<Unit>) {
mDeleteDeviceTask deleteDeviceTask
.configureWith(DeleteDeviceTask.Params(deviceId, accountPassword)) .configureWith(DeleteDeviceTask.Params(deviceId, accountPassword))
.dispatchTo(callback) .dispatchTo(callback)
.executeBy(mTaskExecutor) .executeBy(taskExecutor)
} }


override fun getCryptoVersion(context: Context, longFormat: Boolean): String { override fun getCryptoVersion(context: Context, longFormat: Boolean): String {
return if (longFormat) mOlmManager.getDetailedVersion(context) else mOlmManager.version return if (longFormat) olmManager.getDetailedVersion(context) else olmManager.version
} }


override fun getMyDevice(): MXDeviceInfo { override fun getMyDevice(): MXDeviceInfo {
return mMyDeviceInfoHolder.myDevice return myDeviceInfoHolder.myDevice
} }


override fun getDevicesList(callback: MatrixCallback<DevicesListResponse>) { override fun getDevicesList(callback: MatrixCallback<DevicesListResponse>) {
mGetDevicesTask getDevicesTask
.configureWith(Unit) .configureWith(Unit)
.dispatchTo(callback) .dispatchTo(callback)
.executeBy(mTaskExecutor) .executeBy(taskExecutor)
} }


override fun inboundGroupSessionsCount(onlyBackedUp: Boolean): Int { override fun inboundGroupSessionsCount(onlyBackedUp: Boolean): Int {
return mCryptoStore.inboundGroupSessionsCount(onlyBackedUp) return cryptoStore.inboundGroupSessionsCount(onlyBackedUp)
} }


/** /**
@ -218,7 +212,7 @@ internal class CryptoManager(
* @return the tracking status * @return the tracking status
*/ */
override fun getDeviceTrackingStatus(userId: String): Int { override fun getDeviceTrackingStatus(userId: String): Int {
return mCryptoStore.getDeviceTrackingStatus(userId, DeviceListManager.TRACKING_STATUS_NOT_TRACKED) return cryptoStore.getDeviceTrackingStatus(userId, DeviceListManager.TRACKING_STATUS_NOT_TRACKED)
} }


/** /**
@ -227,7 +221,7 @@ internal class CryptoManager(
* @return true if the crypto is started * @return true if the crypto is started
*/ */
fun isStarted(): Boolean { fun isStarted(): Boolean {
return mIsStarted return isStarted
} }


/** /**
@ -236,7 +230,7 @@ internal class CryptoManager(
* @return true if the crypto is starting * @return true if the crypto is starting
*/ */
fun isStarting(): Boolean { fun isStarting(): Boolean {
return mIsStarting return isStarting
} }


/** /**
@ -248,7 +242,7 @@ internal class CryptoManager(
* @param isInitialSync true if it starts from an initial sync * @param isInitialSync true if it starts from an initial sync
*/ */
fun start(isInitialSync: Boolean) { fun start(isInitialSync: Boolean) {
if (mIsStarting) { if (isStarting) {
return return
} }


@ -261,16 +255,16 @@ internal class CryptoManager(
// return // return
//} //}


mIsStarting = true isStarting = true


// Open the store // Open the store
mCryptoStore.open() cryptoStore.open()


uploadDeviceKeys(object : MatrixCallback<KeysUploadResponse> { uploadDeviceKeys(object : MatrixCallback<KeysUploadResponse> {
private fun onError() { private fun onError() {
Handler().postDelayed({ Handler().postDelayed({
if (!isStarted()) { if (!isStarted()) {
mIsStarting = false isStarting = false
start(isInitialSync) start(isInitialSync)
} }
}, 1000) }, 1000)
@ -278,33 +272,33 @@ internal class CryptoManager(


override fun onSuccess(data: KeysUploadResponse) { override fun onSuccess(data: KeysUploadResponse) {
Timber.v("###########################################################") Timber.v("###########################################################")
Timber.v("uploadDeviceKeys done for " + mCredentials.userId) Timber.v("uploadDeviceKeys done for " + credentials.userId)
Timber.v(" - device id : " + mCredentials.deviceId) Timber.v(" - device id : " + credentials.deviceId)
Timber.v(" - ed25519 : " + mOlmDevice.deviceEd25519Key) Timber.v(" - ed25519 : " + olmDevice.deviceEd25519Key)
Timber.v(" - curve25519 : " + mOlmDevice.deviceCurve25519Key) Timber.v(" - curve25519 : " + olmDevice.deviceCurve25519Key)
Timber.v(" - oneTimeKeys: " + mOneTimeKeysUploader.mLastPublishedOneTimeKeys) Timber.v(" - oneTimeKeys: " + oneTimeKeysUploader.mLastPublishedOneTimeKeys)
Timber.v("") Timber.v("")


mOneTimeKeysUploader.maybeUploadOneTimeKeys(object : MatrixCallback<Unit> { oneTimeKeysUploader.maybeUploadOneTimeKeys(object : MatrixCallback<Unit> {
override fun onSuccess(data: Unit) { override fun onSuccess(data: Unit) {
// TODO // TODO
//if (null != mNetworkConnectivityReceiver) { //if (null != mNetworkConnectivityReceiver) {
// mNetworkConnectivityReceiver!!.removeEventListener(mNetworkListener) // mNetworkConnectivityReceiver!!.removeEventListener(mNetworkListener)
//} //}


mIsStarting = false isStarting = false
mIsStarted = true isStarted = true


mOutgoingRoomKeyRequestManager.start() outgoingRoomKeyRequestManager.start()


mKeysBackup.checkAndStartKeysBackup() keysBackup.checkAndStartKeysBackup()


if (isInitialSync) { if (isInitialSync) {
// refresh the devices list for each known room members // refresh the devices list for each known room members
deviceListManager.invalidateAllDeviceLists() deviceListManager.invalidateAllDeviceLists()
deviceListManager.refreshOutdatedDeviceLists() deviceListManager.refreshOutdatedDeviceLists()
} else { } else {
mIncomingRoomKeyRequestManager.processReceivedRoomKeyRequests() incomingRoomKeyRequestManager.processReceivedRoomKeyRequests()
} }
} }


@ -326,58 +320,51 @@ internal class CryptoManager(
* Close the crypto * Close the crypto
*/ */
fun close() { fun close() {
mOlmDevice.release() olmDevice.release()


mCryptoStore.close() cryptoStore.close()


mOutgoingRoomKeyRequestManager.stop() outgoingRoomKeyRequestManager.stop()
} }


override fun isCryptoEnabled(): Boolean { override fun isCryptoEnabled(): Boolean {
// TODO Check that this test is correct // TODO Check that this test is correct
return mOlmDevice != null return olmDevice != null
} }


/** /**
* @return the Keys backup Service * @return the Keys backup Service
*/ */
override fun getKeysBackupService(): KeysBackupService { override fun getKeysBackupService(): KeysBackupService {
return mKeysBackup return keysBackup
} }


/** /**
* @return the SasVerificationService * @return the SasVerificationService
*/ */
override fun getSasVerificationService(): SasVerificationService { override fun getSasVerificationService(): SasVerificationService {
return mSasVerificationService return sasVerificationService
} }


/** /**
* A sync response has been received * A sync response has been received
* *
* @param syncResponse the syncResponse * @param syncResponse the syncResponse
* @param fromToken the start sync token
* @param isCatchingUp true if there is a catch-up in progress.
*/ */
fun onSyncCompleted(syncResponse: SyncResponse, fromToken: String?, isCatchingUp: Boolean) { fun onSyncCompleted(syncResponse: SyncResponse) {
if (null != syncResponse.deviceLists) { if (syncResponse.deviceLists != null) {
deviceListManager.handleDeviceListsChanges(syncResponse.deviceLists.changed, syncResponse.deviceLists.left) deviceListManager.handleDeviceListsChanges(syncResponse.deviceLists.changed, syncResponse.deviceLists.left)
} }

if (syncResponse.deviceOneTimeKeysCount != null) {
if (null != syncResponse.deviceOneTimeKeysCount) {
val currentCount = syncResponse.deviceOneTimeKeysCount.signedCurve25519 ?: 0 val currentCount = syncResponse.deviceOneTimeKeysCount.signedCurve25519 ?: 0
mOneTimeKeysUploader.updateOneTimeKeyCount(currentCount) oneTimeKeysUploader.updateOneTimeKeyCount(currentCount)
} }


if (isStarted()) { if (isStarted()) {
// Make sure we process to-device messages before generating new one-time-keys #2782 // Make sure we process to-device messages before generating new one-time-keys #2782
deviceListManager.refreshOutdatedDeviceLists() deviceListManager.refreshOutdatedDeviceLists()
} oneTimeKeysUploader.maybeUploadOneTimeKeys()

incomingRoomKeyRequestManager.processReceivedRoomKeyRequests()
if (!isCatchingUp && isStarted()) {
mOneTimeKeysUploader.maybeUploadOneTimeKeys()

mIncomingRoomKeyRequestManager.processReceivedRoomKeyRequests()
} }
} }


@ -392,7 +379,7 @@ internal class CryptoManager(
return if (!TextUtils.equals(algorithm, MXCRYPTO_ALGORITHM_MEGOLM) && !TextUtils.equals(algorithm, MXCRYPTO_ALGORITHM_OLM)) { return if (!TextUtils.equals(algorithm, MXCRYPTO_ALGORITHM_MEGOLM) && !TextUtils.equals(algorithm, MXCRYPTO_ALGORITHM_OLM)) {
// We only deal in olm keys // We only deal in olm keys
null null
} else mCryptoStore.deviceWithIdentityKey(senderKey) } else cryptoStore.deviceWithIdentityKey(senderKey)
} }


/** /**
@ -403,7 +390,7 @@ internal class CryptoManager(
*/ */
override fun getDeviceInfo(userId: String, deviceId: String?): MXDeviceInfo? { override fun getDeviceInfo(userId: String, deviceId: String?): MXDeviceInfo? {
return if (!TextUtils.isEmpty(userId) && !TextUtils.isEmpty(deviceId)) { return if (!TextUtils.isEmpty(userId) && !TextUtils.isEmpty(deviceId)) {
mCryptoStore.getUserDevice(deviceId!!, userId) cryptoStore.getUserDevice(deviceId!!, userId)
} else { } else {
null null
} }
@ -432,7 +419,7 @@ internal class CryptoManager(
val userIds = devicesIdListByUserId.keys val userIds = devicesIdListByUserId.keys


for (userId in userIds) { for (userId in userIds) {
val storedDeviceIDs = mCryptoStore.getUserDevices(userId) val storedDeviceIDs = cryptoStore.getUserDevices(userId)


// sanity checks // sanity checks
if (null != storedDeviceIDs) { if (null != storedDeviceIDs) {
@ -451,7 +438,7 @@ internal class CryptoManager(
} }


if (isUpdated) { if (isUpdated) {
mCryptoStore.storeUserDevices(userId, storedDeviceIDs) cryptoStore.storeUserDevices(userId, storedDeviceIDs)
} }
} }
} }
@ -468,7 +455,7 @@ internal class CryptoManager(
* @param userId the owner of the device * @param userId the owner of the device
*/ */
override fun setDeviceVerification(verificationStatus: Int, deviceId: String, userId: String) { override fun setDeviceVerification(verificationStatus: Int, deviceId: String, userId: String) {
mSetDeviceVerificationAction.handle(verificationStatus, deviceId, userId) setDeviceVerificationAction.handle(verificationStatus, deviceId, userId)
} }


/** /**
@ -484,7 +471,7 @@ internal class CryptoManager(
private fun setEncryptionInRoom(roomId: String, algorithm: String?, inhibitDeviceQuery: Boolean, membersId: List<String>): Boolean { private fun setEncryptionInRoom(roomId: String, algorithm: String?, inhibitDeviceQuery: Boolean, membersId: List<String>): Boolean {
// If we already have encryption in this room, we should ignore this event // If we already have encryption in this room, we should ignore this event
// (for now at least. Maybe we should alert the user somehow?) // (for now at least. Maybe we should alert the user somehow?)
val existingAlgorithm = mCryptoStore.getRoomAlgorithm(roomId) val existingAlgorithm = cryptoStore.getRoomAlgorithm(roomId)


if (!TextUtils.isEmpty(existingAlgorithm) && !TextUtils.equals(existingAlgorithm, algorithm)) { if (!TextUtils.isEmpty(existingAlgorithm) && !TextUtils.equals(existingAlgorithm, algorithm)) {
Timber.e("## setEncryptionInRoom() : Ignoring m.room.encryption event which requests a change of config in $roomId") Timber.e("## setEncryptionInRoom() : Ignoring m.room.encryption event which requests a change of config in $roomId")
@ -498,15 +485,15 @@ internal class CryptoManager(
return false return false
} }


mCryptoStore.storeRoomAlgorithm(roomId, algorithm!!) cryptoStore.storeRoomAlgorithm(roomId, algorithm!!)


val alg: IMXEncrypting = when (algorithm) { val alg: IMXEncrypting = when (algorithm) {
MXCRYPTO_ALGORITHM_MEGOLM -> mMXMegolmEncryptionFactory.instantiate(roomId) MXCRYPTO_ALGORITHM_MEGOLM -> megolmEncryptionFactory.instantiate(roomId)
else -> mMXOlmEncryptionFactory.instantiate(roomId) else -> olmEncryptionFactory.instantiate(roomId)
} }


synchronized(mRoomEncryptors) { synchronized(roomEncryptors) {
mRoomEncryptors.put(roomId, alg) roomEncryptors.put(roomId, alg)
} }


// if encryption was not previously enabled in this room, we will have been // if encryption was not previously enabled in this room, we will have been
@ -538,12 +525,12 @@ internal class CryptoManager(
override fun isRoomEncrypted(roomId: String): Boolean { override fun isRoomEncrypted(roomId: String): Boolean {
var res: Boolean var res: Boolean


synchronized(mRoomEncryptors) { synchronized(roomEncryptors) {
res = mRoomEncryptors.containsKey(roomId) res = roomEncryptors.containsKey(roomId)
} }


if (!res) { if (!res) {
res = !mCryptoStore.getRoomAlgorithm(roomId).isNullOrBlank() res = !cryptoStore.getRoomAlgorithm(roomId).isNullOrBlank()
} }


return res return res
@ -553,7 +540,7 @@ internal class CryptoManager(
* @return the stored device keys for a user. * @return the stored device keys for a user.
*/ */
override fun getUserDevices(userId: String): MutableList<MXDeviceInfo> { override fun getUserDevices(userId: String): MutableList<MXDeviceInfo> {
val map = mCryptoStore.getUserDevices(userId) val map = cryptoStore.getUserDevices(userId)
return if (null != map) ArrayList(map.values) else ArrayList() return if (null != map) ArrayList(map.values) else ArrayList()
} }


@ -568,15 +555,15 @@ internal class CryptoManager(
*/ */
fun ensureOlmSessionsForDevices(devicesByUser: Map<String, List<MXDeviceInfo>>, fun ensureOlmSessionsForDevices(devicesByUser: Map<String, List<MXDeviceInfo>>,
callback: MatrixCallback<MXUsersDevicesMap<MXOlmSessionResult>>?) { callback: MatrixCallback<MXUsersDevicesMap<MXOlmSessionResult>>?) {
mEnsureOlmSessionsForDevicesAction.handle(devicesByUser, callback) ensureOlmSessionsForDevicesAction.handle(devicesByUser, callback)
} }


fun isEncryptionEnabledForInvitedUser(): Boolean { fun isEncryptionEnabledForInvitedUser(): Boolean {
return mCryptoConfig.mEnableEncryptionForInvitedMembers return cryptoConfig.mEnableEncryptionForInvitedMembers
} }


override fun getEncryptionAlgorithm(roomId: String): String? { override fun getEncryptionAlgorithm(roomId: String): String? {
return mCryptoStore.getRoomAlgorithm(roomId) return cryptoStore.getRoomAlgorithm(roomId)
} }


/** /**
@ -588,7 +575,7 @@ internal class CryptoManager(
* @return true if we should encrypt messages for invited users. * @return true if we should encrypt messages for invited users.
*/ */
override fun shouldEncryptForInvitedMembers(roomId: String): Boolean { override fun shouldEncryptForInvitedMembers(roomId: String): Boolean {
return mCryptoStore.shouldEncryptForInvitedMembers(roomId) return cryptoStore.shouldEncryptForInvitedMembers(roomId)
} }


/** /**
@ -612,15 +599,15 @@ internal class CryptoManager(
} }


val userIds = getRoomUserIds(roomId) val userIds = getRoomUserIds(roomId)
var alg = synchronized(mRoomEncryptors) { var alg = synchronized(roomEncryptors) {
mRoomEncryptors[roomId] roomEncryptors[roomId]
} }
if (null == alg) { if (null == alg) {
val algorithm = getEncryptionAlgorithm(roomId) val algorithm = getEncryptionAlgorithm(roomId)
if (null != algorithm) { if (null != algorithm) {
if (setEncryptionInRoom(roomId, algorithm, false, userIds)) { if (setEncryptionInRoom(roomId, algorithm, false, userIds)) {
synchronized(mRoomEncryptors) { synchronized(roomEncryptors) {
alg = mRoomEncryptors[roomId] alg = roomEncryptors[roomId]
} }
} }
} }
@ -706,7 +693,7 @@ internal class CryptoManager(
* @param timelineId the timeline id * @param timelineId the timeline id
*/ */
fun resetReplayAttackCheckInTimeline(timelineId: String) { fun resetReplayAttackCheckInTimeline(timelineId: String) {
mOlmDevice.resetReplayAttackCheckInTimeline(timelineId) olmDevice.resetReplayAttackCheckInTimeline(timelineId)
} }


/** /**
@ -718,7 +705,7 @@ internal class CryptoManager(
if (event.getClearType() == EventType.ROOM_KEY || event.getClearType() == EventType.FORWARDED_ROOM_KEY) { if (event.getClearType() == EventType.ROOM_KEY || event.getClearType() == EventType.FORWARDED_ROOM_KEY) {
onRoomKeyEvent(event) onRoomKeyEvent(event)
} else if (event.getClearType() == EventType.ROOM_KEY_REQUEST) { } else if (event.getClearType() == EventType.ROOM_KEY_REQUEST) {
mIncomingRoomKeyRequestManager.onRoomKeyRequestEvent(event) incomingRoomKeyRequestManager.onRoomKeyRequestEvent(event)
} }
} }


@ -743,7 +730,7 @@ internal class CryptoManager(
return return
} }


alg.onRoomKeyEvent(event, mKeysBackup) alg.onRoomKeyEvent(event, keysBackup)
} }


/** /**
@ -759,7 +746,6 @@ internal class CryptoManager(
.map { allLoaded -> .map { allLoaded ->
val userIds = getRoomUserIds(roomId) val userIds = getRoomUserIds(roomId)
setEncryptionInRoom(roomId, event.content!!["algorithm"] as String, true, userIds) setEncryptionInRoom(roomId, event.content!!["algorithm"] as String, true, userIds)
allLoaded
} }
} }
} }
@ -789,8 +775,8 @@ internal class CryptoManager(
private fun onRoomMembershipEvent(roomId: String, event: Event) { private fun onRoomMembershipEvent(roomId: String, event: Event) {
val alg: IMXEncrypting? val alg: IMXEncrypting?


synchronized(mRoomEncryptors) { synchronized(roomEncryptors) {
alg = mRoomEncryptors[roomId] alg = roomEncryptors[roomId]
} }


if (null == alg) { if (null == alg) {
@ -805,7 +791,7 @@ internal class CryptoManager(
deviceListManager.startTrackingDeviceList(Arrays.asList(userId)) deviceListManager.startTrackingDeviceList(Arrays.asList(userId))
} else if (membership == Membership.INVITE } else if (membership == Membership.INVITE
&& shouldEncryptForInvitedMembers(roomId) && shouldEncryptForInvitedMembers(roomId)
&& mCryptoConfig.mEnableEncryptionForInvitedMembers) { && cryptoConfig.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
// know what other servers are in the room at the time they've been invited. // know what other servers are in the room at the time they've been invited.
@ -820,7 +806,7 @@ internal class CryptoManager(
val eventContent = event.content.toModel<RoomHistoryVisibilityContent>() val eventContent = event.content.toModel<RoomHistoryVisibilityContent>()


eventContent?.historyVisibility?.let { eventContent?.historyVisibility?.let {
mCryptoStore.setShouldEncryptForInvitedMembers(roomId, it != RoomHistoryVisibility.JOINED) cryptoStore.setShouldEncryptForInvitedMembers(roomId, it != RoomHistoryVisibility.JOINED)
} }
} }


@ -837,15 +823,15 @@ internal class CryptoManager(
// Sign it // Sign it
val canonicalJson = MoshiProvider.getCanonicalJson(Map::class.java, getMyDevice().signalableJSONDictionary()) val canonicalJson = MoshiProvider.getCanonicalJson(Map::class.java, getMyDevice().signalableJSONDictionary())


getMyDevice().signatures = mObjectSigner.signObject(canonicalJson) getMyDevice().signatures = objectSigner.signObject(canonicalJson)


// For now, we set the device id explicitly, as we may not be using the // For now, we set the device id explicitly, as we may not be using the
// same one as used in login. // same one as used in login.
mUploadKeysTask uploadKeysTask
.configureWith(UploadKeysTask.Params(getMyDevice().toDeviceKeys(), null, getMyDevice().deviceId)) .configureWith(UploadKeysTask.Params(getMyDevice().toDeviceKeys(), null, getMyDevice().deviceId))
.executeOn(TaskThread.ENCRYPTION) .executeOn(TaskThread.ENCRYPTION)
.dispatchTo(callback) .dispatchTo(callback)
.executeBy(mTaskExecutor) .executeBy(taskExecutor)
} }


/** /**
@ -870,7 +856,7 @@ internal class CryptoManager(


val exportedSessions = ArrayList<MegolmSessionData>() val exportedSessions = ArrayList<MegolmSessionData>()


val inboundGroupSessions = mCryptoStore.getInboundGroupSessions() val inboundGroupSessions = cryptoStore.getInboundGroupSessions()


for (session in inboundGroupSessions) { for (session in inboundGroupSessions) {
val megolmSessionData = session.exportKeys() val megolmSessionData = session.exportKeys()
@ -941,7 +927,7 @@ internal class CryptoManager(


Timber.v("## importRoomKeys : JSON parsing " + (t2 - t1) + " ms") Timber.v("## importRoomKeys : JSON parsing " + (t2 - t1) + " ms")


mMegolmSessionDataImporter.handle(importedSessions, true, progressListener, callback) megolmSessionDataImporter.handle(importedSessions, true, progressListener, callback)
} }


/** /**
@ -950,7 +936,7 @@ internal class CryptoManager(
* @param warn true to warn when some unknown devices are detected. * @param warn true to warn when some unknown devices are detected.
*/ */
override fun setWarnOnUnknownDevices(warn: Boolean) { override fun setWarnOnUnknownDevices(warn: Boolean) {
mWarnOnUnknownDevicesRepository.setWarnOnUnknownDevices(warn) warnOnUnknownDevicesRepository.setWarnOnUnknownDevices(warn)
} }


/** /**
@ -992,7 +978,7 @@ internal class CryptoManager(
* @param block true to unilaterally blacklist all * @param block true to unilaterally blacklist all
*/ */
override fun setGlobalBlacklistUnverifiedDevices(block: Boolean) { override fun setGlobalBlacklistUnverifiedDevices(block: Boolean) {
mCryptoStore.setGlobalBlacklistUnverifiedDevices(block) cryptoStore.setGlobalBlacklistUnverifiedDevices(block)
} }


/** /**
@ -1003,7 +989,7 @@ internal class CryptoManager(
* @return true to unilaterally blacklist all unverified devices. * @return true to unilaterally blacklist all unverified devices.
*/ */
override fun getGlobalBlacklistUnverifiedDevices(): Boolean { override fun getGlobalBlacklistUnverifiedDevices(): Boolean {
return mCryptoStore.getGlobalBlacklistUnverifiedDevices() return cryptoStore.getGlobalBlacklistUnverifiedDevices()
} }


/** /**
@ -1017,7 +1003,7 @@ internal class CryptoManager(
// TODO add this info in CryptoRoomEntity? // 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) cryptoStore.getRoomsListBlacklistUnverifiedDevices().contains(roomId)
} else { } else {
false false
} }
@ -1030,7 +1016,7 @@ 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 roomIds = mCryptoStore.getRoomsListBlacklistUnverifiedDevices().toMutableList() val roomIds = cryptoStore.getRoomsListBlacklistUnverifiedDevices().toMutableList()


if (add) { if (add) {
if (!roomIds.contains(roomId)) { if (!roomIds.contains(roomId)) {
@ -1040,7 +1026,7 @@ internal class CryptoManager(
roomIds.remove(roomId) roomIds.remove(roomId)
} }


mCryptoStore.setRoomsListBlacklistUnverifiedDevices(roomIds) cryptoStore.setRoomsListBlacklistUnverifiedDevices(roomIds)
} }




@ -1069,7 +1055,7 @@ internal class CryptoManager(
* @param requestBody requestBody * @param requestBody requestBody
*/ */
override fun cancelRoomKeyRequest(requestBody: RoomKeyRequestBody) { override fun cancelRoomKeyRequest(requestBody: RoomKeyRequestBody) {
mOutgoingRoomKeyRequestManager.cancelRoomKeyRequest(requestBody) outgoingRoomKeyRequestManager.cancelRoomKeyRequest(requestBody)
} }


/** /**
@ -1091,7 +1077,7 @@ internal class CryptoManager(
requestBody.senderKey = senderKey requestBody.senderKey = senderKey
requestBody.sessionId = sessionId requestBody.sessionId = sessionId


mOutgoingRoomKeyRequestManager.resendRoomKeyRequest(requestBody) outgoingRoomKeyRequestManager.resendRoomKeyRequest(requestBody)
} }


/** /**
@ -1100,7 +1086,7 @@ internal class CryptoManager(
* @param listener listener * @param listener listener
*/ */
override fun addRoomKeysRequestListener(listener: RoomKeysRequestListener) { override fun addRoomKeysRequestListener(listener: RoomKeysRequestListener) {
mIncomingRoomKeyRequestManager.addRoomKeysRequestListener(listener) incomingRoomKeyRequestManager.addRoomKeysRequestListener(listener)
} }


/** /**
@ -1109,7 +1095,7 @@ internal class CryptoManager(
* @param listener listener * @param listener listener
*/ */
fun removeRoomKeysRequestListener(listener: RoomKeysRequestListener) { fun removeRoomKeysRequestListener(listener: RoomKeysRequestListener) {
mIncomingRoomKeyRequestManager.removeRoomKeysRequestListener(listener) incomingRoomKeyRequestManager.removeRoomKeysRequestListener(listener)
} }


/** /**
@ -1141,7 +1127,7 @@ internal class CryptoManager(
* ========================================================================================== */ * ========================================================================================== */


override fun toString(): String { override fun toString(): String {
return "CryptoManager of " + mCredentials.userId + " (" + mCredentials.deviceId + ")" return "CryptoManager of " + credentials.userId + " (" + credentials.deviceId + ")"


} }
} }

View File

@ -171,38 +171,35 @@ internal class CryptoModule {
// CryptoManager // CryptoManager
scope(DefaultSession.SCOPE) { scope(DefaultSession.SCOPE) {
CryptoManager( CryptoManager(
mCredentials = get(), credentials = get(),
mMyDeviceInfoHolder = get(), myDeviceInfoHolder = get(),
mCryptoStore = get(), cryptoStore = get(),
mOlmDevice = get(), olmDevice = get(),
mCryptoConfig = get(), cryptoConfig = get(),
deviceListManager = get(), deviceListManager = get(),
mKeysBackup = get(), keysBackup = get(),
mObjectSigner = get(), objectSigner = get(),
mOneTimeKeysUploader = get(), oneTimeKeysUploader = get(),
roomDecryptorProvider = get(), roomDecryptorProvider = get(),
mSasVerificationService = get(), sasVerificationService = get(),
mIncomingRoomKeyRequestManager = get(), incomingRoomKeyRequestManager = get(),
mOutgoingRoomKeyRequestManager = get(), outgoingRoomKeyRequestManager = get(),
mOlmManager = get(), olmManager = get(),
mSetDeviceVerificationAction = get(), setDeviceVerificationAction = get(),
mMegolmSessionDataImporter = get(), megolmSessionDataImporter = get(),
mEnsureOlmSessionsForDevicesAction = get(), ensureOlmSessionsForDevicesAction = get(),
mWarnOnUnknownDevicesRepository = get(), warnOnUnknownDevicesRepository = get(),
mMXMegolmEncryptionFactory = get(), megolmEncryptionFactory = get(),
mMXOlmEncryptionFactory = get(), olmEncryptionFactory = get(),
mClaimOneTimeKeysForUsersDeviceTask = get(),
// Tasks // Tasks
mDeleteDeviceTask = get(), deleteDeviceTask = get(),
mGetDevicesTask = get(), getDevicesTask = get(),
mGetKeyChangesTask = get(), setDeviceNameTask = get(),
mSendToDeviceTask = get(), uploadKeysTask = get(),
mSetDeviceNameTask = get(),
mUploadKeysTask = get(),
loadRoomMembersTask = get(), loadRoomMembersTask = get(),
monarchy = get(), monarchy = get(),
coroutineDispatchers = get(), coroutineDispatchers = get(),
mTaskExecutor = get() taskExecutor = get()
) )
} }



View File

@ -33,27 +33,27 @@ import timber.log.Timber
import java.util.* import java.util.*


// Legacy name: MXDeviceList // Legacy name: MXDeviceList
internal class DeviceListManager(private val mCryptoStore: IMXCryptoStore, internal class DeviceListManager(private val cryptoStore: IMXCryptoStore,
private val mOlmDevice: MXOlmDevice, private val olmDevice: MXOlmDevice,
private val mSyncTokenStore: SyncTokenStore, private val syncTokenStore: SyncTokenStore,
private val mCredentials: Credentials, private val credentials: Credentials,
private val mDownloadKeysForUsersTask: DownloadKeysForUsersTask, private val downloadKeysForUsersTask: DownloadKeysForUsersTask,
private val mTaskExecutor: TaskExecutor) { private val taskExecutor: TaskExecutor) {


// keys in progress // keys in progress
private val mUserKeyDownloadsInProgress = HashSet<String>() private val userKeyDownloadsInProgress = HashSet<String>()


// HS not ready for retry // HS not ready for retry
private val mNotReadyToRetryHS = HashSet<String>() private val notReadyToRetryHS = HashSet<String>()


// indexed by UserId // indexed by UserId
private val mPendingDownloadKeysRequestToken = HashMap<String, String>() private val pendingDownloadKeysRequestToken = HashMap<String, String>()


// pending queues list // pending queues list
private val mDownloadKeysQueues = ArrayList<DownloadKeysPromise>() private val downloadKeysQueues = ArrayList<DownloadKeysPromise>()


// tells if there is a download keys request in progress // tells if there is a download keys request in progress
private var mIsDownloadingKeys = false private var isDownloadingKeys = false


/** /**
* Creator * Creator
@ -78,7 +78,7 @@ internal class DeviceListManager(private val mCryptoStore: IMXCryptoStore,
init { init {
var isUpdated = false var isUpdated = false


val deviceTrackingStatuses = mCryptoStore.getDeviceTrackingStatuses().toMutableMap() val deviceTrackingStatuses = cryptoStore.getDeviceTrackingStatuses().toMutableMap()
for (userId in deviceTrackingStatuses.keys) { for (userId in deviceTrackingStatuses.keys) {
val status = deviceTrackingStatuses[userId]!! val status = deviceTrackingStatuses[userId]!!


@ -90,7 +90,7 @@ internal class DeviceListManager(private val mCryptoStore: IMXCryptoStore,
} }


if (isUpdated) { if (isUpdated) {
mCryptoStore.saveDeviceTrackingStatuses(deviceTrackingStatuses) cryptoStore.saveDeviceTrackingStatuses(deviceTrackingStatuses)
} }
} }


@ -105,8 +105,8 @@ internal class DeviceListManager(private val mCryptoStore: IMXCryptoStore,


if (!TextUtils.isEmpty(userId) && userId.contains(":")) { if (!TextUtils.isEmpty(userId) && userId.contains(":")) {
try { try {
synchronized(mNotReadyToRetryHS) { synchronized(notReadyToRetryHS) {
res = !mNotReadyToRetryHS.contains(userId.substring(userId.lastIndexOf(":") + 1)) res = !notReadyToRetryHS.contains(userId.substring(userId.lastIndexOf(":") + 1))
} }
} catch (e: Exception) { } catch (e: Exception) {
Timber.e(e, "## canRetryKeysDownload() failed") Timber.e(e, "## canRetryKeysDownload() failed")
@ -138,15 +138,15 @@ internal class DeviceListManager(private val mCryptoStore: IMXCryptoStore,
} }
} }


synchronized(mUserKeyDownloadsInProgress) { synchronized(userKeyDownloadsInProgress) {
filteredUserIds.removeAll(mUserKeyDownloadsInProgress) filteredUserIds.removeAll(userKeyDownloadsInProgress)
mUserKeyDownloadsInProgress.addAll(userIds) userKeyDownloadsInProgress.addAll(userIds)
// got some email addresses instead of matrix ids // got some email addresses instead of matrix ids
mUserKeyDownloadsInProgress.removeAll(invalidUserIds) userKeyDownloadsInProgress.removeAll(invalidUserIds)
userIds.removeAll(invalidUserIds) userIds.removeAll(invalidUserIds)
} }


mDownloadKeysQueues.add(DownloadKeysPromise(userIds, callback)) downloadKeysQueues.add(DownloadKeysPromise(userIds, callback))


return filteredUserIds return filteredUserIds
} else { } else {
@ -158,8 +158,8 @@ internal class DeviceListManager(private val mCryptoStore: IMXCryptoStore,
* Clear the unavailable server lists * Clear the unavailable server lists
*/ */
private fun clearUnavailableServersList() { private fun clearUnavailableServersList() {
synchronized(mNotReadyToRetryHS) { synchronized(notReadyToRetryHS) {
mNotReadyToRetryHS.clear() notReadyToRetryHS.clear()
} }
} }


@ -172,7 +172,7 @@ internal class DeviceListManager(private val mCryptoStore: IMXCryptoStore,
fun startTrackingDeviceList(userIds: List<String>?) { fun startTrackingDeviceList(userIds: List<String>?) {
if (null != userIds) { if (null != userIds) {
var isUpdated = false var isUpdated = false
val deviceTrackingStatuses = mCryptoStore.getDeviceTrackingStatuses().toMutableMap() val deviceTrackingStatuses = cryptoStore.getDeviceTrackingStatuses().toMutableMap()


for (userId in userIds) { for (userId in userIds) {
if (!deviceTrackingStatuses.containsKey(userId) || TRACKING_STATUS_NOT_TRACKED == deviceTrackingStatuses[userId]) { if (!deviceTrackingStatuses.containsKey(userId) || TRACKING_STATUS_NOT_TRACKED == deviceTrackingStatuses[userId]) {
@ -183,7 +183,7 @@ internal class DeviceListManager(private val mCryptoStore: IMXCryptoStore,
} }


if (isUpdated) { if (isUpdated) {
mCryptoStore.saveDeviceTrackingStatuses(deviceTrackingStatuses) cryptoStore.saveDeviceTrackingStatuses(deviceTrackingStatuses)
} }
} }
} }
@ -196,7 +196,7 @@ internal class DeviceListManager(private val mCryptoStore: IMXCryptoStore,
*/ */
fun handleDeviceListsChanges(changed: List<String>?, left: List<String>?) { fun handleDeviceListsChanges(changed: List<String>?, left: List<String>?) {
var isUpdated = false var isUpdated = false
val deviceTrackingStatuses = mCryptoStore.getDeviceTrackingStatuses().toMutableMap() val deviceTrackingStatuses = cryptoStore.getDeviceTrackingStatuses().toMutableMap()


if (changed?.isNotEmpty() == true) { if (changed?.isNotEmpty() == true) {
clearUnavailableServersList() clearUnavailableServersList()
@ -223,7 +223,7 @@ internal class DeviceListManager(private val mCryptoStore: IMXCryptoStore,
} }


if (isUpdated) { if (isUpdated) {
mCryptoStore.saveDeviceTrackingStatuses(deviceTrackingStatuses) cryptoStore.saveDeviceTrackingStatuses(deviceTrackingStatuses)
} }
} }


@ -232,7 +232,7 @@ internal class DeviceListManager(private val mCryptoStore: IMXCryptoStore,
* + update * + update
*/ */
fun invalidateAllDeviceLists() { fun invalidateAllDeviceLists() {
handleDeviceListsChanges(ArrayList(mCryptoStore.getDeviceTrackingStatuses().keys), null) handleDeviceListsChanges(ArrayList(cryptoStore.getDeviceTrackingStatuses().keys), null)
} }


/** /**
@ -242,19 +242,19 @@ internal class DeviceListManager(private val mCryptoStore: IMXCryptoStore,
*/ */
private fun onKeysDownloadFailed(userIds: List<String>?) { private fun onKeysDownloadFailed(userIds: List<String>?) {
if (null != userIds) { if (null != userIds) {
synchronized(mUserKeyDownloadsInProgress) { synchronized(userKeyDownloadsInProgress) {
val deviceTrackingStatuses = mCryptoStore.getDeviceTrackingStatuses().toMutableMap() val deviceTrackingStatuses = cryptoStore.getDeviceTrackingStatuses().toMutableMap()


for (userId in userIds) { for (userId in userIds) {
mUserKeyDownloadsInProgress.remove(userId) userKeyDownloadsInProgress.remove(userId)
deviceTrackingStatuses.put(userId, TRACKING_STATUS_PENDING_DOWNLOAD) deviceTrackingStatuses.put(userId, TRACKING_STATUS_PENDING_DOWNLOAD)
} }


mCryptoStore.saveDeviceTrackingStatuses(deviceTrackingStatuses) cryptoStore.saveDeviceTrackingStatuses(deviceTrackingStatuses)
} }
} }


mIsDownloadingKeys = false isDownloadingKeys = false
} }


/** /**
@ -281,21 +281,21 @@ internal class DeviceListManager(private val mCryptoStore: IMXCryptoStore,
} }


if (statusCode == 503) { if (statusCode == 503) {
synchronized(mNotReadyToRetryHS) { synchronized(notReadyToRetryHS) {
mNotReadyToRetryHS.add(k) notReadyToRetryHS.add(k)
} }
} }
} }
} }
} }


val deviceTrackingStatuses = mCryptoStore.getDeviceTrackingStatuses().toMutableMap() val deviceTrackingStatuses = cryptoStore.getDeviceTrackingStatuses().toMutableMap()


if (null != userIds) { if (null != userIds) {
if (mDownloadKeysQueues.size > 0) { if (downloadKeysQueues.size > 0) {
val promisesToRemove = ArrayList<DownloadKeysPromise>() val promisesToRemove = ArrayList<DownloadKeysPromise>()


for (promise in mDownloadKeysQueues) { for (promise in downloadKeysQueues) {
promise.mPendingUserIdsList.removeAll(userIds) promise.mPendingUserIdsList.removeAll(userIds)


if (promise.mPendingUserIdsList.size == 0) { if (promise.mPendingUserIdsList.size == 0) {
@ -303,7 +303,7 @@ internal class DeviceListManager(private val mCryptoStore: IMXCryptoStore,
val usersDevicesInfoMap = MXUsersDevicesMap<MXDeviceInfo>() val usersDevicesInfoMap = MXUsersDevicesMap<MXDeviceInfo>()


for (userId in promise.mUserIdsList) { for (userId in promise.mUserIdsList) {
val devices = mCryptoStore.getUserDevices(userId) val devices = cryptoStore.getUserDevices(userId)
if (null == devices) { if (null == devices) {
if (canRetryKeysDownload(userId)) { if (canRetryKeysDownload(userId)) {
deviceTrackingStatuses.put(userId, TRACKING_STATUS_PENDING_DOWNLOAD) deviceTrackingStatuses.put(userId, TRACKING_STATUS_PENDING_DOWNLOAD)
@ -336,17 +336,17 @@ internal class DeviceListManager(private val mCryptoStore: IMXCryptoStore,
promisesToRemove.add(promise) promisesToRemove.add(promise)
} }
} }
mDownloadKeysQueues.removeAll(promisesToRemove) downloadKeysQueues.removeAll(promisesToRemove)
} }


for (userId in userIds) { for (userId in userIds) {
mUserKeyDownloadsInProgress.remove(userId) userKeyDownloadsInProgress.remove(userId)
} }


mCryptoStore.saveDeviceTrackingStatuses(deviceTrackingStatuses) cryptoStore.saveDeviceTrackingStatuses(deviceTrackingStatuses)
} }


mIsDownloadingKeys = false isDownloadingKeys = false
} }


/** /**
@ -372,14 +372,14 @@ internal class DeviceListManager(private val mCryptoStore: IMXCryptoStore,
downloadUsers.addAll(userIds) downloadUsers.addAll(userIds)
} else { } else {
for (userId in userIds) { for (userId in userIds) {
val status = mCryptoStore.getDeviceTrackingStatus(userId, TRACKING_STATUS_NOT_TRACKED) val status = cryptoStore.getDeviceTrackingStatus(userId, TRACKING_STATUS_NOT_TRACKED)


// downloading keys ->the keys download won't be triggered twice but the callback requires the dedicated keys // downloading keys ->the keys download won't be triggered twice but the callback requires the dedicated keys
// not yet retrieved // not yet retrieved
if (mUserKeyDownloadsInProgress.contains(userId) || TRACKING_STATUS_UP_TO_DATE != status && TRACKING_STATUS_UNREACHABLE_SERVER != status) { if (userKeyDownloadsInProgress.contains(userId) || TRACKING_STATUS_UP_TO_DATE != status && TRACKING_STATUS_UNREACHABLE_SERVER != status) {
downloadUsers.add(userId) downloadUsers.add(userId)
} else { } else {
val devices = mCryptoStore.getUserDevices(userId) val devices = cryptoStore.getUserDevices(userId)


// should always be true // should always be true
if (null != devices) { if (null != devices) {
@ -444,7 +444,7 @@ internal class DeviceListManager(private val mCryptoStore: IMXCryptoStore,
// return // return
//} //}


mIsDownloadingKeys = true isDownloadingKeys = true


// track the race condition while sending requests // track the race condition while sending requests
// we defines a tag for each request // we defines a tag for each request
@ -452,11 +452,11 @@ internal class DeviceListManager(private val mCryptoStore: IMXCryptoStore,
val downloadToken = filteredUsers.hashCode().toString() + " " + System.currentTimeMillis() val downloadToken = filteredUsers.hashCode().toString() + " " + System.currentTimeMillis()


for (userId in filteredUsers) { for (userId in filteredUsers) {
mPendingDownloadKeysRequestToken[userId] = downloadToken pendingDownloadKeysRequestToken[userId] = downloadToken
} }


mDownloadKeysForUsersTask downloadKeysForUsersTask
.configureWith(DownloadKeysForUsersTask.Params(filteredUsers, mSyncTokenStore.getLastToken())) .configureWith(DownloadKeysForUsersTask.Params(filteredUsers, syncTokenStore.getLastToken()))
.dispatchTo(object : MatrixCallback<KeysQueryResponse> { .dispatchTo(object : MatrixCallback<KeysQueryResponse> {
override fun onSuccess(data: KeysQueryResponse) { override fun onSuccess(data: KeysQueryResponse) {
CryptoAsyncHelper.getEncryptBackgroundHandler().post { CryptoAsyncHelper.getEncryptBackgroundHandler().post {
@ -465,7 +465,7 @@ internal class DeviceListManager(private val mCryptoStore: IMXCryptoStore,


for (userId in userIdsList) { for (userId in userIdsList) {
// test if the response is the latest request one // test if the response is the latest request one
if (!TextUtils.equals(mPendingDownloadKeysRequestToken[userId], downloadToken)) { if (!TextUtils.equals(pendingDownloadKeysRequestToken[userId], downloadToken)) {
Timber.e("## doKeyDownloadForUsers() : Another update in the queue for " Timber.e("## doKeyDownloadForUsers() : Another update in the queue for "
+ userId + " not marking up-to-date") + userId + " not marking up-to-date")
filteredUsers.remove(userId) filteredUsers.remove(userId)
@ -486,12 +486,12 @@ internal class DeviceListManager(private val mCryptoStore: IMXCryptoStore,
//} //}


// Get the potential previously store device keys for this device // Get the potential previously store device keys for this device
val previouslyStoredDeviceKeys = mCryptoStore.getUserDevice(deviceId, userId) val previouslyStoredDeviceKeys = cryptoStore.getUserDevice(deviceId, userId)
val deviceInfo = mutableDevices[deviceId] val deviceInfo = mutableDevices[deviceId]


// in some race conditions (like unit tests) // in some race conditions (like unit tests)
// the self device must be seen as verified // the self device must be seen as verified
if (TextUtils.equals(deviceInfo!!.deviceId, mCredentials.deviceId) && TextUtils.equals(userId, mCredentials.userId)) { if (TextUtils.equals(deviceInfo!!.deviceId, credentials.deviceId) && TextUtils.equals(userId, credentials.userId)) {
deviceInfo.mVerified = MXDeviceInfo.DEVICE_VERIFICATION_VERIFIED deviceInfo.mVerified = MXDeviceInfo.DEVICE_VERIFICATION_VERIFIED
} }


@ -514,11 +514,11 @@ internal class DeviceListManager(private val mCryptoStore: IMXCryptoStore,


// Update the store // Update the store
// Note that devices which aren't in the response will be removed from the stores // Note that devices which aren't in the response will be removed from the stores
mCryptoStore.storeUserDevices(userId, mutableDevices) cryptoStore.storeUserDevices(userId, mutableDevices)
} }


// the response is the latest request one // the response is the latest request one
mPendingDownloadKeysRequestToken.remove(userId) pendingDownloadKeysRequestToken.remove(userId)
} }
} }


@ -532,12 +532,12 @@ internal class DeviceListManager(private val mCryptoStore: IMXCryptoStore,


// test if the response is the latest request one // test if the response is the latest request one
for (userId in userIdsList) { for (userId in userIdsList) {
if (!TextUtils.equals(mPendingDownloadKeysRequestToken[userId], downloadToken)) { if (!TextUtils.equals(pendingDownloadKeysRequestToken[userId], downloadToken)) {
Timber.e("## doKeyDownloadForUsers() : Another update in the queue for $userId not marking up-to-date") Timber.e("## doKeyDownloadForUsers() : Another update in the queue for $userId not marking up-to-date")
filteredUsers.remove(userId) filteredUsers.remove(userId)
} else { } else {
// the response is the latest request one // the response is the latest request one
mPendingDownloadKeysRequestToken.remove(userId) pendingDownloadKeysRequestToken.remove(userId)
} }
} }


@ -553,7 +553,7 @@ internal class DeviceListManager(private val mCryptoStore: IMXCryptoStore,
callback?.onFailure(failure) callback?.onFailure(failure)
} }
}) })
.executeBy(mTaskExecutor) .executeBy(taskExecutor)
} }


/** /**
@ -619,7 +619,7 @@ internal class DeviceListManager(private val mCryptoStore: IMXCryptoStore,
var errorMessage: String? = null var errorMessage: String? = null


try { try {
mOlmDevice.verifySignature(signKey, deviceKeys.signalableJSONDictionary(), signature) olmDevice.verifySignature(signKey, deviceKeys.signalableJSONDictionary(), signature)
isVerified = true isVerified = true
} catch (e: Exception) { } catch (e: Exception) {
errorMessage = e.message errorMessage = e.message
@ -658,7 +658,7 @@ internal class DeviceListManager(private val mCryptoStore: IMXCryptoStore,
fun refreshOutdatedDeviceLists() { fun refreshOutdatedDeviceLists() {
val users = ArrayList<String>() val users = ArrayList<String>()


val deviceTrackingStatuses = mCryptoStore.getDeviceTrackingStatuses().toMutableMap() val deviceTrackingStatuses = cryptoStore.getDeviceTrackingStatuses().toMutableMap()


for (userId in deviceTrackingStatuses.keys) { for (userId in deviceTrackingStatuses.keys) {
if (TRACKING_STATUS_PENDING_DOWNLOAD == deviceTrackingStatuses[userId]) { if (TRACKING_STATUS_PENDING_DOWNLOAD == deviceTrackingStatuses[userId]) {
@ -670,7 +670,7 @@ internal class DeviceListManager(private val mCryptoStore: IMXCryptoStore,
return return
} }


if (mIsDownloadingKeys) { if (isDownloadingKeys) {
// request already in progress - do nothing. (We will automatically // request already in progress - do nothing. (We will automatically
// make another request if there are more users with outdated // make another request if there are more users with outdated
// device lists when the current request completes). // device lists when the current request completes).
@ -686,7 +686,7 @@ internal class DeviceListManager(private val mCryptoStore: IMXCryptoStore,
} }
} }


mCryptoStore.saveDeviceTrackingStatuses(deviceTrackingStatuses) cryptoStore.saveDeviceTrackingStatuses(deviceTrackingStatuses)


doKeyDownloadForUsers(users, object : MatrixCallback<MXUsersDevicesMap<MXDeviceInfo>> { doKeyDownloadForUsers(users, object : MatrixCallback<MXUsersDevicesMap<MXDeviceInfo>> {
override fun onSuccess(data: MXUsersDevicesMap<MXDeviceInfo>) { override fun onSuccess(data: MXUsersDevicesMap<MXDeviceInfo>) {

View File

@ -67,9 +67,9 @@ internal class MXMegolmDecryption(private val mCredentials: Credentials,
} }


@Throws(MXDecryptionException::class) @Throws(MXDecryptionException::class)

private fun decryptEvent(event: Event, timeline: String, requestKeysOnFail: Boolean): MXEventDecryptionResult? { private fun decryptEvent(event: Event, timeline: String, requestKeysOnFail: Boolean): MXEventDecryptionResult? {
val encryptedEventContent = event.content.toModel<EncryptedEventContent>()!! val encryptedEventContent = event.content.toModel<EncryptedEventContent>()!!

if (TextUtils.isEmpty(encryptedEventContent.senderKey) || TextUtils.isEmpty(encryptedEventContent.sessionId) || TextUtils.isEmpty(encryptedEventContent.ciphertext)) { if (TextUtils.isEmpty(encryptedEventContent.senderKey) || TextUtils.isEmpty(encryptedEventContent.sessionId) || TextUtils.isEmpty(encryptedEventContent.ciphertext)) {
throw MXDecryptionException(MXCryptoError(MXCryptoError.MISSING_FIELDS_ERROR_CODE, throw MXDecryptionException(MXCryptoError(MXCryptoError.MISSING_FIELDS_ERROR_CODE,
MXCryptoError.UNABLE_TO_DECRYPT, MXCryptoError.MISSING_FIELDS_REASON)) MXCryptoError.UNABLE_TO_DECRYPT, MXCryptoError.MISSING_FIELDS_REASON))

View File

@ -499,7 +499,7 @@ internal class MXMegolmEncryption(
CryptoAsyncHelper.getUiHandler().post { CryptoAsyncHelper.getUiHandler().post {
// Check if any of these devices are not yet known to the user. // Check if any of these devices are not yet known to the user.
// if so, warn the user so they can verify or ignore. // if so, warn the user so they can verify or ignore.
if (0 != unknownDevices.map.size) { if (unknownDevices.map.isNotEmpty()) {
callback.onFailure(Failure.CryptoError(MXCryptoError(MXCryptoError.UNKNOWN_DEVICES_CODE, callback.onFailure(Failure.CryptoError(MXCryptoError(MXCryptoError.UNKNOWN_DEVICES_CODE,
MXCryptoError.UNABLE_TO_ENCRYPT, MXCryptoError.UNKNOWN_DEVICES_REASON, unknownDevices))) MXCryptoError.UNABLE_TO_ENCRYPT, MXCryptoError.UNKNOWN_DEVICES_REASON, unknownDevices)))
} else { } else {

View File

@ -54,9 +54,9 @@ internal class UploadContentWorker(context: Context, params: WorkerParameters)


override suspend fun doWork(): Result { override suspend fun doWork(): Result {
val params = WorkerParamsFactory.fromData<Params>(inputData) val params = WorkerParamsFactory.fromData<Params>(inputData)
?: return Result.failure() ?: return Result.success()


val eventId = params.event.eventId ?: return Result.failure() val eventId = params.event.eventId ?: return Result.success()
val attachment = params.attachment val attachment = params.attachment


val thumbnailData = ThumbnailExtractor.extractThumbnail(params.attachment) val thumbnailData = ThumbnailExtractor.extractThumbnail(params.attachment)
@ -96,7 +96,7 @@ internal class UploadContentWorker(context: Context, params: WorkerParameters)


private fun handleFailure(params: Params): Result { private fun handleFailure(params: Params): Result {
contentUploadProgressTracker.setFailure(params.event.eventId!!) contentUploadProgressTracker.setFailure(params.event.eventId!!)
return Result.failure() return Result.success()
} }


private fun handleSuccess(params: Params, private fun handleSuccess(params: Params,

View File

@ -44,11 +44,11 @@ internal class EncryptEventWorker(context: Context, params: WorkerParameters)
override fun doWork(): Result { override fun doWork(): Result {


val params = WorkerParamsFactory.fromData<Params>(inputData) val params = WorkerParamsFactory.fromData<Params>(inputData)
?: return Result.failure() ?: return Result.success()


val localEvent = params.event val localEvent = params.event
if (localEvent.eventId == null) { if (localEvent.eventId == null) {
return Result.failure() return Result.success()
} }


// TODO Better async handling // TODO Better async handling
@ -78,7 +78,7 @@ internal class EncryptEventWorker(context: Context, params: WorkerParameters)
val safeResult = result val safeResult = result
// TODO Update local echo // TODO Update local echo
return if (error != null) { return if (error != null) {
Result.failure() // TODO Pass error!!) Result.success() // TODO Pass error!!)
} else if (safeResult != null) { } else if (safeResult != null) {
val encryptedEvent = localEvent.copy( val encryptedEvent = localEvent.copy(
type = safeResult.mEventType, type = safeResult.mEventType,
@ -88,7 +88,7 @@ internal class EncryptEventWorker(context: Context, params: WorkerParameters)
Result.success(WorkerParamsFactory.toData(nextWorkerParams)) Result.success(WorkerParamsFactory.toData(nextWorkerParams))


} else { } else {
Result.failure() Result.success()
} }
} }
} }

View File

@ -20,11 +20,20 @@ import android.content.Context
import androidx.work.Worker import androidx.work.Worker
import androidx.work.WorkerParameters import androidx.work.WorkerParameters
import com.squareup.moshi.JsonClass import com.squareup.moshi.JsonClass
import com.zhuinden.monarchy.Monarchy
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.SendState
import im.vector.matrix.android.internal.database.helper.addSendingEvent
import im.vector.matrix.android.internal.database.model.ChunkEntity
import im.vector.matrix.android.internal.database.model.RoomEntity
import im.vector.matrix.android.internal.database.query.find
import im.vector.matrix.android.internal.database.query.findLastLiveChunkFromRoom
import im.vector.matrix.android.internal.database.query.where
import im.vector.matrix.android.internal.di.MatrixKoinComponent import im.vector.matrix.android.internal.di.MatrixKoinComponent
import im.vector.matrix.android.internal.network.executeRequest import im.vector.matrix.android.internal.network.executeRequest
import im.vector.matrix.android.internal.session.room.RoomAPI import im.vector.matrix.android.internal.session.room.RoomAPI
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 org.koin.standalone.inject import org.koin.standalone.inject


internal class SendEventWorker(context: Context, params: WorkerParameters) internal class SendEventWorker(context: Context, params: WorkerParameters)
@ -38,25 +47,35 @@ internal class SendEventWorker(context: Context, params: WorkerParameters)
) )


private val roomAPI by inject<RoomAPI>() private val roomAPI by inject<RoomAPI>()
private val monarchy by inject<Monarchy>()


override fun doWork(): Result { override fun doWork(): Result {


val params = WorkerParamsFactory.fromData<Params>(inputData) val params = WorkerParamsFactory.fromData<Params>(inputData)
?: return Result.failure() ?: return Result.success()


val localEvent = params.event val event = params.event
if (localEvent.eventId == null) { if (event.eventId == null) {
return Result.failure() return Result.success()
} }


val result = executeRequest<SendResponse> { val result = executeRequest<SendResponse> {
apiCall = roomAPI.send( apiCall = roomAPI.send(
localEvent.eventId, event.eventId,
params.roomId, params.roomId,
localEvent.type, event.type,
localEvent.content event.content
) )
} }
return result.fold({ Result.failure() }, { Result.success() }) return result.fold(
{
//TODO export that in a localEchoRepository
monarchy.tryTransactionAsync { realm ->
val roomEntity = RoomEntity.where(realm, roomId = params.roomId).findFirst()
val eventEntity = roomEntity?.sendingTimelineEvents?.find(event.eventId)
eventEntity?.sendState = SendState.UNSENT
}
Result.success() }
, { Result.success() })
} }
} }

View File

@ -49,7 +49,7 @@ internal class CryptoSyncHandler(private val cryptoManager: CryptoManager,
} }


fun onSyncCompleted(syncResponse: SyncResponse, fromToken: String?, catchingUp: Boolean) { fun onSyncCompleted(syncResponse: SyncResponse, fromToken: String?, catchingUp: Boolean) {
cryptoManager.onSyncCompleted(syncResponse, fromToken, catchingUp) cryptoManager.onSyncCompleted(syncResponse)
} }