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,
// the event has been 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 {
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]!!
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.
deviceTrackingStatuses.put(userId, TRACKING_STATUS_PENDING_DOWNLOAD)
deviceTrackingStatuses[userId] = TRACKING_STATUS_PENDING_DOWNLOAD
isUpdated = true
}
}

View File

@ -21,6 +21,8 @@ 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.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.EventType
import im.vector.matrix.android.internal.crypto.DeviceListManager
@ -88,7 +90,7 @@ internal class MXMegolmEncryption(
keysClaimedMap["ed25519"] = olmDevice.deviceEd25519Key!!

olmDevice.addInboundGroupSession(sessionId!!, olmDevice.getSessionKey(sessionId)!!, roomId, olmDevice.deviceCurve25519Key!!,
ArrayList(), keysClaimedMap, false)
ArrayList(), keysClaimedMap, false)

keysBackup.maybeBackupKeys()

@ -103,10 +105,10 @@ internal class MXMegolmEncryption(
private suspend fun ensureOutboundSession(devicesInRoom: MXUsersDevicesMap<MXDeviceInfo>): Try<MXOutboundSessionInfo> {
var session = outboundSession
if (session == null
// Need to make a brand new session?
|| session.needsRotation(sessionRotationPeriodMsgs, sessionRotationPeriodMs)
// Determine if we have shared with anyone we shouldn't have
|| session.sharedWithTooManyDevices(devicesInRoom)) {
// Need to make a brand new session?
|| session.needsRotation(sessionRotationPeriodMsgs, sessionRotationPeriodMs)
// Determine if we have shared with anyone we shouldn't have
|| session.sharedWithTooManyDevices(devicesInRoom)) {
session = prepareNewSessionInRoom()
outboundSession = session
}
@ -192,7 +194,7 @@ internal class MXMegolmEncryption(
return ensureOlmSessionsForDevicesAction.handle(devicesByUser)
.flatMap {
Timber.v("## shareUserDevicesKey() : ensureOlmSessionsForDevices succeeds after "
+ (System.currentTimeMillis() - t0) + " ms")
+ (System.currentTimeMillis() - t0) + " ms")
val contentMap = MXUsersDevicesMap<Any>()
var haveTargets = false
val userIds = it.userIds
@ -227,7 +229,7 @@ internal class MXMegolmEncryption(
sendToDeviceTask.execute(sendToDeviceParams)
.map {
Timber.v("## shareUserDevicesKey() : sendToDevice succeeds after "
+ (System.currentTimeMillis() - t0) + " ms")
+ (System.currentTimeMillis() - t0) + " ms")

// Add the devices we have shared with to session.sharedWithDevices.
// we deliberately iterate over devicesByUser (ie, the devices we
@ -291,24 +293,23 @@ internal class MXMegolmEncryption(
// an m.new_device.
return deviceListManager
.downloadKeys(userIds, false)
.map {
.flatMap {
val encryptToVerifiedDevicesOnly = cryptoStore.getGlobalBlacklistUnverifiedDevices()
|| cryptoStore.getRoomsListBlacklistUnverifiedDevices().contains(roomId)
|| cryptoStore.getRoomsListBlacklistUnverifiedDevices().contains(roomId)

val devicesInRoom = MXUsersDevicesMap<MXDeviceInfo>()
val unknownDevices = MXUsersDevicesMap<MXDeviceInfo>()

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

for (deviceId in deviceIds!!) {
val deviceInfo = it.getObject(deviceId, userId)
if (warnOnUnknownDevicesRepository.warnOnUnknownDevices() && deviceInfo!!.isUnknown) {
val deviceIds = it.getUserDeviceIds(userId) ?: continue
for (deviceId in deviceIds) {
val deviceInfo = it.getObject(deviceId, userId) ?: continue
if (warnOnUnknownDevicesRepository.warnOnUnknownDevices() && deviceInfo.isUnknown) {
// The device is not yet known by the user
unknownDevices.setObject(deviceInfo, userId, deviceId)
continue
}
if (deviceInfo!!.isBlocked) {
if (deviceInfo.isBlocked) {
// Remove any blocked devices
continue
}
@ -324,7 +325,13 @@ internal class MXMegolmEncryption(
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
public String toString() {
if (null != mMap) {

View File

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

// TODO: set it back to true by default. Need UI
// 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.
*
* @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.
@ -35,7 +35,7 @@ internal class WarnOnUnknownDeviceRepository {
* @param warn true to warn when some unknown devices are detected.
*/
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.UpdateQuickReactionTask
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.SendStateTask
import im.vector.matrix.android.internal.session.room.timeline.*
@ -76,6 +77,10 @@ class RoomModule {
LocalEchoEventFactory(get(), get())
}

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

scope(DefaultSession.SCOPE) {
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 com.squareup.moshi.JsonClass
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.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.di.MatrixKoinComponent
import im.vector.matrix.android.internal.util.WorkerParamsFactory
@ -40,16 +42,18 @@ internal class EncryptEventWorker(context: Context, params: WorkerParameters)
)

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

override fun doWork(): Result {

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

val localEvent = params.event
if (localEvent.eventId == null) {
return Result.success()
}
localEchoUpdater.updateSendState(localEvent.eventId, SendState.ENCRYPTING)

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

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

} else {
Result.success()
return Result.success(WorkerParamsFactory.toData(nextWorkerParams))
}
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 im.vector.matrix.android.api.failure.Failure
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.network.executeRequest
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 localEchoUpdater by inject<LocalEchoUpdater>()

override fun doWork(): Result {

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

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

View File

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

}

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

View File

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

@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)
})
private val _memberNameClickListener = DebouncedClickListener(View.OnClickListener {
private val _memberNameClickListener = DebouncedClickListener(View.OnClickListener {
avatarCallback?.onMemberNameClicked(informationData)
})