diff --git a/changelog.d/5596.bugfix b/changelog.d/5596.bugfix new file mode 100644 index 0000000000..f51794c352 --- /dev/null +++ b/changelog.d/5596.bugfix @@ -0,0 +1 @@ +Align auto-reporting of decryption errors implementation with web client. \ No newline at end of file diff --git a/changelog.d/5639.sdk b/changelog.d/5639.sdk new file mode 100644 index 0000000000..66167273f9 --- /dev/null +++ b/changelog.d/5639.sdk @@ -0,0 +1 @@ +Include original event in live decryption listeners and update sync status naming to InitialSyncProgressing for clarity. \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/LiveEventListener.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/LiveEventListener.kt index 6fda65953a..b4b283c86a 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/LiveEventListener.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/LiveEventListener.kt @@ -25,9 +25,9 @@ interface LiveEventListener { fun onPaginatedEvent(roomId: String, event: Event) - fun onEventDecrypted(eventId: String, roomId: String, clearEvent: JsonDict) + fun onEventDecrypted(event: Event, clearEvent: JsonDict) - fun onEventDecryptionError(eventId: String, roomId: String, throwable: Throwable) + fun onEventDecryptionError(event: Event, throwable: Throwable) fun onLiveToDeviceEvent(event: Event) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/initsync/SyncStatusService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/initsync/SyncStatusService.kt index daab6d9761..267436916e 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/initsync/SyncStatusService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/initsync/SyncStatusService.kt @@ -28,7 +28,7 @@ interface SyncStatusService { abstract class InitialSyncStatus : Status() object Idle : InitialSyncStatus() - data class Progressing( + data class InitialSyncProgressing( val initSyncStep: InitSyncStep, val percentProgress: Int = 0 ) : InitialSyncStatus() diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/StreamEventsManager.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/StreamEventsManager.kt index bb0ca11445..8ae9772714 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/StreamEventsManager.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/StreamEventsManager.kt @@ -71,7 +71,7 @@ internal class StreamEventsManager @Inject constructor() { coroutineScope.launch { listeners.forEach { tryOrNull { - it.onEventDecrypted(event.eventId ?: "", event.roomId ?: "", result.clearEvent) + it.onEventDecrypted(event, result.clearEvent) } } } @@ -82,7 +82,7 @@ internal class StreamEventsManager @Inject constructor() { coroutineScope.launch { listeners.forEach { tryOrNull { - it.onEventDecryptionError(event.eventId ?: "", event.roomId ?: "", error) + it.onEventDecryptionError(event, error) } } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/initsync/DefaultSyncStatusService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/initsync/DefaultSyncStatusService.kt index 079b0d0115..4a1e6661b0 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/initsync/DefaultSyncStatusService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/initsync/DefaultSyncStatusService.kt @@ -72,7 +72,7 @@ internal class DefaultSyncStatusService @Inject constructor() : // Update the progress of the leaf and all its parents leaf.setProgress(progress) // Then update the live data using leaf wording and root progress - status.postValue(SyncStatusService.Status.Progressing(leaf.initSyncStep, root.currentProgress.toInt())) + status.postValue(SyncStatusService.Status.InitialSyncProgressing(leaf.initSyncStep, root.currentProgress.toInt())) } } } diff --git a/vector/src/main/java/im/vector/app/AutoRageShaker.kt b/vector/src/main/java/im/vector/app/AutoRageShaker.kt index 43283254b1..195e2f68dc 100644 --- a/vector/src/main/java/im/vector/app/AutoRageShaker.kt +++ b/vector/src/main/java/im/vector/app/AutoRageShaker.kt @@ -17,9 +17,11 @@ package im.vector.app import android.content.SharedPreferences +import androidx.lifecycle.asFlow import im.vector.app.core.di.ActiveSessionHolder import im.vector.app.features.rageshake.BugReporter import im.vector.app.features.rageshake.ReportType +import im.vector.app.features.session.coroutineScope import im.vector.app.features.settings.VectorPreferences import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -34,6 +36,7 @@ import kotlinx.coroutines.launch import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.events.model.toContent +import org.matrix.android.sdk.api.session.initsync.SyncStatusService import timber.log.Timber import javax.inject.Inject import javax.inject.Singleton @@ -62,10 +65,11 @@ class AutoRageShaker @Inject constructor( private val e2eDetectedFlow = MutableSharedFlow(replay = 0) private val matchingRSRequestFlow = MutableSharedFlow(replay = 0) - + private var hasSynced = false + private var preferenceEnabled = false fun initialize() { observeActiveSession() - enable(vectorPreferences.labsAutoReportUISI()) + preferenceEnabled = vectorPreferences.labsAutoReportUISI() // It's a singleton... vectorPreferences.subscribeToChanges(this) @@ -74,7 +78,7 @@ class AutoRageShaker @Inject constructor( e2eDetectedFlow .onEach { sendRageShake(it) - delay(2_000) + delay(60_000) } .catch { cause -> Timber.w(cause, "Failed to RS") @@ -84,7 +88,7 @@ class AutoRageShaker @Inject constructor( matchingRSRequestFlow .onEach { sendMatchingRageShake(it) - delay(2_000) + delay(60_000) } .catch { cause -> Timber.w(cause, "Failed to send matching rageshake") @@ -93,14 +97,7 @@ class AutoRageShaker @Inject constructor( } override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) { - enable(vectorPreferences.labsAutoReportUISI()) - } - - var _enabled = false - fun enable(enabled: Boolean) { - if (enabled == _enabled) return - _enabled = enabled - detector.enabled = enabled + preferenceEnabled = vectorPreferences.labsAutoReportUISI() } private fun observeActiveSession() { @@ -115,7 +112,6 @@ class AutoRageShaker @Inject constructor( } fun decryptionErrorDetected(target: E2EMessageDetected) { - if (target.source == UISIEventSource.INITIAL_SYNC) return if (activeSessionHolder.getSafeActiveSession()?.sessionId != currentActiveSessionId) return val shouldSendRS = synchronized(alreadyReportedUisi) { val reportInfo = ReportInfo(target.roomId, target.sessionId) @@ -148,7 +144,6 @@ class AutoRageShaker @Inject constructor( append("\"room_id\": \"${target.roomId}\",") append("\"sender_key\": \"${target.senderKey}\",") append("\"device_id\": \"${target.senderDeviceId}\",") - append("\"source\": \"${target.source}\",") append("\"user_id\": \"${target.senderUserId}\",") append("\"session_id\": \"${target.sessionId}\"") append("}") @@ -245,6 +240,9 @@ class AutoRageShaker @Inject constructor( override val reciprocateToDeviceEventType: String get() = AUTO_RS_REQUEST + override val enabled: Boolean + get() = this@AutoRageShaker.preferenceEnabled && this@AutoRageShaker.hasSynced + override fun uisiDetected(source: E2EMessageDetected) { decryptionErrorDetected(source) } @@ -261,7 +259,14 @@ class AutoRageShaker @Inject constructor( return } this.currentActiveSessionId = sessionId - this.detector.enabled = _enabled + + hasSynced = session.hasAlreadySynced() + session.getSyncStatusLive() + .asFlow() + .onEach { + hasSynced = it !is SyncStatusService.Status.InitialSyncProgressing + } + .launchIn(session.coroutineScope) activeSessionIds.add(sessionId) session.addListener(this) session.addEventStreamListener(detector) diff --git a/vector/src/main/java/im/vector/app/UISIDetector.kt b/vector/src/main/java/im/vector/app/UISIDetector.kt index d6a4805e78..bb76a96b92 100644 --- a/vector/src/main/java/im/vector/app/UISIDetector.kt +++ b/vector/src/main/java/im/vector/app/UISIDetector.kt @@ -16,6 +16,7 @@ package im.vector.app +import org.matrix.android.sdk.api.extensions.orFalse import org.matrix.android.sdk.api.session.LiveEventListener import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.events.model.toModel @@ -26,23 +27,17 @@ import java.util.Timer import java.util.TimerTask import java.util.concurrent.Executors -enum class UISIEventSource { - INITIAL_SYNC, - INCREMENTAL_SYNC, - PAGINATION -} - data class E2EMessageDetected( val eventId: String, val roomId: String, val senderUserId: String, val senderDeviceId: String, val senderKey: String, - val sessionId: String, - val source: UISIEventSource) { + val sessionId: String + ) { companion object { - fun fromEvent(event: Event, roomId: String, source: UISIEventSource): E2EMessageDetected { + fun fromEvent(event: Event, roomId: String): E2EMessageDetected { val encryptedContent = event.content.toModel() return E2EMessageDetected( @@ -51,8 +46,7 @@ data class E2EMessageDetected( senderUserId = event.senderId ?: "", senderDeviceId = encryptedContent?.deviceId ?: "", senderKey = encryptedContent?.senderKey ?: "", - sessionId = encryptedContent?.sessionId ?: "", - source = source + sessionId = encryptedContent?.sessionId ?: "" ) } } @@ -61,6 +55,7 @@ data class E2EMessageDetected( class UISIDetector : LiveEventListener { interface UISIDetectorCallback { + val enabled: Boolean val reciprocateToDeviceEventType: String fun uisiDetected(source: E2EMessageDetected) fun uisiReciprocateRequest(source: Event) @@ -68,30 +63,16 @@ class UISIDetector : LiveEventListener { var callback: UISIDetectorCallback? = null - private val trackedEvents = mutableListOf>() + private val trackedEvents = mutableMapOf() private val executor = Executors.newSingleThreadExecutor() private val timer = Timer() private val timeoutMillis = 30_000L - var enabled = false + private val enabled: Boolean get() = callback?.enabled.orFalse() - override fun onLiveEvent(roomId: String, event: Event) { - if (!enabled) return - if (!event.isEncrypted()) return - executor.execute { - handleEventReceived(E2EMessageDetected.fromEvent(event, roomId, UISIEventSource.INCREMENTAL_SYNC)) - } - } - - override fun onPaginatedEvent(roomId: String, event: Event) { - if (!enabled) return - if (!event.isEncrypted()) return - executor.execute { - handleEventReceived(E2EMessageDetected.fromEvent(event, roomId, UISIEventSource.PAGINATION)) - } - } - - override fun onEventDecrypted(eventId: String, roomId: String, clearEvent: JsonDict) { - if (!enabled) return + override fun onEventDecrypted(event: Event, clearEvent: JsonDict) { + val eventId = event.eventId + val roomId = event.roomId + if (!enabled || eventId == null || roomId == null) return executor.execute { unTrack(eventId, roomId) } @@ -104,57 +85,43 @@ class UISIDetector : LiveEventListener { } } - override fun onEventDecryptionError(eventId: String, roomId: String, throwable: Throwable) { - if (!enabled) return - executor.execute { - unTrack(eventId, roomId)?.let { - triggerUISI(it) - } -// if (throwable is MXCryptoError.OlmError) { -// if (throwable.olmException.message == "UNKNOWN_MESSAGE_INDEX") { -// unTrack(eventId, roomId)?.let { -// triggerUISI(it) -// } -// } -// } - } - } + override fun onEventDecryptionError(event: Event, throwable: Throwable) { + val eventId = event.eventId + val roomId = event.roomId + if (!enabled || eventId == null || roomId == null) return - private fun handleEventReceived(detectorEvent: E2EMessageDetected) { - if (!enabled) return - if (trackedEvents.any { it.first == detectorEvent }) { - Timber.w("## UISIDetector: Event ${detectorEvent.eventId} is already tracked") - } else { - // track it and start timer - val timeoutTask = object : TimerTask() { - override fun run() { - executor.execute { - unTrack(detectorEvent.eventId, detectorEvent.roomId) - Timber.v("## UISIDetector: Timeout on ${detectorEvent.eventId} ") - triggerUISI(detectorEvent) - } + val trackerId: String = trackerId(eventId, roomId) + if (trackedEvents.containsKey(trackerId)) { + Timber.w("## UISIDetector: Event $eventId is already tracked") + return + } + // track it and start timer + val timeoutTask = object : TimerTask() { + override fun run() { + executor.execute { + unTrack(eventId, roomId) + Timber.v("## UISIDetector: Timeout on $eventId") + triggerUISI(E2EMessageDetected.fromEvent(event, roomId)) } } - trackedEvents.add(detectorEvent to timeoutTask) - timer.schedule(timeoutTask, timeoutMillis) } + trackedEvents[trackerId] = timeoutTask + timer.schedule(timeoutTask, timeoutMillis) } + override fun onLiveEvent(roomId: String, event: Event) { } + + override fun onPaginatedEvent(roomId: String, event: Event) { } + + private fun trackerId(eventId: String, roomId: String): String = "$roomId-$eventId" + private fun triggerUISI(source: E2EMessageDetected) { if (!enabled) return Timber.i("## UISIDetector: Unable To Decrypt $source") callback?.uisiDetected(source) } - private fun unTrack(eventId: String, roomId: String): E2EMessageDetected? { - val index = trackedEvents.indexOfFirst { it.first.eventId == eventId && it.first.roomId == roomId } - return if (index != -1) { - trackedEvents.removeAt(index).let { - it.second.cancel() - it.first - } - } else { - null - } + private fun unTrack(eventId: String, roomId: String) { + trackedEvents.remove(trackerId(eventId, roomId))?.cancel() } } diff --git a/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt b/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt index 009edcc69e..943b8d054e 100644 --- a/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt +++ b/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt @@ -352,7 +352,7 @@ class HomeActivity : private fun renderState(state: HomeActivityViewState) { when (val status = state.syncStatusServiceStatus) { - is SyncStatusService.Status.Progressing -> { + is SyncStatusService.Status.InitialSyncProgressing -> { val initSyncStepStr = initSyncStepFormatter.format(status.initSyncStep) Timber.v("$initSyncStepStr ${status.percentProgress}") views.waitingView.root.setOnClickListener { diff --git a/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt b/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt index 87de0a32e3..634f6c2c33 100644 --- a/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt @@ -179,11 +179,11 @@ class HomeActivityViewModel @AssistedInject constructor( .asFlow() .onEach { status -> when (status) { - is SyncStatusService.Status.Progressing -> { + is SyncStatusService.Status.InitialSyncProgressing -> { // Schedule a check of the bootstrap when the init sync will be finished checkBootstrap = true } - is SyncStatusService.Status.Idle -> { + is SyncStatusService.Status.Idle -> { if (checkBootstrap) { checkBootstrap = false maybeBootstrapCrossSigningAfterInitialSync()