Crypto: clean some code + add failure send state (but not handled yet).

This commit is contained in:
ganfra 2019-06-10 19:22:48 +02:00
parent f2722f4766
commit 612b13808f
11 changed files with 106 additions and 35 deletions

View File

@ -26,10 +26,19 @@ enum class SendState {
SENDING, SENDING,
// the event has been sent // the event has been sent
SENT, SENT,
SYNCED; // the event has been received from server
SYNCED,
// The event failed to be sent
UNDELIVERED,
// the event failed to be sent because some unknown devices have been found while encrypting it
FAILED_UNKNOWN_DEVICES;


fun isSent(): Boolean { fun isSent(): Boolean {
return this == SENT || this == SYNCED return this == SENT || this == SYNCED
} }


fun hasFailed(): Boolean {
return this == UNDELIVERED || this == FAILED_UNKNOWN_DEVICES
}

} }

View File

@ -47,7 +47,7 @@ internal class DeviceListManager(private val cryptoStore: IMXCryptoStore,
val status = deviceTrackingStatuses[userId]!! val status = deviceTrackingStatuses[userId]!!
if (TRACKING_STATUS_DOWNLOAD_IN_PROGRESS == status || TRACKING_STATUS_UNREACHABLE_SERVER == status) { if (TRACKING_STATUS_DOWNLOAD_IN_PROGRESS == status || TRACKING_STATUS_UNREACHABLE_SERVER == status) {
// if a download was in progress when we got shut down, it isn't any more. // if a download was in progress when we got shut down, it isn't any more.
deviceTrackingStatuses.put(userId, TRACKING_STATUS_PENDING_DOWNLOAD) deviceTrackingStatuses[userId] = TRACKING_STATUS_PENDING_DOWNLOAD
isUpdated = true isUpdated = true
} }
} }

View File

@ -21,6 +21,8 @@ package im.vector.matrix.android.internal.crypto.algorithms.megolm
import android.text.TextUtils import android.text.TextUtils
import arrow.core.Try import arrow.core.Try
import im.vector.matrix.android.api.auth.data.Credentials import im.vector.matrix.android.api.auth.data.Credentials
import im.vector.matrix.android.api.failure.Failure
import im.vector.matrix.android.api.session.crypto.MXCryptoError
import im.vector.matrix.android.api.session.events.model.Content import im.vector.matrix.android.api.session.events.model.Content
import im.vector.matrix.android.api.session.events.model.EventType import im.vector.matrix.android.api.session.events.model.EventType
import im.vector.matrix.android.internal.crypto.DeviceListManager import im.vector.matrix.android.internal.crypto.DeviceListManager
@ -291,7 +293,7 @@ internal class MXMegolmEncryption(
// an m.new_device. // an m.new_device.
return deviceListManager return deviceListManager
.downloadKeys(userIds, false) .downloadKeys(userIds, false)
.map { .flatMap {
val encryptToVerifiedDevicesOnly = cryptoStore.getGlobalBlacklistUnverifiedDevices() val encryptToVerifiedDevicesOnly = cryptoStore.getGlobalBlacklistUnverifiedDevices()
|| cryptoStore.getRoomsListBlacklistUnverifiedDevices().contains(roomId) || cryptoStore.getRoomsListBlacklistUnverifiedDevices().contains(roomId)


@ -299,16 +301,15 @@ internal class MXMegolmEncryption(
val unknownDevices = MXUsersDevicesMap<MXDeviceInfo>() val unknownDevices = MXUsersDevicesMap<MXDeviceInfo>()


for (userId in it.userIds) { for (userId in it.userIds) {
val deviceIds = it.getUserDeviceIds(userId) val deviceIds = it.getUserDeviceIds(userId) ?: continue

for (deviceId in deviceIds) {
for (deviceId in deviceIds!!) { val deviceInfo = it.getObject(deviceId, userId) ?: continue
val deviceInfo = it.getObject(deviceId, userId) if (warnOnUnknownDevicesRepository.warnOnUnknownDevices() && deviceInfo.isUnknown) {
if (warnOnUnknownDevicesRepository.warnOnUnknownDevices() && deviceInfo!!.isUnknown) {
// The device is not yet known by the user // The device is not yet known by the user
unknownDevices.setObject(deviceInfo, userId, deviceId) unknownDevices.setObject(deviceInfo, userId, deviceId)
continue continue
} }
if (deviceInfo!!.isBlocked) { if (deviceInfo.isBlocked) {
// Remove any blocked devices // Remove any blocked devices
continue continue
} }
@ -324,7 +325,13 @@ internal class MXMegolmEncryption(
devicesInRoom.setObject(deviceInfo, userId, deviceId) devicesInRoom.setObject(deviceInfo, userId, deviceId)
} }
} }
devicesInRoom if (unknownDevices.isEmpty) {
Try.just(devicesInRoom)
} else {
val cryptoError = MXCryptoError(MXCryptoError.UNKNOWN_DEVICES_CODE,
MXCryptoError.UNABLE_TO_ENCRYPT, MXCryptoError.UNKNOWN_DEVICES_REASON, unknownDevices)
Try.Failure(Failure.CryptoError(cryptoError))
}
} }
} }
} }

