Skip to content

Commit d0bff50

Browse files
committed
Fix UISIDetector grace period bug
1 parent cbc29d0 commit d0bff50

File tree

3 files changed

+133
-6
lines changed

3 files changed

+133
-6
lines changed

changelog.d/5886.bugfix

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix UISIDetector grace period bug

vector/src/main/java/im/vector/app/UISIDetector.kt

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -90,30 +90,35 @@ class UISIDetector : LiveEventListener {
9090
val roomId = event.roomId
9191
if (!enabled || eventId == null || roomId == null) return
9292

93-
val trackerId: String = trackerId(eventId, roomId)
94-
if (trackedEvents.containsKey(trackerId)) {
95-
Timber.w("## UISIDetector: Event $eventId is already tracked")
93+
val trackedId: String = trackedId(eventId, roomId)
94+
if (trackedEvents.containsKey(trackedId)) {
95+
Timber.v("## UISIDetector: Event $eventId is already tracked")
9696
return
9797
}
9898
// track it and start timer
9999
val timeoutTask = object : TimerTask() {
100100
override fun run() {
101101
executor.execute {
102+
// we should check if it's still tracked (it might have been decrypted)
103+
if (!trackedEvents.containsKey(trackedId)) {
104+
Timber.v("## UISIDetector: E2E error for $eventId was resolved")
105+
return@execute
106+
}
102107
unTrack(eventId, roomId)
103108
Timber.v("## UISIDetector: Timeout on $eventId")
104109
triggerUISI(E2EMessageDetected.fromEvent(event, roomId))
105110
}
106111
}
107112
}
108-
trackedEvents[trackerId] = timeoutTask
113+
trackedEvents[trackedId] = timeoutTask
109114
timer.schedule(timeoutTask, timeoutMillis)
110115
}
111116

112117
override fun onLiveEvent(roomId: String, event: Event) {}
113118

114119
override fun onPaginatedEvent(roomId: String, event: Event) {}
115120

116-
private fun trackerId(eventId: String, roomId: String): String = "$roomId-$eventId"
121+
private fun trackedId(eventId: String, roomId: String): String = "$roomId-$eventId"
117122

118123
private fun triggerUISI(source: E2EMessageDetected) {
119124
if (!enabled) return
@@ -122,6 +127,6 @@ class UISIDetector : LiveEventListener {
122127
}
123128

124129
private fun unTrack(eventId: String, roomId: String) {
125-
trackedEvents.remove(trackerId(eventId, roomId))?.cancel()
130+
trackedEvents.remove(trackedId(eventId, roomId))?.cancel()
126131
}
127132
}
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
/*
2+
* Copyright (c) 2022 New Vector Ltd
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package im.vector.app.features.crypto
18+
19+
import im.vector.app.E2EMessageDetected
20+
import im.vector.app.UISIDetector
21+
import kotlinx.coroutines.CoroutineScope
22+
import kotlinx.coroutines.Dispatchers
23+
import kotlinx.coroutines.SupervisorJob
24+
import kotlinx.coroutines.delay
25+
import kotlinx.coroutines.launch
26+
import kotlinx.coroutines.runBlocking
27+
import org.amshove.kluent.fail
28+
import org.junit.Assert
29+
import org.junit.Test
30+
import org.matrix.android.sdk.api.crypto.MXCRYPTO_ALGORITHM_MEGOLM
31+
import org.matrix.android.sdk.api.session.crypto.MXCryptoError
32+
import org.matrix.android.sdk.api.session.events.model.Event
33+
import org.matrix.android.sdk.api.session.events.model.EventType
34+
import org.matrix.android.sdk.api.session.events.model.content.EncryptedEventContent
35+
import org.matrix.android.sdk.api.session.events.model.toContent
36+
37+
class UISIDetectorTest {
38+
39+
@Test
40+
fun `trigger detection after grace period`() {
41+
val uisiDetector = UISIDetector()
42+
var detectedEvent: E2EMessageDetected? = null
43+
44+
uisiDetector.callback = object : UISIDetector.UISIDetectorCallback {
45+
override val enabled = true
46+
override val reciprocateToDeviceEventType = "foo"
47+
48+
override fun uisiDetected(source: E2EMessageDetected) {
49+
detectedEvent = source
50+
}
51+
52+
override fun uisiReciprocateRequest(source: Event) {
53+
// nop
54+
}
55+
}
56+
57+
// report a decryption error
58+
val eventId = "0001"
59+
val event = fakeEncryptedEvent(eventId, "s1", "r1")
60+
uisiDetector.onEventDecryptionError(event, fakeCryptoError())
61+
62+
runBlocking {
63+
delay(40_000)
64+
}
65+
Assert.assertEquals(eventId, detectedEvent?.eventId)
66+
}
67+
68+
@Test
69+
fun `If event decrypted during grace period should not trigger detection`() {
70+
val scope = CoroutineScope(SupervisorJob())
71+
val uisiDetector = UISIDetector()
72+
73+
uisiDetector.callback = object : UISIDetector.UISIDetectorCallback {
74+
override val enabled = true
75+
override val reciprocateToDeviceEventType = "foo"
76+
77+
override fun uisiDetected(source: E2EMessageDetected) {
78+
fail("Shouldn't trigger")
79+
}
80+
81+
override fun uisiReciprocateRequest(source: Event) {
82+
// nop
83+
}
84+
}
85+
86+
// report a decryption error
87+
val event = fakeEncryptedEvent("0001", "s1", "r1")
88+
uisiDetector.onEventDecryptionError(event, fakeCryptoError())
89+
90+
// the grace period is 30s
91+
scope.launch(Dispatchers.Default) {
92+
delay(10_000)
93+
uisiDetector.onEventDecrypted(event, emptyMap())
94+
}
95+
96+
runBlocking {
97+
delay(60_000)
98+
}
99+
}
100+
101+
private fun fakeEncryptedEvent(eventId: String, sessionId: String, roomId: String): Event {
102+
return Event(
103+
type = EventType.ENCRYPTED,
104+
eventId = eventId,
105+
roomId = roomId,
106+
content = EncryptedEventContent(
107+
algorithm = MXCRYPTO_ALGORITHM_MEGOLM,
108+
ciphertext = "AwgBEpACQEKOkd4Gp0+gSXG4M+btcrnPgsF23xs/lUmS2I4YjmqF...",
109+
sessionId = sessionId,
110+
senderKey = "5e3EIqg3JfooZnLQ2qHIcBarbassQ4qXblai0",
111+
deviceId = "FAKEE"
112+
).toContent()
113+
)
114+
}
115+
116+
private fun fakeCryptoError(error: MXCryptoError.ErrorType = MXCryptoError.ErrorType.UNKNOWN_INBOUND_SESSION_ID) = MXCryptoError.Base(
117+
error,
118+
"A description",
119+
"Human readable"
120+
)
121+
}

0 commit comments

Comments
 (0)