diff --git a/matrix-sdk-android-rx/src/main/java/im/vector/matrix/rx/MatrixCallbackCompletable.kt b/matrix-sdk-android-rx/src/main/java/im/vector/matrix/rx/MatrixCallbackCompletable.kt index 5c6e7498..58c015df 100644 --- a/matrix-sdk-android-rx/src/main/java/im/vector/matrix/rx/MatrixCallbackCompletable.kt +++ b/matrix-sdk-android-rx/src/main/java/im/vector/matrix/rx/MatrixCallbackCompletable.kt @@ -28,7 +28,7 @@ internal class MatrixCallbackCompletable(private val completableEmitter: Comp } override fun onFailure(failure: Throwable) { - completableEmitter.onError(failure) + completableEmitter.tryOnError(failure) } } diff --git a/matrix-sdk-android-rx/src/main/java/im/vector/matrix/rx/MatrixCallbackSingle.kt b/matrix-sdk-android-rx/src/main/java/im/vector/matrix/rx/MatrixCallbackSingle.kt index 05756d49..8d554df2 100644 --- a/matrix-sdk-android-rx/src/main/java/im/vector/matrix/rx/MatrixCallbackSingle.kt +++ b/matrix-sdk-android-rx/src/main/java/im/vector/matrix/rx/MatrixCallbackSingle.kt @@ -27,7 +27,7 @@ internal class MatrixCallbackSingle(private val singleEmitter: SingleEmitter< } override fun onFailure(failure: Throwable) { - singleEmitter.onError(failure) + singleEmitter.tryOnError(failure) } } diff --git a/matrix-sdk-android-rx/src/main/java/im/vector/matrix/rx/RxRoom.kt b/matrix-sdk-android-rx/src/main/java/im/vector/matrix/rx/RxRoom.kt index e62b7df5..201622b3 100644 --- a/matrix-sdk-android-rx/src/main/java/im/vector/matrix/rx/RxRoom.kt +++ b/matrix-sdk-android-rx/src/main/java/im/vector/matrix/rx/RxRoom.kt @@ -41,7 +41,7 @@ class RxRoom(private val room: Room) { return room.liveTimeLineEvent(eventId).asObservable() } - fun loadRoomMembersIfNeeded(): Single = Single.create { + fun loadRoomMembersIfNeeded(): Single = Single.create { room.loadRoomMembersIfNeeded(MatrixCallbackSingle(it)).toSingle(it) } diff --git a/matrix-sdk-android/build.gradle b/matrix-sdk-android/build.gradle index b62b3fea..f9f89388 100644 --- a/matrix-sdk-android/build.gradle +++ b/matrix-sdk-android/build.gradle @@ -126,9 +126,6 @@ dependencies { // FP implementation "io.arrow-kt:arrow-core:$arrow_version" implementation "io.arrow-kt:arrow-instances-core:$arrow_version" - implementation "io.arrow-kt:arrow-effects:$arrow_version" - implementation "io.arrow-kt:arrow-effects-instances:$arrow_version" - implementation "io.arrow-kt:arrow-integration-retrofit-adapter:$arrow_version" // olm lib is now hosted by jitpack: https://jitpack.io/#org.matrix.gitlab.matrix-org/olm implementation 'org.matrix.gitlab.matrix-org:olm:3.1.2' diff --git a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/session/room/timeline/FakeGetContextOfEventTask.kt b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/session/room/timeline/FakeGetContextOfEventTask.kt index f77c9b1a..48f22392 100644 --- a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/session/room/timeline/FakeGetContextOfEventTask.kt +++ b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/session/room/timeline/FakeGetContextOfEventTask.kt @@ -16,7 +16,6 @@ package im.vector.matrix.android.session.room.timeline -import arrow.core.Try import im.vector.matrix.android.internal.session.room.timeline.GetContextOfEventTask import im.vector.matrix.android.internal.session.room.timeline.PaginationDirection import im.vector.matrix.android.internal.session.room.timeline.TokenChunkEventPersistor @@ -24,7 +23,7 @@ import kotlin.random.Random internal class FakeGetContextOfEventTask constructor(private val tokenChunkEventPersistor: TokenChunkEventPersistor) : GetContextOfEventTask { - override suspend fun execute(params: GetContextOfEventTask.Params): Try { + override suspend fun execute(params: GetContextOfEventTask.Params): TokenChunkEventPersistor.Result { val fakeEvents = RoomDataHelper.createFakeListOfEvents(30) val tokenChunkEvent = FakeTokenChunkEvent( Random.nextLong(System.currentTimeMillis()).toString(), diff --git a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/session/room/timeline/FakePaginationTask.kt b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/session/room/timeline/FakePaginationTask.kt index bf163401..2f7f63d7 100644 --- a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/session/room/timeline/FakePaginationTask.kt +++ b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/session/room/timeline/FakePaginationTask.kt @@ -16,7 +16,6 @@ package im.vector.matrix.android.session.room.timeline -import arrow.core.Try import im.vector.matrix.android.internal.session.room.timeline.PaginationTask import im.vector.matrix.android.internal.session.room.timeline.TokenChunkEventPersistor import javax.inject.Inject @@ -24,7 +23,7 @@ import kotlin.random.Random internal class FakePaginationTask @Inject constructor(private val tokenChunkEventPersistor: TokenChunkEventPersistor) : PaginationTask { - override suspend fun execute(params: PaginationTask.Params): Try { + override suspend fun execute(params: PaginationTask.Params): TokenChunkEventPersistor.Result { val fakeEvents = RoomDataHelper.createFakeListOfEvents(30) val tokenChunkEvent = FakeTokenChunkEvent(params.from, Random.nextLong(System.currentTimeMillis()).toString(), fakeEvents) return tokenChunkEventPersistor.insertInDb(tokenChunkEvent, params.roomId, params.direction) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/MatrixCallback.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/MatrixCallback.kt index 00d22b1f..e907bb25 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/MatrixCallback.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/MatrixCallback.kt @@ -38,4 +38,8 @@ interface MatrixCallback { //no-op } + fun onCanceled() { + //no-op + } + } \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/members/MembershipService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/members/MembershipService.kt index 8b56406c..9dba26cb 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/members/MembershipService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/members/MembershipService.kt @@ -30,7 +30,7 @@ interface MembershipService { * This methods load all room members if it was done yet. * @return a [Cancelable] */ - fun loadRoomMembersIfNeeded(matrixCallback: MatrixCallback): Cancelable + fun loadRoomMembersIfNeeded(matrixCallback: MatrixCallback): Cancelable /** * Return the roomMember with userId or null. diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/util/CancelableBag.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/util/CancelableBag.kt index f5689b4d..145ddb66 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/util/CancelableBag.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/util/CancelableBag.kt @@ -19,5 +19,6 @@ package im.vector.matrix.android.api.util class CancelableBag : Cancelable, MutableList by ArrayList() { override fun cancel() { forEach { it.cancel() } + clear() } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/DefaultAuthenticator.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/DefaultAuthenticator.kt index 81e64f5b..e96aa47b 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/DefaultAuthenticator.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/DefaultAuthenticator.kt @@ -68,10 +68,12 @@ internal class DefaultAuthenticator @Inject constructor(@Unauthenticated callback: MatrixCallback): Cancelable { val job = GlobalScope.launch(coroutineDispatchers.main) { - val sessionOrFailure = authenticate(homeServerConnectionConfig, login, password) + val sessionOrFailure = runCatching { + authenticate(homeServerConnectionConfig, login, password) + } sessionOrFailure.foldToCallback(callback) } - return CancelableCoroutine(job) + return CancelableCoroutine(job, callback) } @@ -85,16 +87,12 @@ internal class DefaultAuthenticator @Inject constructor(@Unauthenticated } else { PasswordLoginParams.userIdentifier(login, password, "Mobile") } - executeRequest { + val credentials = executeRequest { apiCall = authAPI.login(loginParams) - }.map { - val sessionParams = SessionParams(it, homeServerConnectionConfig) - sessionParamsStore.save(sessionParams) - sessionParams - }.map { - sessionManager.getOrCreateSession(it) } - + val sessionParams = SessionParams(credentials, homeServerConnectionConfig) + sessionParamsStore.save(sessionParams) + sessionManager.getOrCreateSession(sessionParams) } private fun buildAuthAPI(homeServerConnectionConfig: HomeServerConnectionConfig): AuthAPI { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/CryptoManager.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/CryptoManager.kt index 3fadb09b..136f9708 100755 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/CryptoManager.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/CryptoManager.kt @@ -254,35 +254,36 @@ internal class CryptoManager @Inject constructor( private suspend fun internalStart(isInitialSync: Boolean) { // Open the store cryptoStore.open() - uploadDeviceKeys() - .flatMap { oneTimeKeysUploader.maybeUploadOneTimeKeys() } - .fold( - { - Timber.e("Start failed: $it") - delay(1000) - isStarting.set(false) - internalStart(isInitialSync) - }, - { - outgoingRoomKeyRequestManager.start() - keysBackup.checkAndStartKeysBackup() - if (isInitialSync) { - // refresh the devices list for each known room members - deviceListManager.invalidateAllDeviceLists() - deviceListManager.refreshOutdatedDeviceLists() - } else { - incomingRoomKeyRequestManager.processReceivedRoomKeyRequests() - } - isStarting.set(false) - isStarted.set(true) - } - ) + runCatching { + uploadDeviceKeys() + oneTimeKeysUploader.maybeUploadOneTimeKeys() + outgoingRoomKeyRequestManager.start() + keysBackup.checkAndStartKeysBackup() + if (isInitialSync) { + // refresh the devices list for each known room members + deviceListManager.invalidateAllDeviceLists() + deviceListManager.refreshOutdatedDeviceLists() + } else { + incomingRoomKeyRequestManager.processReceivedRoomKeyRequests() + } + }.fold( + { + isStarting.set(false) + isStarted.set(true) + }, + { + Timber.e("Start failed: $it") + delay(1000) + isStarting.set(false) + internalStart(isInitialSync) + } + ) } /** * Close the crypto */ - fun close() { + fun close() = runBlocking(coroutineDispatchers.crypto){ olmDevice.release() cryptoStore.close() outgoingRoomKeyRequestManager.stop() @@ -556,13 +557,16 @@ internal class CryptoManager @Inject constructor( if (safeAlgorithm != null) { val t0 = System.currentTimeMillis() Timber.v("## encryptEventContent() starts") - safeAlgorithm.encryptEventContent(eventContent, eventType, userIds) + runCatching { + safeAlgorithm.encryptEventContent(eventContent, eventType, userIds) + } .fold( - { callback.onFailure(it) }, { Timber.v("## encryptEventContent() : succeeds after ${System.currentTimeMillis() - t0} ms") callback.onSuccess(MXEncryptEventContentResult(it, EventType.ENCRYPTED)) - } + }, + { callback.onFailure(it) } + ) } else { val algorithm = getEncryptionAlgorithm(roomId) @@ -584,10 +588,7 @@ internal class CryptoManager @Inject constructor( @Throws(MXCryptoError::class) override fun decryptEvent(event: Event, timeline: String): MXEventDecryptionResult { return runBlocking { - internalDecryptEvent(event, timeline).fold( - { throw it }, - { it } - ) + internalDecryptEvent(event, timeline) } } @@ -600,8 +601,10 @@ internal class CryptoManager @Inject constructor( */ override fun decryptEventAsync(event: Event, timeline: String, callback: MatrixCallback) { GlobalScope.launch { - val result = withContext(coroutineDispatchers.crypto) { - internalDecryptEvent(event, timeline) + val result = runCatching { + withContext(coroutineDispatchers.crypto) { + internalDecryptEvent(event, timeline) + } } result.foldToCallback(callback) } @@ -612,22 +615,22 @@ internal class CryptoManager @Inject constructor( * * @param event the raw event. * @param timeline the id of the timeline where the event is decrypted. It is used to prevent replay attack. - * @return the MXEventDecryptionResult data, or null in case of error wrapped into [Try] + * @return the MXEventDecryptionResult data, or null in case of error */ - private suspend fun internalDecryptEvent(event: Event, timeline: String): Try { + private suspend fun internalDecryptEvent(event: Event, timeline: String): MXEventDecryptionResult { val eventContent = event.content - return if (eventContent == null) { + if (eventContent == null) { Timber.e("## decryptEvent : empty event content") - Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.BAD_ENCRYPTED_MESSAGE, MXCryptoError.BAD_ENCRYPTED_MESSAGE_REASON)) + throw MXCryptoError.Base(MXCryptoError.ErrorType.BAD_ENCRYPTED_MESSAGE, MXCryptoError.BAD_ENCRYPTED_MESSAGE_REASON) } else { val algorithm = eventContent["algorithm"]?.toString() val alg = roomDecryptorProvider.getOrCreateRoomDecryptor(event.roomId, algorithm) if (alg == null) { val reason = String.format(MXCryptoError.UNABLE_TO_DECRYPT_REASON, event.eventId, algorithm) Timber.e("## decryptEvent() : $reason") - Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.UNABLE_TO_DECRYPT, reason)) + throw MXCryptoError.Base(MXCryptoError.ErrorType.UNABLE_TO_DECRYPT, reason) } else { - alg.decryptEvent(event, timeline) + return alg.decryptEvent(event, timeline) } } } @@ -689,12 +692,13 @@ internal class CryptoManager @Inject constructor( private fun onRoomEncryptionEvent(roomId: String, event: Event) { GlobalScope.launch(coroutineDispatchers.crypto) { val params = LoadRoomMembersTask.Params(roomId) - loadRoomMembersTask - .execute(params) - .map { _ -> - val userIds = getRoomUserIds(roomId) - setEncryptionInRoom(roomId, event.content?.get("algorithm")?.toString(), true, userIds) - } + try { + loadRoomMembersTask.execute(params) + val userIds = getRoomUserIds(roomId) + setEncryptionInRoom(roomId, event.content?.get("algorithm")?.toString(), true, userIds) + } catch (throwable: Throwable) { + Timber.e(throwable) + } } } @@ -761,7 +765,7 @@ internal class CryptoManager @Inject constructor( /** * Upload my user's device keys. */ - private suspend fun uploadDeviceKeys(): Try { + private suspend fun uploadDeviceKeys(): KeysUploadResponse { // Prepare the device keys data to send // Sign it val canonicalJson = JsonCanonicalizer.getCanonicalJson(Map::class.java, getMyDevice().signalableJSONDictionary()) @@ -868,10 +872,8 @@ internal class CryptoManager @Inject constructor( fun checkUnknownDevices(userIds: List, callback: MatrixCallback) { // force the refresh to ensure that the devices list is up-to-date GlobalScope.launch(coroutineDispatchers.crypto) { - deviceListManager - .downloadKeys(userIds, true) + runCatching { deviceListManager.downloadKeys(userIds, true) } .fold( - { callback.onFailure(it) }, { val unknownDevices = getUnknownDevices(it) if (unknownDevices.map.isEmpty()) { @@ -880,7 +882,8 @@ internal class CryptoManager @Inject constructor( // trigger an an unknown devices exception callback.onFailure(Failure.CryptoError(MXCryptoError.UnknownDevice(unknownDevices))) } - } + }, + { callback.onFailure(it) } ) } } @@ -1035,9 +1038,9 @@ internal class CryptoManager @Inject constructor( override fun downloadKeys(userIds: List, forceDownload: Boolean, callback: MatrixCallback>) { GlobalScope.launch(coroutineDispatchers.crypto) { - deviceListManager - .downloadKeys(userIds, forceDownload) - .foldToCallback(callback) + runCatching { + deviceListManager.downloadKeys(userIds, forceDownload) + }.foldToCallback(callback) } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/DeviceListManager.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/DeviceListManager.kt index e8f65b9d..e7cfdfe1 100755 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/DeviceListManager.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/DeviceListManager.kt @@ -18,14 +18,12 @@ package im.vector.matrix.android.internal.crypto import android.text.TextUtils -import arrow.core.Try import im.vector.matrix.android.api.MatrixPatterns import im.vector.matrix.android.api.auth.data.Credentials import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore import im.vector.matrix.android.internal.crypto.tasks.DownloadKeysForUsersTask -import im.vector.matrix.android.internal.extensions.onError import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.session.sync.SyncTokenStore import timber.log.Timber @@ -237,7 +235,7 @@ internal class DeviceListManager @Inject constructor(private val cryptoStore: IM * @param forceDownload Always download the keys even if cached. * @param callback the asynchronous callback */ - suspend fun downloadKeys(userIds: List?, forceDownload: Boolean): Try> { + suspend fun downloadKeys(userIds: List?, forceDownload: Boolean): MXUsersDevicesMap { Timber.v("## downloadKeys() : forceDownload $forceDownload : $userIds") // Map from userId -> deviceId -> MXDeviceInfo val stored = MXUsersDevicesMap() @@ -268,16 +266,15 @@ internal class DeviceListManager @Inject constructor(private val cryptoStore: IM } return if (downloadUsers.isEmpty()) { Timber.v("## downloadKeys() : no new user device") - Try.just(stored) + stored } else { Timber.v("## downloadKeys() : starts") val t0 = System.currentTimeMillis() - doKeyDownloadForUsers(downloadUsers) - .map { - Timber.v("## downloadKeys() : doKeyDownloadForUsers succeeds after " + (System.currentTimeMillis() - t0) + " ms") - it.addEntriesFromMap(stored) - it - } + val result = doKeyDownloadForUsers(downloadUsers) + Timber.v("## downloadKeys() : doKeyDownloadForUsers succeeds after " + (System.currentTimeMillis() - t0) + " ms") + result.also { + it.addEntriesFromMap(stored) + } } } @@ -286,60 +283,60 @@ internal class DeviceListManager @Inject constructor(private val cryptoStore: IM * * @param downloadUsers the user ids list */ - private suspend fun doKeyDownloadForUsers(downloadUsers: MutableList): Try> { + private suspend fun doKeyDownloadForUsers(downloadUsers: MutableList): MXUsersDevicesMap { Timber.v("## doKeyDownloadForUsers() : doKeyDownloadForUsers $downloadUsers") // get the user ids which did not already trigger a keys download val filteredUsers = downloadUsers.filter { MatrixPatterns.isUserId(it) } if (filteredUsers.isEmpty()) { // trigger nothing - return Try.just(MXUsersDevicesMap()) + return MXUsersDevicesMap() } val params = DownloadKeysForUsersTask.Params(filteredUsers, syncTokenStore.getLastToken()) - return downloadKeysForUsersTask.execute(params) - .map { response -> - Timber.v("## doKeyDownloadForUsers() : Got keys for " + filteredUsers.size + " users") - for (userId in filteredUsers) { - val devices = response.deviceKeys?.get(userId) - Timber.v("## doKeyDownloadForUsers() : Got keys for $userId : $devices") - if (devices != null) { - val mutableDevices = HashMap(devices) - val deviceIds = ArrayList(mutableDevices.keys) - for (deviceId in deviceIds) { - // Get the potential previously store device keys for this device - val previouslyStoredDeviceKeys = cryptoStore.getUserDevice(deviceId, userId) - val deviceInfo = mutableDevices[deviceId] + val response = try { + downloadKeysForUsersTask.execute(params) + } catch (throwable: Throwable) { + Timber.e(throwable, "##doKeyDownloadForUsers(): error") + onKeysDownloadFailed(filteredUsers) + throw throwable + } + Timber.v("## doKeyDownloadForUsers() : Got keys for " + filteredUsers.size + " users") + for (userId in filteredUsers) { + val devices = response.deviceKeys?.get(userId) + Timber.v("## doKeyDownloadForUsers() : Got keys for $userId : $devices") + if (devices != null) { + val mutableDevices = HashMap(devices) + val deviceIds = ArrayList(mutableDevices.keys) + for (deviceId in deviceIds) { + // Get the potential previously store device keys for this device + val previouslyStoredDeviceKeys = cryptoStore.getUserDevice(deviceId, userId) + val deviceInfo = mutableDevices[deviceId] - // in some race conditions (like unit tests) - // the self device must be seen as verified - if (TextUtils.equals(deviceInfo!!.deviceId, credentials.deviceId) && TextUtils.equals(userId, credentials.userId)) { - deviceInfo.verified = MXDeviceInfo.DEVICE_VERIFICATION_VERIFIED - } - // Validate received keys - if (!validateDeviceKeys(deviceInfo, userId, deviceId, previouslyStoredDeviceKeys)) { - // New device keys are not valid. Do not store them - mutableDevices.remove(deviceId) - if (null != previouslyStoredDeviceKeys) { - // But keep old validated ones if any - mutableDevices[deviceId] = previouslyStoredDeviceKeys - } - } else if (null != previouslyStoredDeviceKeys) { - // The verified status is not sync'ed with hs. - // This is a client side information, valid only for this client. - // So, transfer its previous value - mutableDevices[deviceId]!!.verified = previouslyStoredDeviceKeys.verified - } - } - // Update the store - // Note that devices which aren't in the response will be removed from the stores - cryptoStore.storeUserDevices(userId, mutableDevices) - } + // in some race conditions (like unit tests) + // the self device must be seen as verified + if (TextUtils.equals(deviceInfo!!.deviceId, credentials.deviceId) && TextUtils.equals(userId, credentials.userId)) { + deviceInfo.verified = MXDeviceInfo.DEVICE_VERIFICATION_VERIFIED + } + // Validate received keys + if (!validateDeviceKeys(deviceInfo, userId, deviceId, previouslyStoredDeviceKeys)) { + // New device keys are not valid. Do not store them + mutableDevices.remove(deviceId) + if (null != previouslyStoredDeviceKeys) { + // But keep old validated ones if any + mutableDevices[deviceId] = previouslyStoredDeviceKeys + } + } else if (null != previouslyStoredDeviceKeys) { + // The verified status is not sync'ed with hs. + // This is a client side information, valid only for this client. + // So, transfer its previous value + mutableDevices[deviceId]!!.verified = previouslyStoredDeviceKeys.verified } - onKeysDownloadSucceed(filteredUsers, response.failures) - } - .onError { - Timber.e(it, "##doKeyDownloadForUsers(): error") - onKeysDownloadFailed(filteredUsers) } + // Update the store + // Note that devices which aren't in the response will be removed from the stores + cryptoStore.storeUserDevices(userId, mutableDevices) + } + } + return onKeysDownloadSucceed(filteredUsers, response.failures) } /** @@ -465,15 +462,16 @@ internal class DeviceListManager @Inject constructor(private val cryptoStore: IM } cryptoStore.saveDeviceTrackingStatuses(deviceTrackingStatuses) - doKeyDownloadForUsers(users) - .fold( - { - Timber.e(it, "## refreshOutdatedDeviceLists() : ERROR updating device keys for users $users") - }, - { - Timber.v("## refreshOutdatedDeviceLists() : done") - } - ) + runCatching { + doKeyDownloadForUsers(users) + }.fold( + { + Timber.v("## refreshOutdatedDeviceLists() : done") + }, + { + Timber.e(it, "## refreshOutdatedDeviceLists() : ERROR updating device keys for users $users") + } + ) } companion object { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/MXOlmDevice.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/MXOlmDevice.kt index 8f0bfcd4..d9387ad3 100755 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/MXOlmDevice.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/MXOlmDevice.kt @@ -18,7 +18,6 @@ package im.vector.matrix.android.internal.crypto import android.text.TextUtils -import arrow.core.Try import im.vector.matrix.android.api.session.crypto.MXCryptoError import im.vector.matrix.android.api.util.JSON_DICT_PARAMETERIZED_TYPE import im.vector.matrix.android.api.util.JsonDict @@ -506,25 +505,25 @@ internal class MXOlmDevice @Inject constructor( keysClaimed: Map, exportFormat: Boolean): Boolean { val session = OlmInboundGroupSessionWrapper(sessionKey, exportFormat) + runCatching { getInboundGroupSession(sessionId, senderKey, roomId) } + .fold( + { + // If we already have this session, consider updating it + Timber.e("## addInboundGroupSession() : Update for megolm session $senderKey/$sessionId") - getInboundGroupSession(sessionId, senderKey, roomId).fold( - { - // Nothing to do in case of error - }, - { - // If we already have this session, consider updating it - Timber.e("## addInboundGroupSession() : Update for megolm session $senderKey/$sessionId") + val existingFirstKnown = it.firstKnownIndex!! + val newKnownFirstIndex = session.firstKnownIndex - val existingFirstKnown = it.firstKnownIndex!! - val newKnownFirstIndex = session.firstKnownIndex - - //If our existing session is better we keep it - if (newKnownFirstIndex != null && existingFirstKnown <= newKnownFirstIndex) { - session.olmInboundGroupSession?.releaseSession() - return false - } - } - ) + //If our existing session is better we keep it + if (newKnownFirstIndex != null && existingFirstKnown <= newKnownFirstIndex) { + session.olmInboundGroupSession?.releaseSession() + return false + } + }, + { + // Nothing to do in case of error + } + ) // sanity check if (null == session.olmInboundGroupSession) { @@ -595,12 +594,8 @@ internal class MXOlmDevice @Inject constructor( continue } - getInboundGroupSession(sessionId, senderKey, roomId) + runCatching { getInboundGroupSession(sessionId, senderKey, roomId) } .fold( - { - // Session does not already exist, add it - sessions.add(session) - }, { // If we already have this session, consider updating it Timber.e("## importInboundGroupSession() : Update for megolm session $senderKey/$sessionId") @@ -613,7 +608,12 @@ internal class MXOlmDevice @Inject constructor( sessions.add(session) } Unit + }, + { + // Session does not already exist, add it + sessions.add(session) } + ) } @@ -648,61 +648,57 @@ internal class MXOlmDevice @Inject constructor( roomId: String, timeline: String?, sessionId: String, - senderKey: String): Try { - return getInboundGroupSession(sessionId, senderKey, roomId) - .flatMap { session -> - // Check that the room id matches the original one for the session. This stops - // the HS pretending a message was targeting a different room. - if (roomId == session.roomId) { - var decryptResult: OlmInboundGroupSession.DecryptMessageResult? = null - try { - decryptResult = session.olmInboundGroupSession!!.decryptMessage(body) - } catch (e: OlmException) { - Timber.e(e, "## decryptGroupMessage () : decryptMessage failed") - return@flatMap Try.Failure(MXCryptoError.OlmError(e)) - } + senderKey: String): OlmDecryptionResult { + val session = getInboundGroupSession(sessionId, senderKey, roomId) + // Check that the room id matches the original one for the session. This stops + // the HS pretending a message was targeting a different room. + if (roomId == session.roomId) { + var decryptResult: OlmInboundGroupSession.DecryptMessageResult? = null + try { + decryptResult = session.olmInboundGroupSession!!.decryptMessage(body) + } catch (e: OlmException) { + Timber.e(e, "## decryptGroupMessage () : decryptMessage failed") + throw MXCryptoError.OlmError(e) + } - if (null != timeline) { - if (!inboundGroupSessionMessageIndexes.containsKey(timeline)) { - inboundGroupSessionMessageIndexes[timeline] = HashMap() - } - - val messageIndexKey = senderKey + "|" + sessionId + "|" + decryptResult.mIndex - - if (inboundGroupSessionMessageIndexes[timeline]?.get(messageIndexKey) != null) { - val reason = String.format(MXCryptoError.DUPLICATE_MESSAGE_INDEX_REASON, decryptResult.mIndex) - Timber.e("## decryptGroupMessage() : $reason") - return@flatMap Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.DUPLICATED_MESSAGE_INDEX, reason)) - } - - inboundGroupSessionMessageIndexes[timeline]!!.put(messageIndexKey, true) - } - - store.storeInboundGroupSessions(listOf(session)) - val payload = try { - val adapter = MoshiProvider.providesMoshi().adapter(JSON_DICT_PARAMETERIZED_TYPE) - val payloadString = convertFromUTF8(decryptResult.mDecryptedMessage) - adapter.fromJson(payloadString) - } catch (e: Exception) { - Timber.e("## decryptGroupMessage() : fails to parse the payload") - return@flatMap Try.Failure( - MXCryptoError.Base(MXCryptoError.ErrorType.BAD_DECRYPTED_FORMAT, MXCryptoError.BAD_DECRYPTED_FORMAT_TEXT_REASON)) - } - - return@flatMap Try.just( - OlmDecryptionResult( - payload, - session.keysClaimed, - senderKey, - session.forwardingCurve25519KeyChain - ) - ) - } else { - val reason = String.format(MXCryptoError.INBOUND_SESSION_MISMATCH_ROOM_ID_REASON, roomId, session.roomId) - Timber.e("## decryptGroupMessage() : $reason") - return@flatMap Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.INBOUND_SESSION_MISMATCH_ROOM_ID, reason)) - } + if (null != timeline) { + if (!inboundGroupSessionMessageIndexes.containsKey(timeline)) { + inboundGroupSessionMessageIndexes[timeline] = HashMap() } + + val messageIndexKey = senderKey + "|" + sessionId + "|" + decryptResult.mIndex + + if (inboundGroupSessionMessageIndexes[timeline]?.get(messageIndexKey) != null) { + val reason = String.format(MXCryptoError.DUPLICATE_MESSAGE_INDEX_REASON, decryptResult.mIndex) + Timber.e("## decryptGroupMessage() : $reason") + throw MXCryptoError.Base(MXCryptoError.ErrorType.DUPLICATED_MESSAGE_INDEX, reason) + } + + inboundGroupSessionMessageIndexes[timeline]!!.put(messageIndexKey, true) + } + + store.storeInboundGroupSessions(listOf(session)) + val payload = try { + val adapter = MoshiProvider.providesMoshi().adapter(JSON_DICT_PARAMETERIZED_TYPE) + val payloadString = convertFromUTF8(decryptResult.mDecryptedMessage) + adapter.fromJson(payloadString) + } catch (e: Exception) { + Timber.e("## decryptGroupMessage() : fails to parse the payload") + throw + MXCryptoError.Base(MXCryptoError.ErrorType.BAD_DECRYPTED_FORMAT, MXCryptoError.BAD_DECRYPTED_FORMAT_TEXT_REASON) + } + + return OlmDecryptionResult( + payload, + session.keysClaimed, + senderKey, + session.forwardingCurve25519KeyChain + ) + } else { + val reason = String.format(MXCryptoError.INBOUND_SESSION_MISMATCH_ROOM_ID_REASON, roomId, session.roomId) + Timber.e("## decryptGroupMessage() : $reason") + throw MXCryptoError.Base(MXCryptoError.ErrorType.INBOUND_SESSION_MISMATCH_ROOM_ID, reason) + } } /** @@ -766,26 +762,26 @@ internal class MXOlmDevice @Inject constructor( * @param senderKey the base64-encoded curve25519 key of the sender. * @return the inbound group session. */ - fun getInboundGroupSession(sessionId: String?, senderKey: String?, roomId: String?): Try { + fun getInboundGroupSession(sessionId: String?, senderKey: String?, roomId: String?): OlmInboundGroupSessionWrapper { if (sessionId.isNullOrBlank() || senderKey.isNullOrBlank()) { - return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_SENDER_KEY, MXCryptoError.ERROR_MISSING_PROPERTY_REASON)) + throw MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_SENDER_KEY, MXCryptoError.ERROR_MISSING_PROPERTY_REASON) } val session = store.getInboundGroupSession(sessionId, senderKey) - return if (null != session) { + if (session != null) { // Check that the room id matches the original one for the session. This stops // the HS pretending a message was targeting a different room. if (!TextUtils.equals(roomId, session.roomId)) { val errorDescription = String.format(MXCryptoError.INBOUND_SESSION_MISMATCH_ROOM_ID_REASON, roomId, session.roomId) Timber.e("## getInboundGroupSession() : $errorDescription") - Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.INBOUND_SESSION_MISMATCH_ROOM_ID, errorDescription)) + throw MXCryptoError.Base(MXCryptoError.ErrorType.INBOUND_SESSION_MISMATCH_ROOM_ID, errorDescription) } else { - Try.just(session) + return session } } else { Timber.e("## getInboundGroupSession() : Cannot retrieve inbound group session $sessionId") - Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.UNKNOWN_INBOUND_SESSION_ID, MXCryptoError.UNKNOWN_INBOUND_SESSION_ID_REASON)) + throw MXCryptoError.Base(MXCryptoError.ErrorType.UNKNOWN_INBOUND_SESSION_ID, MXCryptoError.UNKNOWN_INBOUND_SESSION_ID_REASON) } } @@ -798,6 +794,6 @@ internal class MXOlmDevice @Inject constructor( * @return true if the unbound session keys are known. */ fun hasInboundSessionKeys(roomId: String, senderKey: String, sessionId: String): Boolean { - return getInboundGroupSession(sessionId, senderKey, roomId).isSuccess() + return runCatching { getInboundGroupSession(sessionId, senderKey, roomId) }.isSuccess } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/OneTimeKeysUploader.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/OneTimeKeysUploader.kt index a81cbfcf..f0d7662f 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/OneTimeKeysUploader.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/OneTimeKeysUploader.kt @@ -16,8 +16,6 @@ package im.vector.matrix.android.internal.crypto -import arrow.core.Try -import arrow.instances.`try`.applicativeError.handleError import im.vector.matrix.android.api.auth.data.Credentials import im.vector.matrix.android.internal.crypto.model.MXKey import im.vector.matrix.android.internal.crypto.model.rest.KeysUploadResponse @@ -59,13 +57,13 @@ internal class OneTimeKeysUploader @Inject constructor( /** * Check if the OTK must be uploaded. */ - suspend fun maybeUploadOneTimeKeys(): Try { + suspend fun maybeUploadOneTimeKeys() { if (oneTimeKeyCheckInProgress) { - return Try.just(Unit) + return } if (System.currentTimeMillis() - lastOneTimeKeyCheck < ONE_TIME_KEY_UPLOAD_PERIOD) { // we've done a key upload recently. - return Try.just(Unit) + return } lastOneTimeKeyCheck = System.currentTimeMillis() @@ -81,41 +79,31 @@ internal class OneTimeKeysUploader @Inject constructor( // discard the oldest private keys first. This will eventually clean // out stale private keys that won't receive a message. val keyLimit = Math.floor(maxOneTimeKeys / 2.0).toInt() - val result = if (oneTimeKeyCount != null) { + if (oneTimeKeyCount != null) { uploadOTK(oneTimeKeyCount!!, keyLimit) } else { // ask the server how many keys we have val uploadKeysParams = UploadKeysTask.Params(null, null, credentials.deviceId!!) - uploadKeysTask.execute(uploadKeysParams) - .flatMap { - // We need to keep a pool of one time public keys on the server so that - // other devices can start conversations with us. But we can only store - // a finite number of private keys in the olm Account object. - // To complicate things further then can be a delay between a device - // claiming a public one time key from the server and it sending us a - // message. We need to keep the corresponding private key locally until - // we receive the message. - // But that message might never arrive leaving us stuck with duff - // private keys clogging up our local storage. - // So we need some kind of engineering compromise to balance all of - // these factors. - // TODO Why we do not set oneTimeKeyCount here? - // TODO This is not needed anymore, see https://github.com/matrix-org/matrix-js-sdk/pull/493 (TODO on iOS also) - val keyCount = it.oneTimeKeyCountsForAlgorithm(MXKey.KEY_SIGNED_CURVE_25519_TYPE) - uploadOTK(keyCount, keyLimit) - } + val response = uploadKeysTask.execute(uploadKeysParams) + // We need to keep a pool of one time public keys on the server so that + // other devices can start conversations with us. But we can only store + // a finite number of private keys in the olm Account object. + // To complicate things further then can be a delay between a device + // claiming a public one time key from the server and it sending us a + // message. We need to keep the corresponding private key locally until + // we receive the message. + // But that message might never arrive leaving us stuck with duff + // private keys clogging up our local storage. + // So we need some kind of engineering compromise to balance all of + // these factors. + // TODO Why we do not set oneTimeKeyCount here? + // TODO This is not needed anymore, see https://github.com/matrix-org/matrix-js-sdk/pull/493 (TODO on iOS also) + val keyCount = response.oneTimeKeyCountsForAlgorithm(MXKey.KEY_SIGNED_CURVE_25519_TYPE) + uploadOTK(keyCount, keyLimit) } - return result - .map { - Timber.v("## uploadKeys() : success") - oneTimeKeyCount = null - oneTimeKeyCheckInProgress = false - } - .handleError { - Timber.e(it, "## uploadKeys() : failed") - oneTimeKeyCount = null - oneTimeKeyCheckInProgress = false - } + Timber.v("## uploadKeys() : success") + oneTimeKeyCount = null + oneTimeKeyCheckInProgress = false } /** @@ -124,29 +112,26 @@ internal class OneTimeKeysUploader @Inject constructor( * @param keyCount the key count * @param keyLimit the limit */ - private suspend fun uploadOTK(keyCount: Int, keyLimit: Int): Try { + private suspend fun uploadOTK(keyCount: Int, keyLimit: Int) { if (keyLimit <= keyCount) { // If we don't need to generate any more keys then we are done. - return Try.just(Unit) + return } - val keysThisLoop = Math.min(keyLimit - keyCount, ONE_TIME_KEY_GENERATION_MAX_NUMBER) olmDevice.generateOneTimeKeys(keysThisLoop) - return uploadOneTimeKeys() - .flatMap { - if (it.hasOneTimeKeyCountsForAlgorithm(MXKey.KEY_SIGNED_CURVE_25519_TYPE)) { - uploadOTK(it.oneTimeKeyCountsForAlgorithm(MXKey.KEY_SIGNED_CURVE_25519_TYPE), keyLimit) - } else { - Timber.e("## uploadLoop() : response for uploading keys does not contain one_time_key_counts.signed_curve25519") - Try.raise(Exception("response for uploading keys does not contain one_time_key_counts.signed_curve25519")) - } - } + val response = uploadOneTimeKeys() + if (response.hasOneTimeKeyCountsForAlgorithm(MXKey.KEY_SIGNED_CURVE_25519_TYPE)) { + uploadOTK(response.oneTimeKeyCountsForAlgorithm(MXKey.KEY_SIGNED_CURVE_25519_TYPE), keyLimit) + } else { + Timber.e("## uploadLoop() : response for uploading keys does not contain one_time_key_counts.signed_curve25519") + throw Exception("response for uploading keys does not contain one_time_key_counts.signed_curve25519") + } } /** * Upload my user's one time keys. */ - private suspend fun uploadOneTimeKeys(): Try { + private suspend fun uploadOneTimeKeys(): KeysUploadResponse { val oneTimeKeys = olmDevice.getOneTimeKeys() val oneTimeJson = HashMap() @@ -169,13 +154,10 @@ internal class OneTimeKeysUploader @Inject constructor( // For now, we set the device id explicitly, as we may not be using the // same one as used in login. val uploadParams = UploadKeysTask.Params(null, oneTimeJson, credentials.deviceId!!) - return uploadKeysTask - .execute(uploadParams) - .map { - lastPublishedOneTimeKeys = oneTimeKeys - olmDevice.markKeysAsPublished() - it - } + val response = uploadKeysTask.execute(uploadParams) + lastPublishedOneTimeKeys = oneTimeKeys + olmDevice.markKeysAsPublished() + return response } companion object { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/actions/EnsureOlmSessionsForDevicesAction.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/actions/EnsureOlmSessionsForDevicesAction.kt index 4c4ee106..7a56f46b 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/actions/EnsureOlmSessionsForDevicesAction.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/actions/EnsureOlmSessionsForDevicesAction.kt @@ -17,7 +17,6 @@ package im.vector.matrix.android.internal.crypto.actions import android.text.TextUtils -import arrow.core.Try import im.vector.matrix.android.internal.crypto.MXOlmDevice import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo import im.vector.matrix.android.internal.crypto.model.MXKey @@ -32,7 +31,7 @@ internal class EnsureOlmSessionsForDevicesAction @Inject constructor(private val private val oneTimeKeysForUsersDeviceTask: ClaimOneTimeKeysForUsersDeviceTask) { - suspend fun handle(devicesByUser: Map>): Try> { + suspend fun handle(devicesByUser: Map>): MXUsersDevicesMap { val devicesWithoutSession = ArrayList() val results = MXUsersDevicesMap() @@ -58,7 +57,7 @@ internal class EnsureOlmSessionsForDevicesAction @Inject constructor(private val } if (devicesWithoutSession.size == 0) { - return Try.just(results) + return results } // Prepare the request for claiming one-time keys @@ -79,39 +78,36 @@ internal class EnsureOlmSessionsForDevicesAction @Inject constructor(private val Timber.v("## claimOneTimeKeysForUsersDevices() : $usersDevicesToClaim") val claimParams = ClaimOneTimeKeysForUsersDeviceTask.Params(usersDevicesToClaim) - return oneTimeKeysForUsersDeviceTask - .execute(claimParams) - .map { - Timber.v("## claimOneTimeKeysForUsersDevices() : keysClaimResponse.oneTimeKeys: $it") - for (userId in userIds) { - val deviceInfos = devicesByUser[userId] - for (deviceInfo in deviceInfos!!) { - var oneTimeKey: MXKey? = null - val deviceIds = it.getUserDeviceIds(userId) - if (null != deviceIds) { - for (deviceId in deviceIds) { - val olmSessionResult = results.getObject(userId, deviceId) - if (olmSessionResult!!.sessionId != null) { - // We already have a result for this device - continue - } - val key = it.getObject(userId, deviceId) - if (key?.type == oneTimeKeyAlgorithm) { - oneTimeKey = key - } - if (oneTimeKey == null) { - Timber.v("## ensureOlmSessionsForDevices() : No one-time keys " + oneTimeKeyAlgorithm - + " for device " + userId + " : " + deviceId) - continue - } - // Update the result for this device in results - olmSessionResult.sessionId = verifyKeyAndStartSession(oneTimeKey, userId, deviceInfo) - } - } + val oneTimeKeys = oneTimeKeysForUsersDeviceTask.execute(claimParams) + Timber.v("## claimOneTimeKeysForUsersDevices() : keysClaimResponse.oneTimeKeys: $oneTimeKeys") + for (userId in userIds) { + val deviceInfos = devicesByUser[userId] + for (deviceInfo in deviceInfos!!) { + var oneTimeKey: MXKey? = null + val deviceIds = oneTimeKeys.getUserDeviceIds(userId) + if (null != deviceIds) { + for (deviceId in deviceIds) { + val olmSessionResult = results.getObject(userId, deviceId) + if (olmSessionResult!!.sessionId != null) { + // We already have a result for this device + continue } + val key = oneTimeKeys.getObject(userId, deviceId) + if (key?.type == oneTimeKeyAlgorithm) { + oneTimeKey = key + } + if (oneTimeKey == null) { + Timber.v("## ensureOlmSessionsForDevices() : No one-time keys " + oneTimeKeyAlgorithm + + " for device " + userId + " : " + deviceId) + continue + } + // Update the result for this device in results + olmSessionResult.sessionId = verifyKeyAndStartSession(oneTimeKey, userId, deviceInfo) } - results } + } + } + return results } private fun verifyKeyAndStartSession(oneTimeKey: MXKey, userId: String, deviceInfo: MXDeviceInfo): String? { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/actions/EnsureOlmSessionsForUsersAction.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/actions/EnsureOlmSessionsForUsersAction.kt index f6817e14..ff0bcef3 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/actions/EnsureOlmSessionsForUsersAction.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/actions/EnsureOlmSessionsForUsersAction.kt @@ -37,7 +37,7 @@ internal class EnsureOlmSessionsForUsersAction @Inject constructor(private val o * Try to make sure we have established olm sessions for the given users. * @param users a list of user ids. */ - suspend fun handle(users: List) : Try> { + suspend fun handle(users: List) : MXUsersDevicesMap { Timber.v("## ensureOlmSessionsForUsers() : ensureOlmSessionsForUsers $users") val devicesByUser = HashMap>() diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/IMXDecrypting.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/IMXDecrypting.kt index 8714e156..32970f99 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/IMXDecrypting.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/IMXDecrypting.kt @@ -35,7 +35,7 @@ internal interface IMXDecrypting { * @param timeline the id of the timeline where the event is decrypted. It is used to prevent replay attack. * @return the decryption information, or an error */ - suspend fun decryptEvent(event: Event, timeline: String): Try + suspend fun decryptEvent(event: Event, timeline: String): MXEventDecryptionResult /** * Handle a key event. diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/IMXEncrypting.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/IMXEncrypting.kt index 544bbe60..555ce9df 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/IMXEncrypting.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/IMXEncrypting.kt @@ -17,7 +17,6 @@ package im.vector.matrix.android.internal.crypto.algorithms -import arrow.core.Try import im.vector.matrix.android.api.session.events.model.Content /** @@ -31,7 +30,7 @@ internal interface IMXEncrypting { * @param eventContent the content of the event. * @param eventType the type of the event. * @param userIds the room members the event will be sent to. - * @return the encrypted content wrapped by [Try] + * @return the encrypted content */ - suspend fun encryptEventContent(eventContent: Content, eventType: String, userIds: List): Try + suspend fun encryptEventContent(eventContent: Content, eventType: String, userIds: List): Content } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmDecryption.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmDecryption.kt index b30176b2..47bf9956 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmDecryption.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmDecryption.kt @@ -18,7 +18,6 @@ package im.vector.matrix.android.internal.crypto.algorithms.megolm import android.text.TextUtils -import arrow.core.Try import im.vector.matrix.android.api.auth.data.Credentials import im.vector.matrix.android.api.session.crypto.MXCryptoError import im.vector.matrix.android.api.session.events.model.Event @@ -40,7 +39,6 @@ import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch import timber.log.Timber -import kotlin.collections.HashMap internal class MXMegolmDecryption(private val credentials: Credentials, private val olmDevice: MXOlmDevice, @@ -61,30 +59,46 @@ internal class MXMegolmDecryption(private val credentials: Credentials, */ private var pendingEvents: MutableMap>> = HashMap() - override suspend fun decryptEvent(event: Event, timeline: String): Try { + override suspend fun decryptEvent(event: Event, timeline: String): MXEventDecryptionResult { return decryptEvent(event, timeline, true) } - private fun decryptEvent(event: Event, timeline: String, requestKeysOnFail: Boolean): Try { + private suspend fun decryptEvent(event: Event, timeline: String, requestKeysOnFail: Boolean): MXEventDecryptionResult { if (event.roomId.isNullOrBlank()) { - return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_FIELDS, MXCryptoError.MISSING_FIELDS_REASON)) + throw MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_FIELDS, MXCryptoError.MISSING_FIELDS_REASON) } val encryptedEventContent = event.content.toModel() - ?: return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_FIELDS, MXCryptoError.MISSING_FIELDS_REASON)) + ?: throw MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_FIELDS, MXCryptoError.MISSING_FIELDS_REASON) if (encryptedEventContent.senderKey.isNullOrBlank() || encryptedEventContent.sessionId.isNullOrBlank() || encryptedEventContent.ciphertext.isNullOrBlank()) { - return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_FIELDS, MXCryptoError.MISSING_FIELDS_REASON)) + throw MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_FIELDS, MXCryptoError.MISSING_FIELDS_REASON) } - return olmDevice.decryptGroupMessage(encryptedEventContent.ciphertext, - event.roomId, - timeline, - encryptedEventContent.sessionId, - encryptedEventContent.senderKey) + return runCatching { + olmDevice.decryptGroupMessage(encryptedEventContent.ciphertext, + event.roomId, + timeline, + encryptedEventContent.sessionId, + encryptedEventContent.senderKey) + } .fold( + { olmDecryptionResult -> + // the decryption succeeds + if (olmDecryptionResult.payload != null) { + MXEventDecryptionResult( + clearEvent = olmDecryptionResult.payload, + senderCurve25519Key = olmDecryptionResult.senderKey, + claimedEd25519Key = olmDecryptionResult.keysClaimed?.get("ed25519"), + forwardingCurve25519KeyChain = olmDecryptionResult.forwardingCurve25519KeyChain + ?: emptyList() + ) + } else { + throw MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_FIELDS, MXCryptoError.MISSING_FIELDS_REASON) + } + }, { throwable -> if (throwable is MXCryptoError.OlmError) { // TODO Check the value of .message @@ -98,10 +112,10 @@ internal class MXMegolmDecryption(private val credentials: Credentials, val reason = String.format(MXCryptoError.OLM_REASON, throwable.olmException.message) val detailedReason = String.format(MXCryptoError.DETAILED_OLM_REASON, encryptedEventContent.ciphertext, reason) - Try.Failure(MXCryptoError.Base( + throw MXCryptoError.Base( MXCryptoError.ErrorType.OLM, reason, - detailedReason)) + detailedReason) } if (throwable is MXCryptoError.Base) { if (throwable.errorType == MXCryptoError.ErrorType.UNKNOWN_INBOUND_SESSION_ID) { @@ -111,23 +125,7 @@ internal class MXMegolmDecryption(private val credentials: Credentials, } } } - - Try.Failure(throwable) - }, - { olmDecryptionResult -> - // the decryption succeeds - if (olmDecryptionResult.payload != null) { - Try.just( - MXEventDecryptionResult( - clearEvent = olmDecryptionResult.payload, - senderCurve25519Key = olmDecryptionResult.senderKey, - claimedEd25519Key = olmDecryptionResult.keysClaimed?.get("ed25519"), - forwardingCurve25519KeyChain = olmDecryptionResult.forwardingCurve25519KeyChain ?: emptyList() - ) - ) - } else { - Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_FIELDS, MXCryptoError.MISSING_FIELDS_REASON)) - } + throw throwable } ) } @@ -311,51 +309,48 @@ internal class MXMegolmDecryption(private val credentials: Credentials, } val userId = request.userId ?: return GlobalScope.launch(coroutineDispatchers.crypto) { - deviceListManager - .downloadKeys(listOf(userId), false) - .flatMap { + runCatching { deviceListManager.downloadKeys(listOf(userId), false) } + .mapCatching { val deviceId = request.deviceId val deviceInfo = cryptoStore.getUserDevice(deviceId ?: "", userId) if (deviceInfo == null) { throw RuntimeException() } else { val devicesByUser = mapOf(userId to listOf(deviceInfo)) - ensureOlmSessionsForDevicesAction - .handle(devicesByUser) - .flatMap { - val body = request.requestBody - val olmSessionResult = it.getObject(userId, deviceId) - if (olmSessionResult?.sessionId == null) { - // no session with this device, probably because there - // were no one-time keys. - Try.just(Unit) - } - Timber.v("## shareKeysWithDevice() : sharing keys for session" + - " ${body?.senderKey}|${body?.sessionId} with device $userId:$deviceId") + val usersDeviceMap = ensureOlmSessionsForDevicesAction.handle(devicesByUser) + val body = request.requestBody + val olmSessionResult = usersDeviceMap.getObject(userId, deviceId) + if (olmSessionResult?.sessionId == null) { + // no session with this device, probably because there + // were no one-time keys. + return@mapCatching + } + Timber.v("## shareKeysWithDevice() : sharing keys for session" + + " ${body?.senderKey}|${body?.sessionId} with device $userId:$deviceId") - val payloadJson = mutableMapOf("type" to EventType.FORWARDED_ROOM_KEY) + val payloadJson = mutableMapOf("type" to EventType.FORWARDED_ROOM_KEY) + runCatching { olmDevice.getInboundGroupSession(body?.sessionId, body?.senderKey, body?.roomId) } + .fold( + { + // TODO + payloadJson["content"] = it.exportKeys() + ?: "" + }, + { + // TODO + } - olmDevice.getInboundGroupSession(body?.sessionId, body?.senderKey, body?.roomId) - .fold( - { - // TODO - }, - { - // TODO - payloadJson["content"] = it.exportKeys() ?: "" - } - ) + ) - val encodedPayload = messageEncrypter.encryptMessage(payloadJson, listOf(deviceInfo)) - val sendToDeviceMap = MXUsersDevicesMap() - sendToDeviceMap.setObject(userId, deviceId, encodedPayload) - Timber.v("## shareKeysWithDevice() : sending to $userId:$deviceId") - val sendToDeviceParams = SendToDeviceTask.Params(EventType.ENCRYPTED, sendToDeviceMap) - sendToDeviceTask.execute(sendToDeviceParams) - } + val encodedPayload = messageEncrypter.encryptMessage(payloadJson, listOf(deviceInfo)) + val sendToDeviceMap = MXUsersDevicesMap() + sendToDeviceMap.setObject(userId, deviceId, encodedPayload) + Timber.v("## shareKeysWithDevice() : sending to $userId:$deviceId") + val sendToDeviceParams = SendToDeviceTask.Params(EventType.ENCRYPTED, sendToDeviceMap) + sendToDeviceTask.execute(sendToDeviceParams) } } } } - } + diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmEncryption.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmEncryption.kt index 165fc736..b465cb6e 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmEncryption.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmEncryption.kt @@ -19,7 +19,6 @@ package im.vector.matrix.android.internal.crypto.algorithms.megolm import android.text.TextUtils -import arrow.core.Try import im.vector.matrix.android.api.auth.data.Credentials import im.vector.matrix.android.api.session.crypto.MXCryptoError import im.vector.matrix.android.api.session.events.model.Content @@ -69,12 +68,10 @@ internal class MXMegolmEncryption( override suspend fun encryptEventContent(eventContent: Content, eventType: String, - userIds: List): Try { - return getDevicesInRoom(userIds) - .flatMap { ensureOutboundSession(it) } - .flatMap { - encryptContent(it, eventType, eventContent) - } + userIds: List): Content { + val devices = getDevicesInRoom(userIds) + val outboundSession = ensureOutboundSession(devices) + return encryptContent(outboundSession, eventType, eventContent) } /** @@ -101,7 +98,7 @@ internal class MXMegolmEncryption( * * @param devicesInRoom the devices list */ - private suspend fun ensureOutboundSession(devicesInRoom: MXUsersDevicesMap): Try { + private suspend fun ensureOutboundSession(devicesInRoom: MXUsersDevicesMap): MXOutboundSessionInfo { var session = outboundSession if (session == null // Need to make a brand new session? @@ -126,7 +123,8 @@ internal class MXMegolmEncryption( } } } - return shareKey(safeSession, shareMap).map { safeSession!! } + shareKey(safeSession, shareMap) + return safeSession } /** @@ -136,11 +134,11 @@ internal class MXMegolmEncryption( * @param devicesByUsers the devices map */ private suspend fun shareKey(session: MXOutboundSessionInfo, - devicesByUsers: Map>): Try { + devicesByUsers: Map>) { // nothing to send, the task is done if (devicesByUsers.isEmpty()) { Timber.v("## shareKey() : nothing more to do") - return Try.just(Unit) + return } // reduce the map size to avoid request timeout when there are too many devices (Users size * devices per user) val subMap = HashMap>() @@ -157,11 +155,9 @@ internal class MXMegolmEncryption( } } Timber.v("## shareKey() ; userId $userIds") - return shareUserDevicesKey(session, subMap) - .flatMap { - val remainingDevices = devicesByUsers.filterKeys { userIds.contains(it).not() } - shareKey(session, remainingDevices) - } + shareUserDevicesKey(session, subMap) + val remainingDevices = devicesByUsers.filterKeys { userIds.contains(it).not() } + shareKey(session, remainingDevices) } /** @@ -172,7 +168,7 @@ internal class MXMegolmEncryption( * @param callback the asynchronous callback */ private suspend fun shareUserDevicesKey(session: MXOutboundSessionInfo, - devicesByUser: Map>): Try { + devicesByUser: Map>) { val sessionKey = olmDevice.getSessionKey(session.sessionId) val chainIndex = olmDevice.getMessageIndex(session.sessionId) @@ -190,94 +186,86 @@ internal class MXMegolmEncryption( var t0 = System.currentTimeMillis() Timber.v("## shareUserDevicesKey() : starts") - return ensureOlmSessionsForDevicesAction.handle(devicesByUser) - .flatMap { - Timber.v("## shareUserDevicesKey() : ensureOlmSessionsForDevices succeeds after " - + (System.currentTimeMillis() - t0) + " ms") - val contentMap = MXUsersDevicesMap() - var haveTargets = false - val userIds = it.userIds - for (userId in userIds) { - val devicesToShareWith = devicesByUser[userId] - for ((deviceID) in devicesToShareWith!!) { - val sessionResult = it.getObject(userId, deviceID) - if (sessionResult?.sessionId == null) { - // no session with this device, probably because there - // were no one-time keys. - // - // we could send them a to_device message anyway, as a - // signal that they have missed out on the key sharing - // message because of the lack of keys, but there's not - // much point in that really; it will mostly serve to clog - // up to_device inboxes. - // - // ensureOlmSessionsForUsers has already done the logging, - // so just skip it. - continue - } - Timber.v("## shareUserDevicesKey() : Sharing keys with device $userId:$deviceID") - //noinspection ArraysAsListWithZeroOrOneArgument,ArraysAsListWithZeroOrOneArgument - contentMap.setObject(userId, deviceID, messageEncrypter.encryptMessage(payload, Arrays.asList(sessionResult.deviceInfo))) - haveTargets = true - } - } - if (haveTargets) { - t0 = System.currentTimeMillis() - Timber.v("## shareUserDevicesKey() : has target") - val sendToDeviceParams = SendToDeviceTask.Params(EventType.ENCRYPTED, contentMap) - sendToDeviceTask.execute(sendToDeviceParams) - .map { - Timber.v("## shareUserDevicesKey() : sendToDevice succeeds after " - + (System.currentTimeMillis() - t0) + " ms") - - // Add the devices we have shared with to session.sharedWithDevices. - // we deliberately iterate over devicesByUser (ie, the devices we - // attempted to share with) rather than the contentMap (those we did - // share with), because we don't want to try to claim a one-time-key - // for dead devices on every message. - for (userId in devicesByUser.keys) { - val devicesToShareWith = devicesByUser[userId] - for ((deviceId) in devicesToShareWith!!) { - session.sharedWithDevices.setObject(userId, deviceId, chainIndex) - } - } - Unit - } - } else { - Timber.v("## shareUserDevicesKey() : no need to sharekey") - Try.just(Unit) - } + val results = ensureOlmSessionsForDevicesAction.handle(devicesByUser) + Timber.v("## shareUserDevicesKey() : ensureOlmSessionsForDevices succeeds after " + + (System.currentTimeMillis() - t0) + " ms") + val contentMap = MXUsersDevicesMap() + var haveTargets = false + val userIds = results.userIds + for (userId in userIds) { + val devicesToShareWith = devicesByUser[userId] + for ((deviceID) in devicesToShareWith!!) { + val sessionResult = results.getObject(userId, deviceID) + if (sessionResult?.sessionId == null) { + // no session with this device, probably because there + // were no one-time keys. + // + // we could send them a to_device message anyway, as a + // signal that they have missed out on the key sharing + // message because of the lack of keys, but there's not + // much point in that really; it will mostly serve to clog + // up to_device inboxes. + // + // ensureOlmSessionsForUsers has already done the logging, + // so just skip it. + continue } + Timber.v("## shareUserDevicesKey() : Sharing keys with device $userId:$deviceID") + //noinspection ArraysAsListWithZeroOrOneArgument,ArraysAsListWithZeroOrOneArgument + contentMap.setObject(userId, deviceID, messageEncrypter.encryptMessage(payload, Arrays.asList(sessionResult.deviceInfo))) + haveTargets = true + } + } + if (haveTargets) { + t0 = System.currentTimeMillis() + Timber.v("## shareUserDevicesKey() : has target") + val sendToDeviceParams = SendToDeviceTask.Params(EventType.ENCRYPTED, contentMap) + sendToDeviceTask.execute(sendToDeviceParams) + Timber.v("## shareUserDevicesKey() : sendToDevice succeeds after " + + (System.currentTimeMillis() - t0) + " ms") + + // Add the devices we have shared with to session.sharedWithDevices. + // we deliberately iterate over devicesByUser (ie, the devices we + // attempted to share with) rather than the contentMap (those we did + // share with), because we don't want to try to claim a one-time-key + // for dead devices on every message. + for (userId in devicesByUser.keys) { + val devicesToShareWith = devicesByUser[userId] + for ((deviceId) in devicesToShareWith!!) { + session.sharedWithDevices.setObject(userId, deviceId, chainIndex) + } + } + } else { + Timber.v("## shareUserDevicesKey() : no need to sharekey") + } } /** * process the pending encryptions */ - private fun encryptContent(session: MXOutboundSessionInfo, eventType: String, eventContent: Content): Try { - return Try { - // Everything is in place, encrypt all pending events - val payloadJson = HashMap() - payloadJson["room_id"] = roomId - payloadJson["type"] = eventType - payloadJson["content"] = eventContent + private fun encryptContent(session: MXOutboundSessionInfo, eventType: String, eventContent: Content): Content { + // Everything is in place, encrypt all pending events + val payloadJson = HashMap() + payloadJson["room_id"] = roomId + payloadJson["type"] = eventType + payloadJson["content"] = eventContent - // Get canonical Json from + // Get canonical Json from - val payloadString = convertToUTF8(JsonCanonicalizer.getCanonicalJson(Map::class.java, payloadJson)) - val ciphertext = olmDevice.encryptGroupMessage(session.sessionId, payloadString!!) + val payloadString = convertToUTF8(JsonCanonicalizer.getCanonicalJson(Map::class.java, payloadJson)) + val ciphertext = olmDevice.encryptGroupMessage(session.sessionId, payloadString!!) - val map = HashMap() - map["algorithm"] = MXCRYPTO_ALGORITHM_MEGOLM - map["sender_key"] = olmDevice.deviceCurve25519Key!! - map["ciphertext"] = ciphertext!! - map["session_id"] = session.sessionId + val map = HashMap() + map["algorithm"] = MXCRYPTO_ALGORITHM_MEGOLM + map["sender_key"] = olmDevice.deviceCurve25519Key!! + map["ciphertext"] = ciphertext!! + map["session_id"] = session.sessionId - // Include our device ID so that recipients can send us a - // m.new_device message if they don't have our session key. - map["device_id"] = credentials.deviceId!! - session.useCount++ - map - } + // Include our device ID so that recipients can send us a + // m.new_device message if they don't have our session key. + map["device_id"] = credentials.deviceId!! + session.useCount++ + return map } /** @@ -287,50 +275,47 @@ internal class MXMegolmEncryption( * @param userIds the user ids whose devices must be checked. * @param callback the asynchronous callback */ - private suspend fun getDevicesInRoom(userIds: List): Try> { + private suspend fun getDevicesInRoom(userIds: List): MXUsersDevicesMap { // We are happy to use a cached version here: we assume that if we already // have a list of the user's devices, then we already share an e2e room // with them, which means that they will have announced any new devices via // an m.new_device. - return deviceListManager - .downloadKeys(userIds, false) - .flatMap { - val encryptToVerifiedDevicesOnly = cryptoStore.getGlobalBlacklistUnverifiedDevices() - || cryptoStore.getRoomsListBlacklistUnverifiedDevices().contains(roomId) + val keys = deviceListManager.downloadKeys(userIds, false) + val encryptToVerifiedDevicesOnly = cryptoStore.getGlobalBlacklistUnverifiedDevices() + || cryptoStore.getRoomsListBlacklistUnverifiedDevices().contains(roomId) - val devicesInRoom = MXUsersDevicesMap() - val unknownDevices = MXUsersDevicesMap() + val devicesInRoom = MXUsersDevicesMap() + val unknownDevices = MXUsersDevicesMap() - for (userId in it.userIds) { - val deviceIds = it.getUserDeviceIds(userId) ?: continue - for (deviceId in deviceIds) { - val deviceInfo = it.getObject(userId, deviceId) ?: continue - if (warnOnUnknownDevicesRepository.warnOnUnknownDevices() && deviceInfo.isUnknown) { - // The device is not yet known by the user - unknownDevices.setObject(userId, deviceId, deviceInfo) - continue - } - if (deviceInfo.isBlocked) { - // Remove any blocked devices - continue - } - - if (!deviceInfo.isVerified && encryptToVerifiedDevicesOnly) { - continue - } - - if (TextUtils.equals(deviceInfo.identityKey(), olmDevice.deviceCurve25519Key)) { - // Don't bother sending to ourself - continue - } - devicesInRoom.setObject(userId, deviceId, deviceInfo) - } - } - if (unknownDevices.isEmpty) { - Try.just(devicesInRoom) - } else { - Try.Failure(MXCryptoError.UnknownDevice(unknownDevices)) - } + for (userId in keys.userIds) { + val deviceIds = keys.getUserDeviceIds(userId) ?: continue + for (deviceId in deviceIds) { + val deviceInfo = keys.getObject(userId, deviceId) ?: continue + if (warnOnUnknownDevicesRepository.warnOnUnknownDevices() && deviceInfo.isUnknown) { + // The device is not yet known by the user + unknownDevices.setObject(userId, deviceId, deviceInfo) + continue } + if (deviceInfo.isBlocked) { + // Remove any blocked devices + continue + } + + if (!deviceInfo.isVerified && encryptToVerifiedDevicesOnly) { + continue + } + + if (TextUtils.equals(deviceInfo.identityKey(), olmDevice.deviceCurve25519Key)) { + // Don't bother sending to ourself + continue + } + devicesInRoom.setObject(userId, deviceId, deviceInfo) + } + } + if (unknownDevices.isEmpty) { + return devicesInRoom + } else { + throw MXCryptoError.UnknownDevice(unknownDevices) + } } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/olm/MXOlmDecryption.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/olm/MXOlmDecryption.kt index 7b5a8cd4..b57486c1 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/olm/MXOlmDecryption.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/olm/MXOlmDecryption.kt @@ -17,7 +17,6 @@ package im.vector.matrix.android.internal.crypto.algorithms.olm -import arrow.core.Try import im.vector.matrix.android.api.auth.data.Credentials import im.vector.matrix.android.api.session.crypto.MXCryptoError import im.vector.matrix.android.api.session.events.model.Event @@ -41,29 +40,28 @@ internal class MXOlmDecryption( private val credentials: Credentials) : IMXDecrypting { - override suspend fun decryptEvent(event: Event, timeline: String): Try { + override suspend fun decryptEvent(event: Event, timeline: String): MXEventDecryptionResult { val olmEventContent = event.content.toModel() ?: run { Timber.e("## decryptEvent() : bad event format") - return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.BAD_EVENT_FORMAT, - MXCryptoError.BAD_EVENT_FORMAT_TEXT_REASON)) + throw MXCryptoError.Base(MXCryptoError.ErrorType.BAD_EVENT_FORMAT, + MXCryptoError.BAD_EVENT_FORMAT_TEXT_REASON) } val cipherText = olmEventContent.ciphertext ?: run { Timber.e("## decryptEvent() : missing cipher text") - return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_CIPHER_TEXT, - MXCryptoError.MISSING_CIPHER_TEXT_REASON)) + throw MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_CIPHER_TEXT, + MXCryptoError.MISSING_CIPHER_TEXT_REASON) } val senderKey = olmEventContent.senderKey ?: run { Timber.e("## decryptEvent() : missing sender key") - return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_SENDER_KEY, - MXCryptoError.MISSING_SENDER_KEY_TEXT_REASON)) + throw MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_SENDER_KEY, + MXCryptoError.MISSING_SENDER_KEY_TEXT_REASON) } val messageAny = cipherText[olmDevice.deviceCurve25519Key] ?: run { Timber.e("## decryptEvent() : our device ${olmDevice.deviceCurve25519Key} is not included in recipients") - return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.NOT_INCLUDE_IN_RECIPIENTS, - MXCryptoError.NOT_INCLUDED_IN_RECIPIENT_REASON)) + throw MXCryptoError.Base(MXCryptoError.ErrorType.NOT_INCLUDE_IN_RECIPIENTS, MXCryptoError.NOT_INCLUDED_IN_RECIPIENT_REASON) } // The message for myUser @@ -73,14 +71,12 @@ internal class MXOlmDecryption( if (decryptedPayload == null) { Timber.e("## decryptEvent() Failed to decrypt Olm event (id= ${event.eventId} from $senderKey") - return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.BAD_ENCRYPTED_MESSAGE, - MXCryptoError.BAD_ENCRYPTED_MESSAGE_REASON)) + throw MXCryptoError.Base(MXCryptoError.ErrorType.BAD_ENCRYPTED_MESSAGE, MXCryptoError.BAD_ENCRYPTED_MESSAGE_REASON) } val payloadString = convertFromUTF8(decryptedPayload) if (payloadString == null) { Timber.e("## decryptEvent() Failed to decrypt Olm event (id= ${event.eventId} from $senderKey") - return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.BAD_ENCRYPTED_MESSAGE, - MXCryptoError.BAD_ENCRYPTED_MESSAGE_REASON)) + throw MXCryptoError.Base(MXCryptoError.ErrorType.BAD_ENCRYPTED_MESSAGE, MXCryptoError.BAD_ENCRYPTED_MESSAGE_REASON) } val adapter = MoshiProvider.providesMoshi().adapter(JSON_DICT_PARAMETERIZED_TYPE) @@ -88,73 +84,70 @@ internal class MXOlmDecryption( if (payload == null) { Timber.e("## decryptEvent failed : null payload") - return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.UNABLE_TO_DECRYPT, - MXCryptoError.MISSING_CIPHER_TEXT_REASON)) + throw MXCryptoError.Base(MXCryptoError.ErrorType.UNABLE_TO_DECRYPT, MXCryptoError.MISSING_CIPHER_TEXT_REASON) } val olmPayloadContent = OlmPayloadContent.fromJsonString(payloadString) ?: run { Timber.e("## decryptEvent() : bad olmPayloadContent format") - return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.BAD_DECRYPTED_FORMAT, - MXCryptoError.BAD_DECRYPTED_FORMAT_TEXT_REASON)) + throw MXCryptoError.Base(MXCryptoError.ErrorType.BAD_DECRYPTED_FORMAT, MXCryptoError.BAD_DECRYPTED_FORMAT_TEXT_REASON) } if (olmPayloadContent.recipient.isNullOrBlank()) { val reason = String.format(MXCryptoError.ERROR_MISSING_PROPERTY_REASON, "recipient") Timber.e("## decryptEvent() : $reason") - return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_PROPERTY, - reason)) + throw MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_PROPERTY, reason) } if (olmPayloadContent.recipient != credentials.userId) { Timber.e("## decryptEvent() : Event ${event.eventId}:" + " Intended recipient ${olmPayloadContent.recipient} does not match our id ${credentials.userId}") - return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.BAD_RECIPIENT, - String.format(MXCryptoError.BAD_RECIPIENT_REASON, olmPayloadContent.recipient))) + throw MXCryptoError.Base(MXCryptoError.ErrorType.BAD_RECIPIENT, + String.format(MXCryptoError.BAD_RECIPIENT_REASON, olmPayloadContent.recipient)) } val recipientKeys = olmPayloadContent.recipient_keys ?: run { Timber.e("## decryptEvent() : Olm event (id=${event.eventId}) contains no 'recipient_keys' property; cannot prevent unknown-key attack") - return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_PROPERTY, - String.format(MXCryptoError.ERROR_MISSING_PROPERTY_REASON, "recipient_keys"))) + throw MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_PROPERTY, + String.format(MXCryptoError.ERROR_MISSING_PROPERTY_REASON, "recipient_keys")) } val ed25519 = recipientKeys["ed25519"] if (ed25519 != olmDevice.deviceEd25519Key) { Timber.e("## decryptEvent() : Event ${event.eventId}: Intended recipient ed25519 key $ed25519 did not match ours") - return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.BAD_RECIPIENT_KEY, - MXCryptoError.BAD_RECIPIENT_KEY_REASON)) + throw MXCryptoError.Base(MXCryptoError.ErrorType.BAD_RECIPIENT_KEY, + MXCryptoError.BAD_RECIPIENT_KEY_REASON) } if (olmPayloadContent.sender.isNullOrBlank()) { Timber.e("## decryptEvent() : Olm event (id=${event.eventId}) contains no 'sender' property; cannot prevent unknown-key attack") - return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_PROPERTY, - String.format(MXCryptoError.ERROR_MISSING_PROPERTY_REASON, "sender"))) + throw MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_PROPERTY, + String.format(MXCryptoError.ERROR_MISSING_PROPERTY_REASON, "sender")) } if (olmPayloadContent.sender != event.senderId) { Timber.e("Event ${event.eventId}: original sender ${olmPayloadContent.sender} does not match reported sender ${event.senderId}") - return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.FORWARDED_MESSAGE, - String.format(MXCryptoError.FORWARDED_MESSAGE_REASON, olmPayloadContent.sender))) + throw MXCryptoError.Base(MXCryptoError.ErrorType.FORWARDED_MESSAGE, + String.format(MXCryptoError.FORWARDED_MESSAGE_REASON, olmPayloadContent.sender)) } if (olmPayloadContent.room_id != event.roomId) { Timber.e("## decryptEvent() : Event ${event.eventId}: original room ${olmPayloadContent.room_id} does not match reported room ${event.roomId}") - return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.BAD_ROOM, - String.format(MXCryptoError.BAD_ROOM_REASON, olmPayloadContent.room_id))) + throw MXCryptoError.Base(MXCryptoError.ErrorType.BAD_ROOM, + String.format(MXCryptoError.BAD_ROOM_REASON, olmPayloadContent.room_id)) } val keys = olmPayloadContent.keys ?: run { Timber.e("## decryptEvent failed : null keys") - return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.UNABLE_TO_DECRYPT, - MXCryptoError.MISSING_CIPHER_TEXT_REASON)) + throw MXCryptoError.Base(MXCryptoError.ErrorType.UNABLE_TO_DECRYPT, + MXCryptoError.MISSING_CIPHER_TEXT_REASON) } - return Try.just(MXEventDecryptionResult( + return MXEventDecryptionResult( clearEvent = payload, senderCurve25519Key = senderKey, claimedEd25519Key = keys["ed25519"] - )) + ) } /** diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/olm/MXOlmEncryption.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/olm/MXOlmEncryption.kt index ad0680a4..10d481dc 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/olm/MXOlmEncryption.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/olm/MXOlmEncryption.kt @@ -19,7 +19,6 @@ package im.vector.matrix.android.internal.crypto.algorithms.olm import android.text.TextUtils -import arrow.core.Try import im.vector.matrix.android.api.session.events.model.Content import im.vector.matrix.android.api.session.events.model.toContent import im.vector.matrix.android.internal.crypto.DeviceListManager @@ -40,37 +39,35 @@ internal class MXOlmEncryption( private val ensureOlmSessionsForUsersAction: EnsureOlmSessionsForUsersAction) : IMXEncrypting { - override suspend fun encryptEventContent(eventContent: Content, eventType: String, userIds: List): Try { + override suspend fun encryptEventContent(eventContent: Content, eventType: String, userIds: List): Content { // pick the list of recipients based on the membership list. // // TODO: there is a race condition here! What if a new user turns up - return ensureSession(userIds) - .map { - val deviceInfos = ArrayList() - for (userId in userIds) { - val devices = cryptoStore.getUserDevices(userId)?.values ?: emptyList() - for (device in devices) { - val key = device.identityKey() - if (TextUtils.equals(key, olmDevice.deviceCurve25519Key)) { - // Don't bother setting up session to ourself - continue - } - if (device.isBlocked) { - // Don't bother setting up sessions with blocked users - continue - } - deviceInfos.add(device) - } - } - - val messageMap = HashMap() - messageMap["room_id"] = roomId - messageMap["type"] = eventType - messageMap["content"] = eventContent - - messageEncrypter.encryptMessage(messageMap, deviceInfos) - messageMap.toContent()!! + ensureSession(userIds) + val deviceInfos = ArrayList() + for (userId in userIds) { + val devices = cryptoStore.getUserDevices(userId)?.values ?: emptyList() + for (device in devices) { + val key = device.identityKey() + if (TextUtils.equals(key, olmDevice.deviceCurve25519Key)) { + // Don't bother setting up session to ourself + continue } + if (device.isBlocked) { + // Don't bother setting up sessions with blocked users + continue + } + deviceInfos.add(device) + } + } + + val messageMap = HashMap() + messageMap["room_id"] = roomId + messageMap["type"] = eventType + messageMap["content"] = eventContent + + messageEncrypter.encryptMessage(messageMap, deviceInfos) + return messageMap.toContent()!! } @@ -78,13 +75,9 @@ internal class MXOlmEncryption( * Ensure that the session * * @param users the user ids list - * @param callback the asynchronous callback */ - private suspend fun ensureSession(users: List): Try { - return deviceListManager - .downloadKeys(users, false) - .flatMap { ensureOlmSessionsForUsersAction.handle(users) } - .map { Unit } - + private suspend fun ensureSession(users: List) { + deviceListManager.downloadKeys(users, false) + ensureOlmSessionsForUsersAction.handle(users) } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/KeysBackup.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/KeysBackup.kt index 88d9aec9..2e9f84bd 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/KeysBackup.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/KeysBackup.kt @@ -52,10 +52,6 @@ import im.vector.matrix.android.internal.di.MoshiProvider import im.vector.matrix.android.internal.extensions.foldToCallback import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.task.* -import im.vector.matrix.android.internal.task.Task -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.JsonCanonicalizer import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers import kotlinx.coroutines.GlobalScope @@ -355,10 +351,8 @@ internal class KeysBackup @Inject constructor( callback: MatrixCallback) { // TODO Validate with François that this is correct object : Task { - override suspend fun execute(params: KeysVersionResult): Try { - return Try { - getKeysBackupTrustBg(params) - } + override suspend fun execute(params: KeysVersionResult): KeysBackupVersionTrust { + return getKeysBackupTrustBg(params) } } .configureWith(keysBackupVersion) @@ -454,7 +448,8 @@ internal class KeysBackup @Inject constructor( val myUserId = credentials.userId // Get current signatures, or create an empty set - val myUserSignatures = authData.signatures?.get(myUserId)?.toMutableMap() ?: HashMap() + val myUserSignatures = authData.signatures?.get(myUserId)?.toMutableMap() + ?: HashMap() if (trust) { // Add current device signature diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/CreateKeysBackupVersionTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/CreateKeysBackupVersionTask.kt index f0c5037f..5aaaaea5 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/CreateKeysBackupVersionTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/CreateKeysBackupVersionTask.kt @@ -16,7 +16,6 @@ package im.vector.matrix.android.internal.crypto.keysbackup.tasks -import arrow.core.Try import im.vector.matrix.android.internal.crypto.keysbackup.api.RoomKeysApi import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.CreateKeysBackupVersionBody import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.KeysVersion @@ -30,7 +29,7 @@ internal class DefaultCreateKeysBackupVersionTask @Inject constructor(private va : CreateKeysBackupVersionTask { - override suspend fun execute(params: CreateKeysBackupVersionBody): Try { + override suspend fun execute(params: CreateKeysBackupVersionBody): KeysVersion { return executeRequest { apiCall = roomKeysApi.createKeysBackupVersion(params) } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/DeleteBackupTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/DeleteBackupTask.kt index a1ad84ab..6c966dd2 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/DeleteBackupTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/DeleteBackupTask.kt @@ -16,10 +16,8 @@ package im.vector.matrix.android.internal.crypto.keysbackup.tasks -import arrow.core.Try import im.vector.matrix.android.internal.crypto.keysbackup.api.RoomKeysApi import im.vector.matrix.android.internal.network.executeRequest -import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.task.Task import javax.inject.Inject @@ -33,10 +31,9 @@ internal interface DeleteBackupTask : Task { internal class DefaultDeleteBackupTask @Inject constructor(private val roomKeysApi: RoomKeysApi) : DeleteBackupTask { - override suspend fun execute(params: DeleteBackupTask.Params): Try { + override suspend fun execute(params: DeleteBackupTask.Params) { return executeRequest { - apiCall = roomKeysApi.deleteBackup( - params.version) + apiCall = roomKeysApi.deleteBackup(params.version) } } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/DeleteRoomSessionDataTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/DeleteRoomSessionDataTask.kt index c24d314c..ccb3645e 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/DeleteRoomSessionDataTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/DeleteRoomSessionDataTask.kt @@ -16,10 +16,8 @@ package im.vector.matrix.android.internal.crypto.keysbackup.tasks -import arrow.core.Try import im.vector.matrix.android.internal.crypto.keysbackup.api.RoomKeysApi import im.vector.matrix.android.internal.network.executeRequest -import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.task.Task import javax.inject.Inject @@ -34,7 +32,7 @@ internal interface DeleteRoomSessionDataTask : Task { + override suspend fun execute(params: DeleteRoomSessionDataTask.Params) { return executeRequest { apiCall = roomKeysApi.deleteRoomSessionData( params.roomId, diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/DeleteRoomSessionsDataTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/DeleteRoomSessionsDataTask.kt index 7476e2d2..98836654 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/DeleteRoomSessionsDataTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/DeleteRoomSessionsDataTask.kt @@ -16,10 +16,8 @@ package im.vector.matrix.android.internal.crypto.keysbackup.tasks -import arrow.core.Try import im.vector.matrix.android.internal.crypto.keysbackup.api.RoomKeysApi import im.vector.matrix.android.internal.network.executeRequest -import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.task.Task import javax.inject.Inject @@ -33,7 +31,7 @@ internal interface DeleteRoomSessionsDataTask : Task { + override suspend fun execute(params: DeleteRoomSessionsDataTask.Params) { return executeRequest { apiCall = roomKeysApi.deleteRoomSessionsData( params.roomId, diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/DeleteSessionsDataTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/DeleteSessionsDataTask.kt index 14030775..12fc1357 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/DeleteSessionsDataTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/DeleteSessionsDataTask.kt @@ -16,10 +16,8 @@ package im.vector.matrix.android.internal.crypto.keysbackup.tasks -import arrow.core.Try import im.vector.matrix.android.internal.crypto.keysbackup.api.RoomKeysApi import im.vector.matrix.android.internal.network.executeRequest -import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.task.Task import javax.inject.Inject @@ -32,10 +30,9 @@ internal interface DeleteSessionsDataTask : Task { + override suspend fun execute(params: DeleteSessionsDataTask.Params) { return executeRequest { - apiCall = roomKeysApi.deleteSessionsData( - params.version) + apiCall = roomKeysApi.deleteSessionsData(params.version) } } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/GetKeysBackupLastVersionTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/GetKeysBackupLastVersionTask.kt index 48f405a0..30076226 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/GetKeysBackupLastVersionTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/GetKeysBackupLastVersionTask.kt @@ -16,7 +16,6 @@ package im.vector.matrix.android.internal.crypto.keysbackup.tasks -import arrow.core.Try import im.vector.matrix.android.internal.crypto.keysbackup.api.RoomKeysApi import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.KeysVersionResult import im.vector.matrix.android.internal.network.executeRequest @@ -29,7 +28,7 @@ internal class DefaultGetKeysBackupLastVersionTask @Inject constructor(private v : GetKeysBackupLastVersionTask { - override suspend fun execute(params: Unit): Try { + override suspend fun execute(params: Unit): KeysVersionResult { return executeRequest { apiCall = roomKeysApi.getKeysBackupLastVersion() } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/GetKeysBackupVersionTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/GetKeysBackupVersionTask.kt index dfaeafa1..c36f039c 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/GetKeysBackupVersionTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/GetKeysBackupVersionTask.kt @@ -16,7 +16,6 @@ package im.vector.matrix.android.internal.crypto.keysbackup.tasks -import arrow.core.Try import im.vector.matrix.android.internal.crypto.keysbackup.api.RoomKeysApi import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.KeysVersionResult import im.vector.matrix.android.internal.network.executeRequest @@ -29,7 +28,7 @@ internal class DefaultGetKeysBackupVersionTask @Inject constructor(private val r : GetKeysBackupVersionTask { - override suspend fun execute(params: String): Try { + override suspend fun execute(params: String): KeysVersionResult { return executeRequest { apiCall = roomKeysApi.getKeysBackupVersion(params) } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/GetRoomSessionDataTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/GetRoomSessionDataTask.kt index 253a2a5a..a36850ba 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/GetRoomSessionDataTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/GetRoomSessionDataTask.kt @@ -16,11 +16,9 @@ package im.vector.matrix.android.internal.crypto.keysbackup.tasks -import arrow.core.Try import im.vector.matrix.android.internal.crypto.keysbackup.api.RoomKeysApi import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.KeyBackupData import im.vector.matrix.android.internal.network.executeRequest -import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.task.Task import javax.inject.Inject @@ -35,7 +33,7 @@ internal interface GetRoomSessionDataTask : Task { + override suspend fun execute(params: GetRoomSessionDataTask.Params): KeyBackupData { return executeRequest { apiCall = roomKeysApi.getRoomSessionData( params.roomId, diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/GetRoomSessionsDataTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/GetRoomSessionsDataTask.kt index c5a6d19a..e8888f25 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/GetRoomSessionsDataTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/GetRoomSessionsDataTask.kt @@ -16,11 +16,9 @@ package im.vector.matrix.android.internal.crypto.keysbackup.tasks -import arrow.core.Try import im.vector.matrix.android.internal.crypto.keysbackup.api.RoomKeysApi import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.RoomKeysBackupData import im.vector.matrix.android.internal.network.executeRequest -import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.task.Task import javax.inject.Inject @@ -35,7 +33,7 @@ internal interface GetRoomSessionsDataTask : Task { + override suspend fun execute(params: GetRoomSessionsDataTask.Params): RoomKeysBackupData { return executeRequest { apiCall = roomKeysApi.getRoomSessionsData( params.roomId, diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/GetSessionsDataTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/GetSessionsDataTask.kt index 837e3eb8..d78159d9 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/GetSessionsDataTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/GetSessionsDataTask.kt @@ -16,11 +16,9 @@ package im.vector.matrix.android.internal.crypto.keysbackup.tasks -import arrow.core.Try import im.vector.matrix.android.internal.crypto.keysbackup.api.RoomKeysApi import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.KeysBackupData import im.vector.matrix.android.internal.network.executeRequest -import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.task.Task import javax.inject.Inject @@ -33,10 +31,9 @@ internal interface GetSessionsDataTask : Task { + override suspend fun execute(params: GetSessionsDataTask.Params): KeysBackupData { return executeRequest { - apiCall = roomKeysApi.getSessionsData( - params.version) + apiCall = roomKeysApi.getSessionsData(params.version) } } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/StoreRoomSessionDataTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/StoreRoomSessionDataTask.kt index 748dd106..b25c327e 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/StoreRoomSessionDataTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/StoreRoomSessionDataTask.kt @@ -16,12 +16,10 @@ package im.vector.matrix.android.internal.crypto.keysbackup.tasks -import arrow.core.Try import im.vector.matrix.android.internal.crypto.keysbackup.api.RoomKeysApi -import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.KeyBackupData import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.BackupKeysResult +import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.KeyBackupData import im.vector.matrix.android.internal.network.executeRequest -import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.task.Task import javax.inject.Inject @@ -37,7 +35,7 @@ internal interface StoreRoomSessionDataTask : Task { + override suspend fun execute(params: StoreRoomSessionDataTask.Params): BackupKeysResult { return executeRequest { apiCall = roomKeysApi.storeRoomSessionData( params.roomId, diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/StoreRoomSessionsDataTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/StoreRoomSessionsDataTask.kt index 1799df33..8290b2af 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/StoreRoomSessionsDataTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/StoreRoomSessionsDataTask.kt @@ -16,12 +16,10 @@ package im.vector.matrix.android.internal.crypto.keysbackup.tasks -import arrow.core.Try import im.vector.matrix.android.internal.crypto.keysbackup.api.RoomKeysApi -import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.RoomKeysBackupData import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.BackupKeysResult +import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.RoomKeysBackupData import im.vector.matrix.android.internal.network.executeRequest -import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.task.Task import javax.inject.Inject @@ -36,7 +34,7 @@ internal interface StoreRoomSessionsDataTask : Task { + override suspend fun execute(params: StoreRoomSessionsDataTask.Params): BackupKeysResult { return executeRequest { apiCall = roomKeysApi.storeRoomSessionsData( params.roomId, diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/StoreSessionsDataTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/StoreSessionsDataTask.kt index 977f8114..f3467edb 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/StoreSessionsDataTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/StoreSessionsDataTask.kt @@ -16,12 +16,10 @@ package im.vector.matrix.android.internal.crypto.keysbackup.tasks -import arrow.core.Try import im.vector.matrix.android.internal.crypto.keysbackup.api.RoomKeysApi -import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.KeysBackupData import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.BackupKeysResult +import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.KeysBackupData import im.vector.matrix.android.internal.network.executeRequest -import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.task.Task import javax.inject.Inject @@ -35,7 +33,7 @@ internal interface StoreSessionsDataTask : Task { + override suspend fun execute(params: StoreSessionsDataTask.Params): BackupKeysResult { return executeRequest { apiCall = roomKeysApi.storeSessionsData( params.version, diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/UpdateKeysBackupVersionTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/UpdateKeysBackupVersionTask.kt index 74c39485..28aedaf8 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/UpdateKeysBackupVersionTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/UpdateKeysBackupVersionTask.kt @@ -16,7 +16,6 @@ package im.vector.matrix.android.internal.crypto.keysbackup.tasks -import arrow.core.Try import im.vector.matrix.android.internal.crypto.keysbackup.api.RoomKeysApi import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.UpdateKeysBackupVersionBody import im.vector.matrix.android.internal.network.executeRequest @@ -34,7 +33,7 @@ internal class DefaultUpdateKeysBackupVersionTask @Inject constructor(private va : UpdateKeysBackupVersionTask { - override suspend fun execute(params: UpdateKeysBackupVersionTask.Params): Try { + override suspend fun execute(params: UpdateKeysBackupVersionTask.Params) { return executeRequest { apiCall = roomKeysApi.updateKeysBackupVersion(params.version, params.keysBackupVersionBody) } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/ClaimOneTimeKeysForUsersDeviceTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/ClaimOneTimeKeysForUsersDeviceTask.kt index d60105e0..23b0f958 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/ClaimOneTimeKeysForUsersDeviceTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/ClaimOneTimeKeysForUsersDeviceTask.kt @@ -16,7 +16,6 @@ package im.vector.matrix.android.internal.crypto.tasks -import arrow.core.Try import im.vector.matrix.android.internal.crypto.api.CryptoApi import im.vector.matrix.android.internal.crypto.model.MXKey import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap @@ -37,35 +36,30 @@ internal interface ClaimOneTimeKeysForUsersDeviceTask : Task> { + override suspend fun execute(params: ClaimOneTimeKeysForUsersDeviceTask.Params): MXUsersDevicesMap { val body = KeysClaimBody(oneTimeKeys = params.usersDevicesKeyTypesMap.map) - return executeRequest { + val keysClaimResponse = executeRequest { apiCall = cryptoApi.claimOneTimeKeysForUsersDevices(body) - }.flatMap { keysClaimResponse -> - Try { - val map = MXUsersDevicesMap() + } + val map = MXUsersDevicesMap() + keysClaimResponse.oneTimeKeys?.let { oneTimeKeys -> + for (userId in oneTimeKeys.keys) { + val mapByUserId = oneTimeKeys[userId] - keysClaimResponse.oneTimeKeys?.let { oneTimeKeys -> - for (userId in oneTimeKeys.keys) { - val mapByUserId = oneTimeKeys[userId] + if (mapByUserId != null) { + for (deviceId in mapByUserId.keys) { + val mxKey = MXKey.from(mapByUserId[deviceId]) - if (mapByUserId != null) { - for (deviceId in mapByUserId.keys) { - val mxKey = MXKey.from(mapByUserId[deviceId]) - - if (mxKey != null) { - map.setObject(userId, deviceId, mxKey) - } else { - Timber.e("## claimOneTimeKeysForUsersDevices : fail to create a MXKey") - } - } + if (mxKey != null) { + map.setObject(userId, deviceId, mxKey) + } else { + Timber.e("## claimOneTimeKeysForUsersDevices : fail to create a MXKey") } } } - - map } } + return map } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/DeleteDeviceTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/DeleteDeviceTask.kt index 0f93e213..3e6e4d8c 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/DeleteDeviceTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/DeleteDeviceTask.kt @@ -16,16 +16,12 @@ package im.vector.matrix.android.internal.crypto.tasks -import arrow.core.Try -import arrow.core.failure -import arrow.core.recoverWith import im.vector.matrix.android.api.failure.Failure import im.vector.matrix.android.internal.auth.registration.RegistrationFlowResponse import im.vector.matrix.android.internal.crypto.api.CryptoApi import im.vector.matrix.android.internal.crypto.model.rest.DeleteDeviceParams import im.vector.matrix.android.internal.di.MoshiProvider import im.vector.matrix.android.internal.network.executeRequest -import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.task.Task import javax.inject.Inject @@ -38,10 +34,12 @@ internal interface DeleteDeviceTask : Task { internal class DefaultDeleteDeviceTask @Inject constructor(private val cryptoApi: CryptoApi) : DeleteDeviceTask { - override suspend fun execute(params: DeleteDeviceTask.Params): Try { - return executeRequest { - apiCall = cryptoApi.deleteDevice(params.deviceId, DeleteDeviceParams()) - }.recoverWith { throwable -> + override suspend fun execute(params: DeleteDeviceTask.Params) { + try { + executeRequest { + apiCall = cryptoApi.deleteDevice(params.deviceId, DeleteDeviceParams()) + } + } catch (throwable: Throwable) { if (throwable is Failure.OtherServerError && throwable.httpCode == 401) { // Parse to get a RegistrationFlowResponse val registrationFlowResponse = try { @@ -51,17 +49,16 @@ internal class DefaultDeleteDeviceTask @Inject constructor(private val cryptoApi } catch (e: Exception) { null } - // check if the server response can be casted if (registrationFlowResponse != null) { - Failure.RegistrationFlowError(registrationFlowResponse).failure() + throw Failure.RegistrationFlowError(registrationFlowResponse) } else { - throwable.failure() + throw throwable } } else { // Other error - throwable.failure() + throw throwable } } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/DeleteDeviceWithUserPasswordTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/DeleteDeviceWithUserPasswordTask.kt index 2476f242..060b57ba 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/DeleteDeviceWithUserPasswordTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/DeleteDeviceWithUserPasswordTask.kt @@ -16,7 +16,6 @@ package im.vector.matrix.android.internal.crypto.tasks -import arrow.core.Try import im.vector.matrix.android.api.auth.data.Credentials import im.vector.matrix.android.internal.auth.data.LoginFlowTypes import im.vector.matrix.android.internal.crypto.api.CryptoApi @@ -38,7 +37,7 @@ internal class DefaultDeleteDeviceWithUserPasswordTask @Inject constructor(priva private val credentials: Credentials) : DeleteDeviceWithUserPasswordTask { - override suspend fun execute(params: DeleteDeviceWithUserPasswordTask.Params): Try { + override suspend fun execute(params: DeleteDeviceWithUserPasswordTask.Params) { return executeRequest { apiCall = cryptoApi.deleteDevice(params.deviceId, DeleteDeviceParams() .apply { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/DownloadKeysForUsersTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/DownloadKeysForUsersTask.kt index 00ea7178..92908071 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/DownloadKeysForUsersTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/DownloadKeysForUsersTask.kt @@ -17,12 +17,10 @@ package im.vector.matrix.android.internal.crypto.tasks import android.text.TextUtils -import arrow.core.Try import im.vector.matrix.android.internal.crypto.api.CryptoApi import im.vector.matrix.android.internal.crypto.model.rest.KeysQueryBody import im.vector.matrix.android.internal.crypto.model.rest.KeysQueryResponse import im.vector.matrix.android.internal.network.executeRequest -import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.task.Task import java.util.* import javax.inject.Inject @@ -38,7 +36,7 @@ internal interface DownloadKeysForUsersTask : Task { + override suspend fun execute(params: DownloadKeysForUsersTask.Params): KeysQueryResponse { val downloadQuery = HashMap>() if (null != params.userIds) { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/GetDevicesTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/GetDevicesTask.kt index 3779a102..d6e82adb 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/GetDevicesTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/GetDevicesTask.kt @@ -16,11 +16,9 @@ package im.vector.matrix.android.internal.crypto.tasks -import arrow.core.Try import im.vector.matrix.android.internal.crypto.api.CryptoApi import im.vector.matrix.android.internal.crypto.model.rest.DevicesListResponse import im.vector.matrix.android.internal.network.executeRequest -import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.task.Task import javax.inject.Inject @@ -29,7 +27,7 @@ internal interface GetDevicesTask : Task internal class DefaultGetDevicesTask @Inject constructor(private val cryptoApi: CryptoApi) : GetDevicesTask { - override suspend fun execute(params: Unit): Try { + override suspend fun execute(params: Unit): DevicesListResponse { return executeRequest { apiCall = cryptoApi.getDevices() } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/GetKeyChangesTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/GetKeyChangesTask.kt index 21eabc22..42c36bd1 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/GetKeyChangesTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/GetKeyChangesTask.kt @@ -16,11 +16,9 @@ package im.vector.matrix.android.internal.crypto.tasks -import arrow.core.Try import im.vector.matrix.android.internal.crypto.api.CryptoApi import im.vector.matrix.android.internal.crypto.model.rest.KeyChangesResponse import im.vector.matrix.android.internal.network.executeRequest -import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.task.Task import javax.inject.Inject @@ -36,10 +34,9 @@ internal interface GetKeyChangesTask : Task { + override suspend fun execute(params: GetKeyChangesTask.Params): KeyChangesResponse { return executeRequest { - apiCall = cryptoApi.getKeyChanges(params.from, - params.to) + apiCall = cryptoApi.getKeyChanges(params.from, params.to) } } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/SendToDeviceTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/SendToDeviceTask.kt index 27559f74..1865c633 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/SendToDeviceTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/SendToDeviceTask.kt @@ -16,12 +16,10 @@ package im.vector.matrix.android.internal.crypto.tasks -import arrow.core.Try import im.vector.matrix.android.internal.crypto.api.CryptoApi import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap import im.vector.matrix.android.internal.crypto.model.rest.SendToDeviceBody import im.vector.matrix.android.internal.network.executeRequest -import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.task.Task import java.util.* import javax.inject.Inject @@ -40,7 +38,7 @@ internal interface SendToDeviceTask : Task { internal class DefaultSendToDeviceTask @Inject constructor(private val cryptoApi: CryptoApi) : SendToDeviceTask { - override suspend fun execute(params: SendToDeviceTask.Params): Try { + override suspend fun execute(params: SendToDeviceTask.Params) { val sendToDeviceBody = SendToDeviceBody() sendToDeviceBody.messages = params.contentMap.map diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/SetDeviceNameTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/SetDeviceNameTask.kt index 04613ddd..1abef576 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/SetDeviceNameTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/SetDeviceNameTask.kt @@ -17,11 +17,9 @@ package im.vector.matrix.android.internal.crypto.tasks import android.text.TextUtils -import arrow.core.Try import im.vector.matrix.android.internal.crypto.api.CryptoApi import im.vector.matrix.android.internal.crypto.model.rest.UpdateDeviceInfoBody import im.vector.matrix.android.internal.network.executeRequest -import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.task.Task import javax.inject.Inject @@ -37,11 +35,10 @@ internal interface SetDeviceNameTask : Task { internal class DefaultSetDeviceNameTask @Inject constructor(private val cryptoApi: CryptoApi) : SetDeviceNameTask { - override suspend fun execute(params: SetDeviceNameTask.Params): Try { + override suspend fun execute(params: SetDeviceNameTask.Params) { val body = UpdateDeviceInfoBody( displayName = if (TextUtils.isEmpty(params.deviceName)) "" else params.deviceName ) - return executeRequest { apiCall = cryptoApi.updateDeviceInfo(params.deviceId, body) } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/UploadKeysTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/UploadKeysTask.kt index 96c3a558..fd7a5e8e 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/UploadKeysTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/UploadKeysTask.kt @@ -16,14 +16,12 @@ package im.vector.matrix.android.internal.crypto.tasks -import arrow.core.Try import im.vector.matrix.android.api.util.JsonDict import im.vector.matrix.android.internal.crypto.api.CryptoApi -import im.vector.matrix.android.internal.crypto.model.rest.KeysUploadResponse import im.vector.matrix.android.internal.crypto.model.rest.DeviceKeys import im.vector.matrix.android.internal.crypto.model.rest.KeysUploadBody +import im.vector.matrix.android.internal.crypto.model.rest.KeysUploadResponse import im.vector.matrix.android.internal.network.executeRequest -import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.task.Task import im.vector.matrix.android.internal.util.convertToUTF8 import javax.inject.Inject @@ -41,7 +39,7 @@ internal interface UploadKeysTask : Task { + override suspend fun execute(params: UploadKeysTask.Params): KeysUploadResponse { val encodedDeviceId = convertToUTF8(params.deviceId) val body = KeysUploadBody() diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/DefaultSasVerificationService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/DefaultSasVerificationService.kt index 54a4e14d..35732805 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/DefaultSasVerificationService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/DefaultSasVerificationService.kt @@ -219,17 +219,20 @@ internal class DefaultSasVerificationService @Inject constructor(private val cre startReq: KeyVerificationStart, success: (MXUsersDevicesMap) -> Unit, error: () -> Unit) { - deviceListManager.downloadKeys(listOf(otherUserId), true) - .fold( - { error() }, - { - if (it.getUserDeviceIds(otherUserId)?.contains(startReq.fromDevice) == true) { - success(it) - } else { - error() - } - } - ) + runCatching { + deviceListManager.downloadKeys(listOf(otherUserId), true) + }.fold( + { + if (it.getUserDeviceIds(otherUserId)?.contains(startReq.fromDevice) == true) { + success(it) + } else { + error() + } + }, + { + error() + } + ) } private suspend fun onCancelReceived(event: Event) { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/RealmExtensions.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/RealmExtensions.kt new file mode 100644 index 00000000..872b0b1d --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/RealmExtensions.kt @@ -0,0 +1,46 @@ +/* + * 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.database + +import io.realm.Realm +import kotlinx.coroutines.suspendCancellableCoroutine +import timber.log.Timber +import kotlin.coroutines.resume +import kotlin.coroutines.resumeWithException + + +suspend fun Realm.awaitTransaction(transaction: (realm: Realm) -> Unit) { + return suspendCancellableCoroutine { continuation -> + beginTransaction() + try { + transaction(this) + commitTransaction() + continuation.resume(Unit) + } catch (e: Throwable) { + if (isInTransaction) { + cancelTransaction() + } else { + Timber.w("Could not cancel transaction, not currently in a transaction.") + } + continuation.resumeWithException(e) + } + continuation.invokeOnCancellation { + if (isInTransaction) { + cancelTransaction() + } + } + } +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/extensions/Result.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/extensions/Result.kt new file mode 100644 index 00000000..7d4ba7aa --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/extensions/Result.kt @@ -0,0 +1,25 @@ +/* + + * 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.extensions + +import im.vector.matrix.android.api.MatrixCallback + +fun Result.foldToCallback(callback: MatrixCallback): Unit = fold( + { callback.onSuccess(it) }, + { callback.onFailure(it) } +) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/network/Request.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/network/Request.kt index 4dfc5810..3d1e433b 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/network/Request.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/network/Request.kt @@ -16,9 +16,6 @@ package im.vector.matrix.android.internal.network -import arrow.core.Try -import arrow.core.failure -import arrow.core.recoverWith import com.squareup.moshi.JsonDataException import com.squareup.moshi.Moshi import im.vector.matrix.android.api.failure.Failure @@ -36,8 +33,8 @@ internal class Request { private val moshi: Moshi = MoshiProvider.providesMoshi() lateinit var apiCall: Call - suspend fun execute(): Try { - return Try { + suspend fun execute(): DATA { + return try { val response = apiCall.awaitResponse() if (response.isSuccessful) { response.body() @@ -45,13 +42,13 @@ internal class Request { } else { throw manageFailure(response.errorBody(), response.code()) } - }.recoverWith { - when (it) { - is IOException -> Failure.NetworkConnection(it) + } catch (exception: Throwable) { + throw when (exception) { + is IOException -> Failure.NetworkConnection(exception) is Failure.ServerError, - is Failure.OtherServerError -> it - else -> Failure.Unknown(it) - }.failure() + is Failure.OtherServerError -> exception + else -> Failure.Unknown(exception) + } } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/DefaultSession.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/DefaultSession.kt index 09baebb2..cc68c087 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/DefaultSession.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/DefaultSession.kt @@ -47,6 +47,7 @@ import im.vector.matrix.android.internal.session.sync.job.SyncWorker import im.vector.matrix.android.internal.worker.WorkManagerUtil import timber.log.Timber import javax.inject.Inject +import javax.inject.Provider @SessionScope internal class DefaultSession @Inject constructor(override val sessionParams: SessionParams, @@ -64,7 +65,7 @@ internal class DefaultSession @Inject constructor(override val sessionParams: Se private val pushersService: Lazy, private val cryptoService: Lazy, private val fileService: Lazy, - private val syncThread: SyncThread, + private val syncThreadProvider: Provider, private val contentUrlResolver: ContentUrlResolver, private val contentUploadProgressTracker: ContentUploadStateTracker, private val initialSyncProgressService: Lazy) @@ -84,6 +85,7 @@ internal class DefaultSession @Inject constructor(override val sessionParams: Se private var isOpen = false + private var syncThread: SyncThread? = null @MainThread override fun open() { @@ -105,21 +107,23 @@ internal class DefaultSession @Inject constructor(override val sessionParams: Se SyncWorker.stopAnyBackgroundSync(context) } - override fun startSync(fromForeground : Boolean) { + override fun startSync(fromForeground: Boolean) { Timber.i("Starting sync thread") assert(isOpen) - syncThread.setInitialForeground(fromForeground) - if (!syncThread.isAlive) { - syncThread.start() + val localSyncThread = getSyncThread() + localSyncThread.setInitialForeground(fromForeground) + if (!localSyncThread.isAlive) { + localSyncThread.start() } else { - syncThread.restart() + localSyncThread.restart() Timber.w("Attempt to start an already started thread") } } override fun stopSync() { assert(isOpen) - syncThread.kill() + syncThread?.kill() + syncThread = null } override fun close() { @@ -131,7 +135,13 @@ internal class DefaultSession @Inject constructor(override val sessionParams: Se } override fun syncState(): LiveData { - return syncThread.liveState() + return getSyncThread().liveState() + } + + private fun getSyncThread(): SyncThread { + return syncThread ?: syncThreadProvider.get().also { + syncThread = it + } } @MainThread @@ -172,6 +182,22 @@ internal class DefaultSession @Inject constructor(override val sessionParams: Se }) } + override fun clearCache(callback: MatrixCallback) { + stopSync() + stopAnyBackgroundSync() + cacheService.get().clearCache(object : MatrixCallback { + override fun onSuccess(data: Unit) { + startSync(true) + callback.onSuccess(data) + } + + override fun onFailure(failure: Throwable) { + startSync(true) + callback.onFailure(failure) + } + }) + } + override fun contentUrlResolver() = contentUrlResolver override fun contentUploadProgressTracker() = contentUploadProgressTracker diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/cache/ClearCacheTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/cache/ClearCacheTask.kt index 0dd39c10..7d101031 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/cache/ClearCacheTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/cache/ClearCacheTask.kt @@ -16,27 +16,21 @@ package im.vector.matrix.android.internal.session.cache -import arrow.core.Try -import im.vector.matrix.android.internal.session.SessionScope +import im.vector.matrix.android.internal.database.awaitTransaction import im.vector.matrix.android.internal.task.Task import io.realm.Realm import io.realm.RealmConfiguration import javax.inject.Inject -import javax.inject.Named internal interface ClearCacheTask : Task internal class RealmClearCacheTask @Inject constructor(private val realmConfiguration: RealmConfiguration) : ClearCacheTask { - override suspend fun execute(params: Unit): Try { - return Try { - val realm = Realm.getInstance(realmConfiguration) - - realm.executeTransaction { - it.deleteAll() - } - - realm.close() + override suspend fun execute(params: Unit) { + val realm = Realm.getInstance(realmConfiguration) + realm.awaitTransaction { + it.deleteAll() } + realm.close() } } \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/cache/DefaultCacheService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/cache/DefaultCacheService.kt index c23c9eea..336a7a63 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/cache/DefaultCacheService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/cache/DefaultCacheService.kt @@ -23,10 +23,12 @@ import im.vector.matrix.android.internal.task.TaskExecutor import im.vector.matrix.android.internal.task.toConfigurableTask import javax.inject.Inject -internal class DefaultCacheService @Inject constructor(@SessionDatabase private val clearCacheTask: ClearCacheTask, +internal class DefaultCacheService @Inject constructor(@SessionDatabase + private val clearCacheTask: ClearCacheTask, private val taskExecutor: TaskExecutor) : CacheService { override fun clearCache(callback: MatrixCallback) { + taskExecutor.cancelAll() clearCacheTask .toConfigurableTask() .dispatchTo(callback) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/filter/DefaultSaveFilterTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/filter/DefaultSaveFilterTask.kt index 5fa5d0a2..a5da026a 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/filter/DefaultSaveFilterTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/filter/DefaultSaveFilterTask.kt @@ -16,7 +16,6 @@ package im.vector.matrix.android.internal.session.filter -import arrow.core.Try import im.vector.matrix.android.api.auth.data.SessionParams import im.vector.matrix.android.internal.network.executeRequest import im.vector.matrix.android.internal.task.Task @@ -39,15 +38,12 @@ internal class DefaultSaveFilterTask @Inject constructor(private val sessionPara private val filterRepository: FilterRepository ) : SaveFilterTask { - override suspend fun execute(params: SaveFilterTask.Params): Try { - return executeRequest { + override suspend fun execute(params: SaveFilterTask.Params) { + val filterResponse = executeRequest { // TODO auto retry apiCall = filterAPI.uploadFilter(sessionParams.credentials.userId, params.filter) - }.flatMap { filterResponse -> - Try { - filterRepository.storeFilterId(params.filter, filterResponse.filterId) - } } + filterRepository.storeFilterId(params.filter, filterResponse.filterId) } } \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/DefaultGetGroupDataTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/DefaultGetGroupDataTask.kt index ed8c11bc..6964ccf8 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/DefaultGetGroupDataTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/DefaultGetGroupDataTask.kt @@ -16,10 +16,6 @@ package im.vector.matrix.android.internal.session.group -import arrow.core.Try -import arrow.core.fix -import arrow.instances.`try`.monad.monad -import arrow.typeclasses.binding import com.zhuinden.monarchy.Monarchy import im.vector.matrix.android.internal.database.model.GroupSummaryEntity import im.vector.matrix.android.internal.database.query.where @@ -28,8 +24,6 @@ import im.vector.matrix.android.internal.session.group.model.GroupRooms import im.vector.matrix.android.internal.session.group.model.GroupSummaryResponse import im.vector.matrix.android.internal.session.group.model.GroupUsers import im.vector.matrix.android.internal.task.Task -import im.vector.matrix.android.internal.util.tryTransactionSync -import io.realm.kotlin.createObject import javax.inject.Inject internal interface GetGroupDataTask : Task { @@ -43,7 +37,7 @@ internal class DefaultGetGroupDataTask @Inject constructor( private val monarchy: Monarchy ) : GetGroupDataTask { - override suspend fun execute(params: GetGroupDataTask.Params): Try { + override suspend fun execute(params: GetGroupDataTask.Params) { val groupId = params.groupId val groupSummary = executeRequest { apiCall = groupAPI.getSummary(groupId) @@ -54,20 +48,18 @@ internal class DefaultGetGroupDataTask @Inject constructor( val groupUsers = executeRequest { apiCall = groupAPI.getUsers(groupId) } - return Try.monad().binding { - insertInDb(groupSummary.bind(), groupRooms.bind(), groupUsers.bind(), groupId).bind() - }.fix() + insertInDb(groupSummary, groupRooms, groupUsers, groupId) } private fun insertInDb(groupSummary: GroupSummaryResponse, groupRooms: GroupRooms, groupUsers: GroupUsers, - groupId: String): Try { - return monarchy - .tryTransactionSync { realm -> + groupId: String) { + monarchy + .writeAsync { realm -> val groupSummaryEntity = GroupSummaryEntity.where(realm, groupId).findFirst() - ?: realm.createObject(groupId) + ?: realm.createObject(GroupSummaryEntity::class.java, groupId) groupSummaryEntity.avatarUrl = groupSummary.profile?.avatarUrl ?: "" val name = groupSummary.profile?.name @@ -82,7 +74,6 @@ internal class DefaultGetGroupDataTask @Inject constructor( val userIds = groupUsers.users.map { it.userId } groupSummaryEntity.userIds.clear() groupSummaryEntity.userIds.addAll(userIds) - } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/GetGroupDataWorker.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/GetGroupDataWorker.kt index 081739cb..913a468b 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/GetGroupDataWorker.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/GetGroupDataWorker.kt @@ -19,7 +19,6 @@ package im.vector.matrix.android.internal.session.group import android.content.Context import androidx.work.CoroutineWorker import androidx.work.WorkerParameters -import arrow.core.Try import com.squareup.moshi.JsonClass import im.vector.matrix.android.internal.worker.SessionWorkerParams import im.vector.matrix.android.internal.worker.WorkerParamsFactory @@ -43,16 +42,15 @@ internal class GetGroupDataWorker(context: Context, params: WorkerParameters) : val sessionComponent = getSessionComponent(params.userId) ?: return Result.success() sessionComponent.inject(this) - val results = params.groupIds.map { groupId -> - fetchGroupData(groupId) + runCatching { fetchGroupData(groupId) } } - val isSuccessful = results.none { it.isFailure() } + val isSuccessful = results.none { it.isFailure } return if (isSuccessful) Result.success() else Result.retry() } - private suspend fun fetchGroupData(groupId: String): Try { - return getGroupDataTask.execute(GetGroupDataTask.Params(groupId)) + private suspend fun fetchGroupData(groupId: String) { + getGroupDataTask.execute(GetGroupDataTask.Params(groupId)) } } \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/notification/ProcessEventForPushTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/notification/ProcessEventForPushTask.kt index d196e563..a434c6e9 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/notification/ProcessEventForPushTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/notification/ProcessEventForPushTask.kt @@ -16,7 +16,6 @@ package im.vector.matrix.android.internal.session.notification -import arrow.core.Try import im.vector.matrix.android.api.auth.data.SessionParams import im.vector.matrix.android.api.pushrules.rest.PushRule import im.vector.matrix.android.api.session.events.model.Event @@ -41,48 +40,45 @@ internal class DefaultProcessEventForPushTask @Inject constructor( private val sessionParams: SessionParams ) : ProcessEventForPushTask { - - override suspend fun execute(params: ProcessEventForPushTask.Params): Try { - return Try { - // Handle left rooms - params.syncResponse.leave.keys.forEach { - defaultPushRuleService.dispatchRoomLeft(it) - } - val newJoinEvents = params.syncResponse.join - .map { entries -> - entries.value.timeline?.events?.map { it.copy(roomId = entries.key) } - } - .fold(emptyList(), { acc, next -> - acc + (next ?: emptyList()) - }) - val inviteEvents = params.syncResponse.invite - .map { entries -> - entries.value.inviteState?.events?.map { it.copy(roomId = entries.key) } - } - .fold(emptyList(), { acc, next -> - acc + (next ?: emptyList()) - }) - val allEvents = (newJoinEvents + inviteEvents).filter { event -> - when (event.type) { - EventType.MESSAGE, - EventType.REDACTION, - EventType.ENCRYPTED, - EventType.STATE_ROOM_MEMBER -> true - else -> false - } - }.filter { - it.senderId != sessionParams.credentials.userId - } - Timber.v("[PushRules] Found ${allEvents.size} out of ${(newJoinEvents + inviteEvents).size}" + - " to check for push rules with ${params.rules.size} rules") - allEvents.forEach { event -> - fulfilledBingRule(event, params.rules)?.let { - Timber.v("[PushRules] Rule $it match for event ${event.eventId}") - defaultPushRuleService.dispatchBing(event, it) - } - } - defaultPushRuleService.dispatchFinish() + override suspend fun execute(params: ProcessEventForPushTask.Params) { + // Handle left rooms + params.syncResponse.leave.keys.forEach { + defaultPushRuleService.dispatchRoomLeft(it) } + val newJoinEvents = params.syncResponse.join + .map { entries -> + entries.value.timeline?.events?.map { it.copy(roomId = entries.key) } + } + .fold(emptyList(), { acc, next -> + acc + (next ?: emptyList()) + }) + val inviteEvents = params.syncResponse.invite + .map { entries -> + entries.value.inviteState?.events?.map { it.copy(roomId = entries.key) } + } + .fold(emptyList(), { acc, next -> + acc + (next ?: emptyList()) + }) + val allEvents = (newJoinEvents + inviteEvents).filter { event -> + when (event.type) { + EventType.MESSAGE, + EventType.REDACTION, + EventType.ENCRYPTED, + EventType.STATE_ROOM_MEMBER -> true + else -> false + } + }.filter { + it.senderId != sessionParams.credentials.userId + } + Timber.v("[PushRules] Found ${allEvents.size} out of ${(newJoinEvents + inviteEvents).size}" + + " to check for push rules with ${params.rules.size} rules") + allEvents.forEach { event -> + fulfilledBingRule(event, params.rules)?.let { + Timber.v("[PushRules] Rule $it match for event ${event.eventId}") + defaultPushRuleService.dispatchBing(event, it) + } + } + defaultPushRuleService.dispatchFinish() } private fun fulfilledBingRule(event: Event, rules: List): PushRule? { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/pushers/AddHttpPusherWorker.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/pushers/AddHttpPusherWorker.kt index 6ac59607..bc587d91 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/pushers/AddHttpPusherWorker.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/pushers/AddHttpPusherWorker.kt @@ -26,6 +26,7 @@ import im.vector.matrix.android.internal.database.mapper.toEntity import im.vector.matrix.android.internal.database.model.PusherEntity import im.vector.matrix.android.internal.database.query.where import im.vector.matrix.android.internal.network.executeRequest +import im.vector.matrix.android.internal.util.awaitTransaction import im.vector.matrix.android.internal.worker.WorkerParamsFactory import im.vector.matrix.android.internal.worker.getSessionComponent import javax.inject.Inject @@ -55,15 +56,14 @@ internal class AddHttpPusherWorker(context: Context, params: WorkerParameters) if (pusher.pushKey.isBlank()) { return Result.failure() } - - val result = executeRequest { - apiCall = pushersAPI.setPusher(pusher) - } - return result.fold({ - when (it) { + return try { + setPusher(pusher, params.userId) + Result.success() + } catch (exception: Throwable) { + when (exception) { is Failure.NetworkConnection -> Result.retry() else -> { - monarchy.runTransactionSync { realm -> + monarchy.awaitTransaction { realm -> PusherEntity.where(realm, params.userId, pusher.pushKey).findFirst()?.let { //update it it.state = PusherState.FAILED_TO_REGISTER @@ -73,28 +73,31 @@ internal class AddHttpPusherWorker(context: Context, params: WorkerParameters) Result.failure() } } - }, { - monarchy.runTransactionSync { realm -> - val echo = PusherEntity.where(realm, params.userId, pusher.pushKey).findFirst() - if (echo != null) { - //update it - echo.appDisplayName = pusher.appDisplayName - echo.appId = pusher.appId - echo.kind = pusher.kind - echo.lang = pusher.lang - echo.profileTag = pusher.profileTag - echo.data?.format = pusher.data?.format - echo.data?.url = pusher.data?.url - echo.state = PusherState.REGISTERED - } else { - pusher.toEntity(params.userId).also { - it.state = PusherState.REGISTERED - realm.insertOrUpdate(it) - } + } + } + + private suspend fun setPusher(pusher: JsonPusher, userId: String) { + executeRequest { + apiCall = pushersAPI.setPusher(pusher) + } + monarchy.awaitTransaction { realm -> + val echo = PusherEntity.where(realm, userId, pusher.pushKey).findFirst() + if (echo != null) { + //update it + echo.appDisplayName = pusher.appDisplayName + echo.appId = pusher.appId + echo.kind = pusher.kind + echo.lang = pusher.lang + echo.profileTag = pusher.profileTag + echo.data?.format = pusher.data?.format + echo.data?.url = pusher.data?.url + echo.state = PusherState.REGISTERED + } else { + pusher.toEntity(userId).also { + it.state = PusherState.REGISTERED + realm.insertOrUpdate(it) } } - Result.success() - - }) + } } } \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/pushers/GetPushRulesTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/pushers/GetPushRulesTask.kt index cbd8dcac..627c6f89 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/pushers/GetPushRulesTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/pushers/GetPushRulesTask.kt @@ -15,7 +15,6 @@ */ package im.vector.matrix.android.internal.session.pushers -import arrow.core.Try import com.zhuinden.monarchy.Monarchy import im.vector.matrix.android.api.auth.data.SessionParams import im.vector.matrix.android.api.pushrules.rest.GetPushRulesResponse @@ -24,7 +23,7 @@ import im.vector.matrix.android.internal.database.model.PushRulesEntity import im.vector.matrix.android.internal.database.model.PusherEntityFields import im.vector.matrix.android.internal.network.executeRequest import im.vector.matrix.android.internal.task.Task -import im.vector.matrix.android.internal.util.tryTransactionSync +import im.vector.matrix.android.internal.util.awaitTransaction import javax.inject.Inject @@ -39,58 +38,57 @@ internal class DefaultGetPushRulesTask @Inject constructor(private val pushRules private val monarchy: Monarchy, private val sessionParams: SessionParams) : GetPushRulesTask { - override suspend fun execute(params: GetPushRulesTask.Params): Try { - return executeRequest { + override suspend fun execute(params: GetPushRulesTask.Params) { + val response = executeRequest { apiCall = pushRulesApi.getAllRules() - }.flatMap { response -> - val scope = params.scope - return monarchy.tryTransactionSync { realm -> - //clear existings? - //TODO - realm.where(PushRulesEntity::class.java) - .equalTo(PusherEntityFields.USER_ID, sessionParams.credentials.userId) - .findAll().deleteAllFromRealm() + } + val scope = params.scope + monarchy.awaitTransaction { realm -> + //clear existings? + //TODO + realm.where(PushRulesEntity::class.java) + .equalTo(PusherEntityFields.USER_ID, sessionParams.credentials.userId) + .findAll().deleteAllFromRealm() - val content = PushRulesEntity(sessionParams.credentials.userId, scope, "content") - response.global.content?.forEach { rule -> - PushRulesMapper.map(rule).also { - content.pushRules.add(it) - } + val content = PushRulesEntity(sessionParams.credentials.userId, scope, "content") + response.global.content?.forEach { rule -> + PushRulesMapper.map(rule).also { + content.pushRules.add(it) } - realm.insertOrUpdate(content) - - val override = PushRulesEntity(sessionParams.credentials.userId, scope, "override") - response.global.override?.forEach { rule -> - PushRulesMapper.map(rule).also { - override.pushRules.add(it) - } - } - realm.insertOrUpdate(override) - - val rooms = PushRulesEntity(sessionParams.credentials.userId, scope, "room") - response.global.room?.forEach { rule -> - PushRulesMapper.map(rule).also { - rooms.pushRules.add(it) - } - } - realm.insertOrUpdate(rooms) - - val senders = PushRulesEntity(sessionParams.credentials.userId, scope, "sender") - response.global.sender?.forEach { rule -> - PushRulesMapper.map(rule).also { - senders.pushRules.add(it) - } - } - realm.insertOrUpdate(senders) - - val underrides = PushRulesEntity(sessionParams.credentials.userId, scope, "underride") - response.global.underride?.forEach { rule -> - PushRulesMapper.map(rule).also { - underrides.pushRules.add(it) - } - } - realm.insertOrUpdate(underrides) } + realm.insertOrUpdate(content) + + val override = PushRulesEntity(sessionParams.credentials.userId, scope, "override") + response.global.override?.forEach { rule -> + PushRulesMapper.map(rule).also { + override.pushRules.add(it) + } + } + realm.insertOrUpdate(override) + + val rooms = PushRulesEntity(sessionParams.credentials.userId, scope, "room") + response.global.room?.forEach { rule -> + PushRulesMapper.map(rule).also { + rooms.pushRules.add(it) + } + } + realm.insertOrUpdate(rooms) + + val senders = PushRulesEntity(sessionParams.credentials.userId, scope, "sender") + response.global.sender?.forEach { rule -> + PushRulesMapper.map(rule).also { + senders.pushRules.add(it) + } + } + realm.insertOrUpdate(senders) + + val underrides = PushRulesEntity(sessionParams.credentials.userId, scope, "underride") + response.global.underride?.forEach { rule -> + PushRulesMapper.map(rule).also { + underrides.pushRules.add(it) + } + } + realm.insertOrUpdate(underrides) } } } \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/pushers/GetPushersTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/pushers/GetPushersTask.kt index 57ea8a38..b3199ea3 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/pushers/GetPushersTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/pushers/GetPushersTask.kt @@ -15,7 +15,6 @@ */ package im.vector.matrix.android.internal.session.pushers -import arrow.core.Try import com.zhuinden.monarchy.Monarchy import im.vector.matrix.android.api.auth.data.SessionParams import im.vector.matrix.android.api.session.pushers.PusherState @@ -24,7 +23,7 @@ import im.vector.matrix.android.internal.database.model.PusherEntity import im.vector.matrix.android.internal.database.model.PusherEntityFields import im.vector.matrix.android.internal.network.executeRequest import im.vector.matrix.android.internal.task.Task -import im.vector.matrix.android.internal.util.tryTransactionSync +import im.vector.matrix.android.internal.util.awaitTransaction import javax.inject.Inject internal interface GetPushersTask : Task @@ -33,20 +32,19 @@ internal class DefaultGetPusherTask @Inject constructor(private val pushersAPI: private val monarchy: Monarchy, private val sessionParams: SessionParams) : GetPushersTask { - override suspend fun execute(params: Unit): Try { - return executeRequest { + override suspend fun execute(params: Unit) { + val response = executeRequest { apiCall = pushersAPI.getPushers() - }.flatMap { response -> - monarchy.tryTransactionSync { realm -> - //clear existings? - realm.where(PusherEntity::class.java) - .equalTo(PusherEntityFields.USER_ID, sessionParams.credentials.userId) - .findAll().deleteAllFromRealm() - response.pushers?.forEach { jsonPusher -> - jsonPusher.toEntity(sessionParams.credentials.userId).also { - it.state = PusherState.REGISTERED - realm.insertOrUpdate(it) - } + } + monarchy.awaitTransaction { realm -> + //clear existings? + realm.where(PusherEntity::class.java) + .equalTo(PusherEntityFields.USER_ID, sessionParams.credentials.userId) + .findAll().deleteAllFromRealm() + response.pushers?.forEach { jsonPusher -> + jsonPusher.toEntity(sessionParams.credentials.userId).also { + it.state = PusherState.REGISTERED + realm.insertOrUpdate(it) } } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/pushers/RemovePusherTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/pushers/RemovePusherTask.kt index 92f62fe0..722700fc 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/pushers/RemovePusherTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/pushers/RemovePusherTask.kt @@ -16,16 +16,16 @@ package im.vector.matrix.android.internal.session.pushers -import arrow.core.Try import com.zhuinden.monarchy.Monarchy -import im.vector.matrix.android.api.session.pushers.Pusher import im.vector.matrix.android.api.session.pushers.PusherState +import im.vector.matrix.android.internal.database.awaitTransaction import im.vector.matrix.android.internal.database.mapper.asDomain import im.vector.matrix.android.internal.database.model.PusherEntity import im.vector.matrix.android.internal.database.query.where import im.vector.matrix.android.internal.network.executeRequest import im.vector.matrix.android.internal.task.Task -import im.vector.matrix.android.internal.util.tryTransactionSync +import im.vector.matrix.android.internal.util.awaitTransaction +import io.realm.Realm import javax.inject.Inject internal interface RemovePusherTask : Task { @@ -39,42 +39,32 @@ internal class DefaultRemovePusherTask @Inject constructor( private val monarchy: Monarchy ) : RemovePusherTask { - override suspend fun execute(params: RemovePusherTask.Params): Try { - return Try { - var existing: Pusher? = null - monarchy.runTransactionSync { - val existingEntity = PusherEntity.where(it, params.userId, params.pushKey).findFirst() - existingEntity?.state == PusherState.UNREGISTERING - existing = existingEntity?.asDomain() - } - if (existing == null) { - throw Exception("No existing pusher") - } else { - existing!! - } - }.flatMap { - executeRequest { - val deleteBody = JsonPusher( - pushKey = params.pushKey, - appId = params.pushAppId, - // kind null deletes the pusher - kind = null, - appDisplayName = it.appDisplayName ?: "", - deviceDisplayName = it.deviceDisplayName ?: "", - profileTag = it.profileTag ?: "", - lang = it.lang, - data = JsonPusherData(it.data.url, it.data.format), - append = false - ) - apiCall = pushersAPI.setPusher(deleteBody) - } - }.flatMap { - monarchy.tryTransactionSync { - val existing = PusherEntity.where(it, params.userId, params.pushKey).findFirst() - existing?.deleteFromRealm() + override suspend fun execute(params: RemovePusherTask.Params) { + val existing = Realm.getInstance(monarchy.realmConfiguration).use { realm -> + val existingEntity = PusherEntity.where(realm, params.userId, params.pushKey).findFirst() + realm.awaitTransaction { + existingEntity?.state = PusherState.UNREGISTERING } + existingEntity?.asDomain() + } ?: throw Exception("No existing pusher") + + val deleteBody = JsonPusher( + pushKey = params.pushKey, + appId = params.pushAppId, + // kind null deletes the pusher + kind = null, + appDisplayName = existing.appDisplayName ?: "", + deviceDisplayName = existing.deviceDisplayName ?: "", + profileTag = existing.profileTag ?: "", + lang = existing.lang, + data = JsonPusherData(existing.data.url, existing.data.format), + append = false + ) + executeRequest { + apiCall = pushersAPI.setPusher(deleteBody) + } + monarchy.awaitTransaction { + PusherEntity.where(it, params.userId, params.pushKey).findFirst()?.deleteFromRealm() } } - - } \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/pushers/UpdatePushRuleEnableStatusTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/pushers/UpdatePushRuleEnableStatusTask.kt index 9af67a6b..828d9e71 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/pushers/UpdatePushRuleEnableStatusTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/pushers/UpdatePushRuleEnableStatusTask.kt @@ -15,7 +15,6 @@ */ package im.vector.matrix.android.internal.session.pushers -import arrow.core.Try import im.vector.matrix.android.api.pushrules.rest.PushRule import im.vector.matrix.android.internal.network.executeRequest import im.vector.matrix.android.internal.task.Task @@ -31,7 +30,7 @@ internal interface UpdatePushRuleEnableStatusTask : Task { + override suspend fun execute(params: UpdatePushRuleEnableStatusTask.Params) { return executeRequest { apiCall = pushRulesApi.updateEnableRuleStatus(params.kind, params.pushRule.ruleId, params.enabled) } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/DefaultRoomService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/DefaultRoomService.kt index 5a7f82b0..2dabb64e 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/DefaultRoomService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/DefaultRoomService.kt @@ -34,7 +34,7 @@ import im.vector.matrix.android.internal.session.room.create.CreateRoomTask import im.vector.matrix.android.internal.session.room.membership.joining.JoinRoomTask import im.vector.matrix.android.internal.task.TaskExecutor import im.vector.matrix.android.internal.task.configureWith -import im.vector.matrix.android.internal.util.fetchManaged +import io.realm.Realm import javax.inject.Inject internal class DefaultRoomService @Inject constructor(private val monarchy: Monarchy, @@ -52,8 +52,13 @@ internal class DefaultRoomService @Inject constructor(private val monarchy: Mona } override fun getRoom(roomId: String): Room? { - monarchy.fetchManaged { RoomEntity.where(it, roomId).findFirst() } ?: return null - return roomFactory.create(roomId) + return Realm.getInstance(monarchy.realmConfiguration).use { + if (RoomEntity.where(it, roomId).findFirst() != null) { + roomFactory.create(roomId) + } else { + null + } + } } override fun liveRoomSummaries(): LiveData> { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/EventRelationsAggregationTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/EventRelationsAggregationTask.kt index 2e1a4273..786ba168 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/EventRelationsAggregationTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/EventRelationsAggregationTask.kt @@ -15,7 +15,6 @@ */ package im.vector.matrix.android.internal.session.room -import arrow.core.Try import com.zhuinden.monarchy.Monarchy import im.vector.matrix.android.api.session.crypto.CryptoService import im.vector.matrix.android.api.session.crypto.MXCryptoError @@ -31,7 +30,7 @@ import im.vector.matrix.android.internal.database.query.create import im.vector.matrix.android.internal.database.query.where import im.vector.matrix.android.internal.session.room.send.LocalEchoEventFactory import im.vector.matrix.android.internal.task.Task -import im.vector.matrix.android.internal.util.tryTransactionSync +import im.vector.matrix.android.internal.util.awaitTransaction import io.realm.Realm import timber.log.Timber import javax.inject.Inject @@ -54,10 +53,10 @@ internal class DefaultEventRelationsAggregationTask @Inject constructor( //OPT OUT serer aggregation until API mature enough private val SHOULD_HANDLE_SERVER_AGREGGATION = false - override suspend fun execute(params: EventRelationsAggregationTask.Params): Try { + override suspend fun execute(params: EventRelationsAggregationTask.Params) { val events = params.events val userId = params.userId - return monarchy.tryTransactionSync { realm -> + monarchy.awaitTransaction { realm -> Timber.v(">>> DefaultEventRelationsAggregationTask[${params.hashCode()}] called with ${events.size} events") update(realm, events, userId) Timber.v("<<< DefaultEventRelationsAggregationTask[${params.hashCode()}] finished") diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/create/CreateRoomTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/create/CreateRoomTask.kt index 04982c9a..93c2eb0b 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/create/CreateRoomTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/create/CreateRoomTask.kt @@ -16,8 +16,6 @@ package im.vector.matrix.android.internal.session.room.create -import arrow.core.Try -import arrow.core.recoverWith import com.zhuinden.monarchy.Monarchy import im.vector.matrix.android.api.session.room.failure.CreateRoomFailure import im.vector.matrix.android.api.session.room.model.create.CreateRoomParams @@ -34,7 +32,7 @@ import im.vector.matrix.android.internal.session.room.read.SetReadMarkersTask import im.vector.matrix.android.internal.session.user.accountdata.DirectChatsHelper import im.vector.matrix.android.internal.session.user.accountdata.UpdateUserAccountDataTask import im.vector.matrix.android.internal.task.Task -import im.vector.matrix.android.internal.util.tryTransactionSync +import im.vector.matrix.android.internal.util.awaitTransaction import io.realm.RealmConfiguration import java.util.concurrent.TimeUnit import javax.inject.Inject @@ -46,60 +44,49 @@ internal class DefaultCreateRoomTask @Inject constructor(private val roomAPI: Ro private val directChatsHelper: DirectChatsHelper, private val updateUserAccountDataTask: UpdateUserAccountDataTask, private val readMarkersTask: SetReadMarkersTask, - @SessionDatabase private val realmConfiguration: RealmConfiguration) : CreateRoomTask { + @SessionDatabase + private val realmConfiguration: RealmConfiguration) : CreateRoomTask { - override suspend fun execute(params: CreateRoomParams): Try { - return executeRequest { + override suspend fun execute(params: CreateRoomParams): String { + val createRoomResponse = executeRequest { apiCall = roomAPI.createRoom(params) - }.flatMap { createRoomResponse -> - val roomId = createRoomResponse.roomId!! - // Wait for room to come back from the sync (but it can maybe be in the DB is the sync response is received before) - val rql = RealmQueryLatch(realmConfiguration) { realm -> - realm.where(RoomEntity::class.java) - .equalTo(RoomEntityFields.ROOM_ID, roomId) - } - try { - rql.await(timeout = 1L, timeUnit = TimeUnit.MINUTES) - Try.just(roomId) - } catch (exception: Exception) { - Try.raise(CreateRoomFailure.CreatedWithTimeout) - } - }.flatMap { roomId -> - if (params.isDirect()) { - handleDirectChatCreation(params, roomId) - } else { - Try.just(roomId) - } - }.flatMap { roomId -> - setReadMarkers(roomId) } + val roomId = createRoomResponse.roomId!! + // Wait for room to come back from the sync (but it can maybe be in the DB is the sync response is received before) + val rql = RealmQueryLatch(realmConfiguration) { realm -> + realm.where(RoomEntity::class.java) + .equalTo(RoomEntityFields.ROOM_ID, roomId) + } + try { + rql.await(timeout = 1L, timeUnit = TimeUnit.MINUTES) + } catch (exception: Exception) { + throw CreateRoomFailure.CreatedWithTimeout + } + if (params.isDirect()) { + handleDirectChatCreation(params, roomId) + } + setReadMarkers(roomId) + return roomId } - private suspend fun handleDirectChatCreation(params: CreateRoomParams, roomId: String): Try { + private suspend fun handleDirectChatCreation(params: CreateRoomParams, roomId: String) { val otherUserId = params.getFirstInvitedUserId() - ?: return Try.raise(IllegalStateException("You can't create a direct room without an invitedUser")) + ?: throw IllegalStateException("You can't create a direct room without an invitedUser") - return monarchy.tryTransactionSync { realm -> + monarchy.awaitTransaction { realm -> RoomSummaryEntity.where(realm, roomId).findFirst()?.apply { this.directUserId = otherUserId this.isDirect = true } - }.flatMap { - val directChats = directChatsHelper.getLocalUserAccount() - updateUserAccountDataTask.execute(UpdateUserAccountDataTask.DirectChatParams(directMessages = directChats)) - }.flatMap { - Try.just(roomId) } + val directChats = directChatsHelper.getLocalUserAccount() + updateUserAccountDataTask.execute(UpdateUserAccountDataTask.DirectChatParams(directMessages = directChats)) } - private suspend fun setReadMarkers(roomId: String): Try { + private suspend fun setReadMarkers(roomId: String) { val setReadMarkerParams = SetReadMarkersTask.Params(roomId, markAllAsRead = true) - return readMarkersTask - .execute(setReadMarkerParams) - .flatMap { - Try.just(roomId) - } + return readMarkersTask.execute(setReadMarkerParams) } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/directory/GetPublicRoomTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/directory/GetPublicRoomTask.kt index b2cc1a74..a24765e0 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/directory/GetPublicRoomTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/directory/GetPublicRoomTask.kt @@ -16,11 +16,9 @@ package im.vector.matrix.android.internal.session.room.directory -import arrow.core.Try import im.vector.matrix.android.api.session.room.model.roomdirectory.PublicRoomsParams import im.vector.matrix.android.api.session.room.model.roomdirectory.PublicRoomsResponse import im.vector.matrix.android.internal.network.executeRequest -import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.session.room.RoomAPI import im.vector.matrix.android.internal.task.Task import javax.inject.Inject @@ -34,7 +32,7 @@ internal interface GetPublicRoomTask : Task { + override suspend fun execute(params: GetPublicRoomTask.Params): PublicRoomsResponse { return executeRequest { apiCall = roomAPI.publicRooms(params.server, params.publicRoomsParams) } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/directory/GetThirdPartyProtocolsTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/directory/GetThirdPartyProtocolsTask.kt index cd53c92d..4d3bc8fd 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/directory/GetThirdPartyProtocolsTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/directory/GetThirdPartyProtocolsTask.kt @@ -16,19 +16,17 @@ package im.vector.matrix.android.internal.session.room.directory -import arrow.core.Try import im.vector.matrix.android.api.session.room.model.thirdparty.ThirdPartyProtocol import im.vector.matrix.android.internal.network.executeRequest -import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.session.room.RoomAPI import im.vector.matrix.android.internal.task.Task import javax.inject.Inject internal interface GetThirdPartyProtocolsTask : Task> -internal class DefaultGetThirdPartyProtocolsTask @Inject constructor (private val roomAPI: RoomAPI) : GetThirdPartyProtocolsTask { +internal class DefaultGetThirdPartyProtocolsTask @Inject constructor(private val roomAPI: RoomAPI) : GetThirdPartyProtocolsTask { - override suspend fun execute(params: Unit): Try> { + override suspend fun execute(params: Unit): Map { return executeRequest { apiCall = roomAPI.thirdPartyProtocols() } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/DefaultMembershipService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/DefaultMembershipService.kt index f5d46b9b..85cc7dab 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/DefaultMembershipService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/DefaultMembershipService.kt @@ -42,7 +42,7 @@ internal class DefaultMembershipService @Inject constructor(private val roomId: private val leaveRoomTask: LeaveRoomTask ) : MembershipService { - override fun loadRoomMembersIfNeeded(matrixCallback: MatrixCallback): Cancelable { + override fun loadRoomMembersIfNeeded(matrixCallback: MatrixCallback): Cancelable { val params = LoadRoomMembersTask.Params(roomId, Membership.LEAVE) return loadRoomMembersTask.configureWith(params) .dispatchTo(matrixCallback) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/LoadRoomMembersTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/LoadRoomMembersTask.kt index a3090605..709e8da9 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/LoadRoomMembersTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/LoadRoomMembersTask.kt @@ -16,8 +16,6 @@ package im.vector.matrix.android.internal.session.room.membership -import arrow.core.Try -import com.squareup.moshi.JsonReader import com.zhuinden.monarchy.Monarchy import im.vector.matrix.android.api.session.room.model.Membership import im.vector.matrix.android.internal.database.helper.addStateEvent @@ -30,14 +28,12 @@ import im.vector.matrix.android.internal.session.room.RoomSummaryUpdater import im.vector.matrix.android.internal.session.sync.SyncTokenStore import im.vector.matrix.android.internal.session.user.UserEntityFactory import im.vector.matrix.android.internal.task.Task -import im.vector.matrix.android.internal.util.tryTransactionSync +import im.vector.matrix.android.internal.util.awaitTransaction import io.realm.Realm import io.realm.kotlin.createObject -import okhttp3.ResponseBody -import okio.Okio import javax.inject.Inject -internal interface LoadRoomMembersTask : Task { +internal interface LoadRoomMembersTask : Task { data class Params( val roomId: String, @@ -51,39 +47,36 @@ internal class DefaultLoadRoomMembersTask @Inject constructor(private val roomAP private val roomSummaryUpdater: RoomSummaryUpdater ) : LoadRoomMembersTask { - override suspend fun execute(params: LoadRoomMembersTask.Params): Try { - return if (areAllMembersAlreadyLoaded(params.roomId)) { - Try.just(true) - } else { - val lastToken = syncTokenStore.getLastToken() - executeRequest { - apiCall = roomAPI.getMembers(params.roomId, lastToken, null, params.excludeMembership?.value) - }.flatMap { response -> - insertInDb(response, params.roomId) - }.map { true } + override suspend fun execute(params: LoadRoomMembersTask.Params) { + if (areAllMembersAlreadyLoaded(params.roomId)) { + return } + val lastToken = syncTokenStore.getLastToken() + val response = executeRequest { + apiCall = roomAPI.getMembers(params.roomId, lastToken, null, params.excludeMembership?.value) + } + insertInDb(response, params.roomId) } - private fun insertInDb(response: RoomMembersResponse, roomId: String): Try { - return monarchy - .tryTransactionSync { realm -> - // We ignore all the already known members - val roomEntity = RoomEntity.where(realm, roomId).findFirst() - ?: realm.createObject(roomId) + private suspend fun insertInDb(response: RoomMembersResponse, roomId: String) { + monarchy.awaitTransaction { realm -> + // We ignore all the already known members + val roomEntity = RoomEntity.where(realm, roomId).findFirst() + ?: realm.createObject(roomId) - for (roomMemberEvent in response.roomMemberEvents) { - roomEntity.addStateEvent(roomMemberEvent) - UserEntityFactory.createOrNull(roomMemberEvent)?.also { - realm.insertOrUpdate(it) - } - } - roomEntity.chunks.flatMap { it.timelineEvents }.forEach { - it.updateSenderData() - } - roomEntity.areAllMembersLoaded = true - roomSummaryUpdater.update(realm, roomId) + for (roomMemberEvent in response.roomMemberEvents) { + roomEntity.addStateEvent(roomMemberEvent) + UserEntityFactory.createOrNull(roomMemberEvent)?.also { + realm.insertOrUpdate(it) } + } + roomEntity.chunks.flatMap { it.timelineEvents }.forEach { + it.updateSenderData() + } + roomEntity.areAllMembersLoaded = true + roomSummaryUpdater.update(realm, roomId) + } } private fun areAllMembersAlreadyLoaded(roomId: String): Boolean { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/joining/InviteTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/joining/InviteTask.kt index 5f656c01..68d72c6b 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/joining/InviteTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/joining/InviteTask.kt @@ -16,9 +16,7 @@ package im.vector.matrix.android.internal.session.room.membership.joining -import arrow.core.Try import im.vector.matrix.android.internal.network.executeRequest -import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.session.room.RoomAPI import im.vector.matrix.android.internal.task.Task import javax.inject.Inject @@ -33,7 +31,7 @@ internal interface InviteTask : Task { internal class DefaultInviteTask @Inject constructor(private val roomAPI: RoomAPI) : InviteTask { - override suspend fun execute(params: InviteTask.Params): Try { + override suspend fun execute(params: InviteTask.Params) { return executeRequest { val body = InviteBody(params.userId) apiCall = roomAPI.invite(params.roomId, body) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/joining/JoinRoomTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/joining/JoinRoomTask.kt index bc76a211..5ca23a0d 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/joining/JoinRoomTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/joining/JoinRoomTask.kt @@ -16,8 +16,6 @@ package im.vector.matrix.android.internal.session.room.membership.joining -import arrow.core.Try -import im.vector.matrix.android.api.session.room.failure.CreateRoomFailure import im.vector.matrix.android.api.session.room.failure.JoinRoomFailure import im.vector.matrix.android.internal.database.RealmQueryLatch import im.vector.matrix.android.internal.database.model.RoomEntity @@ -40,32 +38,30 @@ internal interface JoinRoomTask : Task { internal class DefaultJoinRoomTask @Inject constructor(private val roomAPI: RoomAPI, private val readMarkersTask: SetReadMarkersTask, - @SessionDatabase private val realmConfiguration: RealmConfiguration) : JoinRoomTask { + @SessionDatabase + private val realmConfiguration: RealmConfiguration) : JoinRoomTask { - override suspend fun execute(params: JoinRoomTask.Params): Try { - return executeRequest { + override suspend fun execute(params: JoinRoomTask.Params) { + executeRequest { apiCall = roomAPI.join(params.roomId, params.viaServers, HashMap()) - }.flatMap { - val roomId = params.roomId - // Wait for room to come back from the sync (but it can maybe be in the DB is the sync response is received before) - val rql = RealmQueryLatch(realmConfiguration) { realm -> - realm.where(RoomEntity::class.java) - .equalTo(RoomEntityFields.ROOM_ID, roomId) - } - try { - rql.await(timeout = 1L, timeUnit = TimeUnit.MINUTES) - Try.just(roomId) - } catch (exception: Exception) { - Try.raise(JoinRoomFailure.JoinedWithTimeout) - } - }.flatMap { roomId -> - setReadMarkers(roomId) } + val roomId = params.roomId + // Wait for room to come back from the sync (but it can maybe be in the DB is the sync response is received before) + val rql = RealmQueryLatch(realmConfiguration) { realm -> + realm.where(RoomEntity::class.java) + .equalTo(RoomEntityFields.ROOM_ID, roomId) + } + try { + rql.await(timeout = 1L, timeUnit = TimeUnit.MINUTES) + } catch (exception: Exception) { + throw JoinRoomFailure.JoinedWithTimeout + } + setReadMarkers(roomId) } - private suspend fun setReadMarkers(roomId: String): Try { + private suspend fun setReadMarkers(roomId: String) { val setReadMarkerParams = SetReadMarkersTask.Params(roomId, markAllAsRead = true) - return readMarkersTask.execute(setReadMarkerParams) + readMarkersTask.execute(setReadMarkerParams) } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/leaving/LeaveRoomTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/leaving/LeaveRoomTask.kt index fe055e99..d4ff169c 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/leaving/LeaveRoomTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/leaving/LeaveRoomTask.kt @@ -16,9 +16,7 @@ package im.vector.matrix.android.internal.session.room.membership.leaving -import arrow.core.Try import im.vector.matrix.android.internal.network.executeRequest -import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.session.room.RoomAPI import im.vector.matrix.android.internal.task.Task import javax.inject.Inject @@ -31,7 +29,7 @@ internal interface LeaveRoomTask : Task { internal class DefaultLeaveRoomTask @Inject constructor(private val roomAPI: RoomAPI) : LeaveRoomTask { - override suspend fun execute(params: LeaveRoomTask.Params): Try { + override suspend fun execute(params: LeaveRoomTask.Params) { return executeRequest { apiCall = roomAPI.leave(params.roomId, HashMap()) } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/prune/PruneEventTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/prune/PruneEventTask.kt index ab373a6e..35df20df 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/prune/PruneEventTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/prune/PruneEventTask.kt @@ -15,12 +15,10 @@ */ package im.vector.matrix.android.internal.session.room.prune -import arrow.core.Try import com.zhuinden.monarchy.Monarchy 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.UnsignedData -import im.vector.matrix.android.api.session.room.send.SendState import im.vector.matrix.android.internal.database.helper.updateSenderData import im.vector.matrix.android.internal.database.mapper.ContentMapper import im.vector.matrix.android.internal.database.mapper.EventMapper @@ -31,7 +29,7 @@ import im.vector.matrix.android.internal.database.query.where import im.vector.matrix.android.internal.di.MoshiProvider import im.vector.matrix.android.internal.session.room.send.LocalEchoEventFactory import im.vector.matrix.android.internal.task.Task -import im.vector.matrix.android.internal.util.tryTransactionSync +import im.vector.matrix.android.internal.util.awaitTransaction import io.realm.Realm import timber.log.Timber import javax.inject.Inject @@ -48,8 +46,8 @@ internal interface PruneEventTask : Task { internal class DefaultPruneEventTask @Inject constructor(private val monarchy: Monarchy) : PruneEventTask { - override suspend fun execute(params: PruneEventTask.Params): Try { - return monarchy.tryTransactionSync { realm -> + override suspend fun execute(params: PruneEventTask.Params) { + monarchy.awaitTransaction { realm -> params.redactionEvents.forEach { event -> pruneEvent(realm, event, params.userId) } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/read/SetReadMarkersTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/read/SetReadMarkersTask.kt index c32fc598..41c9cca5 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/read/SetReadMarkersTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/read/SetReadMarkersTask.kt @@ -16,7 +16,6 @@ package im.vector.matrix.android.internal.session.room.read -import arrow.core.Try import com.zhuinden.monarchy.Monarchy import im.vector.matrix.android.api.auth.data.Credentials import im.vector.matrix.android.internal.database.model.ChunkEntity @@ -31,7 +30,6 @@ 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.send.LocalEchoEventFactory import im.vector.matrix.android.internal.task.Task -import im.vector.matrix.android.internal.util.tryTransactionAsync import io.realm.Realm import timber.log.Timber import javax.inject.Inject @@ -54,7 +52,7 @@ internal class DefaultSetReadMarkersTask @Inject constructor(private val roomAPI private val monarchy: Monarchy ) : SetReadMarkersTask { - override suspend fun execute(params: SetReadMarkersTask.Params): Try { + override suspend fun execute(params: SetReadMarkersTask.Params) { val markers = HashMap() val fullyReadEventId: String? val readReceiptEventId: String? @@ -78,7 +76,7 @@ internal class DefaultSetReadMarkersTask @Inject constructor(private val roomAPI } } if (readReceiptEventId != null - && !isEventRead(params.roomId, readReceiptEventId)) { + && !isEventRead(params.roomId, readReceiptEventId)) { if (LocalEchoEventFactory.isLocalEchoId(readReceiptEventId)) { Timber.w("Can't set read receipt for local event ${params.fullyReadEventId}") @@ -87,21 +85,20 @@ internal class DefaultSetReadMarkersTask @Inject constructor(private val roomAPI markers[READ_RECEIPT] = readReceiptEventId } } - return if (markers.isEmpty()) { - Try.just(Unit) - } else { - executeRequest { - apiCall = roomAPI.sendReadMarker(params.roomId, markers) - } + if (markers.isEmpty()) { + return + } + executeRequest { + apiCall = roomAPI.sendReadMarker(params.roomId, markers) } } private fun updateNotificationCountIfNecessary(roomId: String, eventId: String) { - monarchy.tryTransactionAsync { realm -> + monarchy.writeAsync { realm -> val isLatestReceived = TimelineEventEntity.latestEvent(realm, roomId = roomId, includesSending = false)?.eventId == eventId if (isLatestReceived) { val roomSummary = RoomSummaryEntity.where(realm, roomId).findFirst() - ?: return@tryTransactionAsync + ?: return@writeAsync roomSummary.notificationCount = 0 roomSummary.highlightCount = 0 } @@ -112,13 +109,13 @@ internal class DefaultSetReadMarkersTask @Inject constructor(private val roomAPI var isEventRead = false monarchy.doWithRealm { val readReceipt = ReadReceiptEntity.where(it, roomId, credentials.userId).findFirst() - ?: return@doWithRealm + ?: return@doWithRealm val liveChunk = ChunkEntity.findLastLiveChunkFromRoom(it, roomId) - ?: return@doWithRealm + ?: return@doWithRealm val readReceiptIndex = liveChunk.timelineEvents.find(readReceipt.eventId)?.root?.displayIndex - ?: Int.MIN_VALUE + ?: Int.MIN_VALUE val eventToCheckIndex = liveChunk.timelineEvents.find(eventId)?.root?.displayIndex - ?: Int.MAX_VALUE + ?: Int.MAX_VALUE isEventRead = eventToCheckIndex <= readReceiptIndex } return isEventRead diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/relation/DefaultRelationService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/relation/DefaultRelationService.kt index 183f2a6c..cc621345 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/relation/DefaultRelationService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/relation/DefaultRelationService.kt @@ -43,7 +43,6 @@ import im.vector.matrix.android.internal.session.room.timeline.TimelineSendEvent import im.vector.matrix.android.internal.task.TaskExecutor import im.vector.matrix.android.internal.task.configureWith import im.vector.matrix.android.internal.util.CancelableWork -import im.vector.matrix.android.internal.util.tryTransactionAsync import im.vector.matrix.android.internal.worker.WorkerParamsFactory import timber.log.Timber import javax.inject.Inject @@ -223,9 +222,9 @@ internal class DefaultRelationService @Inject constructor(private val context: C * the same transaction id is received (in unsigned data) */ private fun saveLocalEcho(event: Event) { - monarchy.tryTransactionAsync { realm -> + monarchy.writeAsync { realm -> val roomEntity = RoomEntity.where(realm, roomId = roomId).findFirst() - ?: return@tryTransactionAsync + ?: return@writeAsync roomEntity.addSendingEvent(event) } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/relation/FetchEditHistoryTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/relation/FetchEditHistoryTask.kt index 7afbe288..792df3b1 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/relation/FetchEditHistoryTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/relation/FetchEditHistoryTask.kt @@ -15,7 +15,6 @@ */ package im.vector.matrix.android.internal.session.room.relation -import arrow.core.Try 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.RelationType @@ -39,16 +38,16 @@ internal class DefaultFetchEditHistoryTask @Inject constructor( private val roomAPI: RoomAPI ) : FetchEditHistoryTask { - override suspend fun execute(params: FetchEditHistoryTask.Params): Try> { - return executeRequest { + override suspend fun execute(params: FetchEditHistoryTask.Params): List { + val response = executeRequest { apiCall = roomAPI.getRelations(params.roomId, params.eventId, RelationType.REPLACE, if (params.isRoomEncrypted) EventType.ENCRYPTED else EventType.MESSAGE) - }.map { resp -> - val events = resp.chunks.toMutableList() - resp.originalEvent?.let { events.add(it) } - events } + + val events = response.chunks.toMutableList() + response.originalEvent?.let { events.add(it) } + return events } } \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/relation/FindReactionEventForUndoTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/relation/FindReactionEventForUndoTask.kt index 8841819e..f74a7ebb 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/relation/FindReactionEventForUndoTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/relation/FindReactionEventForUndoTask.kt @@ -15,13 +15,11 @@ */ package im.vector.matrix.android.internal.session.room.relation -import arrow.core.Try import com.zhuinden.monarchy.Monarchy import im.vector.matrix.android.internal.database.model.EventAnnotationsSummaryEntity import im.vector.matrix.android.internal.database.model.EventEntity import im.vector.matrix.android.internal.database.model.ReactionAggregatedSummaryEntityFields import im.vector.matrix.android.internal.database.query.where -import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.task.Task import io.realm.Realm import javax.inject.Inject @@ -44,14 +42,11 @@ internal interface FindReactionEventForUndoTask : Task { - return Try { - var eventId: String? = null - monarchy.doWithRealm { realm -> - eventId = getReactionToRedact(realm, params.reaction, params.eventId, params.myUserId)?.eventId - } - FindReactionEventForUndoTask.Result(eventId) + override suspend fun execute(params: FindReactionEventForUndoTask.Params): FindReactionEventForUndoTask.Result { + val eventId = Realm.getInstance(monarchy.realmConfiguration).use { realm -> + getReactionToRedact(realm, params.reaction, params.eventId, params.myUserId)?.eventId } + return FindReactionEventForUndoTask.Result(eventId) } private fun getReactionToRedact(realm: Realm, reaction: String, eventId: String, userId: String): EventEntity? { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/relation/SendRelationWorker.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/relation/SendRelationWorker.kt index 81a888ee..5df995d9 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/relation/SendRelationWorker.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/relation/SendRelationWorker.kt @@ -66,18 +66,11 @@ internal class SendRelationWorker(context: Context, params: WorkerParameters) : val relatedEventId = relationContent.relatesTo?.eventId ?: return Result.failure() val relationType = (relationContent.relatesTo as? ReactionInfo)?.type ?: params.relationType ?: return Result.failure() - - val result = executeRequest { - apiCall = roomAPI.sendRelation( - roomId = params.roomId, - parent_id = relatedEventId, - relationType = relationType, - eventType = localEvent.type, - content = localEvent.content - ) - } - return result.fold({ - when (it) { + return try { + sendRelation(params.roomId, relationType, relatedEventId, localEvent) + Result.success() + } catch (exception: Throwable) { + when (exception) { is Failure.NetworkConnection -> Result.retry() else -> { //TODO mark as failed to send? @@ -85,7 +78,19 @@ internal class SendRelationWorker(context: Context, params: WorkerParameters) : Result.success() } } - }, { Result.success() }) + } + } + + private suspend fun sendRelation(roomId: String, relationType: String, relatedEventId: String, localEvent: Event) { + executeRequest { + apiCall = roomAPI.sendRelation( + roomId = roomId, + parent_id = relatedEventId, + relationType = relationType, + eventType = localEvent.type, + content = localEvent.content + ) + } } } \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/relation/UpdateQuickReactionTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/relation/UpdateQuickReactionTask.kt index 66e02ce9..fd081fbe 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/relation/UpdateQuickReactionTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/relation/UpdateQuickReactionTask.kt @@ -15,13 +15,11 @@ */ package im.vector.matrix.android.internal.session.room.relation -import arrow.core.Try import com.zhuinden.monarchy.Monarchy import im.vector.matrix.android.internal.database.model.EventAnnotationsSummaryEntity import im.vector.matrix.android.internal.database.model.EventEntity import im.vector.matrix.android.internal.database.model.ReactionAggregatedSummaryEntityFields import im.vector.matrix.android.internal.database.query.where -import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.task.Task import io.realm.Realm import javax.inject.Inject @@ -44,14 +42,13 @@ internal interface UpdateQuickReactionTask : Task { - return Try { - var res: Pair?>? = null - monarchy.doWithRealm { realm -> - res = updateQuickReaction(realm, params.reaction, params.oppositeReaction, params.eventId, params.myUserId) - } - UpdateQuickReactionTask.Result(res?.first, res?.second ?: emptyList()) + + override suspend fun execute(params: UpdateQuickReactionTask.Params): UpdateQuickReactionTask.Result { + var res: Pair?>? = null + monarchy.doWithRealm { realm -> + res = updateQuickReaction(realm, params.reaction, params.oppositeReaction, params.eventId, params.myUserId) } + return UpdateQuickReactionTask.Result(res?.first, res?.second ?: emptyList()) } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/DefaultSendService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/DefaultSendService.kt index 8adb45dd..2d585ad6 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/DefaultSendService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/DefaultSendService.kt @@ -41,7 +41,6 @@ import im.vector.matrix.android.internal.database.query.where import im.vector.matrix.android.internal.session.content.UploadContentWorker import im.vector.matrix.android.internal.session.room.timeline.TimelineSendEventWorkCommon import im.vector.matrix.android.internal.util.CancelableWork -import im.vector.matrix.android.internal.util.tryTransactionAsync import im.vector.matrix.android.internal.worker.WorkManagerUtil import im.vector.matrix.android.internal.worker.WorkManagerUtil.matrixOneTimeWorkRequestBuilder import im.vector.matrix.android.internal.worker.WorkerParamsFactory @@ -149,7 +148,7 @@ internal class DefaultSendService @Inject constructor(private val context: Conte } override fun deleteFailedEcho(localEcho: TimelineEvent) { - monarchy.tryTransactionAsync { realm -> + monarchy.writeAsync { realm -> TimelineEventEntity.where(realm, eventId = localEcho.root.eventId ?: "").findFirst()?.let { it.deleteFromRealm() @@ -175,7 +174,7 @@ internal class DefaultSendService @Inject constructor(private val context: Conte .enqueue() } - monarchy.tryTransactionAsync { realm -> + monarchy.writeAsync { realm -> RoomEntity.where(realm, roomId).findFirst()?.let { room -> room.sendingTimelineEvents.forEach { it.root?.sendState = SendState.UNDELIVERED @@ -186,7 +185,7 @@ internal class DefaultSendService @Inject constructor(private val context: Conte } override fun resendAllFailedMessages() { - monarchy.tryTransactionAsync { realm -> + monarchy.writeAsync { realm -> RoomEntity.where(realm, roomId).findFirst()?.let { room -> room.sendingTimelineEvents.filter { it.root?.sendState?.hasFailed() ?: false diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/EncryptEventWorker.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/EncryptEventWorker.kt index e83181b8..409a43f1 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/EncryptEventWorker.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/EncryptEventWorker.kt @@ -17,7 +17,7 @@ package im.vector.matrix.android.internal.session.room.send import android.content.Context -import androidx.work.Worker +import androidx.work.CoroutineWorker import androidx.work.WorkerParameters import com.squareup.moshi.JsonClass import im.vector.matrix.android.api.MatrixCallback @@ -34,7 +34,7 @@ import java.util.concurrent.CountDownLatch import javax.inject.Inject internal class EncryptEventWorker(context: Context, params: WorkerParameters) - : Worker(context, params) { + : CoroutineWorker(context, params) { @JsonClass(generateAdapter = true) internal data class Params( @@ -49,7 +49,7 @@ internal class EncryptEventWorker(context: Context, params: WorkerParameters) @Inject lateinit var crypto: CryptoService @Inject lateinit var localEchoUpdater: LocalEchoUpdater - override fun doWork(): Result { + override suspend fun doWork(): Result { Timber.v("Start Encrypt work") val params = WorkerParamsFactory.fromData(inputData) ?: return Result.success().also { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/LocalEchoEventFactory.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/LocalEchoEventFactory.kt index c6b68647..ddcaf417 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/LocalEchoEventFactory.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/LocalEchoEventFactory.kt @@ -36,7 +36,6 @@ import im.vector.matrix.android.internal.database.query.where import im.vector.matrix.android.internal.session.content.ThumbnailExtractor import im.vector.matrix.android.internal.session.room.RoomSummaryUpdater import im.vector.matrix.android.internal.util.StringProvider -import im.vector.matrix.android.internal.util.tryTransactionAsync import org.commonmark.parser.Parser import org.commonmark.renderer.html.HtmlRenderer import java.util.* @@ -380,9 +379,9 @@ internal class LocalEchoEventFactory @Inject constructor(private val credentials fun saveLocalEcho(monarchy: Monarchy, event: Event) { if (event.roomId == null) throw IllegalStateException("Your event should have a roomId") - monarchy.tryTransactionAsync { realm -> + monarchy.writeAsync { realm -> val roomEntity = RoomEntity.where(realm, roomId = event.roomId).findFirst() - ?: return@tryTransactionAsync + ?: return@writeAsync roomEntity.addSendingEvent(event) roomSummaryUpdater.update(realm, event.roomId) } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/LocalEchoUpdater.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/LocalEchoUpdater.kt index 9d41979b..c4242239 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/LocalEchoUpdater.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/LocalEchoUpdater.kt @@ -20,15 +20,15 @@ import com.zhuinden.monarchy.Monarchy import im.vector.matrix.android.api.session.room.send.SendState 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.tryTransactionAsync +import im.vector.matrix.android.internal.util.awaitTransaction import timber.log.Timber import javax.inject.Inject internal class LocalEchoUpdater @Inject constructor(private val monarchy: Monarchy) { - fun updateSendState(eventId: String, sendState: SendState) { + suspend fun updateSendState(eventId: String, sendState: SendState) { Timber.v("Update local state of $eventId to ${sendState.name}") - monarchy.tryTransactionAsync { realm -> + monarchy.awaitTransaction { realm -> val sendingEventEntity = EventEntity.where(realm, eventId).findFirst() if (sendingEventEntity != null) { if (sendState == SendState.SENT && sendingEventEntity.sendState == SendState.SYNCED) { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/RedactEventWorker.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/RedactEventWorker.kt index 5acc16e1..bac71cf7 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/RedactEventWorker.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/RedactEventWorker.kt @@ -54,28 +54,32 @@ internal class RedactEventWorker(context: Context, params: WorkerParameters) : C sessionComponent.inject(this) val eventId = params.eventId - val result = executeRequest { - apiCall = roomAPI.redactEvent( - params.txID, - params.roomId, - eventId, - if (params.reason == null) emptyMap() else mapOf("reason" to params.reason) - ) - } - return result.fold({ - when (it) { - is Failure.NetworkConnection -> Result.retry() - else -> { - //TODO mark as failed to send? - //always return success, or the chain will be stuck for ever! - Result.success(WorkerParamsFactory.toData(params.copy( - lastFailureMessage = it.localizedMessage - ))) - } + return runCatching { + executeRequest { + apiCall = roomAPI.redactEvent( + params.txID, + params.roomId, + eventId, + if (params.reason == null) emptyMap() else mapOf("reason" to params.reason) + ) } - }, { - Result.success() - }) + }.fold( + { + Result.success() + }, + { + when (it) { + is Failure.NetworkConnection -> Result.retry() + else -> { + //TODO mark as failed to send? + //always return success, or the chain will be stuck for ever! + Result.success(WorkerParamsFactory.toData(params.copy( + lastFailureMessage = it.localizedMessage + ))) + } + } + } + ) } } \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/SendEventWorker.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/SendEventWorker.kt index ddc18e87..88ecb8d3 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/SendEventWorker.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/SendEventWorker.kt @@ -21,6 +21,7 @@ import androidx.work.CoroutineWorker import androidx.work.WorkerParameters import com.squareup.moshi.JsonClass import im.vector.matrix.android.api.failure.Failure +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.room.send.SendState import im.vector.matrix.android.internal.network.executeRequest @@ -59,22 +60,14 @@ internal class SendEventWorker constructor(context: Context, params: WorkerParam if (params.lastFailureMessage != null) { localEchoUpdater.updateSendState(event.eventId, SendState.UNDELIVERED) - // Transmit the error return Result.success(inputData) } - - localEchoUpdater.updateSendState(event.eventId, SendState.SENDING) - val result = executeRequest { - apiCall = roomAPI.send( - event.eventId, - params.roomId, - event.type, - event.content - ) - } - return result.fold({ - when (it) { + return try { + sendEvent(event.eventId, event.type, event.content, params.roomId) + Result.success() + } catch (exception: Throwable) { + when (exception) { is Failure.NetworkConnection -> Result.retry() else -> { localEchoUpdater.updateSendState(event.eventId, SendState.UNDELIVERED) @@ -82,10 +75,20 @@ internal class SendEventWorker constructor(context: Context, params: WorkerParam Result.success() } } - }, { - localEchoUpdater.updateSendState(event.eventId, SendState.SENT) - Result.success() - }) + } + } + + private suspend fun sendEvent(eventId: String, eventType: String, content: Content?, roomId: String) { + localEchoUpdater.updateSendState(eventId, SendState.SENDING) + executeRequest { + apiCall = roomAPI.send( + eventId, + roomId, + eventType, + content + ) + } + localEchoUpdater.updateSendState(eventId, SendState.SENT) } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/state/SendStateTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/state/SendStateTask.kt index 085340e6..39d606f5 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/state/SendStateTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/state/SendStateTask.kt @@ -16,9 +16,7 @@ package im.vector.matrix.android.internal.session.room.state -import arrow.core.Try import im.vector.matrix.android.internal.network.executeRequest -import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.session.room.RoomAPI import im.vector.matrix.android.internal.task.Task import javax.inject.Inject @@ -32,7 +30,7 @@ internal interface SendStateTask : Task { } internal class DefaultSendStateTask @Inject constructor(private val roomAPI: RoomAPI) : SendStateTask { - override suspend fun execute(params: SendStateTask.Params): Try { + override suspend fun execute(params: SendStateTask.Params) { return executeRequest { apiCall = roomAPI.sendStateEvent(params.roomId, params.eventType, params.body) } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/DefaultGetContextOfEventTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/DefaultGetContextOfEventTask.kt index a47bab62..d59d6fc4 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/DefaultGetContextOfEventTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/DefaultGetContextOfEventTask.kt @@ -38,13 +38,12 @@ internal class DefaultGetContextOfEventTask @Inject constructor(private val room private val tokenChunkEventPersistor: TokenChunkEventPersistor ) : GetContextOfEventTask { - override suspend fun execute(params: GetContextOfEventTask.Params): Try { + override suspend fun execute(params: GetContextOfEventTask.Params): TokenChunkEventPersistor.Result { val filter = filterRepository.getRoomFilter() - return executeRequest { + val response = executeRequest { apiCall = roomAPI.getContextOfEvent(params.roomId, params.eventId, 0, filter) - }.flatMap { response -> - tokenChunkEventPersistor.insertInDb(response, params.roomId, PaginationDirection.BACKWARDS) } + return tokenChunkEventPersistor.insertInDb(response, params.roomId, PaginationDirection.BACKWARDS) } } \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/DefaultPaginationTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/DefaultPaginationTask.kt index bc8a93b6..57efcdae 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/DefaultPaginationTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/DefaultPaginationTask.kt @@ -16,9 +16,7 @@ package im.vector.matrix.android.internal.session.room.timeline -import arrow.core.Try import im.vector.matrix.android.internal.network.executeRequest -import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.session.filter.FilterRepository import im.vector.matrix.android.internal.session.room.RoomAPI import im.vector.matrix.android.internal.task.Task @@ -41,14 +39,12 @@ internal class DefaultPaginationTask @Inject constructor(private val roomAPI: Ro private val tokenChunkEventPersistor: TokenChunkEventPersistor ) : PaginationTask { - override suspend fun execute(params: PaginationTask.Params): Try { + override suspend fun execute(params: PaginationTask.Params): TokenChunkEventPersistor.Result { val filter = filterRepository.getRoomFilter() - return executeRequest { + val chunk = executeRequest { apiCall = roomAPI.getRoomMessagesFrom(params.roomId, params.from, params.direction.value, params.limit, filter) - }.flatMap { chunk -> - tokenChunkEventPersistor - .insertInDb(chunk, params.roomId, params.direction) } + return tokenChunkEventPersistor.insertInDb(chunk, params.roomId, params.direction) } } \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/GetEventTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/GetEventTask.kt index 5ce58e02..5fdee1b5 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/GetEventTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/GetEventTask.kt @@ -16,12 +16,10 @@ package im.vector.matrix.android.internal.session.room.timeline -import arrow.core.Try import im.vector.matrix.android.api.session.events.model.Event -import im.vector.matrix.android.internal.task.Task import im.vector.matrix.android.internal.network.executeRequest -import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.session.room.RoomAPI +import im.vector.matrix.android.internal.task.Task import javax.inject.Inject internal class GetEventTask @Inject constructor(private val roomAPI: RoomAPI @@ -32,7 +30,7 @@ internal class GetEventTask @Inject constructor(private val roomAPI: RoomAPI val eventId: String ) - override suspend fun execute(params: Params): Try { + override suspend fun execute(params: Params): Event { return executeRequest { apiCall = roomAPI.getEvent(params.roomId, params.eventId) } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/TokenChunkEventPersistor.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/TokenChunkEventPersistor.kt index fb8b6271..af845040 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/TokenChunkEventPersistor.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/TokenChunkEventPersistor.kt @@ -16,7 +16,6 @@ package im.vector.matrix.android.internal.session.room.timeline -import arrow.core.Try import com.zhuinden.monarchy.Monarchy import im.vector.matrix.android.internal.database.helper.* import im.vector.matrix.android.internal.database.model.ChunkEntity @@ -26,7 +25,7 @@ import im.vector.matrix.android.internal.database.query.find import im.vector.matrix.android.internal.database.query.findAllIncludingEvents import im.vector.matrix.android.internal.database.query.where import im.vector.matrix.android.internal.session.user.UserEntityFactory -import im.vector.matrix.android.internal.util.tryTransactionSync +import im.vector.matrix.android.internal.util.awaitTransaction import io.realm.kotlin.createObject import timber.log.Timber import javax.inject.Inject @@ -104,12 +103,12 @@ internal class TokenChunkEventPersistor @Inject constructor(private val monarchy SUCCESS } - fun insertInDb(receivedChunk: TokenChunkEvent, - roomId: String, - direction: PaginationDirection): Try { + suspend fun insertInDb(receivedChunk: TokenChunkEvent, + roomId: String, + direction: PaginationDirection): Result { - return monarchy - .tryTransactionSync { realm -> + monarchy + .awaitTransaction { realm -> Timber.v("Start persisting ${receivedChunk.events.size} events in $roomId towards $direction") val roomEntity = RoomEntity.where(realm, roomId).findFirst() @@ -127,7 +126,7 @@ internal class TokenChunkEventPersistor @Inject constructor(private val monarchy if (ChunkEntity.find(realm, roomId, nextToken = nextToken) != null || ChunkEntity.find(realm, roomId, prevToken = prevToken) != null) { Timber.v("Already inserted - SKIP") - return@tryTransactionSync + return@awaitTransaction } val prevChunk = ChunkEntity.find(realm, roomId, nextToken = prevToken) @@ -181,13 +180,11 @@ internal class TokenChunkEventPersistor @Inject constructor(private val monarchy currentChunk.updateSenderDataFor(eventIds) } } - .map { - if (receivedChunk.events.isEmpty() && receivedChunk.stateEvents.isEmpty() && receivedChunk.start != receivedChunk.end) { - Result.SHOULD_FETCH_MORE - } else { - Result.SUCCESS - } - } + return if (receivedChunk.events.isEmpty() && receivedChunk.stateEvents.isEmpty() && receivedChunk.start != receivedChunk.end) { + Result.SHOULD_FETCH_MORE + } else { + Result.SUCCESS + } } private fun handleMerge(roomEntity: RoomEntity, diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/signout/SignOutTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/signout/SignOutTask.kt index 903763e4..6f4441b1 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/signout/SignOutTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/signout/SignOutTask.kt @@ -16,7 +16,6 @@ package im.vector.matrix.android.internal.session.signout -import arrow.core.Try import im.vector.matrix.android.api.auth.data.Credentials import im.vector.matrix.android.internal.SessionManager import im.vector.matrix.android.internal.auth.SessionParamsStore @@ -31,15 +30,11 @@ internal class DefaultSignOutTask @Inject constructor(private val credentials: C private val sessionManager: SessionManager, private val sessionParamsStore: SessionParamsStore) : SignOutTask { - override suspend fun execute(params: Unit): Try { - return executeRequest { + override suspend fun execute(params: Unit) { + executeRequest { apiCall = signOutAPI.signOut() - }.flatMap { - sessionParamsStore.delete(credentials.userId) - }.flatMap { - Try { - sessionManager.releaseSession(credentials.userId) - } } + sessionParamsStore.delete(credentials.userId) + sessionManager.releaseSession(credentials.userId) } } \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/SyncTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/SyncTask.kt index 84490404..ea4efa47 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/SyncTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/SyncTask.kt @@ -16,9 +16,6 @@ package im.vector.matrix.android.internal.session.sync -import arrow.core.Try -import arrow.core.failure -import arrow.core.recoverWith import com.zhuinden.monarchy.Monarchy import im.vector.matrix.android.R import im.vector.matrix.android.api.auth.data.Credentials @@ -30,7 +27,6 @@ import im.vector.matrix.android.internal.session.DefaultInitialSyncProgressServi import im.vector.matrix.android.internal.session.filter.FilterRepository import im.vector.matrix.android.internal.session.sync.model.SyncResponse import im.vector.matrix.android.internal.task.Task -import im.vector.matrix.android.internal.util.tryTransactionAsync import javax.inject.Inject internal interface SyncTask : Task { @@ -50,7 +46,7 @@ internal class DefaultSyncTask @Inject constructor(private val syncAPI: SyncAPI, ) : SyncTask { - override suspend fun execute(params: SyncTask.Params): Try { + override suspend fun execute(params: SyncTask.Params) { val requestParams = HashMap() var timeout = 0L val token = syncTokenStore.getLastToken() @@ -66,27 +62,22 @@ internal class DefaultSyncTask @Inject constructor(private val syncAPI: SyncAPI, initialSyncProgressService.endAll() initialSyncProgressService.startTask(R.string.initial_sync_start_importing_account, 100) } - return executeRequest { - apiCall = syncAPI.sync(requestParams) - }.recoverWith { throwable -> + val syncResponse = try { + executeRequest { + apiCall = syncAPI.sync(requestParams) + } + } catch (throwable: Throwable) { // Intercept 401 if (throwable is Failure.ServerError && throwable.error.code == MatrixError.UNKNOWN_TOKEN) { sessionParamsStore.delete(credentials.userId) } - - // Transmit the throwable - throwable.failure() - }.flatMap { syncResponse -> - syncResponseHandler.handleResponse(syncResponse, token, false).also { - if (isInitialSync) { - monarchy.tryTransactionAsync { - initialSyncProgressService.endAll() - } - } - } - }.map { - syncTokenStore.saveToken(it.nextBatch) + throw throwable + } + syncResponseHandler.handleResponse(syncResponse, token, false) + syncTokenStore.saveToken(syncResponse.nextBatch) + if (isInitialSync) { + initialSyncProgressService.endAll() } } } \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/job/SyncThread.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/job/SyncThread.kt index c08f6101..80c54ddc 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/job/SyncThread.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/job/SyncThread.kt @@ -134,9 +134,12 @@ internal class SyncThread @Inject constructor(private val syncTask: SyncTask, latch.countDown() } + override fun onCanceled() { + latch.countDown() + } + }) .executeBy(taskExecutor) - latch.await() if (state is SyncState.RUNNING) { updateStateTo(SyncState.RUNNING(afterPause = false)) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/job/SyncWorker.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/job/SyncWorker.kt index 570aa9c4..b5d7118b 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/job/SyncWorker.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/job/SyncWorker.kt @@ -18,18 +18,15 @@ package im.vector.matrix.android.internal.session.sync.job import android.content.Context import androidx.work.* import com.squareup.moshi.JsonClass -import im.vector.matrix.android.api.MatrixCallback -import im.vector.matrix.android.api.util.Cancelable import im.vector.matrix.android.internal.session.sync.SyncTask 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.MatrixCoroutineDispatchers import im.vector.matrix.android.internal.worker.WorkManagerUtil import im.vector.matrix.android.internal.worker.WorkManagerUtil.matrixOneTimeWorkRequestBuilder import im.vector.matrix.android.internal.worker.WorkerParamsFactory import im.vector.matrix.android.internal.worker.getSessionComponent +import kotlinx.coroutines.withContext import timber.log.Timber -import java.util.concurrent.CountDownLatch import java.util.concurrent.TimeUnit import javax.inject.Inject @@ -47,45 +44,26 @@ internal class SyncWorker(context: Context, val automaticallyRetry: Boolean = false ) - @Inject - lateinit var syncTask: SyncTask - @Inject - lateinit var taskExecutor: TaskExecutor + @Inject lateinit var syncTask: SyncTask + @Inject lateinit var taskExecutor: TaskExecutor + @Inject lateinit var coroutineDispatchers: MatrixCoroutineDispatchers override suspend fun doWork(): Result { Timber.i("Sync work starting") val params = WorkerParamsFactory.fromData(inputData) ?: return Result.success() val sessionComponent = getSessionComponent(params.userId) ?: return Result.success() sessionComponent.inject(this) - - - val latch = CountDownLatch(1) - val taskParams = SyncTask.Params(0) - cancelableTask = syncTask.configureWith(taskParams) - .callbackOn(TaskThread.SYNC) - .executeOn(TaskThread.SYNC) - .dispatchTo(object : MatrixCallback { - override fun onSuccess(data: Unit) { - latch.countDown() - } - - override fun onFailure(failure: Throwable) { - Timber.e(failure) - latch.countDown() - } - - }) - .executeBy(taskExecutor) - - latch.await() + runCatching { + withContext(coroutineDispatchers.sync) { + val taskParams = SyncTask.Params(0) + syncTask.execute(taskParams) + } + } return Result.success() } companion object { - - private var cancelableTask: Cancelable? = null - fun requireBackgroundSync(context: Context, userId: String, serverTimeout: Long = 0) { val data = WorkerParamsFactory.toData(Params(userId, serverTimeout, false)) val workRequest = matrixOneTimeWorkRequestBuilder() @@ -107,7 +85,6 @@ internal class SyncWorker(context: Context, } fun stopAnyBackgroundSync(context: Context) { - cancelableTask?.cancel() WorkManager.getInstance(context).cancelUniqueWork("BG_SYNCP") } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/accountdata/UpdateUserAccountDataTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/accountdata/UpdateUserAccountDataTask.kt index 57ee9632..ddbdb973 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/accountdata/UpdateUserAccountDataTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/accountdata/UpdateUserAccountDataTask.kt @@ -16,7 +16,6 @@ package im.vector.matrix.android.internal.session.user.accountdata -import arrow.core.Try import im.vector.matrix.android.api.auth.data.Credentials import im.vector.matrix.android.internal.network.executeRequest import im.vector.matrix.android.internal.session.sync.model.UserAccountData @@ -45,8 +44,7 @@ internal interface UpdateUserAccountDataTask : Task { - + override suspend fun execute(params: UpdateUserAccountDataTask.Params) { return executeRequest { apiCall = accountDataApi.setAccountData(credentials.userId, params.type, params.getData()) } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/model/SearchUserTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/model/SearchUserTask.kt index 85264dba..4472bcf8 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/model/SearchUserTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/model/SearchUserTask.kt @@ -16,7 +16,6 @@ package im.vector.matrix.android.internal.session.user.model -import arrow.core.Try import im.vector.matrix.android.api.session.user.model.User import im.vector.matrix.android.internal.network.executeRequest import im.vector.matrix.android.internal.session.user.SearchUserAPI @@ -34,13 +33,12 @@ internal interface SearchUserTask : Task> { internal class DefaultSearchUserTask @Inject constructor(private val searchUserAPI: SearchUserAPI) : SearchUserTask { - override suspend fun execute(params: SearchUserTask.Params): Try> { - return executeRequest { + override suspend fun execute(params: SearchUserTask.Params): List { + val response = executeRequest { apiCall = searchUserAPI.searchUsers(SearchUsersParams(params.search, params.limit)) - }.map { response -> - response.users.map { - User(it.userId, it.displayName, it.avatarUrl) - } + } + return response.users.map { + User(it.userId, it.displayName, it.avatarUrl) } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/task/ConfigurableTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/task/ConfigurableTask.kt index 955ccc67..ec131393 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/task/ConfigurableTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/task/ConfigurableTask.kt @@ -16,7 +16,6 @@ package im.vector.matrix.android.internal.task -import arrow.core.Try import im.vector.matrix.android.api.MatrixCallback import im.vector.matrix.android.api.util.Cancelable @@ -41,7 +40,7 @@ internal data class ConfigurableTask( ) : Task { - override suspend fun execute(params: PARAMS): Try { + override suspend fun execute(params: PARAMS): RESULT { return task.execute(params) } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/task/Task.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/task/Task.kt index 81a2add8..be761fdb 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/task/Task.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/task/Task.kt @@ -16,11 +16,9 @@ package im.vector.matrix.android.internal.task -import arrow.core.Try - internal interface Task { - suspend fun execute(params: PARAMS): Try + suspend fun execute(params: PARAMS): RESULT } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/task/TaskExecutor.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/task/TaskExecutor.kt index 31de5ae7..a4e6dc5a 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/task/TaskExecutor.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/task/TaskExecutor.kt @@ -17,10 +17,10 @@ package im.vector.matrix.android.internal.task -import arrow.core.Try import im.vector.matrix.android.api.util.Cancelable +import im.vector.matrix.android.api.util.CancelableBag +import im.vector.matrix.android.internal.di.MatrixScope import im.vector.matrix.android.internal.extensions.foldToCallback -import im.vector.matrix.android.internal.extensions.onError import im.vector.matrix.android.internal.util.CancelableCoroutine import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers import kotlinx.coroutines.GlobalScope @@ -31,24 +31,35 @@ import timber.log.Timber import javax.inject.Inject import kotlin.coroutines.EmptyCoroutineContext +@MatrixScope internal class TaskExecutor @Inject constructor(private val coroutineDispatchers: MatrixCoroutineDispatchers) { + private val cancelableBag = CancelableBag() + fun execute(task: ConfigurableTask): Cancelable { val job = GlobalScope.launch(task.callbackThread.toDispatcher()) { - val resultOrFailure = withContext(task.executionThread.toDispatcher()) { - Timber.v("Executing $task on ${Thread.currentThread().name}") - retry(task.retryCount) { - task.execute(task.params) + val resultOrFailure = runCatching { + withContext(task.executionThread.toDispatcher()) { + Timber.v("Executing $task on ${Thread.currentThread().name}") + retry(task.retryCount) { + task.execute(task.params) + } } } resultOrFailure - .onError { + .onFailure { Timber.d(it, "Task failed") } .foldToCallback(task.callback) } - return CancelableCoroutine(job) + return CancelableCoroutine(job, task.callback).also { + cancelableBag += it + } + } + + fun cancelAll() = synchronized(this) { + cancelableBag.cancel() } private suspend fun retry( @@ -56,14 +67,13 @@ internal class TaskExecutor @Inject constructor(private val coroutineDispatchers initialDelay: Long = 100, // 0.1 second maxDelay: Long = 10_000, // 10 second factor: Double = 2.0, - block: suspend () -> Try): Try { + block: suspend () -> T): T { var currentDelay = initialDelay repeat(times - 1) { - val blockResult = block() - if (blockResult.isSuccess()) { - return blockResult - } else { + try { + return block() + } catch (e: Exception) { delay(currentDelay) currentDelay = (currentDelay * factor).toLong().coerceAtMost(maxDelay) } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/util/CancelableCoroutine.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/util/CancelableCoroutine.kt index 0ad14b91..f5b93eb3 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/util/CancelableCoroutine.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/util/CancelableCoroutine.kt @@ -16,13 +16,17 @@ package im.vector.matrix.android.internal.util +import im.vector.matrix.android.api.MatrixCallback import im.vector.matrix.android.api.util.Cancelable import kotlinx.coroutines.Job -internal class CancelableCoroutine(private val job: Job) : Cancelable { +internal class CancelableCoroutine(private val job: Job, private val callback: MatrixCallback<*>) : Cancelable { override fun cancel() { - job.cancel() + if (!job.isCancelled) { + job.cancel() + callback.onCanceled() + } } } \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/util/Monarchy.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/util/Monarchy.kt index 6b547db1..1d0f66df 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/util/Monarchy.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/util/Monarchy.kt @@ -16,30 +16,27 @@ package im.vector.matrix.android.internal.util -import arrow.core.Try import com.zhuinden.monarchy.Monarchy +import im.vector.matrix.android.internal.database.awaitTransaction import io.realm.Realm import io.realm.RealmModel import java.util.concurrent.atomic.AtomicReference -internal fun Monarchy.tryTransactionSync(transaction: (realm: Realm) -> Unit): Try { - return Try { - this.runTransactionSync(transaction) +internal suspend fun Monarchy.awaitTransaction(transaction: (realm: Realm) -> Unit) { + Realm.getInstance(realmConfiguration).use { + it.awaitTransaction(transaction) } } -internal fun Monarchy.tryTransactionAsync(transaction: (realm: Realm) -> Unit): Try { - return Try { - this.writeAsync(transaction) - } -} - -fun Monarchy.fetchManaged(query: (Realm) -> T?): T? { - return fetch(query, false) -} - fun Monarchy.fetchCopied(query: (Realm) -> T?): T? { - return fetch(query, true) + val ref = AtomicReference() + doWithRealm { realm -> + val result = query.invoke(realm)?.let { + realm.copyFromRealm(it) + } + ref.set(result) + } + return ref.get() } fun Monarchy.fetchCopyMap(query: (Realm) -> T?, map: (T, realm: Realm) -> U): U? { @@ -52,18 +49,3 @@ fun Monarchy.fetchCopyMap(query: (Realm) -> T?, map: (T, rea } return ref.get() } - -private fun Monarchy.fetch(query: (Realm) -> T?, copyFromRealm: Boolean): T? { - val ref = AtomicReference() - doWithRealm { realm -> - val result = query.invoke(realm)?.let { - if (copyFromRealm) { - realm.copyFromRealm(it) - } else { - it - } - } - ref.set(result) - } - return ref.get() -}