View File

@ -175,6 +175,10 @@ public class MXUsersDevicesMap<E> implements Serializable {
} }
} }


public boolean isEmpty(){
return mMap.isEmpty();
}

@Override @Override
public String toString() { public String toString() {
if (null != mMap) { if (null != mMap) {

View File

@ -20,14 +20,14 @@ internal class WarnOnUnknownDeviceRepository {


// TODO: set it back to true by default. Need UI // TODO: set it back to true by default. Need UI
// Warn the user if some new devices are detected while encrypting a message. // Warn the user if some new devices are detected while encrypting a message.
private var mWarnOnUnknownDevices = false private var warnOnUnknownDevices = false


/** /**
* Tells if the encryption must fail if some unknown devices are detected. * Tells if the encryption must fail if some unknown devices are detected.
* *
* @return true to warn when some unknown devices are detected. * @return true to warn when some unknown devices are detected.
*/ */
fun warnOnUnknownDevices() = mWarnOnUnknownDevices fun warnOnUnknownDevices() = warnOnUnknownDevices


/** /**
* Update the warn status when some unknown devices are detected. * Update the warn status when some unknown devices are detected.
@ -35,7 +35,7 @@ internal class WarnOnUnknownDeviceRepository {
* @param warn true to warn when some unknown devices are detected. * @param warn true to warn when some unknown devices are detected.
*/ */
fun setWarnOnUnknownDevices(warn: Boolean) { fun setWarnOnUnknownDevices(warn: Boolean) {
mWarnOnUnknownDevices = warn warnOnUnknownDevices = warn
} }


} }

View File

@ -36,6 +36,7 @@ import im.vector.matrix.android.internal.session.room.relation.DefaultUpdateQuic
import im.vector.matrix.android.internal.session.room.relation.FindReactionEventForUndoTask import im.vector.matrix.android.internal.session.room.relation.FindReactionEventForUndoTask
import im.vector.matrix.android.internal.session.room.relation.UpdateQuickReactionTask import im.vector.matrix.android.internal.session.room.relation.UpdateQuickReactionTask
import im.vector.matrix.android.internal.session.room.send.LocalEchoEventFactory import im.vector.matrix.android.internal.session.room.send.LocalEchoEventFactory
import im.vector.matrix.android.internal.session.room.send.LocalEchoUpdater
import im.vector.matrix.android.internal.session.room.state.DefaultSendStateTask import im.vector.matrix.android.internal.session.room.state.DefaultSendStateTask
import im.vector.matrix.android.internal.session.room.state.SendStateTask import im.vector.matrix.android.internal.session.room.state.SendStateTask
import im.vector.matrix.android.internal.session.room.timeline.* import im.vector.matrix.android.internal.session.room.timeline.*
@ -76,6 +77,10 @@ class RoomModule {
LocalEchoEventFactory(get(), get()) LocalEchoEventFactory(get(), get())
} }


scope(DefaultSession.SCOPE) {
LocalEchoUpdater(get())
}

scope(DefaultSession.SCOPE) { scope(DefaultSession.SCOPE) {
RoomFactory(get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get()) RoomFactory(get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get())
} }

View File

@ -21,8 +21,10 @@ import androidx.work.Worker
import androidx.work.WorkerParameters import androidx.work.WorkerParameters
import com.squareup.moshi.JsonClass import com.squareup.moshi.JsonClass
import im.vector.matrix.android.api.MatrixCallback import im.vector.matrix.android.api.MatrixCallback
import im.vector.matrix.android.api.failure.Failure
import im.vector.matrix.android.api.session.crypto.CryptoService import im.vector.matrix.android.api.session.crypto.CryptoService
import im.vector.matrix.android.api.session.events.model.Event import im.vector.matrix.android.api.session.events.model.Event
import im.vector.matrix.android.api.session.room.send.SendState
import im.vector.matrix.android.internal.crypto.model.MXEncryptEventContentResult import im.vector.matrix.android.internal.crypto.model.MXEncryptEventContentResult
import im.vector.matrix.android.internal.di.MatrixKoinComponent import im.vector.matrix.android.internal.di.MatrixKoinComponent
import im.vector.matrix.android.internal.util.WorkerParamsFactory import im.vector.matrix.android.internal.util.WorkerParamsFactory
@ -40,6 +42,7 @@ internal class EncryptEventWorker(context: Context, params: WorkerParameters)
) )


private val crypto by inject<CryptoService>() private val crypto by inject<CryptoService>()
private val localEchoUpdater by inject<LocalEchoUpdater>()


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


@ -50,6 +53,7 @@ internal class EncryptEventWorker(context: Context, params: WorkerParameters)
if (localEvent.eventId == null) { if (localEvent.eventId == null) {
return Result.success() return Result.success()
} }
localEchoUpdater.updateSendState(localEvent.eventId, SendState.ENCRYPTING)


// TODO Better async handling // TODO Better async handling
val latch = CountDownLatch(1) val latch = CountDownLatch(1)
@ -76,19 +80,21 @@ internal class EncryptEventWorker(context: Context, params: WorkerParameters)
latch.await() latch.await()


val safeResult = result val safeResult = result
// TODO Update local echo if (safeResult != null) {
return if (error != null) {
Result.success() // TODO Pass error!!)
} else if (safeResult != null) {
val encryptedEvent = localEvent.copy( val encryptedEvent = localEvent.copy(
type = safeResult.mEventType, type = safeResult.mEventType,
content = safeResult.mEventContent content = safeResult.mEventContent
) )
val nextWorkerParams = SendEventWorker.Params(params.roomId, encryptedEvent) val nextWorkerParams = SendEventWorker.Params(params.roomId, encryptedEvent)
Result.success(WorkerParamsFactory.toData(nextWorkerParams)) return Result.success(WorkerParamsFactory.toData(nextWorkerParams))

} else {
Result.success()
} }
val safeError = error
val sendState = when (safeError) {
is Failure.CryptoError -> SendState.FAILED_UNKNOWN_DEVICES
else -> SendState.UNDELIVERED
}
localEchoUpdater.updateSendState(localEvent.eventId, sendState)
//always return success, or the chain will be stuck for ever!
return Result.success()
} }
} }

View File

@ -0,0 +1,38 @@
/*
*
* * Copyright 2019 New Vector Ltd
* *
* * Licensed under the Apache License, Version 2.0 (the "License");
* * you may not use this file except in compliance with the License.
* * You may obtain a copy of the License at
* *
* * http://www.apache.org/licenses/LICENSE-2.0
* *
* * Unless required by applicable law or agreed to in writing, software
* * distributed under the License is distributed on an "AS IS" BASIS,
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* * See the License for the specific language governing permissions and
* * limitations under the License.
*
*/

package im.vector.matrix.android.internal.session.room.send

import 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

internal class LocalEchoUpdater(private val monarchy: Monarchy) {

fun updateSendState(eventId: String, sendState: SendState) {
monarchy.tryTransactionAsync { realm ->
val sendingEventEntity = EventEntity.where(realm, eventId).findFirst()
if (sendingEventEntity != null) {
sendingEventEntity.sendState = sendState
}
}
}

}

View File

@ -22,6 +22,7 @@ import androidx.work.WorkerParameters
import com.squareup.moshi.JsonClass import com.squareup.moshi.JsonClass
import im.vector.matrix.android.api.failure.Failure import im.vector.matrix.android.api.failure.Failure
import im.vector.matrix.android.api.session.events.model.Event import im.vector.matrix.android.api.session.events.model.Event
import im.vector.matrix.android.api.session.room.send.SendState
import im.vector.matrix.android.internal.di.MatrixKoinComponent import im.vector.matrix.android.internal.di.MatrixKoinComponent
import im.vector.matrix.android.internal.network.executeRequest import im.vector.matrix.android.internal.network.executeRequest
import im.vector.matrix.android.internal.session.room.RoomAPI import im.vector.matrix.android.internal.session.room.RoomAPI
@ -39,6 +40,7 @@ internal class SendEventWorker(context: Context, params: WorkerParameters)
) )


private val roomAPI by inject<RoomAPI>() private val roomAPI by inject<RoomAPI>()
private val localEchoUpdater by inject<LocalEchoUpdater>()


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


@ -50,6 +52,7 @@ internal class SendEventWorker(context: Context, params: WorkerParameters)
return Result.success() return Result.success()
} }


localEchoUpdater.updateSendState(event.eventId, SendState.SENDING)
val result = executeRequest<SendResponse> { val result = executeRequest<SendResponse> {
apiCall = roomAPI.send( apiCall = roomAPI.send(
event.eventId, event.eventId,
@ -62,7 +65,7 @@ internal class SendEventWorker(context: Context, params: WorkerParameters)
when (it) { when (it) {
is Failure.NetworkConnection -> Result.retry() is Failure.NetworkConnection -> Result.retry()
else -> { else -> {
//TODO mark as failed to send? localEchoUpdater.updateSendState(event.eventId, SendState.UNDELIVERED)
//always return success, or the chain will be stuck for ever! //always return success, or the chain will be stuck for ever!
Result.success() Result.success()
} }

View File

@ -56,7 +56,6 @@ internal object TimelineSendEventWorkCommon {
WorkManager.getInstance() WorkManager.getInstance()
.beginUniqueWork(buildWorkIdentifier(roomId), ExistingWorkPolicy.APPEND, workRequest) .beginUniqueWork(buildWorkIdentifier(roomId), ExistingWorkPolicy.APPEND, workRequest)
.enqueue() .enqueue()

} }


inline fun <reified W : ListenableWorker> createWork(data: Data): OneTimeWorkRequest { inline fun <reified W : ListenableWorker> createWork(data: Data): OneTimeWorkRequest {

View File

@ -56,7 +56,7 @@ abstract class AbsMessageItem<H : AbsMessageItem.Holder> : BaseEventItem<H>() {
var reactionPillCallback: TimelineEventController.ReactionPillCallback? = null var reactionPillCallback: TimelineEventController.ReactionPillCallback? = null


@EpoxyAttribute @EpoxyAttribute
var avatarCallback: TimelineEventController.AvatarCallback?= null var avatarCallback: TimelineEventController.AvatarCallback? = null


private val _avatarClickListener = DebouncedClickListener(View.OnClickListener { private val _avatarClickListener = DebouncedClickListener(View.OnClickListener {
avatarCallback?.onAvatarClicked(informationData) avatarCallback?.onAvatarClicked(informationData)