mirror of
https://github.com/SchildiChat/SchildiChat-android.git
synced 2024-11-28 22:18:46 +03:00
Merge pull request #5907 from vector-im/feature/bma/currentTimeMillis
Use Clock interface
This commit is contained in:
commit
330d802079
134 changed files with 1109 additions and 610 deletions
1
changelog.d/5907.sdk
Normal file
1
changelog.d/5907.sdk
Normal file
|
@ -0,0 +1 @@
|
||||||
|
Replace usage of `System.currentTimeMillis()` by a `Clock` interface
|
|
@ -57,7 +57,8 @@ import java.util.concurrent.TimeUnit
|
||||||
class CommonTestHelper(context: Context) {
|
class CommonTestHelper(context: Context) {
|
||||||
|
|
||||||
internal val matrix: TestMatrix
|
internal val matrix: TestMatrix
|
||||||
val coroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.Main)
|
private val coroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.Main)
|
||||||
|
private var accountNumber = 0
|
||||||
|
|
||||||
fun getTestInterceptor(session: Session): MockOkHttpInterceptor? = TestModule.interceptorForSession(session.sessionId) as? MockOkHttpInterceptor
|
fun getTestInterceptor(session: Session): MockOkHttpInterceptor? = TestModule.interceptorForSession(session.sessionId) as? MockOkHttpInterceptor
|
||||||
|
|
||||||
|
@ -167,7 +168,8 @@ class CommonTestHelper(context: Context) {
|
||||||
if (rootThreadEventId != null) {
|
if (rootThreadEventId != null) {
|
||||||
room.relationService().replyInThread(
|
room.relationService().replyInThread(
|
||||||
rootThreadEventId = rootThreadEventId,
|
rootThreadEventId = rootThreadEventId,
|
||||||
replyInThreadText = formattedMessage)
|
replyInThreadText = formattedMessage
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
room.sendService().sendTextMessage(formattedMessage)
|
room.sendService().sendTextMessage(formattedMessage)
|
||||||
}
|
}
|
||||||
|
@ -237,7 +239,7 @@ class CommonTestHelper(context: Context) {
|
||||||
password: String,
|
password: String,
|
||||||
testParams: SessionTestParams): Session {
|
testParams: SessionTestParams): Session {
|
||||||
val session = createAccountAndSync(
|
val session = createAccountAndSync(
|
||||||
userNamePrefix + "_" + System.currentTimeMillis() + UUID.randomUUID(),
|
userNamePrefix + "_" + accountNumber++ + "_" + UUID.randomUUID(),
|
||||||
password,
|
password,
|
||||||
testParams
|
testParams
|
||||||
)
|
)
|
||||||
|
|
|
@ -29,8 +29,10 @@ import org.matrix.android.sdk.api.session.crypto.attachments.toElementToDecrypt
|
||||||
import org.matrix.android.sdk.api.session.crypto.model.EncryptedFileInfo
|
import org.matrix.android.sdk.api.session.crypto.model.EncryptedFileInfo
|
||||||
import org.matrix.android.sdk.api.session.crypto.model.EncryptedFileKey
|
import org.matrix.android.sdk.api.session.crypto.model.EncryptedFileKey
|
||||||
import org.matrix.android.sdk.internal.crypto.attachments.MXEncryptedAttachments
|
import org.matrix.android.sdk.internal.crypto.attachments.MXEncryptedAttachments
|
||||||
|
import org.matrix.android.sdk.internal.util.time.DefaultClock
|
||||||
import java.io.ByteArrayOutputStream
|
import java.io.ByteArrayOutputStream
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
|
import java.util.UUID
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unit tests AttachmentEncryptionTest.
|
* Unit tests AttachmentEncryptionTest.
|
||||||
|
@ -48,13 +50,18 @@ class AttachmentEncryptionTest {
|
||||||
inputStream = if (inputAsByteArray.isEmpty()) {
|
inputStream = if (inputAsByteArray.isEmpty()) {
|
||||||
inputAsByteArray.inputStream()
|
inputAsByteArray.inputStream()
|
||||||
} else {
|
} else {
|
||||||
val memoryFile = MemoryFile("file" + System.currentTimeMillis(), inputAsByteArray.size)
|
val memoryFile = MemoryFile("file_" + UUID.randomUUID(), inputAsByteArray.size)
|
||||||
memoryFile.outputStream.write(inputAsByteArray)
|
memoryFile.outputStream.write(inputAsByteArray)
|
||||||
memoryFile.inputStream
|
memoryFile.inputStream
|
||||||
}
|
}
|
||||||
|
|
||||||
val decryptedStream = ByteArrayOutputStream()
|
val decryptedStream = ByteArrayOutputStream()
|
||||||
val result = MXEncryptedAttachments.decryptAttachment(inputStream, encryptedFileInfo.toElementToDecrypt()!!, decryptedStream)
|
val result = MXEncryptedAttachments.decryptAttachment(
|
||||||
|
attachmentStream = inputStream,
|
||||||
|
elementToDecrypt = encryptedFileInfo.toElementToDecrypt()!!,
|
||||||
|
outputStream = decryptedStream,
|
||||||
|
clock = DefaultClock()
|
||||||
|
)
|
||||||
|
|
||||||
assert(result)
|
assert(result)
|
||||||
|
|
||||||
|
@ -117,9 +124,13 @@ class AttachmentEncryptionTest {
|
||||||
url = "dummyUrl"
|
url = "dummyUrl"
|
||||||
)
|
)
|
||||||
|
|
||||||
assertEquals("YWxwaGFudW1lcmljYWxseWFscGhhbnVtZXJpY2FsbHlhbHBoYW51bWVyaWNhbGx5YWxwaGFudW1lcmljYWxseQ",
|
assertEquals(
|
||||||
checkDecryption("zhtFStAeFx0s+9L/sSQO+WQMtldqYEHqTxMduJrCIpnkyer09kxJJuA4K+adQE4w+7jZe/vR9kIcqj9rOhDR8Q",
|
"YWxwaGFudW1lcmljYWxseWFscGhhbnVtZXJpY2FsbHlhbHBoYW51bWVyaWNhbGx5YWxwaGFudW1lcmljYWxseQ",
|
||||||
encryptedFileInfo))
|
checkDecryption(
|
||||||
|
"zhtFStAeFx0s+9L/sSQO+WQMtldqYEHqTxMduJrCIpnkyer09kxJJuA4K+adQE4w+7jZe/vR9kIcqj9rOhDR8Q",
|
||||||
|
encryptedFileInfo
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -138,8 +149,12 @@ class AttachmentEncryptionTest {
|
||||||
url = "dummyUrl"
|
url = "dummyUrl"
|
||||||
)
|
)
|
||||||
|
|
||||||
assertNotEquals("YWxwaGFudW1lcmljYWxseWFscGhhbnVtZXJpY2FsbHlhbHBoYW51bWVyaWNhbGx5YWxwaGFudW1lcmljYWxseQ",
|
assertNotEquals(
|
||||||
checkDecryption("tJVNBVJ/vl36UQt4Y5e5m84bRUrQHhcdLPvS/7EkDvlkDLZXamBB6k8THbiawiKZ5Mnq9PZMSSbgOCvmnUBOMA",
|
"YWxwaGFudW1lcmljYWxseWFscGhhbnVtZXJpY2FsbHlhbHBoYW51bWVyaWNhbGx5YWxwaGFudW1lcmljYWxseQ",
|
||||||
encryptedFileInfo))
|
checkDecryption(
|
||||||
|
"tJVNBVJ/vl36UQt4Y5e5m84bRUrQHhcdLPvS/7EkDvlkDLZXamBB6k8THbiawiKZ5Mnq9PZMSSbgOCvmnUBOMA",
|
||||||
|
encryptedFileInfo
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,7 @@ import org.matrix.android.sdk.internal.crypto.store.db.RealmCryptoStore
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.RealmCryptoStoreModule
|
import org.matrix.android.sdk.internal.crypto.store.db.RealmCryptoStoreModule
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.mapper.CrossSigningKeysMapper
|
import org.matrix.android.sdk.internal.crypto.store.db.mapper.CrossSigningKeysMapper
|
||||||
import org.matrix.android.sdk.internal.di.MoshiProvider
|
import org.matrix.android.sdk.internal.di.MoshiProvider
|
||||||
|
import org.matrix.android.sdk.internal.util.time.DefaultClock
|
||||||
import kotlin.random.Random
|
import kotlin.random.Random
|
||||||
|
|
||||||
internal class CryptoStoreHelper {
|
internal class CryptoStoreHelper {
|
||||||
|
@ -34,7 +35,8 @@ internal class CryptoStoreHelper {
|
||||||
.build(),
|
.build(),
|
||||||
crossSigningKeysMapper = CrossSigningKeysMapper(MoshiProvider.providesMoshi()),
|
crossSigningKeysMapper = CrossSigningKeysMapper(MoshiProvider.providesMoshi()),
|
||||||
userId = "userId_" + Random.nextInt(),
|
userId = "userId_" + Random.nextInt(),
|
||||||
deviceId = "deviceId_sample"
|
deviceId = "deviceId_sample",
|
||||||
|
clock = DefaultClock(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,7 @@ import org.junit.runner.RunWith
|
||||||
import org.matrix.android.sdk.InstrumentedTest
|
import org.matrix.android.sdk.InstrumentedTest
|
||||||
import org.matrix.android.sdk.internal.crypto.model.OlmSessionWrapper
|
import org.matrix.android.sdk.internal.crypto.model.OlmSessionWrapper
|
||||||
import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore
|
import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore
|
||||||
|
import org.matrix.android.sdk.internal.util.time.DefaultClock
|
||||||
import org.matrix.olm.OlmAccount
|
import org.matrix.olm.OlmAccount
|
||||||
import org.matrix.olm.OlmManager
|
import org.matrix.olm.OlmManager
|
||||||
import org.matrix.olm.OlmSession
|
import org.matrix.olm.OlmSession
|
||||||
|
@ -37,6 +38,7 @@ private const val DUMMY_DEVICE_KEY = "DeviceKey"
|
||||||
class CryptoStoreTest : InstrumentedTest {
|
class CryptoStoreTest : InstrumentedTest {
|
||||||
|
|
||||||
private val cryptoStoreHelper = CryptoStoreHelper()
|
private val cryptoStoreHelper = CryptoStoreHelper()
|
||||||
|
private val clock = DefaultClock()
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
fun setup() {
|
fun setup() {
|
||||||
|
@ -106,7 +108,7 @@ class CryptoStoreTest : InstrumentedTest {
|
||||||
|
|
||||||
// Note: we cannot be sure what will be the result of getLastUsedSessionId() here
|
// Note: we cannot be sure what will be the result of getLastUsedSessionId() here
|
||||||
|
|
||||||
olmSessionWrapper2.onMessageReceived()
|
olmSessionWrapper2.onMessageReceived(clock.epochMillis())
|
||||||
cryptoStore.storeSession(olmSessionWrapper2, DUMMY_DEVICE_KEY)
|
cryptoStore.storeSession(olmSessionWrapper2, DUMMY_DEVICE_KEY)
|
||||||
|
|
||||||
// sessionId2 is returned now
|
// sessionId2 is returned now
|
||||||
|
@ -114,7 +116,7 @@ class CryptoStoreTest : InstrumentedTest {
|
||||||
|
|
||||||
Thread.sleep(2)
|
Thread.sleep(2)
|
||||||
|
|
||||||
olmSessionWrapper1.onMessageReceived()
|
olmSessionWrapper1.onMessageReceived(clock.epochMillis())
|
||||||
cryptoStore.storeSession(olmSessionWrapper1, DUMMY_DEVICE_KEY)
|
cryptoStore.storeSession(olmSessionWrapper1, DUMMY_DEVICE_KEY)
|
||||||
|
|
||||||
// sessionId1 is returned now
|
// sessionId1 is returned now
|
||||||
|
|
|
@ -35,6 +35,7 @@ import org.matrix.android.sdk.internal.database.mapper.toEntity
|
||||||
import org.matrix.android.sdk.internal.database.model.ChunkEntity
|
import org.matrix.android.sdk.internal.database.model.ChunkEntity
|
||||||
import org.matrix.android.sdk.internal.database.model.SessionRealmModule
|
import org.matrix.android.sdk.internal.database.model.SessionRealmModule
|
||||||
import org.matrix.android.sdk.internal.session.room.timeline.PaginationDirection
|
import org.matrix.android.sdk.internal.session.room.timeline.PaginationDirection
|
||||||
|
import org.matrix.android.sdk.internal.util.time.DefaultClock
|
||||||
import org.matrix.android.sdk.session.room.timeline.RoomDataHelper.createFakeListOfEvents
|
import org.matrix.android.sdk.session.room.timeline.RoomDataHelper.createFakeListOfEvents
|
||||||
import org.matrix.android.sdk.session.room.timeline.RoomDataHelper.createFakeMessageEvent
|
import org.matrix.android.sdk.session.room.timeline.RoomDataHelper.createFakeMessageEvent
|
||||||
|
|
||||||
|
@ -42,6 +43,7 @@ import org.matrix.android.sdk.session.room.timeline.RoomDataHelper.createFakeMes
|
||||||
internal class ChunkEntityTest : InstrumentedTest {
|
internal class ChunkEntityTest : InstrumentedTest {
|
||||||
|
|
||||||
private lateinit var monarchy: Monarchy
|
private lateinit var monarchy: Monarchy
|
||||||
|
private val clock = DefaultClock()
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
fun setup() {
|
fun setup() {
|
||||||
|
@ -59,7 +61,7 @@ internal class ChunkEntityTest : InstrumentedTest {
|
||||||
monarchy.runTransactionSync { realm ->
|
monarchy.runTransactionSync { realm ->
|
||||||
val chunk: ChunkEntity = realm.createObject()
|
val chunk: ChunkEntity = realm.createObject()
|
||||||
|
|
||||||
val fakeEvent = createFakeMessageEvent().toEntity(ROOM_ID, SendState.SYNCED, System.currentTimeMillis()).let {
|
val fakeEvent = createFakeMessageEvent().toEntity(ROOM_ID, SendState.SYNCED, clock.epochMillis()).let {
|
||||||
realm.copyToRealm(it)
|
realm.copyToRealm(it)
|
||||||
}
|
}
|
||||||
chunk.addTimelineEvent(
|
chunk.addTimelineEvent(
|
||||||
|
@ -75,7 +77,7 @@ internal class ChunkEntityTest : InstrumentedTest {
|
||||||
fun add_shouldNotAdd_whenAlreadyIncluded() {
|
fun add_shouldNotAdd_whenAlreadyIncluded() {
|
||||||
monarchy.runTransactionSync { realm ->
|
monarchy.runTransactionSync { realm ->
|
||||||
val chunk: ChunkEntity = realm.createObject()
|
val chunk: ChunkEntity = realm.createObject()
|
||||||
val fakeEvent = createFakeMessageEvent().toEntity(ROOM_ID, SendState.SYNCED, System.currentTimeMillis()).let {
|
val fakeEvent = createFakeMessageEvent().toEntity(ROOM_ID, SendState.SYNCED, clock.epochMillis()).let {
|
||||||
realm.copyToRealm(it)
|
realm.copyToRealm(it)
|
||||||
}
|
}
|
||||||
chunk.addTimelineEvent(
|
chunk.addTimelineEvent(
|
||||||
|
@ -153,7 +155,7 @@ internal class ChunkEntityTest : InstrumentedTest {
|
||||||
events: List<Event>,
|
events: List<Event>,
|
||||||
direction: PaginationDirection) {
|
direction: PaginationDirection) {
|
||||||
events.forEach { event ->
|
events.forEach { event ->
|
||||||
val fakeEvent = event.toEntity(roomId, SendState.SYNCED, System.currentTimeMillis()).let {
|
val fakeEvent = event.toEntity(roomId, SendState.SYNCED, clock.epochMillis()).let {
|
||||||
realm.copyToRealm(it)
|
realm.copyToRealm(it)
|
||||||
}
|
}
|
||||||
addTimelineEvent(
|
addTimelineEvent(
|
||||||
|
|
|
@ -26,8 +26,8 @@ internal class FakeGetContextOfEventTask constructor(private val tokenChunkEvent
|
||||||
override suspend fun execute(params: GetContextOfEventTask.Params): TokenChunkEventPersistor.Result {
|
override suspend fun execute(params: GetContextOfEventTask.Params): TokenChunkEventPersistor.Result {
|
||||||
val fakeEvents = RoomDataHelper.createFakeListOfEvents(30)
|
val fakeEvents = RoomDataHelper.createFakeListOfEvents(30)
|
||||||
val tokenChunkEvent = FakeTokenChunkEvent(
|
val tokenChunkEvent = FakeTokenChunkEvent(
|
||||||
Random.nextLong(System.currentTimeMillis()).toString(),
|
Random.nextLong().toString(),
|
||||||
Random.nextLong(System.currentTimeMillis()).toString(),
|
Random.nextLong().toString(),
|
||||||
fakeEvents
|
fakeEvents
|
||||||
)
|
)
|
||||||
return tokenChunkEventPersistor.insertInDb(tokenChunkEvent, params.roomId, PaginationDirection.BACKWARDS)
|
return tokenChunkEventPersistor.insertInDb(tokenChunkEvent, params.roomId, PaginationDirection.BACKWARDS)
|
||||||
|
|
|
@ -25,7 +25,7 @@ internal class FakePaginationTask @Inject constructor(private val tokenChunkEven
|
||||||
|
|
||||||
override suspend fun execute(params: PaginationTask.Params): TokenChunkEventPersistor.Result {
|
override suspend fun execute(params: PaginationTask.Params): TokenChunkEventPersistor.Result {
|
||||||
val fakeEvents = RoomDataHelper.createFakeListOfEvents(30)
|
val fakeEvents = RoomDataHelper.createFakeListOfEvents(30)
|
||||||
val tokenChunkEvent = FakeTokenChunkEvent(params.from, Random.nextLong(System.currentTimeMillis()).toString(), fakeEvents)
|
val tokenChunkEvent = FakeTokenChunkEvent(params.from, Random.nextLong().toString(), fakeEvents)
|
||||||
return tokenChunkEventPersistor.insertInDb(tokenChunkEvent, params.roomId, params.direction)
|
return tokenChunkEventPersistor.insertInDb(tokenChunkEvent, params.roomId, params.direction)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,8 +46,9 @@ data class IncomingRequestCancellation(
|
||||||
* Factory
|
* Factory
|
||||||
*
|
*
|
||||||
* @param event the event
|
* @param event the event
|
||||||
|
* @param currentTimeMillis the current time in milliseconds
|
||||||
*/
|
*/
|
||||||
fun fromEvent(event: Event): IncomingRequestCancellation? {
|
fun fromEvent(event: Event, currentTimeMillis: Long): IncomingRequestCancellation? {
|
||||||
return event.getClearContent()
|
return event.getClearContent()
|
||||||
.toModel<ShareRequestCancellation>()
|
.toModel<ShareRequestCancellation>()
|
||||||
?.let {
|
?.let {
|
||||||
|
@ -55,7 +56,7 @@ data class IncomingRequestCancellation(
|
||||||
userId = event.senderId,
|
userId = event.senderId,
|
||||||
deviceId = it.requestingDeviceId,
|
deviceId = it.requestingDeviceId,
|
||||||
requestId = it.requestId,
|
requestId = it.requestId,
|
||||||
localCreationTimestamp = event.ageLocalTs ?: System.currentTimeMillis()
|
localCreationTimestamp = event.ageLocalTs ?: currentTimeMillis
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,8 +64,9 @@ data class IncomingRoomKeyRequest(
|
||||||
* Factory
|
* Factory
|
||||||
*
|
*
|
||||||
* @param event the event
|
* @param event the event
|
||||||
|
* @param currentTimeMillis the current time in milliseconds
|
||||||
*/
|
*/
|
||||||
fun fromEvent(event: Event): IncomingRoomKeyRequest? {
|
fun fromEvent(event: Event, currentTimeMillis: Long): IncomingRoomKeyRequest? {
|
||||||
return event.getClearContent()
|
return event.getClearContent()
|
||||||
.toModel<RoomKeyShareRequest>()
|
.toModel<RoomKeyShareRequest>()
|
||||||
?.let {
|
?.let {
|
||||||
|
@ -74,7 +75,7 @@ data class IncomingRoomKeyRequest(
|
||||||
deviceId = it.requestingDeviceId,
|
deviceId = it.requestingDeviceId,
|
||||||
requestId = it.requestId,
|
requestId = it.requestId,
|
||||||
requestBody = it.body ?: RoomKeyRequestBody(),
|
requestBody = it.body ?: RoomKeyRequestBody(),
|
||||||
localCreationTimestamp = event.ageLocalTs ?: System.currentTimeMillis()
|
localCreationTimestamp = event.ageLocalTs ?: currentTimeMillis
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,8 +64,9 @@ data class IncomingSecretShareRequest(
|
||||||
* Factory
|
* Factory
|
||||||
*
|
*
|
||||||
* @param event the event
|
* @param event the event
|
||||||
|
* @param currentTimeMillis the current time in milliseconds
|
||||||
*/
|
*/
|
||||||
fun fromEvent(event: Event): IncomingSecretShareRequest? {
|
fun fromEvent(event: Event, currentTimeMillis: Long): IncomingSecretShareRequest? {
|
||||||
return event.getClearContent()
|
return event.getClearContent()
|
||||||
.toModel<SecretShareRequest>()
|
.toModel<SecretShareRequest>()
|
||||||
?.let {
|
?.let {
|
||||||
|
@ -74,7 +75,7 @@ data class IncomingSecretShareRequest(
|
||||||
deviceId = it.requestingDeviceId,
|
deviceId = it.requestingDeviceId,
|
||||||
requestId = it.requestId,
|
requestId = it.requestId,
|
||||||
secretName = it.secretName,
|
secretName = it.secretName,
|
||||||
localCreationTimestamp = event.ageLocalTs ?: System.currentTimeMillis()
|
localCreationTimestamp = event.ageLocalTs ?: currentTimeMillis
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -129,11 +129,10 @@ interface VerificationService {
|
||||||
private const val TEN_MINUTES_IN_MILLIS = 10 * 60 * 1000
|
private const val TEN_MINUTES_IN_MILLIS = 10 * 60 * 1000
|
||||||
private const val FIVE_MINUTES_IN_MILLIS = 5 * 60 * 1000
|
private const val FIVE_MINUTES_IN_MILLIS = 5 * 60 * 1000
|
||||||
|
|
||||||
fun isValidRequest(age: Long?): Boolean {
|
fun isValidRequest(age: Long?, currentTimeMillis: Long): Boolean {
|
||||||
if (age == null) return false
|
if (age == null) return false
|
||||||
val now = System.currentTimeMillis()
|
val tooInThePast = currentTimeMillis - TEN_MINUTES_IN_MILLIS
|
||||||
val tooInThePast = now - TEN_MINUTES_IN_MILLIS
|
val tooInTheFuture = currentTimeMillis + FIVE_MINUTES_IN_MILLIS
|
||||||
val tooInTheFuture = now + FIVE_MINUTES_IN_MILLIS
|
|
||||||
return age in tooInThePast..tooInTheFuture
|
return age in tooInThePast..tooInTheFuture
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,6 +32,7 @@ import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore
|
||||||
import org.matrix.android.sdk.internal.crypto.tasks.SendToDeviceTask
|
import org.matrix.android.sdk.internal.crypto.tasks.SendToDeviceTask
|
||||||
import org.matrix.android.sdk.internal.crypto.tasks.createUniqueTxnId
|
import org.matrix.android.sdk.internal.crypto.tasks.createUniqueTxnId
|
||||||
import org.matrix.android.sdk.internal.session.SessionComponent
|
import org.matrix.android.sdk.internal.session.SessionComponent
|
||||||
|
import org.matrix.android.sdk.internal.util.time.Clock
|
||||||
import org.matrix.android.sdk.internal.worker.SessionSafeCoroutineWorker
|
import org.matrix.android.sdk.internal.worker.SessionSafeCoroutineWorker
|
||||||
import org.matrix.android.sdk.internal.worker.SessionWorkerParams
|
import org.matrix.android.sdk.internal.worker.SessionWorkerParams
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
@ -65,6 +66,7 @@ internal class CancelGossipRequestWorker(context: Context, params: WorkerParamet
|
||||||
@Inject lateinit var sendToDeviceTask: SendToDeviceTask
|
@Inject lateinit var sendToDeviceTask: SendToDeviceTask
|
||||||
@Inject lateinit var cryptoStore: IMXCryptoStore
|
@Inject lateinit var cryptoStore: IMXCryptoStore
|
||||||
@Inject lateinit var credentials: Credentials
|
@Inject lateinit var credentials: Credentials
|
||||||
|
@Inject lateinit var clock: Clock
|
||||||
|
|
||||||
override fun injectWith(injector: SessionComponent) {
|
override fun injectWith(injector: SessionComponent) {
|
||||||
injector.inject(this)
|
injector.inject(this)
|
||||||
|
@ -85,7 +87,7 @@ internal class CancelGossipRequestWorker(context: Context, params: WorkerParamet
|
||||||
content = toDeviceContent.toContent(),
|
content = toDeviceContent.toContent(),
|
||||||
senderId = credentials.userId
|
senderId = credentials.userId
|
||||||
).also {
|
).also {
|
||||||
it.ageLocalTs = System.currentTimeMillis()
|
it.ageLocalTs = clock.epochMillis()
|
||||||
})
|
})
|
||||||
|
|
||||||
params.recipients.forEach { userToDeviceMap ->
|
params.recipients.forEach { userToDeviceMap ->
|
||||||
|
|
|
@ -103,6 +103,7 @@ import org.matrix.android.sdk.internal.task.TaskThread
|
||||||
import org.matrix.android.sdk.internal.task.configureWith
|
import org.matrix.android.sdk.internal.task.configureWith
|
||||||
import org.matrix.android.sdk.internal.task.launchToCallback
|
import org.matrix.android.sdk.internal.task.launchToCallback
|
||||||
import org.matrix.android.sdk.internal.util.JsonCanonicalizer
|
import org.matrix.android.sdk.internal.util.JsonCanonicalizer
|
||||||
|
import org.matrix.android.sdk.internal.util.time.Clock
|
||||||
import org.matrix.olm.OlmManager
|
import org.matrix.olm.OlmManager
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.util.concurrent.atomic.AtomicBoolean
|
import java.util.concurrent.atomic.AtomicBoolean
|
||||||
|
@ -130,6 +131,7 @@ internal class DefaultCryptoService @Inject constructor(
|
||||||
private val userId: String,
|
private val userId: String,
|
||||||
@DeviceId
|
@DeviceId
|
||||||
private val deviceId: String?,
|
private val deviceId: String?,
|
||||||
|
private val clock: Clock,
|
||||||
private val myDeviceInfoHolder: Lazy<MyDeviceInfoHolder>,
|
private val myDeviceInfoHolder: Lazy<MyDeviceInfoHolder>,
|
||||||
// the crypto store
|
// the crypto store
|
||||||
private val cryptoStore: IMXCryptoStore,
|
private val cryptoStore: IMXCryptoStore,
|
||||||
|
@ -701,11 +703,11 @@ internal class DefaultCryptoService @Inject constructor(
|
||||||
}
|
}
|
||||||
val safeAlgorithm = alg
|
val safeAlgorithm = alg
|
||||||
if (safeAlgorithm != null) {
|
if (safeAlgorithm != null) {
|
||||||
val t0 = System.currentTimeMillis()
|
val t0 = clock.epochMillis()
|
||||||
Timber.tag(loggerTag.value).v("encryptEventContent() starts")
|
Timber.tag(loggerTag.value).v("encryptEventContent() starts")
|
||||||
runCatching {
|
runCatching {
|
||||||
val content = safeAlgorithm.encryptEventContent(eventContent, eventType, userIds)
|
val content = safeAlgorithm.encryptEventContent(eventContent, eventType, userIds)
|
||||||
Timber.tag(loggerTag.value).v("## CRYPTO | encryptEventContent() : succeeds after ${System.currentTimeMillis() - t0} ms")
|
Timber.tag(loggerTag.value).v("## CRYPTO | encryptEventContent() : succeeds after ${clock.epochMillis() - t0} ms")
|
||||||
MXEncryptEventContentResult(content, EventType.ENCRYPTED)
|
MXEncryptEventContentResult(content, EventType.ENCRYPTED)
|
||||||
}.foldToCallback(callback)
|
}.foldToCallback(callback)
|
||||||
} else {
|
} else {
|
||||||
|
@ -1022,9 +1024,9 @@ internal class DefaultCryptoService @Inject constructor(
|
||||||
return withContext(coroutineDispatchers.crypto) {
|
return withContext(coroutineDispatchers.crypto) {
|
||||||
Timber.tag(loggerTag.value).v("importRoomKeys starts")
|
Timber.tag(loggerTag.value).v("importRoomKeys starts")
|
||||||
|
|
||||||
val t0 = System.currentTimeMillis()
|
val t0 = clock.epochMillis()
|
||||||
val roomKeys = MXMegolmExportEncryption.decryptMegolmKeyFile(roomKeysAsArray, password)
|
val roomKeys = MXMegolmExportEncryption.decryptMegolmKeyFile(roomKeysAsArray, password)
|
||||||
val t1 = System.currentTimeMillis()
|
val t1 = clock.epochMillis()
|
||||||
|
|
||||||
Timber.tag(loggerTag.value).v("importRoomKeys : decryptMegolmKeyFile done in ${t1 - t0} ms")
|
Timber.tag(loggerTag.value).v("importRoomKeys : decryptMegolmKeyFile done in ${t1 - t0} ms")
|
||||||
|
|
||||||
|
@ -1032,7 +1034,7 @@ internal class DefaultCryptoService @Inject constructor(
|
||||||
.adapter<List<MegolmSessionData>>(Types.newParameterizedType(List::class.java, MegolmSessionData::class.java))
|
.adapter<List<MegolmSessionData>>(Types.newParameterizedType(List::class.java, MegolmSessionData::class.java))
|
||||||
.fromJson(roomKeys)
|
.fromJson(roomKeys)
|
||||||
|
|
||||||
val t2 = System.currentTimeMillis()
|
val t2 = clock.epochMillis()
|
||||||
|
|
||||||
Timber.tag(loggerTag.value).v("importRoomKeys : JSON parsing ${t2 - t1} ms")
|
Timber.tag(loggerTag.value).v("importRoomKeys : JSON parsing ${t2 - t1} ms")
|
||||||
|
|
||||||
|
@ -1224,7 +1226,7 @@ internal class DefaultCryptoService @Inject constructor(
|
||||||
// val deviceKey = deviceInfo.identityKey()
|
// val deviceKey = deviceInfo.identityKey()
|
||||||
//
|
//
|
||||||
// val lastForcedDate = lastNewSessionForcedDates.getObject(senderId, deviceKey) ?: 0
|
// val lastForcedDate = lastNewSessionForcedDates.getObject(senderId, deviceKey) ?: 0
|
||||||
// val now = System.currentTimeMillis()
|
// val now = clock.epochMillis()
|
||||||
// if (now - lastForcedDate < CRYPTO_MIN_FORCE_SESSION_PERIOD_MILLIS) {
|
// if (now - lastForcedDate < CRYPTO_MIN_FORCE_SESSION_PERIOD_MILLIS) {
|
||||||
// Timber.d("## CRYPTO | markOlmSessionForUnwedging: New session already forced with device at $lastForcedDate. Not forcing another")
|
// Timber.d("## CRYPTO | markOlmSessionForUnwedging: New session already forced with device at $lastForcedDate. Not forcing another")
|
||||||
// return
|
// return
|
||||||
|
|
|
@ -31,19 +31,23 @@ import org.matrix.android.sdk.internal.session.SessionScope
|
||||||
import org.matrix.android.sdk.internal.session.sync.SyncTokenStore
|
import org.matrix.android.sdk.internal.session.sync.SyncTokenStore
|
||||||
import org.matrix.android.sdk.internal.task.TaskExecutor
|
import org.matrix.android.sdk.internal.task.TaskExecutor
|
||||||
import org.matrix.android.sdk.internal.util.logLimit
|
import org.matrix.android.sdk.internal.util.logLimit
|
||||||
|
import org.matrix.android.sdk.internal.util.time.Clock
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
// Legacy name: MXDeviceList
|
// Legacy name: MXDeviceList
|
||||||
@SessionScope
|
@SessionScope
|
||||||
internal class DeviceListManager @Inject constructor(private val cryptoStore: IMXCryptoStore,
|
internal class DeviceListManager @Inject constructor(
|
||||||
|
private val cryptoStore: IMXCryptoStore,
|
||||||
private val olmDevice: MXOlmDevice,
|
private val olmDevice: MXOlmDevice,
|
||||||
private val syncTokenStore: SyncTokenStore,
|
private val syncTokenStore: SyncTokenStore,
|
||||||
private val credentials: Credentials,
|
private val credentials: Credentials,
|
||||||
private val downloadKeysForUsersTask: DownloadKeysForUsersTask,
|
private val downloadKeysForUsersTask: DownloadKeysForUsersTask,
|
||||||
private val cryptoSessionInfoProvider: CryptoSessionInfoProvider,
|
private val cryptoSessionInfoProvider: CryptoSessionInfoProvider,
|
||||||
coroutineDispatchers: MatrixCoroutineDispatchers,
|
coroutineDispatchers: MatrixCoroutineDispatchers,
|
||||||
private val taskExecutor: TaskExecutor) {
|
private val taskExecutor: TaskExecutor,
|
||||||
|
private val clock: Clock,
|
||||||
|
) {
|
||||||
|
|
||||||
interface UserDevicesUpdateListener {
|
interface UserDevicesUpdateListener {
|
||||||
fun onUsersDeviceUpdate(userIds: List<String>)
|
fun onUsersDeviceUpdate(userIds: List<String>)
|
||||||
|
@ -310,9 +314,9 @@ internal class DeviceListManager @Inject constructor(private val cryptoStore: IM
|
||||||
stored
|
stored
|
||||||
} else {
|
} else {
|
||||||
Timber.v("## CRYPTO | downloadKeys() : starts")
|
Timber.v("## CRYPTO | downloadKeys() : starts")
|
||||||
val t0 = System.currentTimeMillis()
|
val t0 = clock.epochMillis()
|
||||||
val result = doKeyDownloadForUsers(downloadUsers)
|
val result = doKeyDownloadForUsers(downloadUsers)
|
||||||
Timber.v("## CRYPTO | downloadKeys() : doKeyDownloadForUsers succeeds after ${System.currentTimeMillis() - t0} ms")
|
Timber.v("## CRYPTO | downloadKeys() : doKeyDownloadForUsers succeeds after ${clock.epochMillis() - t0} ms")
|
||||||
result.also {
|
result.also {
|
||||||
it.addEntriesFromMap(stored)
|
it.addEntriesFromMap(stored)
|
||||||
}
|
}
|
||||||
|
@ -475,8 +479,10 @@ internal class DeviceListManager @Inject constructor(private val cryptoStore: IM
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isVerified) {
|
if (!isVerified) {
|
||||||
Timber.e("## CRYPTO | validateDeviceKeys() : Unable to verify signature on device " + userId + ":" +
|
Timber.e(
|
||||||
deviceKeys.deviceId + " with error " + errorMessage)
|
"## CRYPTO | validateDeviceKeys() : Unable to verify signature on device " + userId + ":" +
|
||||||
|
deviceKeys.deviceId + " with error " + errorMessage
|
||||||
|
)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -486,9 +492,11 @@ internal class DeviceListManager @Inject constructor(private val cryptoStore: IM
|
||||||
// best off sticking with the original keys.
|
// best off sticking with the original keys.
|
||||||
//
|
//
|
||||||
// Should we warn the user about it somehow?
|
// Should we warn the user about it somehow?
|
||||||
Timber.e("## CRYPTO | validateDeviceKeys() : WARNING:Ed25519 key for device " + userId + ":" +
|
Timber.e(
|
||||||
|
"## CRYPTO | validateDeviceKeys() : WARNING:Ed25519 key for device " + userId + ":" +
|
||||||
deviceKeys.deviceId + " has changed : " +
|
deviceKeys.deviceId + " has changed : " +
|
||||||
previouslyStoredDeviceKeys.fingerprint() + " -> " + signKey)
|
previouslyStoredDeviceKeys.fingerprint() + " -> " + signKey
|
||||||
|
)
|
||||||
|
|
||||||
Timber.e("## CRYPTO | validateDeviceKeys() : $previouslyStoredDeviceKeys -> $deviceKeys")
|
Timber.e("## CRYPTO | validateDeviceKeys() : $previouslyStoredDeviceKeys -> $deviceKeys")
|
||||||
Timber.e("## CRYPTO | validateDeviceKeys() : ${previouslyStoredDeviceKeys.keys} -> ${deviceKeys.keys}")
|
Timber.e("## CRYPTO | validateDeviceKeys() : ${previouslyStoredDeviceKeys.keys} -> ${deviceKeys.keys}")
|
||||||
|
|
|
@ -36,6 +36,7 @@ import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore
|
||||||
import org.matrix.android.sdk.internal.crypto.tasks.SendToDeviceTask
|
import org.matrix.android.sdk.internal.crypto.tasks.SendToDeviceTask
|
||||||
import org.matrix.android.sdk.internal.extensions.foldToCallback
|
import org.matrix.android.sdk.internal.extensions.foldToCallback
|
||||||
import org.matrix.android.sdk.internal.session.SessionScope
|
import org.matrix.android.sdk.internal.session.SessionScope
|
||||||
|
import org.matrix.android.sdk.internal.util.time.Clock
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@ -47,6 +48,7 @@ private val loggerTag = LoggerTag("CryptoSyncHandler", LoggerTag.CRYPTO)
|
||||||
internal class EventDecryptor @Inject constructor(
|
internal class EventDecryptor @Inject constructor(
|
||||||
private val cryptoCoroutineScope: CoroutineScope,
|
private val cryptoCoroutineScope: CoroutineScope,
|
||||||
private val coroutineDispatchers: MatrixCoroutineDispatchers,
|
private val coroutineDispatchers: MatrixCoroutineDispatchers,
|
||||||
|
private val clock: Clock,
|
||||||
private val roomDecryptorProvider: RoomDecryptorProvider,
|
private val roomDecryptorProvider: RoomDecryptorProvider,
|
||||||
private val messageEncrypter: MessageEncrypter,
|
private val messageEncrypter: MessageEncrypter,
|
||||||
private val sendToDeviceTask: SendToDeviceTask,
|
private val sendToDeviceTask: SendToDeviceTask,
|
||||||
|
@ -153,7 +155,7 @@ internal class EventDecryptor @Inject constructor(
|
||||||
// we should force start a new session for those
|
// we should force start a new session for those
|
||||||
Timber.tag(loggerTag.value).v("Unwedging: ${wedgedDevices.size} are wedged")
|
Timber.tag(loggerTag.value).v("Unwedging: ${wedgedDevices.size} are wedged")
|
||||||
// get the one that should be retried according to rate limit
|
// get the one that should be retried according to rate limit
|
||||||
val now = System.currentTimeMillis()
|
val now = clock.epochMillis()
|
||||||
val toUnwedge = wedgedDevices.filter {
|
val toUnwedge = wedgedDevices.filter {
|
||||||
val lastForcedDate = lastNewSessionForcedDates[it] ?: 0
|
val lastForcedDate = lastNewSessionForcedDates[it] ?: 0
|
||||||
if (now - lastForcedDate < DefaultCryptoService.CRYPTO_MIN_FORCE_SESSION_PERIOD_MILLIS) {
|
if (now - lastForcedDate < DefaultCryptoService.CRYPTO_MIN_FORCE_SESSION_PERIOD_MILLIS) {
|
||||||
|
|
|
@ -26,6 +26,7 @@ import org.matrix.android.sdk.internal.di.WorkManagerProvider
|
||||||
import org.matrix.android.sdk.internal.session.SessionScope
|
import org.matrix.android.sdk.internal.session.SessionScope
|
||||||
import org.matrix.android.sdk.internal.util.CancelableWork
|
import org.matrix.android.sdk.internal.util.CancelableWork
|
||||||
import org.matrix.android.sdk.internal.worker.startChain
|
import org.matrix.android.sdk.internal.worker.startChain
|
||||||
|
import java.util.UUID
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@ -44,8 +45,8 @@ internal class GossipingWorkManager @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prevent sending queue to stay broken after app restart
|
// Prevent sending queue to stay broken after app restart
|
||||||
// The unique queue id will stay the same as long as this object is instanciated
|
// The unique queue id will stay the same as long as this object is instantiated
|
||||||
val queueSuffixApp = System.currentTimeMillis()
|
private val queueSuffixApp = UUID.randomUUID()
|
||||||
|
|
||||||
fun postWork(workRequest: OneTimeWorkRequest, policy: ExistingWorkPolicy = ExistingWorkPolicy.APPEND): Cancelable {
|
fun postWork(workRequest: OneTimeWorkRequest, policy: ExistingWorkPolicy = ExistingWorkPolicy.APPEND): Cancelable {
|
||||||
workManagerProvider.workManager
|
workManagerProvider.workManager
|
||||||
|
|
|
@ -44,6 +44,7 @@ import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore
|
||||||
import org.matrix.android.sdk.internal.crypto.tasks.createUniqueTxnId
|
import org.matrix.android.sdk.internal.crypto.tasks.createUniqueTxnId
|
||||||
import org.matrix.android.sdk.internal.di.SessionId
|
import org.matrix.android.sdk.internal.di.SessionId
|
||||||
import org.matrix.android.sdk.internal.session.SessionScope
|
import org.matrix.android.sdk.internal.session.SessionScope
|
||||||
|
import org.matrix.android.sdk.internal.util.time.Clock
|
||||||
import org.matrix.android.sdk.internal.worker.WorkerParamsFactory
|
import org.matrix.android.sdk.internal.worker.WorkerParamsFactory
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.util.concurrent.Executors
|
import java.util.concurrent.Executors
|
||||||
|
@ -59,7 +60,9 @@ internal class IncomingGossipingRequestManager @Inject constructor(
|
||||||
private val roomEncryptorsStore: RoomEncryptorsStore,
|
private val roomEncryptorsStore: RoomEncryptorsStore,
|
||||||
private val roomDecryptorProvider: RoomDecryptorProvider,
|
private val roomDecryptorProvider: RoomDecryptorProvider,
|
||||||
private val coroutineDispatchers: MatrixCoroutineDispatchers,
|
private val coroutineDispatchers: MatrixCoroutineDispatchers,
|
||||||
private val cryptoCoroutineScope: CoroutineScope) {
|
private val cryptoCoroutineScope: CoroutineScope,
|
||||||
|
private val clock: Clock,
|
||||||
|
) {
|
||||||
|
|
||||||
private val executor = Executors.newSingleThreadExecutor()
|
private val executor = Executors.newSingleThreadExecutor()
|
||||||
|
|
||||||
|
@ -89,7 +92,7 @@ internal class IncomingGossipingRequestManager @Inject constructor(
|
||||||
fun onVerificationCompleteForDevice(deviceId: String) {
|
fun onVerificationCompleteForDevice(deviceId: String) {
|
||||||
// For now we just keep an in memory cache
|
// For now we just keep an in memory cache
|
||||||
synchronized(recentlyVerifiedDevices) {
|
synchronized(recentlyVerifiedDevices) {
|
||||||
recentlyVerifiedDevices[deviceId] = System.currentTimeMillis()
|
recentlyVerifiedDevices[deviceId] = clock.epochMillis()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -100,7 +103,7 @@ internal class IncomingGossipingRequestManager @Inject constructor(
|
||||||
}
|
}
|
||||||
if (verifTimestamp == null) return false
|
if (verifTimestamp == null) return false
|
||||||
|
|
||||||
val age = System.currentTimeMillis() - verifTimestamp
|
val age = clock.epochMillis() - verifTimestamp
|
||||||
|
|
||||||
return age < FIVE_MINUTES_IN_MILLIS
|
return age < FIVE_MINUTES_IN_MILLIS
|
||||||
}
|
}
|
||||||
|
@ -114,11 +117,11 @@ internal class IncomingGossipingRequestManager @Inject constructor(
|
||||||
fun onGossipingRequestEvent(event: Event) {
|
fun onGossipingRequestEvent(event: Event) {
|
||||||
val roomKeyShare = event.getClearContent().toModel<GossipingDefaultContent>()
|
val roomKeyShare = event.getClearContent().toModel<GossipingDefaultContent>()
|
||||||
Timber.i("## CRYPTO | GOSSIP onGossipingRequestEvent received type ${event.type} from user:${event.senderId}, content:$roomKeyShare")
|
Timber.i("## CRYPTO | GOSSIP onGossipingRequestEvent received type ${event.type} from user:${event.senderId}, content:$roomKeyShare")
|
||||||
// val ageLocalTs = event.unsignedData?.age?.let { System.currentTimeMillis() - it }
|
// val ageLocalTs = event.unsignedData?.age?.let { clock.epochMillis() - it }
|
||||||
when (roomKeyShare?.action) {
|
when (roomKeyShare?.action) {
|
||||||
GossipingToDeviceObject.ACTION_SHARE_REQUEST -> {
|
GossipingToDeviceObject.ACTION_SHARE_REQUEST -> {
|
||||||
if (event.getClearType() == EventType.REQUEST_SECRET) {
|
if (event.getClearType() == EventType.REQUEST_SECRET) {
|
||||||
IncomingSecretShareRequest.fromEvent(event)?.let {
|
IncomingSecretShareRequest.fromEvent(event, clock.epochMillis())?.let {
|
||||||
if (event.senderId == credentials.userId && it.deviceId == credentials.deviceId) {
|
if (event.senderId == credentials.userId && it.deviceId == credentials.deviceId) {
|
||||||
// ignore, it was sent by me as *
|
// ignore, it was sent by me as *
|
||||||
Timber.v("## GOSSIP onGossipingRequestEvent type ${event.type} ignore remote echo")
|
Timber.v("## GOSSIP onGossipingRequestEvent type ${event.type} ignore remote echo")
|
||||||
|
@ -129,7 +132,7 @@ internal class IncomingGossipingRequestManager @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (event.getClearType() == EventType.ROOM_KEY_REQUEST) {
|
} else if (event.getClearType() == EventType.ROOM_KEY_REQUEST) {
|
||||||
IncomingRoomKeyRequest.fromEvent(event)?.let {
|
IncomingRoomKeyRequest.fromEvent(event, clock.epochMillis())?.let {
|
||||||
if (event.senderId == credentials.userId && it.deviceId == credentials.deviceId) {
|
if (event.senderId == credentials.userId && it.deviceId == credentials.deviceId) {
|
||||||
// ignore, it was sent by me as *
|
// ignore, it was sent by me as *
|
||||||
Timber.v("## GOSSIP onGossipingRequestEvent type ${event.type} ignore remote echo")
|
Timber.v("## GOSSIP onGossipingRequestEvent type ${event.type} ignore remote echo")
|
||||||
|
@ -141,7 +144,7 @@ internal class IncomingGossipingRequestManager @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
GossipingToDeviceObject.ACTION_SHARE_CANCELLATION -> {
|
GossipingToDeviceObject.ACTION_SHARE_CANCELLATION -> {
|
||||||
IncomingRequestCancellation.fromEvent(event)?.let {
|
IncomingRequestCancellation.fromEvent(event, clock.epochMillis())?.let {
|
||||||
receivedRequestCancellations.add(it)
|
receivedRequestCancellations.add(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,6 +29,7 @@ import javax.crypto.spec.SecretKeySpec
|
||||||
import kotlin.experimental.and
|
import kotlin.experimental.and
|
||||||
import kotlin.experimental.xor
|
import kotlin.experimental.xor
|
||||||
import kotlin.math.min
|
import kotlin.math.min
|
||||||
|
import kotlin.system.measureTimeMillis
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Utility class to import/export the crypto data
|
* Utility class to import/export the crypto data
|
||||||
|
@ -310,8 +311,8 @@ internal object MXMegolmExportEncryption {
|
||||||
*/
|
*/
|
||||||
@Throws(Exception::class)
|
@Throws(Exception::class)
|
||||||
private fun deriveKeys(salt: ByteArray, iterations: Int, password: String): ByteArray {
|
private fun deriveKeys(salt: ByteArray, iterations: Int, password: String): ByteArray {
|
||||||
val t0 = System.currentTimeMillis()
|
val key = ByteArray(64)
|
||||||
|
measureTimeMillis {
|
||||||
// based on https://en.wikipedia.org/wiki/PBKDF2 algorithm
|
// based on https://en.wikipedia.org/wiki/PBKDF2 algorithm
|
||||||
// it is simpler than the generic algorithm because the expected key length is equal to the mac key length.
|
// it is simpler than the generic algorithm because the expected key length is equal to the mac key length.
|
||||||
// noticed as dklen/hlen
|
// noticed as dklen/hlen
|
||||||
|
@ -319,7 +320,6 @@ internal object MXMegolmExportEncryption {
|
||||||
prf.init(SecretKeySpec(password.toByteArray(Charsets.UTF_8), "HmacSHA512"))
|
prf.init(SecretKeySpec(password.toByteArray(Charsets.UTF_8), "HmacSHA512"))
|
||||||
|
|
||||||
// 512 bits key length
|
// 512 bits key length
|
||||||
val key = ByteArray(64)
|
|
||||||
val uc = ByteArray(64)
|
val uc = ByteArray(64)
|
||||||
|
|
||||||
// U1 = PRF(Password, Salt || INT_32_BE(i))
|
// U1 = PRF(Password, Salt || INT_32_BE(i))
|
||||||
|
@ -342,8 +342,9 @@ internal object MXMegolmExportEncryption {
|
||||||
key[byteIndex] = key[byteIndex] xor uc[byteIndex]
|
key[byteIndex] = key[byteIndex] xor uc[byteIndex]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}.also {
|
||||||
Timber.v("## deriveKeys() : $iterations in ${System.currentTimeMillis() - t0} ms")
|
Timber.v("## deriveKeys() : $iterations in $it ms")
|
||||||
|
}
|
||||||
|
|
||||||
return key
|
return key
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,6 +35,7 @@ import org.matrix.android.sdk.internal.session.SessionScope
|
||||||
import org.matrix.android.sdk.internal.util.JsonCanonicalizer
|
import org.matrix.android.sdk.internal.util.JsonCanonicalizer
|
||||||
import org.matrix.android.sdk.internal.util.convertFromUTF8
|
import org.matrix.android.sdk.internal.util.convertFromUTF8
|
||||||
import org.matrix.android.sdk.internal.util.convertToUTF8
|
import org.matrix.android.sdk.internal.util.convertToUTF8
|
||||||
|
import org.matrix.android.sdk.internal.util.time.Clock
|
||||||
import org.matrix.olm.OlmAccount
|
import org.matrix.olm.OlmAccount
|
||||||
import org.matrix.olm.OlmException
|
import org.matrix.olm.OlmException
|
||||||
import org.matrix.olm.OlmMessage
|
import org.matrix.olm.OlmMessage
|
||||||
|
@ -55,7 +56,8 @@ internal class MXOlmDevice @Inject constructor(
|
||||||
*/
|
*/
|
||||||
private val store: IMXCryptoStore,
|
private val store: IMXCryptoStore,
|
||||||
private val olmSessionStore: OlmSessionStore,
|
private val olmSessionStore: OlmSessionStore,
|
||||||
private val inboundGroupSessionStore: InboundGroupSessionStore
|
private val inboundGroupSessionStore: InboundGroupSessionStore,
|
||||||
|
private val clock: Clock,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
val mutex = Mutex()
|
val mutex = Mutex()
|
||||||
|
@ -277,7 +279,7 @@ internal class MXOlmDevice @Inject constructor(
|
||||||
// Pretend we've received a message at this point, otherwise
|
// Pretend we've received a message at this point, otherwise
|
||||||
// if we try to send a message to the device, it won't use
|
// if we try to send a message to the device, it won't use
|
||||||
// this session
|
// this session
|
||||||
olmSessionWrapper.onMessageReceived()
|
olmSessionWrapper.onMessageReceived(clock.epochMillis())
|
||||||
|
|
||||||
olmSessionStore.storeSession(olmSessionWrapper, theirIdentityKey)
|
olmSessionStore.storeSession(olmSessionWrapper, theirIdentityKey)
|
||||||
|
|
||||||
|
@ -348,7 +350,7 @@ internal class MXOlmDevice @Inject constructor(
|
||||||
|
|
||||||
val olmSessionWrapper = OlmSessionWrapper(olmSession, 0)
|
val olmSessionWrapper = OlmSessionWrapper(olmSession, 0)
|
||||||
// This counts as a received message: set last received message time to now
|
// This counts as a received message: set last received message time to now
|
||||||
olmSessionWrapper.onMessageReceived()
|
olmSessionWrapper.onMessageReceived(clock.epochMillis())
|
||||||
|
|
||||||
olmSessionStore.storeSession(olmSessionWrapper, theirDeviceIdentityKey)
|
olmSessionStore.storeSession(olmSessionWrapper, theirDeviceIdentityKey)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
|
@ -454,7 +456,7 @@ internal class MXOlmDevice @Inject constructor(
|
||||||
payloadString =
|
payloadString =
|
||||||
olmSessionWrapper.mutex.withLock {
|
olmSessionWrapper.mutex.withLock {
|
||||||
olmSessionWrapper.olmSession.decryptMessage(olmMessage).also {
|
olmSessionWrapper.olmSession.decryptMessage(olmMessage).also {
|
||||||
olmSessionWrapper.onMessageReceived()
|
olmSessionWrapper.onMessageReceived(clock.epochMillis())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
olmSessionStore.storeSession(olmSessionWrapper, theirDeviceIdentityKey)
|
olmSessionStore.storeSession(olmSessionWrapper, theirDeviceIdentityKey)
|
||||||
|
@ -520,6 +522,7 @@ internal class MXOlmDevice @Inject constructor(
|
||||||
return MXOutboundSessionInfo(
|
return MXOutboundSessionInfo(
|
||||||
sessionId = sessionId,
|
sessionId = sessionId,
|
||||||
sharedWithHelper = SharedWithHelper(roomId, sessionId, store),
|
sharedWithHelper = SharedWithHelper(roomId, sessionId, store),
|
||||||
|
clock,
|
||||||
restoredOutboundGroupSession.creationTime
|
restoredOutboundGroupSession.creationTime
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,7 @@ import org.matrix.android.sdk.internal.crypto.model.rest.KeysUploadResponse
|
||||||
import org.matrix.android.sdk.internal.crypto.tasks.UploadKeysTask
|
import org.matrix.android.sdk.internal.crypto.tasks.UploadKeysTask
|
||||||
import org.matrix.android.sdk.internal.session.SessionScope
|
import org.matrix.android.sdk.internal.session.SessionScope
|
||||||
import org.matrix.android.sdk.internal.util.JsonCanonicalizer
|
import org.matrix.android.sdk.internal.util.JsonCanonicalizer
|
||||||
|
import org.matrix.android.sdk.internal.util.time.Clock
|
||||||
import org.matrix.olm.OlmAccount
|
import org.matrix.olm.OlmAccount
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
@ -38,6 +39,7 @@ internal class OneTimeKeysUploader @Inject constructor(
|
||||||
private val olmDevice: MXOlmDevice,
|
private val olmDevice: MXOlmDevice,
|
||||||
private val objectSigner: ObjectSigner,
|
private val objectSigner: ObjectSigner,
|
||||||
private val uploadKeysTask: UploadKeysTask,
|
private val uploadKeysTask: UploadKeysTask,
|
||||||
|
private val clock: Clock,
|
||||||
context: Context
|
context: Context
|
||||||
) {
|
) {
|
||||||
// tell if there is a OTK check in progress
|
// tell if there is a OTK check in progress
|
||||||
|
@ -77,7 +79,7 @@ internal class OneTimeKeysUploader @Inject constructor(
|
||||||
Timber.v("maybeUploadOneTimeKeys: already in progress")
|
Timber.v("maybeUploadOneTimeKeys: already in progress")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (System.currentTimeMillis() - lastOneTimeKeyCheck < ONE_TIME_KEY_UPLOAD_PERIOD) {
|
if (clock.epochMillis() - lastOneTimeKeyCheck < ONE_TIME_KEY_UPLOAD_PERIOD) {
|
||||||
// we've done a key upload recently.
|
// we've done a key upload recently.
|
||||||
Timber.v("maybeUploadOneTimeKeys: executed too recently")
|
Timber.v("maybeUploadOneTimeKeys: executed too recently")
|
||||||
return
|
return
|
||||||
|
@ -94,7 +96,7 @@ internal class OneTimeKeysUploader @Inject constructor(
|
||||||
|
|
||||||
Timber.d("maybeUploadOneTimeKeys: otk count $oneTimeKeyCountFromSync , unpublished fallback key ${olmDevice.hasUnpublishedFallbackKey()}")
|
Timber.d("maybeUploadOneTimeKeys: otk count $oneTimeKeyCountFromSync , unpublished fallback key ${olmDevice.hasUnpublishedFallbackKey()}")
|
||||||
|
|
||||||
lastOneTimeKeyCheck = System.currentTimeMillis()
|
lastOneTimeKeyCheck = clock.epochMillis()
|
||||||
|
|
||||||
// We then check how many keys we can store in the Account object.
|
// We then check how many keys we can store in the Account object.
|
||||||
val maxOneTimeKeys = olmDevice.getMaxNumberOfOneTimeKeys()
|
val maxOneTimeKeys = olmDevice.getMaxNumberOfOneTimeKeys()
|
||||||
|
@ -126,7 +128,7 @@ internal class OneTimeKeysUploader @Inject constructor(
|
||||||
|
|
||||||
// Check if we need to forget a fallback key
|
// Check if we need to forget a fallback key
|
||||||
val latestPublishedTime = getLastFallbackKeyPublishTime()
|
val latestPublishedTime = getLastFallbackKeyPublishTime()
|
||||||
if (latestPublishedTime != 0L && System.currentTimeMillis() - latestPublishedTime > FALLBACK_KEY_FORGET_DELAY) {
|
if (latestPublishedTime != 0L && clock.epochMillis() - latestPublishedTime > FALLBACK_KEY_FORGET_DELAY) {
|
||||||
// This should be called once you are reasonably certain that you will not receive any more messages
|
// This should be called once you are reasonably certain that you will not receive any more messages
|
||||||
// that use the old fallback key
|
// that use the old fallback key
|
||||||
Timber.d("## forgetFallbackKey()")
|
Timber.d("## forgetFallbackKey()")
|
||||||
|
@ -168,7 +170,7 @@ internal class OneTimeKeysUploader @Inject constructor(
|
||||||
olmDevice.markKeysAsPublished()
|
olmDevice.markKeysAsPublished()
|
||||||
if (hadUnpublishedFallbackKey) {
|
if (hadUnpublishedFallbackKey) {
|
||||||
// It had an unpublished fallback key that was published just now
|
// It had an unpublished fallback key that was published just now
|
||||||
saveLastFallbackKeyPublishTime(System.currentTimeMillis())
|
saveLastFallbackKeyPublishTime(clock.epochMillis())
|
||||||
}
|
}
|
||||||
|
|
||||||
if (response.hasOneTimeKeyCountsForAlgorithm(MXKey.KEY_SIGNED_CURVE_25519_TYPE)) {
|
if (response.hasOneTimeKeyCountsForAlgorithm(MXKey.KEY_SIGNED_CURVE_25519_TYPE)) {
|
||||||
|
|
|
@ -35,6 +35,7 @@ import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore
|
||||||
import org.matrix.android.sdk.internal.crypto.tasks.SendToDeviceTask
|
import org.matrix.android.sdk.internal.crypto.tasks.SendToDeviceTask
|
||||||
import org.matrix.android.sdk.internal.crypto.tasks.createUniqueTxnId
|
import org.matrix.android.sdk.internal.crypto.tasks.createUniqueTxnId
|
||||||
import org.matrix.android.sdk.internal.session.SessionComponent
|
import org.matrix.android.sdk.internal.session.SessionComponent
|
||||||
|
import org.matrix.android.sdk.internal.util.time.Clock
|
||||||
import org.matrix.android.sdk.internal.worker.SessionSafeCoroutineWorker
|
import org.matrix.android.sdk.internal.worker.SessionSafeCoroutineWorker
|
||||||
import org.matrix.android.sdk.internal.worker.SessionWorkerParams
|
import org.matrix.android.sdk.internal.worker.SessionWorkerParams
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
@ -57,6 +58,7 @@ internal class SendGossipRequestWorker(context: Context, params: WorkerParameter
|
||||||
@Inject lateinit var sendToDeviceTask: SendToDeviceTask
|
@Inject lateinit var sendToDeviceTask: SendToDeviceTask
|
||||||
@Inject lateinit var cryptoStore: IMXCryptoStore
|
@Inject lateinit var cryptoStore: IMXCryptoStore
|
||||||
@Inject lateinit var credentials: Credentials
|
@Inject lateinit var credentials: Credentials
|
||||||
|
@Inject lateinit var clock: Clock
|
||||||
|
|
||||||
override fun injectWith(injector: SessionComponent) {
|
override fun injectWith(injector: SessionComponent) {
|
||||||
injector.inject(this)
|
injector.inject(this)
|
||||||
|
@ -85,7 +87,7 @@ internal class SendGossipRequestWorker(context: Context, params: WorkerParameter
|
||||||
content = toDeviceContent.toContent(),
|
content = toDeviceContent.toContent(),
|
||||||
senderId = credentials.userId
|
senderId = credentials.userId
|
||||||
).also {
|
).also {
|
||||||
it.ageLocalTs = System.currentTimeMillis()
|
it.ageLocalTs = clock.epochMillis()
|
||||||
})
|
})
|
||||||
|
|
||||||
params.keyShareRequest.recipients.forEach { userToDeviceMap ->
|
params.keyShareRequest.recipients.forEach { userToDeviceMap ->
|
||||||
|
@ -109,7 +111,7 @@ internal class SendGossipRequestWorker(context: Context, params: WorkerParameter
|
||||||
content = toDeviceContent.toContent(),
|
content = toDeviceContent.toContent(),
|
||||||
senderId = credentials.userId
|
senderId = credentials.userId
|
||||||
).also {
|
).also {
|
||||||
it.ageLocalTs = System.currentTimeMillis()
|
it.ageLocalTs = clock.epochMillis()
|
||||||
})
|
})
|
||||||
|
|
||||||
params.secretShareRequest.recipients.forEach { userToDeviceMap ->
|
params.secretShareRequest.recipients.forEach { userToDeviceMap ->
|
||||||
|
|
|
@ -34,6 +34,7 @@ import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore
|
||||||
import org.matrix.android.sdk.internal.crypto.tasks.SendToDeviceTask
|
import org.matrix.android.sdk.internal.crypto.tasks.SendToDeviceTask
|
||||||
import org.matrix.android.sdk.internal.crypto.tasks.createUniqueTxnId
|
import org.matrix.android.sdk.internal.crypto.tasks.createUniqueTxnId
|
||||||
import org.matrix.android.sdk.internal.session.SessionComponent
|
import org.matrix.android.sdk.internal.session.SessionComponent
|
||||||
|
import org.matrix.android.sdk.internal.util.time.Clock
|
||||||
import org.matrix.android.sdk.internal.worker.SessionSafeCoroutineWorker
|
import org.matrix.android.sdk.internal.worker.SessionSafeCoroutineWorker
|
||||||
import org.matrix.android.sdk.internal.worker.SessionWorkerParams
|
import org.matrix.android.sdk.internal.worker.SessionWorkerParams
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
@ -63,6 +64,7 @@ internal class SendGossipWorker(
|
||||||
@Inject lateinit var credentials: Credentials
|
@Inject lateinit var credentials: Credentials
|
||||||
@Inject lateinit var messageEncrypter: MessageEncrypter
|
@Inject lateinit var messageEncrypter: MessageEncrypter
|
||||||
@Inject lateinit var ensureOlmSessionsForDevicesAction: EnsureOlmSessionsForDevicesAction
|
@Inject lateinit var ensureOlmSessionsForDevicesAction: EnsureOlmSessionsForDevicesAction
|
||||||
|
@Inject lateinit var clock: Clock
|
||||||
|
|
||||||
override fun injectWith(injector: SessionComponent) {
|
override fun injectWith(injector: SessionComponent) {
|
||||||
injector.inject(this)
|
injector.inject(this)
|
||||||
|
@ -129,7 +131,7 @@ internal class SendGossipWorker(
|
||||||
content = toDeviceContent.toContent(),
|
content = toDeviceContent.toContent(),
|
||||||
senderId = credentials.userId
|
senderId = credentials.userId
|
||||||
).also {
|
).also {
|
||||||
it.ageLocalTs = System.currentTimeMillis()
|
it.ageLocalTs = clock.epochMillis()
|
||||||
})
|
})
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -26,13 +26,17 @@ import org.matrix.android.sdk.internal.crypto.OutgoingGossipingRequestManager
|
||||||
import org.matrix.android.sdk.internal.crypto.RoomDecryptorProvider
|
import org.matrix.android.sdk.internal.crypto.RoomDecryptorProvider
|
||||||
import org.matrix.android.sdk.internal.crypto.algorithms.megolm.MXMegolmDecryption
|
import org.matrix.android.sdk.internal.crypto.algorithms.megolm.MXMegolmDecryption
|
||||||
import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore
|
import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore
|
||||||
|
import org.matrix.android.sdk.internal.util.time.Clock
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
internal class MegolmSessionDataImporter @Inject constructor(private val olmDevice: MXOlmDevice,
|
internal class MegolmSessionDataImporter @Inject constructor(
|
||||||
|
private val olmDevice: MXOlmDevice,
|
||||||
private val roomDecryptorProvider: RoomDecryptorProvider,
|
private val roomDecryptorProvider: RoomDecryptorProvider,
|
||||||
private val outgoingGossipingRequestManager: OutgoingGossipingRequestManager,
|
private val outgoingGossipingRequestManager: OutgoingGossipingRequestManager,
|
||||||
private val cryptoStore: IMXCryptoStore) {
|
private val cryptoStore: IMXCryptoStore,
|
||||||
|
private val clock: Clock,
|
||||||
|
) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Import a list of megolm session keys.
|
* Import a list of megolm session keys.
|
||||||
|
@ -47,7 +51,7 @@ internal class MegolmSessionDataImporter @Inject constructor(private val olmDevi
|
||||||
fun handle(megolmSessionsData: List<MegolmSessionData>,
|
fun handle(megolmSessionsData: List<MegolmSessionData>,
|
||||||
fromBackup: Boolean,
|
fromBackup: Boolean,
|
||||||
progressListener: ProgressListener?): ImportRoomKeysResult {
|
progressListener: ProgressListener?): ImportRoomKeysResult {
|
||||||
val t0 = System.currentTimeMillis()
|
val t0 = clock.epochMillis()
|
||||||
|
|
||||||
val totalNumbersOfKeys = megolmSessionsData.size
|
val totalNumbersOfKeys = megolmSessionsData.size
|
||||||
var lastProgress = 0
|
var lastProgress = 0
|
||||||
|
@ -103,7 +107,7 @@ internal class MegolmSessionDataImporter @Inject constructor(private val olmDevi
|
||||||
cryptoStore.markBackupDoneForInboundGroupSessions(olmInboundGroupSessionWrappers)
|
cryptoStore.markBackupDoneForInboundGroupSessions(olmInboundGroupSessionWrappers)
|
||||||
}
|
}
|
||||||
|
|
||||||
val t1 = System.currentTimeMillis()
|
val t1 = clock.epochMillis()
|
||||||
|
|
||||||
Timber.v("## importMegolmSessionsData : sessions import " + (t1 - t0) + " ms (" + megolmSessionsData.size + " sessions)")
|
Timber.v("## importMegolmSessionsData : sessions import " + (t1 - t0) + " ms (" + megolmSessionsData.size + " sessions)")
|
||||||
|
|
||||||
|
|
|
@ -46,6 +46,7 @@ import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore
|
||||||
import org.matrix.android.sdk.internal.crypto.tasks.SendToDeviceTask
|
import org.matrix.android.sdk.internal.crypto.tasks.SendToDeviceTask
|
||||||
import org.matrix.android.sdk.internal.util.JsonCanonicalizer
|
import org.matrix.android.sdk.internal.util.JsonCanonicalizer
|
||||||
import org.matrix.android.sdk.internal.util.convertToUTF8
|
import org.matrix.android.sdk.internal.util.convertToUTF8
|
||||||
|
import org.matrix.android.sdk.internal.util.time.Clock
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
|
||||||
private val loggerTag = LoggerTag("MXMegolmEncryption", LoggerTag.CRYPTO)
|
private val loggerTag = LoggerTag("MXMegolmEncryption", LoggerTag.CRYPTO)
|
||||||
|
@ -64,7 +65,8 @@ internal class MXMegolmEncryption(
|
||||||
private val messageEncrypter: MessageEncrypter,
|
private val messageEncrypter: MessageEncrypter,
|
||||||
private val warnOnUnknownDevicesRepository: WarnOnUnknownDeviceRepository,
|
private val warnOnUnknownDevicesRepository: WarnOnUnknownDeviceRepository,
|
||||||
private val coroutineDispatchers: MatrixCoroutineDispatchers,
|
private val coroutineDispatchers: MatrixCoroutineDispatchers,
|
||||||
private val cryptoCoroutineScope: CoroutineScope
|
private val cryptoCoroutineScope: CoroutineScope,
|
||||||
|
private val clock: Clock,
|
||||||
) : IMXEncrypting, IMXGroupEncryption {
|
) : IMXEncrypting, IMXGroupEncryption {
|
||||||
|
|
||||||
// OutboundSessionInfo. Null if we haven't yet started setting one up. Note
|
// OutboundSessionInfo. Null if we haven't yet started setting one up. Note
|
||||||
|
@ -86,11 +88,11 @@ internal class MXMegolmEncryption(
|
||||||
override suspend fun encryptEventContent(eventContent: Content,
|
override suspend fun encryptEventContent(eventContent: Content,
|
||||||
eventType: String,
|
eventType: String,
|
||||||
userIds: List<String>): Content {
|
userIds: List<String>): Content {
|
||||||
val ts = System.currentTimeMillis()
|
val ts = clock.epochMillis()
|
||||||
Timber.tag(loggerTag.value).v("encryptEventContent : getDevicesInRoom")
|
Timber.tag(loggerTag.value).v("encryptEventContent : getDevicesInRoom")
|
||||||
val devices = getDevicesInRoom(userIds)
|
val devices = getDevicesInRoom(userIds)
|
||||||
Timber.tag(loggerTag.value).d("encrypt event in room=$roomId - devices count in room ${devices.allowedDevices.toDebugCount()}")
|
Timber.tag(loggerTag.value).d("encrypt event in room=$roomId - devices count in room ${devices.allowedDevices.toDebugCount()}")
|
||||||
Timber.tag(loggerTag.value).v("encryptEventContent ${System.currentTimeMillis() - ts}: getDevicesInRoom ${devices.allowedDevices.toDebugString()}")
|
Timber.tag(loggerTag.value).v("encryptEventContent ${clock.epochMillis() - ts}: getDevicesInRoom ${devices.allowedDevices.toDebugString()}")
|
||||||
val outboundSession = ensureOutboundSession(devices.allowedDevices)
|
val outboundSession = ensureOutboundSession(devices.allowedDevices)
|
||||||
|
|
||||||
return encryptContent(outboundSession, eventType, eventContent)
|
return encryptContent(outboundSession, eventType, eventContent)
|
||||||
|
@ -99,7 +101,7 @@ internal class MXMegolmEncryption(
|
||||||
// annoyingly we have to serialize again the saved outbound session to store message index :/
|
// annoyingly we have to serialize again the saved outbound session to store message index :/
|
||||||
// if not we would see duplicate message index errors
|
// if not we would see duplicate message index errors
|
||||||
olmDevice.storeOutboundGroupSessionForRoom(roomId, outboundSession.sessionId)
|
olmDevice.storeOutboundGroupSessionForRoom(roomId, outboundSession.sessionId)
|
||||||
Timber.tag(loggerTag.value).d("encrypt event in room=$roomId Finished in ${System.currentTimeMillis() - ts} millis")
|
Timber.tag(loggerTag.value).d("encrypt event in room=$roomId Finished in ${clock.epochMillis() - ts} millis")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -125,14 +127,14 @@ internal class MXMegolmEncryption(
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun preshareKey(userIds: List<String>) {
|
override suspend fun preshareKey(userIds: List<String>) {
|
||||||
val ts = System.currentTimeMillis()
|
val ts = clock.epochMillis()
|
||||||
Timber.tag(loggerTag.value).d("preshareKey started in $roomId ...")
|
Timber.tag(loggerTag.value).d("preshareKey started in $roomId ...")
|
||||||
val devices = getDevicesInRoom(userIds)
|
val devices = getDevicesInRoom(userIds)
|
||||||
val outboundSession = ensureOutboundSession(devices.allowedDevices)
|
val outboundSession = ensureOutboundSession(devices.allowedDevices)
|
||||||
|
|
||||||
notifyWithheldForSession(devices.withHeldDevices, outboundSession)
|
notifyWithheldForSession(devices.withHeldDevices, outboundSession)
|
||||||
|
|
||||||
Timber.tag(loggerTag.value).d("preshareKey in $roomId done in ${System.currentTimeMillis() - ts} millis")
|
Timber.tag(loggerTag.value).d("preshareKey in $roomId done in ${clock.epochMillis() - ts} millis")
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -148,12 +150,14 @@ internal class MXMegolmEncryption(
|
||||||
"ed25519" to olmDevice.deviceEd25519Key!!
|
"ed25519" to olmDevice.deviceEd25519Key!!
|
||||||
)
|
)
|
||||||
|
|
||||||
olmDevice.addInboundGroupSession(sessionId!!, olmDevice.getSessionKey(sessionId)!!, roomId, olmDevice.deviceCurve25519Key!!,
|
olmDevice.addInboundGroupSession(
|
||||||
emptyList(), keysClaimedMap, false)
|
sessionId!!, olmDevice.getSessionKey(sessionId)!!, roomId, olmDevice.deviceCurve25519Key!!,
|
||||||
|
emptyList(), keysClaimedMap, false
|
||||||
|
)
|
||||||
|
|
||||||
defaultKeysBackupService.maybeBackupKeys()
|
defaultKeysBackupService.maybeBackupKeys()
|
||||||
|
|
||||||
return MXOutboundSessionInfo(sessionId, SharedWithHelper(roomId, sessionId, cryptoStore))
|
return MXOutboundSessionInfo(sessionId, SharedWithHelper(roomId, sessionId, cryptoStore), clock)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -243,12 +247,12 @@ internal class MXMegolmEncryption(
|
||||||
payload["type"] = EventType.ROOM_KEY
|
payload["type"] = EventType.ROOM_KEY
|
||||||
payload["content"] = submap
|
payload["content"] = submap
|
||||||
|
|
||||||
var t0 = System.currentTimeMillis()
|
var t0 = clock.epochMillis()
|
||||||
Timber.tag(loggerTag.value).v("shareUserDevicesKey() : starts")
|
Timber.tag(loggerTag.value).v("shareUserDevicesKey() : starts")
|
||||||
|
|
||||||
val results = ensureOlmSessionsForDevicesAction.handle(devicesByUser)
|
val results = ensureOlmSessionsForDevicesAction.handle(devicesByUser)
|
||||||
Timber.tag(loggerTag.value).v(
|
Timber.tag(loggerTag.value).v(
|
||||||
"""shareUserDevicesKey(): ensureOlmSessionsForDevices succeeds after ${System.currentTimeMillis() - t0} ms"""
|
"""shareUserDevicesKey(): ensureOlmSessionsForDevices succeeds after ${clock.epochMillis() - t0} ms"""
|
||||||
.trimMargin()
|
.trimMargin()
|
||||||
)
|
)
|
||||||
val contentMap = MXUsersDevicesMap<Any>()
|
val contentMap = MXUsersDevicesMap<Any>()
|
||||||
|
@ -301,7 +305,7 @@ internal class MXMegolmEncryption(
|
||||||
cryptoStore.saveGossipingEvents(gossipingEventBuffer)
|
cryptoStore.saveGossipingEvents(gossipingEventBuffer)
|
||||||
|
|
||||||
if (haveTargets) {
|
if (haveTargets) {
|
||||||
t0 = System.currentTimeMillis()
|
t0 = clock.epochMillis()
|
||||||
Timber.tag(loggerTag.value).i("shareUserDevicesKey() ${session.sessionId} : has target")
|
Timber.tag(loggerTag.value).i("shareUserDevicesKey() ${session.sessionId} : has target")
|
||||||
Timber.tag(loggerTag.value).d("sending to device room key for ${session.sessionId} to ${contentMap.toDebugString()}")
|
Timber.tag(loggerTag.value).d("sending to device room key for ${session.sessionId} to ${contentMap.toDebugString()}")
|
||||||
val sendToDeviceParams = SendToDeviceTask.Params(EventType.ENCRYPTED, contentMap)
|
val sendToDeviceParams = SendToDeviceTask.Params(EventType.ENCRYPTED, contentMap)
|
||||||
|
@ -309,7 +313,7 @@ internal class MXMegolmEncryption(
|
||||||
withContext(coroutineDispatchers.io) {
|
withContext(coroutineDispatchers.io) {
|
||||||
sendToDeviceTask.execute(sendToDeviceParams)
|
sendToDeviceTask.execute(sendToDeviceParams)
|
||||||
}
|
}
|
||||||
Timber.tag(loggerTag.value).i("shareUserDevicesKey() : sendToDevice succeeds after ${System.currentTimeMillis() - t0} ms")
|
Timber.tag(loggerTag.value).i("shareUserDevicesKey() : sendToDevice succeeds after ${clock.epochMillis() - t0} ms")
|
||||||
} catch (failure: Throwable) {
|
} catch (failure: Throwable) {
|
||||||
// What to do here...
|
// What to do here...
|
||||||
Timber.tag(loggerTag.value).e("shareUserDevicesKey() : Failed to share <${session.sessionId}>")
|
Timber.tag(loggerTag.value).e("shareUserDevicesKey() : Failed to share <${session.sessionId}>")
|
||||||
|
@ -334,7 +338,8 @@ internal class MXMegolmEncryption(
|
||||||
senderKey: String?,
|
senderKey: String?,
|
||||||
code: WithHeldCode) {
|
code: WithHeldCode) {
|
||||||
Timber.tag(loggerTag.value).d("notifyKeyWithHeld() :sending withheld for session:$sessionId and code $code to" +
|
Timber.tag(loggerTag.value).d("notifyKeyWithHeld() :sending withheld for session:$sessionId and code $code to" +
|
||||||
" ${targets.joinToString { "${it.userId}|${it.deviceId}" }}")
|
" ${targets.joinToString { "${it.userId}|${it.deviceId}" }}"
|
||||||
|
)
|
||||||
val withHeldContent = RoomKeyWithHeldContent(
|
val withHeldContent = RoomKeyWithHeldContent(
|
||||||
roomId = roomId,
|
roomId = roomId,
|
||||||
senderKey = senderKey,
|
senderKey = senderKey,
|
||||||
|
|
|
@ -28,6 +28,7 @@ import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore
|
||||||
import org.matrix.android.sdk.internal.crypto.tasks.SendToDeviceTask
|
import org.matrix.android.sdk.internal.crypto.tasks.SendToDeviceTask
|
||||||
import org.matrix.android.sdk.internal.di.DeviceId
|
import org.matrix.android.sdk.internal.di.DeviceId
|
||||||
import org.matrix.android.sdk.internal.di.UserId
|
import org.matrix.android.sdk.internal.di.UserId
|
||||||
|
import org.matrix.android.sdk.internal.util.time.Clock
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
internal class MXMegolmEncryptionFactory @Inject constructor(
|
internal class MXMegolmEncryptionFactory @Inject constructor(
|
||||||
|
@ -42,7 +43,9 @@ internal class MXMegolmEncryptionFactory @Inject constructor(
|
||||||
private val messageEncrypter: MessageEncrypter,
|
private val messageEncrypter: MessageEncrypter,
|
||||||
private val warnOnUnknownDevicesRepository: WarnOnUnknownDeviceRepository,
|
private val warnOnUnknownDevicesRepository: WarnOnUnknownDeviceRepository,
|
||||||
private val coroutineDispatchers: MatrixCoroutineDispatchers,
|
private val coroutineDispatchers: MatrixCoroutineDispatchers,
|
||||||
private val cryptoCoroutineScope: CoroutineScope) {
|
private val cryptoCoroutineScope: CoroutineScope,
|
||||||
|
private val clock: Clock,
|
||||||
|
) {
|
||||||
|
|
||||||
fun create(roomId: String): MXMegolmEncryption {
|
fun create(roomId: String): MXMegolmEncryption {
|
||||||
return MXMegolmEncryption(
|
return MXMegolmEncryption(
|
||||||
|
@ -58,7 +61,8 @@ internal class MXMegolmEncryptionFactory @Inject constructor(
|
||||||
messageEncrypter = messageEncrypter,
|
messageEncrypter = messageEncrypter,
|
||||||
warnOnUnknownDevicesRepository = warnOnUnknownDevicesRepository,
|
warnOnUnknownDevicesRepository = warnOnUnknownDevicesRepository,
|
||||||
coroutineDispatchers = coroutineDispatchers,
|
coroutineDispatchers = coroutineDispatchers,
|
||||||
cryptoCoroutineScope = cryptoCoroutineScope
|
cryptoCoroutineScope = cryptoCoroutineScope,
|
||||||
|
clock = clock,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,21 +18,24 @@ package org.matrix.android.sdk.internal.crypto.algorithms.megolm
|
||||||
|
|
||||||
import org.matrix.android.sdk.api.session.crypto.model.CryptoDeviceInfo
|
import org.matrix.android.sdk.api.session.crypto.model.CryptoDeviceInfo
|
||||||
import org.matrix.android.sdk.api.session.crypto.model.MXUsersDevicesMap
|
import org.matrix.android.sdk.api.session.crypto.model.MXUsersDevicesMap
|
||||||
|
import org.matrix.android.sdk.internal.util.time.Clock
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
|
||||||
internal class MXOutboundSessionInfo(
|
internal class MXOutboundSessionInfo(
|
||||||
// The id of the session
|
// The id of the session
|
||||||
val sessionId: String,
|
val sessionId: String,
|
||||||
val sharedWithHelper: SharedWithHelper,
|
val sharedWithHelper: SharedWithHelper,
|
||||||
|
private val clock: Clock,
|
||||||
// When the session was created
|
// When the session was created
|
||||||
private val creationTime: Long = System.currentTimeMillis()) {
|
private val creationTime: Long = clock.epochMillis(),
|
||||||
|
) {
|
||||||
|
|
||||||
// Number of times this session has been used
|
// Number of times this session has been used
|
||||||
var useCount: Int = 0
|
var useCount: Int = 0
|
||||||
|
|
||||||
fun needsRotation(rotationPeriodMsgs: Int, rotationPeriodMs: Int): Boolean {
|
fun needsRotation(rotationPeriodMsgs: Int, rotationPeriodMs: Int): Boolean {
|
||||||
var needsRotation = false
|
var needsRotation = false
|
||||||
val sessionLifetime = System.currentTimeMillis() - creationTime
|
val sessionLifetime = clock.epochMillis() - creationTime
|
||||||
|
|
||||||
if (useCount >= rotationPeriodMsgs || sessionLifetime >= rotationPeriodMs) {
|
if (useCount >= rotationPeriodMsgs || sessionLifetime >= rotationPeriodMs) {
|
||||||
Timber.v("## needsRotation() : Rotating megolm session after $useCount, ${sessionLifetime}ms")
|
Timber.v("## needsRotation() : Rotating megolm session after $useCount, ${sessionLifetime}ms")
|
||||||
|
|
|
@ -23,6 +23,7 @@ import org.matrix.android.sdk.api.session.crypto.model.EncryptedFileKey
|
||||||
import org.matrix.android.sdk.internal.util.base64ToBase64Url
|
import org.matrix.android.sdk.internal.util.base64ToBase64Url
|
||||||
import org.matrix.android.sdk.internal.util.base64ToUnpaddedBase64
|
import org.matrix.android.sdk.internal.util.base64ToUnpaddedBase64
|
||||||
import org.matrix.android.sdk.internal.util.base64UrlToBase64
|
import org.matrix.android.sdk.internal.util.base64UrlToBase64
|
||||||
|
import org.matrix.android.sdk.internal.util.time.Clock
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.io.ByteArrayOutputStream
|
import java.io.ByteArrayOutputStream
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
@ -42,8 +43,9 @@ internal object MXEncryptedAttachments {
|
||||||
|
|
||||||
fun encrypt(clearStream: InputStream,
|
fun encrypt(clearStream: InputStream,
|
||||||
outputFile: File,
|
outputFile: File,
|
||||||
|
clock: Clock,
|
||||||
progress: ((current: Int, total: Int) -> Unit)): EncryptedFileInfo {
|
progress: ((current: Int, total: Int) -> Unit)): EncryptedFileInfo {
|
||||||
val t0 = System.currentTimeMillis()
|
val t0 = clock.epochMillis()
|
||||||
val secureRandom = SecureRandom()
|
val secureRandom = SecureRandom()
|
||||||
val initVectorBytes = ByteArray(16) { 0.toByte() }
|
val initVectorBytes = ByteArray(16) { 0.toByte() }
|
||||||
|
|
||||||
|
@ -100,7 +102,7 @@ internal object MXEncryptedAttachments {
|
||||||
hashes = mapOf("sha256" to base64ToUnpaddedBase64(Base64.encodeToString(messageDigest.digest(), Base64.DEFAULT))),
|
hashes = mapOf("sha256" to base64ToUnpaddedBase64(Base64.encodeToString(messageDigest.digest(), Base64.DEFAULT))),
|
||||||
v = "v2"
|
v = "v2"
|
||||||
)
|
)
|
||||||
.also { Timber.v("Encrypt in ${System.currentTimeMillis() - t0}ms") }
|
.also { Timber.v("Encrypt in ${clock.epochMillis() - t0}ms") }
|
||||||
}
|
}
|
||||||
|
|
||||||
// fun cipherInputStream(attachmentStream: InputStream, mimetype: String?): Pair<DigestInputStream, EncryptedFileInfo> {
|
// fun cipherInputStream(attachmentStream: InputStream, mimetype: String?): Pair<DigestInputStream, EncryptedFileInfo> {
|
||||||
|
@ -159,8 +161,8 @@ internal object MXEncryptedAttachments {
|
||||||
* @param attachmentStream the attachment stream. Will be closed after this method call.
|
* @param attachmentStream the attachment stream. Will be closed after this method call.
|
||||||
* @return the encryption file info
|
* @return the encryption file info
|
||||||
*/
|
*/
|
||||||
fun encryptAttachment(attachmentStream: InputStream): EncryptionResult {
|
fun encryptAttachment(attachmentStream: InputStream, clock: Clock): EncryptionResult {
|
||||||
val t0 = System.currentTimeMillis()
|
val t0 = clock.epochMillis()
|
||||||
val secureRandom = SecureRandom()
|
val secureRandom = SecureRandom()
|
||||||
|
|
||||||
// generate a random iv key
|
// generate a random iv key
|
||||||
|
@ -221,7 +223,7 @@ internal object MXEncryptedAttachments {
|
||||||
),
|
),
|
||||||
encryptedByteArray = byteArrayOutputStream.toByteArray()
|
encryptedByteArray = byteArrayOutputStream.toByteArray()
|
||||||
)
|
)
|
||||||
.also { Timber.v("Encrypt in ${System.currentTimeMillis() - t0}ms") }
|
.also { Timber.v("Encrypt in ${clock.epochMillis() - t0}ms") }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -234,14 +236,16 @@ internal object MXEncryptedAttachments {
|
||||||
*/
|
*/
|
||||||
fun decryptAttachment(attachmentStream: InputStream?,
|
fun decryptAttachment(attachmentStream: InputStream?,
|
||||||
elementToDecrypt: ElementToDecrypt?,
|
elementToDecrypt: ElementToDecrypt?,
|
||||||
outputStream: OutputStream): Boolean {
|
outputStream: OutputStream,
|
||||||
|
clock: Clock
|
||||||
|
): Boolean {
|
||||||
// sanity checks
|
// sanity checks
|
||||||
if (null == attachmentStream || elementToDecrypt == null) {
|
if (null == attachmentStream || elementToDecrypt == null) {
|
||||||
Timber.e("## decryptAttachment() : null stream")
|
Timber.e("## decryptAttachment() : null stream")
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
val t0 = System.currentTimeMillis()
|
val t0 = clock.epochMillis()
|
||||||
|
|
||||||
try {
|
try {
|
||||||
val key = Base64.decode(base64UrlToBase64(elementToDecrypt.k), Base64.DEFAULT)
|
val key = Base64.decode(base64UrlToBase64(elementToDecrypt.k), Base64.DEFAULT)
|
||||||
|
@ -279,7 +283,8 @@ internal object MXEncryptedAttachments {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
return true.also { Timber.v("Decrypt in ${System.currentTimeMillis() - t0}ms") }
|
Timber.v("Decrypt in ${clock.epochMillis() - t0} ms")
|
||||||
|
return true
|
||||||
} catch (oom: OutOfMemoryError) {
|
} catch (oom: OutOfMemoryError) {
|
||||||
Timber.e(oom, "## decryptAttachment() failed: OOM")
|
Timber.e(oom, "## decryptAttachment() failed: OOM")
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
|
|
|
@ -26,6 +26,7 @@ import java.util.UUID
|
||||||
import javax.crypto.Mac
|
import javax.crypto.Mac
|
||||||
import javax.crypto.spec.SecretKeySpec
|
import javax.crypto.spec.SecretKeySpec
|
||||||
import kotlin.experimental.xor
|
import kotlin.experimental.xor
|
||||||
|
import kotlin.system.measureTimeMillis
|
||||||
|
|
||||||
private const val SALT_LENGTH = 32
|
private const val SALT_LENGTH = 32
|
||||||
private const val DEFAULT_ITERATION = 500_000
|
private const val DEFAULT_ITERATION = 500_000
|
||||||
|
@ -91,20 +92,20 @@ internal fun deriveKey(password: String,
|
||||||
iterations: Int,
|
iterations: Int,
|
||||||
progressListener: ProgressListener?): ByteArray {
|
progressListener: ProgressListener?): ByteArray {
|
||||||
// Note: copied and adapted from MXMegolmExportEncryption
|
// Note: copied and adapted from MXMegolmExportEncryption
|
||||||
val t0 = System.currentTimeMillis()
|
|
||||||
|
|
||||||
// based on https://en.wikipedia.org/wiki/PBKDF2 algorithm
|
// based on https://en.wikipedia.org/wiki/PBKDF2 algorithm
|
||||||
// it is simpler than the generic algorithm because the expected key length is equal to the mac key length.
|
// it is simpler than the generic algorithm because the expected key length is equal to the mac key length.
|
||||||
// noticed as dklen/hlen
|
// noticed as dklen/hlen
|
||||||
|
|
||||||
|
// 256 bits key length
|
||||||
|
val dk = ByteArray(32)
|
||||||
|
|
||||||
|
measureTimeMillis {
|
||||||
// dklen = 256
|
// dklen = 256
|
||||||
// hlen = 512
|
// hlen = 512
|
||||||
val prf = Mac.getInstance("HmacSHA512")
|
val prf = Mac.getInstance("HmacSHA512")
|
||||||
|
|
||||||
prf.init(SecretKeySpec(password.toByteArray(), "HmacSHA512"))
|
prf.init(SecretKeySpec(password.toByteArray(), "HmacSHA512"))
|
||||||
|
|
||||||
// 256 bits key length
|
|
||||||
val dk = ByteArray(32)
|
|
||||||
val uc = ByteArray(64)
|
val uc = ByteArray(64)
|
||||||
|
|
||||||
// U1 = PRF(Password, Salt || INT_32_BE(i)) with i goes from 1 to dklen/hlen
|
// U1 = PRF(Password, Salt || INT_32_BE(i)) with i goes from 1 to dklen/hlen
|
||||||
|
@ -134,8 +135,9 @@ internal fun deriveKey(password: String,
|
||||||
progressListener?.onProgress(lastProgress, 100)
|
progressListener?.onProgress(lastProgress, 100)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}.also {
|
||||||
Timber.v("KeysBackupPassword: deriveKeys() : " + iterations + " in " + (System.currentTimeMillis() - t0) + " ms")
|
Timber.v("KeysBackupPassword: deriveKeys() : $iterations in $it ms")
|
||||||
|
}
|
||||||
|
|
||||||
return dk
|
return dk
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,7 +34,7 @@ internal data class OlmSessionWrapper(
|
||||||
/**
|
/**
|
||||||
* Notify that a message has been received on this olm session so that it updates `lastReceivedMessageTs`
|
* Notify that a message has been received on this olm session so that it updates `lastReceivedMessageTs`
|
||||||
*/
|
*/
|
||||||
fun onMessageReceived() {
|
fun onMessageReceived(currentTimeMillis: Long) {
|
||||||
lastReceivedMessageTs = System.currentTimeMillis()
|
lastReceivedMessageTs = currentTimeMillis
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -97,6 +97,7 @@ import org.matrix.android.sdk.internal.di.MoshiProvider
|
||||||
import org.matrix.android.sdk.internal.di.UserId
|
import org.matrix.android.sdk.internal.di.UserId
|
||||||
import org.matrix.android.sdk.internal.extensions.clearWith
|
import org.matrix.android.sdk.internal.extensions.clearWith
|
||||||
import org.matrix.android.sdk.internal.session.SessionScope
|
import org.matrix.android.sdk.internal.session.SessionScope
|
||||||
|
import org.matrix.android.sdk.internal.util.time.Clock
|
||||||
import org.matrix.olm.OlmAccount
|
import org.matrix.olm.OlmAccount
|
||||||
import org.matrix.olm.OlmException
|
import org.matrix.olm.OlmException
|
||||||
import org.matrix.olm.OlmOutboundGroupSession
|
import org.matrix.olm.OlmOutboundGroupSession
|
||||||
|
@ -110,7 +111,8 @@ internal class RealmCryptoStore @Inject constructor(
|
||||||
@CryptoDatabase private val realmConfiguration: RealmConfiguration,
|
@CryptoDatabase private val realmConfiguration: RealmConfiguration,
|
||||||
private val crossSigningKeysMapper: CrossSigningKeysMapper,
|
private val crossSigningKeysMapper: CrossSigningKeysMapper,
|
||||||
@UserId private val userId: String,
|
@UserId private val userId: String,
|
||||||
@DeviceId private val deviceId: String?
|
@DeviceId private val deviceId: String?,
|
||||||
|
private val clock: Clock,
|
||||||
) : IMXCryptoStore {
|
) : IMXCryptoStore {
|
||||||
|
|
||||||
/* ==========================================================================================
|
/* ==========================================================================================
|
||||||
|
@ -307,7 +309,7 @@ internal class RealmCryptoStore @Inject constructor(
|
||||||
// Add the device
|
// Add the device
|
||||||
Timber.d("Add device ${cryptoDeviceInfo.deviceId} of user $userId")
|
Timber.d("Add device ${cryptoDeviceInfo.deviceId} of user $userId")
|
||||||
val newEntity = CryptoMapper.mapToEntity(cryptoDeviceInfo)
|
val newEntity = CryptoMapper.mapToEntity(cryptoDeviceInfo)
|
||||||
newEntity.firstTimeSeenLocalTs = System.currentTimeMillis()
|
newEntity.firstTimeSeenLocalTs = clock.epochMillis()
|
||||||
userEntity.devices.add(newEntity)
|
userEntity.devices.add(newEntity)
|
||||||
} else {
|
} else {
|
||||||
// Update the device
|
// Update the device
|
||||||
|
@ -792,7 +794,7 @@ internal class RealmCryptoStore @Inject constructor(
|
||||||
|
|
||||||
if (outboundGroupSession != null) {
|
if (outboundGroupSession != null) {
|
||||||
val info = realm.createObject(OutboundGroupSessionInfoEntity::class.java).apply {
|
val info = realm.createObject(OutboundGroupSessionInfoEntity::class.java).apply {
|
||||||
creationTime = System.currentTimeMillis()
|
creationTime = clock.epochMillis()
|
||||||
putOutboundGroupSession(outboundGroupSession)
|
putOutboundGroupSession(outboundGroupSession)
|
||||||
}
|
}
|
||||||
entity.outboundSessionInfo = info
|
entity.outboundSessionInfo = info
|
||||||
|
@ -882,7 +884,8 @@ internal class RealmCryptoStore @Inject constructor(
|
||||||
try {
|
try {
|
||||||
val key = OlmInboundGroupSessionEntity.createPrimaryKey(
|
val key = OlmInboundGroupSessionEntity.createPrimaryKey(
|
||||||
olmInboundGroupSessionWrapper.olmInboundGroupSession?.sessionIdentifier(),
|
olmInboundGroupSessionWrapper.olmInboundGroupSession?.sessionIdentifier(),
|
||||||
olmInboundGroupSessionWrapper.senderKey)
|
olmInboundGroupSessionWrapper.senderKey
|
||||||
|
)
|
||||||
|
|
||||||
it.where<OlmInboundGroupSessionEntity>()
|
it.where<OlmInboundGroupSessionEntity>()
|
||||||
.equalTo(OlmInboundGroupSessionEntityFields.PRIMARY_KEY, key)
|
.equalTo(OlmInboundGroupSessionEntityFields.PRIMARY_KEY, key)
|
||||||
|
@ -1057,13 +1060,16 @@ internal class RealmCryptoStore @Inject constructor(
|
||||||
localCreationTimestamp = 0
|
localCreationTimestamp = 0
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
return monarchy.findAllPagedWithChanges(realmDataSourceFactory,
|
return monarchy.findAllPagedWithChanges(
|
||||||
LivePagedListBuilder(dataSourceFactory,
|
realmDataSourceFactory,
|
||||||
|
LivePagedListBuilder(
|
||||||
|
dataSourceFactory,
|
||||||
PagedList.Config.Builder()
|
PagedList.Config.Builder()
|
||||||
.setPageSize(20)
|
.setPageSize(20)
|
||||||
.setEnablePlaceholders(false)
|
.setEnablePlaceholders(false)
|
||||||
.setPrefetchDistance(1)
|
.setPrefetchDistance(1)
|
||||||
.build())
|
.build()
|
||||||
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1072,13 +1078,16 @@ internal class RealmCryptoStore @Inject constructor(
|
||||||
realm.where<GossipingEventEntity>().sort(GossipingEventEntityFields.AGE_LOCAL_TS, Sort.DESCENDING)
|
realm.where<GossipingEventEntity>().sort(GossipingEventEntityFields.AGE_LOCAL_TS, Sort.DESCENDING)
|
||||||
}
|
}
|
||||||
val dataSourceFactory = realmDataSourceFactory.map { it.toModel() }
|
val dataSourceFactory = realmDataSourceFactory.map { it.toModel() }
|
||||||
val trail = monarchy.findAllPagedWithChanges(realmDataSourceFactory,
|
val trail = monarchy.findAllPagedWithChanges(
|
||||||
LivePagedListBuilder(dataSourceFactory,
|
realmDataSourceFactory,
|
||||||
|
LivePagedListBuilder(
|
||||||
|
dataSourceFactory,
|
||||||
PagedList.Config.Builder()
|
PagedList.Config.Builder()
|
||||||
.setPageSize(20)
|
.setPageSize(20)
|
||||||
.setEnablePlaceholders(false)
|
.setEnablePlaceholders(false)
|
||||||
.setPrefetchDistance(1)
|
.setPrefetchDistance(1)
|
||||||
.build())
|
.build()
|
||||||
|
)
|
||||||
)
|
)
|
||||||
return trail
|
return trail
|
||||||
}
|
}
|
||||||
|
@ -1153,7 +1162,7 @@ internal class RealmCryptoStore @Inject constructor(
|
||||||
|
|
||||||
override fun saveGossipingEvents(events: List<Event>) {
|
override fun saveGossipingEvents(events: List<Event>) {
|
||||||
monarchy.writeAsync { realm ->
|
monarchy.writeAsync { realm ->
|
||||||
val now = System.currentTimeMillis()
|
val now = clock.epochMillis()
|
||||||
events.forEach { event ->
|
events.forEach { event ->
|
||||||
val ageLocalTs = event.unsignedData?.age?.let { now - it } ?: now
|
val ageLocalTs = event.unsignedData?.age?.let { now - it } ?: now
|
||||||
val entity = GossipingEventEntity(
|
val entity = GossipingEventEntity(
|
||||||
|
@ -1359,7 +1368,7 @@ internal class RealmCryptoStore @Inject constructor(
|
||||||
it.otherUserId = request.userId
|
it.otherUserId = request.userId
|
||||||
it.requestId = request.requestId ?: ""
|
it.requestId = request.requestId ?: ""
|
||||||
it.requestState = GossipingRequestState.PENDING
|
it.requestState = GossipingRequestState.PENDING
|
||||||
it.localCreationTimestamp = ageLocalTS ?: System.currentTimeMillis()
|
it.localCreationTimestamp = ageLocalTS ?: clock.epochMillis()
|
||||||
if (request is IncomingSecretShareRequest) {
|
if (request is IncomingSecretShareRequest) {
|
||||||
it.type = GossipRequestType.SECRET
|
it.type = GossipRequestType.SECRET
|
||||||
it.requestedInfoStr = request.secretName
|
it.requestedInfoStr = request.secretName
|
||||||
|
@ -1380,7 +1389,7 @@ internal class RealmCryptoStore @Inject constructor(
|
||||||
it.otherUserId = request.userId
|
it.otherUserId = request.userId
|
||||||
it.requestId = request.requestId ?: ""
|
it.requestId = request.requestId ?: ""
|
||||||
it.requestState = GossipingRequestState.PENDING
|
it.requestState = GossipingRequestState.PENDING
|
||||||
it.localCreationTimestamp = request.localCreationTimestamp ?: System.currentTimeMillis()
|
it.localCreationTimestamp = request.localCreationTimestamp ?: clock.epochMillis()
|
||||||
if (request is IncomingSecretShareRequest) {
|
if (request is IncomingSecretShareRequest) {
|
||||||
it.type = GossipRequestType.SECRET
|
it.type = GossipRequestType.SECRET
|
||||||
it.requestedInfoStr = request.secretName
|
it.requestedInfoStr = request.secretName
|
||||||
|
@ -1536,13 +1545,16 @@ internal class RealmCryptoStore @Inject constructor(
|
||||||
it.toOutgoingGossipingRequest() as? OutgoingRoomKeyRequest
|
it.toOutgoingGossipingRequest() as? OutgoingRoomKeyRequest
|
||||||
?: OutgoingRoomKeyRequest(requestBody = null, requestId = "?", recipients = emptyMap(), state = OutgoingGossipingRequestState.CANCELLED)
|
?: OutgoingRoomKeyRequest(requestBody = null, requestId = "?", recipients = emptyMap(), state = OutgoingGossipingRequestState.CANCELLED)
|
||||||
}
|
}
|
||||||
val trail = monarchy.findAllPagedWithChanges(realmDataSourceFactory,
|
val trail = monarchy.findAllPagedWithChanges(
|
||||||
LivePagedListBuilder(dataSourceFactory,
|
realmDataSourceFactory,
|
||||||
|
LivePagedListBuilder(
|
||||||
|
dataSourceFactory,
|
||||||
PagedList.Config.Builder()
|
PagedList.Config.Builder()
|
||||||
.setPageSize(20)
|
.setPageSize(20)
|
||||||
.setEnablePlaceholders(false)
|
.setEnablePlaceholders(false)
|
||||||
.setPrefetchDistance(1)
|
.setPrefetchDistance(1)
|
||||||
.build())
|
.build()
|
||||||
|
)
|
||||||
)
|
)
|
||||||
return trail
|
return trail
|
||||||
}
|
}
|
||||||
|
@ -1707,7 +1719,7 @@ internal class RealmCryptoStore @Inject constructor(
|
||||||
* So we need to tidy up a bit
|
* So we need to tidy up a bit
|
||||||
*/
|
*/
|
||||||
override fun tidyUpDataBase() {
|
override fun tidyUpDataBase() {
|
||||||
val prevWeekTs = System.currentTimeMillis() - 7 * 24 * 60 * 60 * 1_000
|
val prevWeekTs = clock.epochMillis() - 7 * 24 * 60 * 60 * 1_000
|
||||||
doRealmTransaction(realmConfiguration) { realm ->
|
doRealmTransaction(realmConfiguration) { realm ->
|
||||||
|
|
||||||
// Only keep one week history
|
// Only keep one week history
|
||||||
|
|
|
@ -33,10 +33,13 @@ import org.matrix.android.sdk.internal.crypto.store.db.migration.MigrateCryptoTo
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.migration.MigrateCryptoTo013
|
import org.matrix.android.sdk.internal.crypto.store.db.migration.MigrateCryptoTo013
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.migration.MigrateCryptoTo014
|
import org.matrix.android.sdk.internal.crypto.store.db.migration.MigrateCryptoTo014
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.migration.MigrateCryptoTo015
|
import org.matrix.android.sdk.internal.crypto.store.db.migration.MigrateCryptoTo015
|
||||||
|
import org.matrix.android.sdk.internal.util.time.Clock
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
internal class RealmCryptoStoreMigration @Inject constructor() : RealmMigration {
|
internal class RealmCryptoStoreMigration @Inject constructor(
|
||||||
|
private val clock: Clock,
|
||||||
|
) : RealmMigration {
|
||||||
/**
|
/**
|
||||||
* Forces all RealmCryptoStoreMigration instances to be equal
|
* Forces all RealmCryptoStoreMigration instances to be equal
|
||||||
* Avoids Realm throwing when multiple instances of the migration are set
|
* Avoids Realm throwing when multiple instances of the migration are set
|
||||||
|
@ -59,7 +62,7 @@ internal class RealmCryptoStoreMigration @Inject constructor() : RealmMigration
|
||||||
if (oldVersion < 5) MigrateCryptoTo005(realm).perform()
|
if (oldVersion < 5) MigrateCryptoTo005(realm).perform()
|
||||||
if (oldVersion < 6) MigrateCryptoTo006(realm).perform()
|
if (oldVersion < 6) MigrateCryptoTo006(realm).perform()
|
||||||
if (oldVersion < 7) MigrateCryptoTo007(realm).perform()
|
if (oldVersion < 7) MigrateCryptoTo007(realm).perform()
|
||||||
if (oldVersion < 8) MigrateCryptoTo008(realm).perform()
|
if (oldVersion < 8) MigrateCryptoTo008(realm, clock).perform()
|
||||||
if (oldVersion < 9) MigrateCryptoTo009(realm).perform()
|
if (oldVersion < 9) MigrateCryptoTo009(realm).perform()
|
||||||
if (oldVersion < 10) MigrateCryptoTo010(realm).perform()
|
if (oldVersion < 10) MigrateCryptoTo010(realm).perform()
|
||||||
if (oldVersion < 11) MigrateCryptoTo011(realm).perform()
|
if (oldVersion < 11) MigrateCryptoTo011(realm).perform()
|
||||||
|
|
|
@ -21,8 +21,12 @@ import org.matrix.android.sdk.api.extensions.tryOrNull
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.model.DeviceInfoEntityFields
|
import org.matrix.android.sdk.internal.crypto.store.db.model.DeviceInfoEntityFields
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.model.MyDeviceLastSeenInfoEntityFields
|
import org.matrix.android.sdk.internal.crypto.store.db.model.MyDeviceLastSeenInfoEntityFields
|
||||||
import org.matrix.android.sdk.internal.util.database.RealmMigrator
|
import org.matrix.android.sdk.internal.util.database.RealmMigrator
|
||||||
|
import org.matrix.android.sdk.internal.util.time.Clock
|
||||||
|
|
||||||
internal class MigrateCryptoTo008(realm: DynamicRealm) : RealmMigrator(realm, 8) {
|
internal class MigrateCryptoTo008(
|
||||||
|
realm: DynamicRealm,
|
||||||
|
private val clock: Clock,
|
||||||
|
) : RealmMigrator(realm, 8) {
|
||||||
|
|
||||||
override fun doMigrate(realm: DynamicRealm) {
|
override fun doMigrate(realm: DynamicRealm) {
|
||||||
realm.schema.create("MyDeviceLastSeenInfoEntity")
|
realm.schema.create("MyDeviceLastSeenInfoEntity")
|
||||||
|
@ -33,7 +37,7 @@ internal class MigrateCryptoTo008(realm: DynamicRealm) : RealmMigrator(realm, 8)
|
||||||
.addField(MyDeviceLastSeenInfoEntityFields.LAST_SEEN_TS, Long::class.java)
|
.addField(MyDeviceLastSeenInfoEntityFields.LAST_SEEN_TS, Long::class.java)
|
||||||
.setNullable(MyDeviceLastSeenInfoEntityFields.LAST_SEEN_TS, true)
|
.setNullable(MyDeviceLastSeenInfoEntityFields.LAST_SEEN_TS, true)
|
||||||
|
|
||||||
val now = System.currentTimeMillis()
|
val now = clock.epochMillis()
|
||||||
realm.schema.get("DeviceInfoEntity")
|
realm.schema.get("DeviceInfoEntity")
|
||||||
?.addField(DeviceInfoEntityFields.FIRST_TIME_SEEN_LOCAL_TS, Long::class.java)
|
?.addField(DeviceInfoEntityFields.FIRST_TIME_SEEN_LOCAL_TS, Long::class.java)
|
||||||
?.setNullable(DeviceInfoEntityFields.FIRST_TIME_SEEN_LOCAL_TS, true)
|
?.setNullable(DeviceInfoEntityFields.FIRST_TIME_SEEN_LOCAL_TS, true)
|
||||||
|
|
|
@ -123,7 +123,7 @@ internal class DefaultOutgoingSASDefaultVerificationTransaction(
|
||||||
// val requestMessage = KeyVerificationRequest(
|
// val requestMessage = KeyVerificationRequest(
|
||||||
// fromDevice = session.sessionParams.deviceId ?: "",
|
// fromDevice = session.sessionParams.deviceId ?: "",
|
||||||
// methods = listOf(KeyVerificationStart.VERIF_METHOD_SAS),
|
// methods = listOf(KeyVerificationStart.VERIF_METHOD_SAS),
|
||||||
// timestamp = System.currentTimeMillis().toInt(),
|
// timestamp = clock.epochMillis().toInt(),
|
||||||
// transactionId = transactionId
|
// transactionId = transactionId
|
||||||
// )
|
// )
|
||||||
//
|
//
|
||||||
|
|
|
@ -84,6 +84,7 @@ import org.matrix.android.sdk.internal.di.DeviceId
|
||||||
import org.matrix.android.sdk.internal.di.UserId
|
import org.matrix.android.sdk.internal.di.UserId
|
||||||
import org.matrix.android.sdk.internal.session.SessionScope
|
import org.matrix.android.sdk.internal.session.SessionScope
|
||||||
import org.matrix.android.sdk.internal.task.TaskExecutor
|
import org.matrix.android.sdk.internal.task.TaskExecutor
|
||||||
|
import org.matrix.android.sdk.internal.util.time.Clock
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
@ -104,7 +105,8 @@ internal class DefaultVerificationService @Inject constructor(
|
||||||
private val verificationTransportToDeviceFactory: VerificationTransportToDeviceFactory,
|
private val verificationTransportToDeviceFactory: VerificationTransportToDeviceFactory,
|
||||||
private val crossSigningService: CrossSigningService,
|
private val crossSigningService: CrossSigningService,
|
||||||
private val cryptoCoroutineScope: CoroutineScope,
|
private val cryptoCoroutineScope: CoroutineScope,
|
||||||
private val taskExecutor: TaskExecutor
|
private val taskExecutor: TaskExecutor,
|
||||||
|
private val clock: Clock,
|
||||||
) : DefaultVerificationTransaction.Listener, VerificationService {
|
) : DefaultVerificationTransaction.Listener, VerificationService {
|
||||||
|
|
||||||
private val uiHandler = Handler(Looper.getMainLooper())
|
private val uiHandler = Handler(Looper.getMainLooper())
|
||||||
|
@ -261,9 +263,11 @@ internal class DefaultVerificationService @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun markedLocallyAsManuallyVerified(userId: String, deviceID: String) {
|
override fun markedLocallyAsManuallyVerified(userId: String, deviceID: String) {
|
||||||
setDeviceVerificationAction.handle(DeviceTrustLevel(false, true),
|
setDeviceVerificationAction.handle(
|
||||||
|
DeviceTrustLevel(crossSigningVerified = false, locallyVerified = true),
|
||||||
userId,
|
userId,
|
||||||
deviceID)
|
deviceID
|
||||||
|
)
|
||||||
|
|
||||||
listeners.forEach {
|
listeners.forEach {
|
||||||
try {
|
try {
|
||||||
|
@ -313,7 +317,7 @@ internal class DefaultVerificationService @Inject constructor(
|
||||||
val requestsForUser = pendingRequests.getOrPut(senderId) { mutableListOf() }
|
val requestsForUser = pendingRequests.getOrPut(senderId) { mutableListOf() }
|
||||||
|
|
||||||
val pendingVerificationRequest = PendingVerificationRequest(
|
val pendingVerificationRequest = PendingVerificationRequest(
|
||||||
ageLocalTs = event.ageLocalTs ?: System.currentTimeMillis(),
|
ageLocalTs = event.ageLocalTs ?: clock.epochMillis(),
|
||||||
isIncoming = true,
|
isIncoming = true,
|
||||||
otherUserId = senderId, // requestInfo.toUserId,
|
otherUserId = senderId, // requestInfo.toUserId,
|
||||||
roomId = null,
|
roomId = null,
|
||||||
|
@ -352,7 +356,7 @@ internal class DefaultVerificationService @Inject constructor(
|
||||||
val requestsForUser = pendingRequests.getOrPut(senderId) { mutableListOf() }
|
val requestsForUser = pendingRequests.getOrPut(senderId) { mutableListOf() }
|
||||||
|
|
||||||
val pendingVerificationRequest = PendingVerificationRequest(
|
val pendingVerificationRequest = PendingVerificationRequest(
|
||||||
ageLocalTs = event.ageLocalTs ?: System.currentTimeMillis(),
|
ageLocalTs = event.ageLocalTs ?: clock.epochMillis(),
|
||||||
isIncoming = true,
|
isIncoming = true,
|
||||||
otherUserId = senderId, // requestInfo.toUserId,
|
otherUserId = senderId, // requestInfo.toUserId,
|
||||||
roomId = event.roomId,
|
roomId = event.roomId,
|
||||||
|
@ -552,7 +556,8 @@ internal class DefaultVerificationService @Inject constructor(
|
||||||
myDeviceInfoHolder.get().myDevice.fingerprint()!!,
|
myDeviceInfoHolder.get().myDevice.fingerprint()!!,
|
||||||
startReq.transactionId,
|
startReq.transactionId,
|
||||||
otherUserId,
|
otherUserId,
|
||||||
autoAccept).also { txConfigure(it) }
|
autoAccept
|
||||||
|
).also { txConfigure(it) }
|
||||||
addTransaction(tx)
|
addTransaction(tx)
|
||||||
tx.onVerificationStart(startReq)
|
tx.onVerificationStart(startReq)
|
||||||
return null
|
return null
|
||||||
|
@ -644,9 +649,11 @@ internal class DefaultVerificationService @Inject constructor(
|
||||||
|
|
||||||
if (existingRequest != null) {
|
if (existingRequest != null) {
|
||||||
// Mark this request as cancelled
|
// Mark this request as cancelled
|
||||||
updatePendingRequest(existingRequest.copy(
|
updatePendingRequest(
|
||||||
|
existingRequest.copy(
|
||||||
cancelConclusion = safeValueOf(cancelReq.code)
|
cancelConclusion = safeValueOf(cancelReq.code)
|
||||||
))
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
existingTransaction?.state = VerificationTxState.Cancelled(safeValueOf(cancelReq.code), false)
|
existingTransaction?.state = VerificationTxState.Cancelled(safeValueOf(cancelReq.code), false)
|
||||||
|
@ -809,15 +816,19 @@ internal class DefaultVerificationService @Inject constructor(
|
||||||
?.let { vt ->
|
?.let { vt ->
|
||||||
val otherDeviceId = vt.otherDeviceId
|
val otherDeviceId = vt.otherDeviceId
|
||||||
if (!crossSigningService.canCrossSign()) {
|
if (!crossSigningService.canCrossSign()) {
|
||||||
outgoingGossipingRequestManager.sendSecretShareRequest(MASTER_KEY_SSSS_NAME, mapOf(userId to listOf(otherDeviceId
|
outgoingGossipingRequestManager.sendSecretShareRequest(
|
||||||
?: "*")))
|
MASTER_KEY_SSSS_NAME, mapOf(userId to listOf(otherDeviceId ?: "*"))
|
||||||
outgoingGossipingRequestManager.sendSecretShareRequest(SELF_SIGNING_KEY_SSSS_NAME, mapOf(userId to listOf(otherDeviceId
|
)
|
||||||
?: "*")))
|
outgoingGossipingRequestManager.sendSecretShareRequest(
|
||||||
outgoingGossipingRequestManager.sendSecretShareRequest(USER_SIGNING_KEY_SSSS_NAME, mapOf(userId to listOf(otherDeviceId
|
SELF_SIGNING_KEY_SSSS_NAME, mapOf(userId to listOf(otherDeviceId ?: "*"))
|
||||||
?: "*")))
|
)
|
||||||
|
outgoingGossipingRequestManager.sendSecretShareRequest(
|
||||||
|
USER_SIGNING_KEY_SSSS_NAME, mapOf(userId to listOf(otherDeviceId ?: "*"))
|
||||||
|
)
|
||||||
}
|
}
|
||||||
outgoingGossipingRequestManager.sendSecretShareRequest(KEYBACKUP_SECRET_SSSS_NAME, mapOf(userId to listOf(otherDeviceId
|
outgoingGossipingRequestManager.sendSecretShareRequest(
|
||||||
?: "*")))
|
KEYBACKUP_SECRET_SSSS_NAME, mapOf(userId to listOf(otherDeviceId ?: "*"))
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -917,16 +928,19 @@ internal class DefaultVerificationService @Inject constructor(
|
||||||
qrCodeData = qrCodeData,
|
qrCodeData = qrCodeData,
|
||||||
userId = userId,
|
userId = userId,
|
||||||
deviceId = deviceId ?: "",
|
deviceId = deviceId ?: "",
|
||||||
isIncoming = false)
|
isIncoming = false
|
||||||
|
)
|
||||||
|
|
||||||
tx.transport = transportCreator.invoke(tx)
|
tx.transport = transportCreator.invoke(tx)
|
||||||
|
|
||||||
addTransaction(tx)
|
addTransaction(tx)
|
||||||
}
|
}
|
||||||
|
|
||||||
updatePendingRequest(existingRequest.copy(
|
updatePendingRequest(
|
||||||
|
existingRequest.copy(
|
||||||
readyInfo = readyReq
|
readyInfo = readyReq
|
||||||
))
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createQrCodeData(requestId: String?, otherUserId: String, otherDeviceId: String?): QrCodeData? {
|
private fun createQrCodeData(requestId: String?, otherUserId: String, otherDeviceId: String?): QrCodeData? {
|
||||||
|
@ -1115,7 +1129,8 @@ internal class DefaultVerificationService @Inject constructor(
|
||||||
myDeviceInfoHolder.get().myDevice.fingerprint()!!,
|
myDeviceInfoHolder.get().myDevice.fingerprint()!!,
|
||||||
txID,
|
txID,
|
||||||
otherUserId,
|
otherUserId,
|
||||||
otherDeviceId)
|
otherDeviceId
|
||||||
|
)
|
||||||
tx.transport = verificationTransportToDeviceFactory.createTransport(tx)
|
tx.transport = verificationTransportToDeviceFactory.createTransport(tx)
|
||||||
addTransaction(tx)
|
addTransaction(tx)
|
||||||
|
|
||||||
|
@ -1150,7 +1165,7 @@ internal class DefaultVerificationService @Inject constructor(
|
||||||
val validLocalId = localId ?: LocalEcho.createLocalEchoId()
|
val validLocalId = localId ?: LocalEcho.createLocalEchoId()
|
||||||
|
|
||||||
val verificationRequest = PendingVerificationRequest(
|
val verificationRequest = PendingVerificationRequest(
|
||||||
ageLocalTs = System.currentTimeMillis(),
|
ageLocalTs = clock.epochMillis(),
|
||||||
isIncoming = false,
|
isIncoming = false,
|
||||||
roomId = roomId,
|
roomId = roomId,
|
||||||
localId = validLocalId,
|
localId = validLocalId,
|
||||||
|
@ -1175,11 +1190,13 @@ internal class DefaultVerificationService @Inject constructor(
|
||||||
|
|
||||||
transport.sendVerificationRequest(methodValues, validLocalId, otherUserId, roomId, null) { syncedId, info ->
|
transport.sendVerificationRequest(methodValues, validLocalId, otherUserId, roomId, null) { syncedId, info ->
|
||||||
// We need to update with the syncedID
|
// We need to update with the syncedID
|
||||||
updatePendingRequest(verificationRequest.copy(
|
updatePendingRequest(
|
||||||
|
verificationRequest.copy(
|
||||||
transactionId = syncedId,
|
transactionId = syncedId,
|
||||||
// localId stays different
|
// localId stays different
|
||||||
requestInfo = info
|
requestInfo = info
|
||||||
))
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
requestsForUser.add(verificationRequest)
|
requestsForUser.add(verificationRequest)
|
||||||
|
@ -1228,7 +1245,7 @@ internal class DefaultVerificationService @Inject constructor(
|
||||||
|
|
||||||
val verificationRequest = PendingVerificationRequest(
|
val verificationRequest = PendingVerificationRequest(
|
||||||
transactionId = localId,
|
transactionId = localId,
|
||||||
ageLocalTs = System.currentTimeMillis(),
|
ageLocalTs = clock.epochMillis(),
|
||||||
isIncoming = false,
|
isIncoming = false,
|
||||||
roomId = null,
|
roomId = null,
|
||||||
localId = localId,
|
localId = localId,
|
||||||
|
@ -1254,10 +1271,12 @@ internal class DefaultVerificationService @Inject constructor(
|
||||||
|
|
||||||
transport.sendVerificationRequest(methodValues, localId, otherUserId, null, targetDevices) { _, info ->
|
transport.sendVerificationRequest(methodValues, localId, otherUserId, null, targetDevices) { _, info ->
|
||||||
// Nothing special to do in to device mode
|
// Nothing special to do in to device mode
|
||||||
updatePendingRequest(verificationRequest.copy(
|
updatePendingRequest(
|
||||||
|
verificationRequest.copy(
|
||||||
// localId stays different
|
// localId stays different
|
||||||
requestInfo = info
|
requestInfo = info
|
||||||
))
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
requestsForUser.add(verificationRequest)
|
requestsForUser.add(verificationRequest)
|
||||||
|
@ -1271,9 +1290,11 @@ internal class DefaultVerificationService @Inject constructor(
|
||||||
.cancelTransaction(transactionId, otherUserId, null, CancelCode.User)
|
.cancelTransaction(transactionId, otherUserId, null, CancelCode.User)
|
||||||
|
|
||||||
getExistingVerificationRequest(otherUserId, transactionId)?.let {
|
getExistingVerificationRequest(otherUserId, transactionId)?.let {
|
||||||
updatePendingRequest(it.copy(
|
updatePendingRequest(
|
||||||
|
it.copy(
|
||||||
cancelConclusion = CancelCode.User
|
cancelConclusion = CancelCode.User
|
||||||
))
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1307,7 +1328,8 @@ internal class DefaultVerificationService @Inject constructor(
|
||||||
myDeviceInfoHolder.get().myDevice.fingerprint()!!,
|
myDeviceInfoHolder.get().myDevice.fingerprint()!!,
|
||||||
transactionId,
|
transactionId,
|
||||||
otherUserId,
|
otherUserId,
|
||||||
otherDeviceId)
|
otherDeviceId
|
||||||
|
)
|
||||||
tx.transport = verificationTransportRoomMessageFactory.createTransport(roomId, tx)
|
tx.transport = verificationTransportRoomMessageFactory.createTransport(roomId, tx)
|
||||||
addTransaction(tx)
|
addTransaction(tx)
|
||||||
|
|
||||||
|
@ -1333,7 +1355,8 @@ internal class DefaultVerificationService @Inject constructor(
|
||||||
otherUserId,
|
otherUserId,
|
||||||
existingRequest.requestInfo?.fromDevice ?: "",
|
existingRequest.requestInfo?.fromDevice ?: "",
|
||||||
existingRequest.requestInfo?.methods,
|
existingRequest.requestInfo?.methods,
|
||||||
methods) {
|
methods
|
||||||
|
) {
|
||||||
verificationTransportRoomMessageFactory.createTransport(roomId, it)
|
verificationTransportRoomMessageFactory.createTransport(roomId, it)
|
||||||
}
|
}
|
||||||
if (methods.isNullOrEmpty()) {
|
if (methods.isNullOrEmpty()) {
|
||||||
|
@ -1343,7 +1366,8 @@ internal class DefaultVerificationService @Inject constructor(
|
||||||
}
|
}
|
||||||
// TODO this is not yet related to a transaction, maybe we should use another method like for cancel?
|
// TODO this is not yet related to a transaction, maybe we should use another method like for cancel?
|
||||||
val readyMsg = transport.createReady(transactionId, deviceId ?: "", computedMethods)
|
val readyMsg = transport.createReady(transactionId, deviceId ?: "", computedMethods)
|
||||||
transport.sendToOther(EventType.KEY_VERIFICATION_READY,
|
transport.sendToOther(
|
||||||
|
EventType.KEY_VERIFICATION_READY,
|
||||||
readyMsg,
|
readyMsg,
|
||||||
VerificationTxState.None,
|
VerificationTxState.None,
|
||||||
CancelCode.User,
|
CancelCode.User,
|
||||||
|
@ -1372,7 +1396,8 @@ internal class DefaultVerificationService @Inject constructor(
|
||||||
otherUserId,
|
otherUserId,
|
||||||
existingRequest.requestInfo?.fromDevice ?: "",
|
existingRequest.requestInfo?.fromDevice ?: "",
|
||||||
existingRequest.requestInfo?.methods,
|
existingRequest.requestInfo?.methods,
|
||||||
methods) {
|
methods
|
||||||
|
) {
|
||||||
verificationTransportToDeviceFactory.createTransport(it)
|
verificationTransportToDeviceFactory.createTransport(it)
|
||||||
}
|
}
|
||||||
if (methods.isNullOrEmpty()) {
|
if (methods.isNullOrEmpty()) {
|
||||||
|
@ -1446,7 +1471,8 @@ internal class DefaultVerificationService @Inject constructor(
|
||||||
qrCodeData = qrCodeData,
|
qrCodeData = qrCodeData,
|
||||||
userId = userId,
|
userId = userId,
|
||||||
deviceId = deviceId ?: "",
|
deviceId = deviceId ?: "",
|
||||||
isIncoming = false)
|
isIncoming = false
|
||||||
|
)
|
||||||
|
|
||||||
tx.transport = transportCreator.invoke(tx)
|
tx.transport = transportCreator.invoke(tx)
|
||||||
|
|
||||||
|
|
|
@ -34,11 +34,13 @@ import org.matrix.android.sdk.internal.database.model.EventInsertType
|
||||||
import org.matrix.android.sdk.internal.di.DeviceId
|
import org.matrix.android.sdk.internal.di.DeviceId
|
||||||
import org.matrix.android.sdk.internal.di.UserId
|
import org.matrix.android.sdk.internal.di.UserId
|
||||||
import org.matrix.android.sdk.internal.session.EventInsertLiveProcessor
|
import org.matrix.android.sdk.internal.session.EventInsertLiveProcessor
|
||||||
|
import org.matrix.android.sdk.internal.util.time.Clock
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
internal class VerificationMessageProcessor @Inject constructor(
|
internal class VerificationMessageProcessor @Inject constructor(
|
||||||
private val eventDecryptor: EventDecryptor,
|
private val eventDecryptor: EventDecryptor,
|
||||||
|
private val clock: Clock,
|
||||||
private val verificationService: DefaultVerificationService,
|
private val verificationService: DefaultVerificationService,
|
||||||
@UserId private val userId: String,
|
@UserId private val userId: String,
|
||||||
@DeviceId private val deviceId: String?
|
@DeviceId private val deviceId: String?
|
||||||
|
@ -71,8 +73,7 @@ internal class VerificationMessageProcessor @Inject constructor(
|
||||||
// If the request is in the future by more than 5 minutes or more than 10 minutes in the past,
|
// If the request is in the future by more than 5 minutes or more than 10 minutes in the past,
|
||||||
// the message should be ignored by the receiver.
|
// the message should be ignored by the receiver.
|
||||||
|
|
||||||
if (!VerificationService.isValidRequest(event.ageLocalTs
|
if (!VerificationService.isValidRequest(event.ageLocalTs ?: event.originServerTs, clock.epochMillis())) return Unit.also {
|
||||||
?: event.originServerTs)) return Unit.also {
|
|
||||||
Timber.d("## SAS Verification live observer: msgId: ${event.eventId} is outdated")
|
Timber.d("## SAS Verification live observer: msgId: ${event.eventId} is outdated")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -47,6 +47,7 @@ import org.matrix.android.sdk.internal.crypto.model.rest.VERIFICATION_METHOD_REC
|
||||||
import org.matrix.android.sdk.internal.crypto.model.rest.VERIFICATION_METHOD_SAS
|
import org.matrix.android.sdk.internal.crypto.model.rest.VERIFICATION_METHOD_SAS
|
||||||
import org.matrix.android.sdk.internal.di.WorkManagerProvider
|
import org.matrix.android.sdk.internal.di.WorkManagerProvider
|
||||||
import org.matrix.android.sdk.internal.session.room.send.LocalEchoEventFactory
|
import org.matrix.android.sdk.internal.session.room.send.LocalEchoEventFactory
|
||||||
|
import org.matrix.android.sdk.internal.util.time.Clock
|
||||||
import org.matrix.android.sdk.internal.worker.SessionSafeCoroutineWorker
|
import org.matrix.android.sdk.internal.worker.SessionSafeCoroutineWorker
|
||||||
import org.matrix.android.sdk.internal.worker.WorkerParamsFactory
|
import org.matrix.android.sdk.internal.worker.WorkerParamsFactory
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
@ -61,7 +62,8 @@ internal class VerificationTransportRoomMessage(
|
||||||
private val roomId: String,
|
private val roomId: String,
|
||||||
private val localEchoEventFactory: LocalEchoEventFactory,
|
private val localEchoEventFactory: LocalEchoEventFactory,
|
||||||
private val tx: DefaultVerificationTransaction?,
|
private val tx: DefaultVerificationTransaction?,
|
||||||
private val coroutineScope: CoroutineScope
|
private val coroutineScope: CoroutineScope,
|
||||||
|
private val clock: Clock,
|
||||||
) : VerificationTransport {
|
) : VerificationTransport {
|
||||||
|
|
||||||
override fun <T> sendToOther(type: String,
|
override fun <T> sendToOther(type: String,
|
||||||
|
@ -77,10 +79,12 @@ internal class VerificationTransportRoomMessage(
|
||||||
content = verificationInfo.toEventContent()!!
|
content = verificationInfo.toEventContent()!!
|
||||||
)
|
)
|
||||||
|
|
||||||
val workerParams = WorkerParamsFactory.toData(SendVerificationMessageWorker.Params(
|
val workerParams = WorkerParamsFactory.toData(
|
||||||
|
SendVerificationMessageWorker.Params(
|
||||||
sessionId = sessionId,
|
sessionId = sessionId,
|
||||||
eventId = event.eventId ?: ""
|
eventId = event.eventId ?: ""
|
||||||
))
|
)
|
||||||
|
)
|
||||||
val enqueueInfo = enqueueSendWork(workerParams)
|
val enqueueInfo = enqueueSendWork(workerParams)
|
||||||
|
|
||||||
// I cannot just listen to the given work request, because when used in a uniqueWork,
|
// I cannot just listen to the given work request, because when used in a uniqueWork,
|
||||||
|
@ -155,7 +159,7 @@ internal class VerificationTransportRoomMessage(
|
||||||
transactionId = "",
|
transactionId = "",
|
||||||
fromDevice = userDeviceId ?: "",
|
fromDevice = userDeviceId ?: "",
|
||||||
methods = supportedMethods,
|
methods = supportedMethods,
|
||||||
timestamp = System.currentTimeMillis()
|
timestamp = clock.epochMillis()
|
||||||
)
|
)
|
||||||
|
|
||||||
val info = MessageVerificationRequestContent(
|
val info = MessageVerificationRequestContent(
|
||||||
|
@ -175,10 +179,12 @@ internal class VerificationTransportRoomMessage(
|
||||||
content
|
content
|
||||||
)
|
)
|
||||||
|
|
||||||
val workerParams = WorkerParamsFactory.toData(SendVerificationMessageWorker.Params(
|
val workerParams = WorkerParamsFactory.toData(
|
||||||
|
SendVerificationMessageWorker.Params(
|
||||||
sessionId = sessionId,
|
sessionId = sessionId,
|
||||||
eventId = event.eventId ?: ""
|
eventId = event.eventId ?: ""
|
||||||
))
|
)
|
||||||
|
)
|
||||||
|
|
||||||
val workRequest = workManagerProvider.matrixOneTimeWorkRequestBuilder<SendVerificationMessageWorker>()
|
val workRequest = workManagerProvider.matrixOneTimeWorkRequestBuilder<SendVerificationMessageWorker>()
|
||||||
.setConstraints(WorkManagerProvider.workConstraints)
|
.setConstraints(WorkManagerProvider.workConstraints)
|
||||||
|
@ -230,10 +236,12 @@ internal class VerificationTransportRoomMessage(
|
||||||
roomId = roomId,
|
roomId = roomId,
|
||||||
content = MessageVerificationCancelContent.create(transactionId, code).toContent()
|
content = MessageVerificationCancelContent.create(transactionId, code).toContent()
|
||||||
)
|
)
|
||||||
val workerParams = WorkerParamsFactory.toData(SendVerificationMessageWorker.Params(
|
val workerParams = WorkerParamsFactory.toData(
|
||||||
|
SendVerificationMessageWorker.Params(
|
||||||
sessionId = sessionId,
|
sessionId = sessionId,
|
||||||
eventId = event.eventId ?: ""
|
eventId = event.eventId ?: ""
|
||||||
))
|
)
|
||||||
|
)
|
||||||
enqueueSendWork(workerParams)
|
enqueueSendWork(workerParams)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -250,10 +258,12 @@ internal class VerificationTransportRoomMessage(
|
||||||
)
|
)
|
||||||
).toContent()
|
).toContent()
|
||||||
)
|
)
|
||||||
val workerParams = WorkerParamsFactory.toData(SendVerificationMessageWorker.Params(
|
val workerParams = WorkerParamsFactory.toData(
|
||||||
|
SendVerificationMessageWorker.Params(
|
||||||
sessionId = sessionId,
|
sessionId = sessionId,
|
||||||
eventId = event.eventId ?: ""
|
eventId = event.eventId ?: ""
|
||||||
))
|
)
|
||||||
|
)
|
||||||
val enqueueInfo = enqueueSendWork(workerParams)
|
val enqueueInfo = enqueueSendWork(workerParams)
|
||||||
|
|
||||||
val workLiveData = workManagerProvider.workManager
|
val workLiveData = workManagerProvider.workManager
|
||||||
|
@ -361,7 +371,7 @@ internal class VerificationTransportRoomMessage(
|
||||||
private fun createEventAndLocalEcho(localId: String = LocalEcho.createLocalEchoId(), type: String, roomId: String, content: Content): Event {
|
private fun createEventAndLocalEcho(localId: String = LocalEcho.createLocalEchoId(), type: String, roomId: String, content: Content): Event {
|
||||||
return Event(
|
return Event(
|
||||||
roomId = roomId,
|
roomId = roomId,
|
||||||
originServerTs = System.currentTimeMillis(),
|
originServerTs = clock.epochMillis(),
|
||||||
senderId = userId,
|
senderId = userId,
|
||||||
eventId = localId,
|
eventId = localId,
|
||||||
type = type,
|
type = type,
|
||||||
|
|
|
@ -22,6 +22,7 @@ import org.matrix.android.sdk.internal.di.UserId
|
||||||
import org.matrix.android.sdk.internal.di.WorkManagerProvider
|
import org.matrix.android.sdk.internal.di.WorkManagerProvider
|
||||||
import org.matrix.android.sdk.internal.session.room.send.LocalEchoEventFactory
|
import org.matrix.android.sdk.internal.session.room.send.LocalEchoEventFactory
|
||||||
import org.matrix.android.sdk.internal.task.TaskExecutor
|
import org.matrix.android.sdk.internal.task.TaskExecutor
|
||||||
|
import org.matrix.android.sdk.internal.util.time.Clock
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
internal class VerificationTransportRoomMessageFactory @Inject constructor(
|
internal class VerificationTransportRoomMessageFactory @Inject constructor(
|
||||||
|
@ -33,17 +34,21 @@ internal class VerificationTransportRoomMessageFactory @Inject constructor(
|
||||||
@DeviceId
|
@DeviceId
|
||||||
private val deviceId: String?,
|
private val deviceId: String?,
|
||||||
private val localEchoEventFactory: LocalEchoEventFactory,
|
private val localEchoEventFactory: LocalEchoEventFactory,
|
||||||
private val taskExecutor: TaskExecutor
|
private val taskExecutor: TaskExecutor,
|
||||||
|
private val clock: Clock,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
fun createTransport(roomId: String, tx: DefaultVerificationTransaction?): VerificationTransportRoomMessage {
|
fun createTransport(roomId: String, tx: DefaultVerificationTransaction?): VerificationTransportRoomMessage {
|
||||||
return VerificationTransportRoomMessage(workManagerProvider,
|
return VerificationTransportRoomMessage(
|
||||||
|
workManagerProvider,
|
||||||
sessionId,
|
sessionId,
|
||||||
userId,
|
userId,
|
||||||
deviceId,
|
deviceId,
|
||||||
roomId,
|
roomId,
|
||||||
localEchoEventFactory,
|
localEchoEventFactory,
|
||||||
tx,
|
tx,
|
||||||
taskExecutor.executorScope)
|
taskExecutor.executorScope,
|
||||||
|
clock
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,13 +35,16 @@ import org.matrix.android.sdk.internal.crypto.model.rest.VERIFICATION_METHOD_SAS
|
||||||
import org.matrix.android.sdk.internal.crypto.tasks.SendToDeviceTask
|
import org.matrix.android.sdk.internal.crypto.tasks.SendToDeviceTask
|
||||||
import org.matrix.android.sdk.internal.task.TaskExecutor
|
import org.matrix.android.sdk.internal.task.TaskExecutor
|
||||||
import org.matrix.android.sdk.internal.task.configureWith
|
import org.matrix.android.sdk.internal.task.configureWith
|
||||||
|
import org.matrix.android.sdk.internal.util.time.Clock
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
|
||||||
|
// TODO var could be val
|
||||||
internal class VerificationTransportToDevice(
|
internal class VerificationTransportToDevice(
|
||||||
private var tx: DefaultVerificationTransaction?,
|
private var tx: DefaultVerificationTransaction?,
|
||||||
private var sendToDeviceTask: SendToDeviceTask,
|
private var sendToDeviceTask: SendToDeviceTask,
|
||||||
private val myDeviceId: String?,
|
private val myDeviceId: String?,
|
||||||
private var taskExecutor: TaskExecutor
|
private var taskExecutor: TaskExecutor,
|
||||||
|
private val clock: Clock,
|
||||||
) : VerificationTransport {
|
) : VerificationTransport {
|
||||||
|
|
||||||
override fun sendVerificationRequest(supportedMethods: List<String>,
|
override fun sendVerificationRequest(supportedMethods: List<String>,
|
||||||
|
@ -56,7 +59,7 @@ internal class VerificationTransportToDevice(
|
||||||
transactionId = localId,
|
transactionId = localId,
|
||||||
fromDevice = myDeviceId ?: "",
|
fromDevice = myDeviceId ?: "",
|
||||||
methods = supportedMethods,
|
methods = supportedMethods,
|
||||||
timestamp = System.currentTimeMillis()
|
timestamp = clock.epochMillis()
|
||||||
)
|
)
|
||||||
val keyReq = KeyVerificationRequest(
|
val keyReq = KeyVerificationRequest(
|
||||||
fromDevice = validKeyReq.fromDevice,
|
fromDevice = validKeyReq.fromDevice,
|
||||||
|
@ -201,7 +204,8 @@ internal class VerificationTransportToDevice(
|
||||||
hash,
|
hash,
|
||||||
commitment,
|
commitment,
|
||||||
messageAuthenticationCode,
|
messageAuthenticationCode,
|
||||||
shortAuthenticationStrings)
|
shortAuthenticationStrings
|
||||||
|
)
|
||||||
|
|
||||||
override fun createKey(tid: String, pubKey: String): VerificationInfoKey = KeyVerificationKey.create(tid, pubKey)
|
override fun createKey(tid: String, pubKey: String): VerificationInfoKey = KeyVerificationKey.create(tid, pubKey)
|
||||||
|
|
||||||
|
@ -221,7 +225,8 @@ internal class VerificationTransportToDevice(
|
||||||
hashes,
|
hashes,
|
||||||
messageAuthenticationCodes,
|
messageAuthenticationCodes,
|
||||||
shortAuthenticationStrings,
|
shortAuthenticationStrings,
|
||||||
null)
|
null
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun createStartForQrCode(fromDevice: String,
|
override fun createStartForQrCode(fromDevice: String,
|
||||||
|
@ -235,7 +240,8 @@ internal class VerificationTransportToDevice(
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
sharedSecret)
|
sharedSecret
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun createReady(tid: String, fromDevice: String, methods: List<String>): VerificationInfoReady {
|
override fun createReady(tid: String, fromDevice: String, methods: List<String>): VerificationInfoReady {
|
||||||
|
|
|
@ -19,14 +19,17 @@ package org.matrix.android.sdk.internal.crypto.verification
|
||||||
import org.matrix.android.sdk.internal.crypto.tasks.SendToDeviceTask
|
import org.matrix.android.sdk.internal.crypto.tasks.SendToDeviceTask
|
||||||
import org.matrix.android.sdk.internal.di.DeviceId
|
import org.matrix.android.sdk.internal.di.DeviceId
|
||||||
import org.matrix.android.sdk.internal.task.TaskExecutor
|
import org.matrix.android.sdk.internal.task.TaskExecutor
|
||||||
|
import org.matrix.android.sdk.internal.util.time.Clock
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
internal class VerificationTransportToDeviceFactory @Inject constructor(
|
internal class VerificationTransportToDeviceFactory @Inject constructor(
|
||||||
private val sendToDeviceTask: SendToDeviceTask,
|
private val sendToDeviceTask: SendToDeviceTask,
|
||||||
@DeviceId val myDeviceId: String?,
|
@DeviceId val myDeviceId: String?,
|
||||||
private val taskExecutor: TaskExecutor) {
|
private val taskExecutor: TaskExecutor,
|
||||||
|
private val clock: Clock,
|
||||||
|
) {
|
||||||
|
|
||||||
fun createTransport(tx: DefaultVerificationTransaction?): VerificationTransportToDevice {
|
fun createTransport(tx: DefaultVerificationTransaction?): VerificationTransportToDevice {
|
||||||
return VerificationTransportToDevice(tx, sendToDeviceTask, myDeviceId, taskExecutor)
|
return VerificationTransportToDevice(tx, sendToDeviceTask, myDeviceId, taskExecutor, clock)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@ import kotlinx.coroutines.isActive
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
import kotlin.system.measureTimeMillis
|
||||||
|
|
||||||
internal fun <T> CoroutineScope.asyncTransaction(monarchy: Monarchy, transaction: suspend (realm: Realm) -> T) {
|
internal fun <T> CoroutineScope.asyncTransaction(monarchy: Monarchy, transaction: suspend (realm: Realm) -> T) {
|
||||||
asyncTransaction(monarchy.realmConfiguration, transaction)
|
asyncTransaction(monarchy.realmConfiguration, transaction)
|
||||||
|
@ -41,13 +42,13 @@ internal suspend fun <T> awaitTransaction(config: RealmConfiguration, transactio
|
||||||
bgRealm.beginTransaction()
|
bgRealm.beginTransaction()
|
||||||
val result: T
|
val result: T
|
||||||
try {
|
try {
|
||||||
val start = System.currentTimeMillis()
|
measureTimeMillis {
|
||||||
result = transaction(bgRealm)
|
result = transaction(bgRealm)
|
||||||
if (isActive) {
|
if (isActive) {
|
||||||
bgRealm.commitTransaction()
|
bgRealm.commitTransaction()
|
||||||
val end = System.currentTimeMillis()
|
}
|
||||||
val time = end - start
|
}.also {
|
||||||
Timber.v("Execute transaction in $time millis")
|
Timber.v("Execute transaction in $it millis")
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
if (bgRealm.isInTransaction) {
|
if (bgRealm.isInTransaction) {
|
||||||
|
|
|
@ -137,7 +137,8 @@ internal fun ThreadSummaryEntity.Companion.createOrUpdate(
|
||||||
roomMemberContentsByUser: HashMap<String, RoomMemberContent?>,
|
roomMemberContentsByUser: HashMap<String, RoomMemberContent?>,
|
||||||
roomEntity: RoomEntity,
|
roomEntity: RoomEntity,
|
||||||
userId: String,
|
userId: String,
|
||||||
cryptoService: CryptoService? = null
|
cryptoService: CryptoService? = null,
|
||||||
|
currentTimeMillis: Long,
|
||||||
) {
|
) {
|
||||||
when (threadSummaryType) {
|
when (threadSummaryType) {
|
||||||
ThreadSummaryUpdateType.REPLACE -> {
|
ThreadSummaryUpdateType.REPLACE -> {
|
||||||
|
@ -153,14 +154,14 @@ internal fun ThreadSummaryEntity.Companion.createOrUpdate(
|
||||||
Timber.i("###THREADS ThreadSummaryHelper REPLACE eventId:${it.rootThreadEventId} ")
|
Timber.i("###THREADS ThreadSummaryHelper REPLACE eventId:${it.rootThreadEventId} ")
|
||||||
}
|
}
|
||||||
|
|
||||||
val rootThreadEventEntity = createEventEntity(roomId, rootThreadEvent, realm).also {
|
val rootThreadEventEntity = createEventEntity(realm, roomId, rootThreadEvent, currentTimeMillis).also {
|
||||||
try {
|
try {
|
||||||
decryptIfNeeded(cryptoService, it, roomId)
|
decryptIfNeeded(cryptoService, it, roomId)
|
||||||
} catch (e: InterruptedException) {
|
} catch (e: InterruptedException) {
|
||||||
Timber.i("Decryption got interrupted")
|
Timber.i("Decryption got interrupted")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val latestThreadEventEntity = createLatestEventEntity(roomId, rootThreadEvent, roomMemberContentsByUser, realm)?.also {
|
val latestThreadEventEntity = createLatestEventEntity(realm, roomId, rootThreadEvent, roomMemberContentsByUser, currentTimeMillis)?.also {
|
||||||
try {
|
try {
|
||||||
decryptIfNeeded(cryptoService, it, roomId)
|
decryptIfNeeded(cryptoService, it, roomId)
|
||||||
} catch (e: InterruptedException) {
|
} catch (e: InterruptedException) {
|
||||||
|
@ -268,8 +269,8 @@ private fun HashMap<String, RoomMemberContent?>.addSenderState(realm: Realm, roo
|
||||||
/**
|
/**
|
||||||
* Create an EventEntity for the root thread event or get an existing one
|
* Create an EventEntity for the root thread event or get an existing one
|
||||||
*/
|
*/
|
||||||
private fun createEventEntity(roomId: String, event: Event, realm: Realm): EventEntity {
|
private fun createEventEntity(realm: Realm, roomId: String, event: Event, currentTimeMillis: Long): EventEntity {
|
||||||
val ageLocalTs = event.unsignedData?.age?.let { System.currentTimeMillis() - it }
|
val ageLocalTs = event.unsignedData?.age?.let { currentTimeMillis - it }
|
||||||
return event.toEntity(roomId, SendState.SYNCED, ageLocalTs).copyToRealmOrIgnore(realm, EventInsertType.PAGINATION)
|
return event.toEntity(roomId, SendState.SYNCED, ageLocalTs).copyToRealmOrIgnore(realm, EventInsertType.PAGINATION)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -278,15 +279,17 @@ private fun createEventEntity(roomId: String, event: Event, realm: Realm): Event
|
||||||
* state
|
* state
|
||||||
*/
|
*/
|
||||||
private fun createLatestEventEntity(
|
private fun createLatestEventEntity(
|
||||||
|
realm: Realm,
|
||||||
roomId: String,
|
roomId: String,
|
||||||
rootThreadEvent: Event,
|
rootThreadEvent: Event,
|
||||||
roomMemberContentsByUser: HashMap<String, RoomMemberContent?>,
|
roomMemberContentsByUser: HashMap<String, RoomMemberContent?>,
|
||||||
realm: Realm): EventEntity? {
|
currentTimeMillis: Long,
|
||||||
|
): EventEntity? {
|
||||||
return getLatestEvent(rootThreadEvent)?.let {
|
return getLatestEvent(rootThreadEvent)?.let {
|
||||||
it.senderId?.let { senderId ->
|
it.senderId?.let { senderId ->
|
||||||
roomMemberContentsByUser.addSenderState(realm, roomId, senderId)
|
roomMemberContentsByUser.addSenderState(realm, roomId, senderId)
|
||||||
}
|
}
|
||||||
createEventEntity(roomId, it, realm)
|
createEventEntity(realm, roomId, it, currentTimeMillis)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -30,13 +30,14 @@ import org.matrix.android.sdk.api.session.threads.ThreadNotificationState
|
||||||
import org.matrix.android.sdk.internal.database.model.EventEntity
|
import org.matrix.android.sdk.internal.database.model.EventEntity
|
||||||
import org.matrix.android.sdk.internal.di.MoshiProvider
|
import org.matrix.android.sdk.internal.di.MoshiProvider
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
import kotlin.random.Random
|
||||||
|
|
||||||
internal object EventMapper {
|
internal object EventMapper {
|
||||||
|
|
||||||
fun map(event: Event, roomId: String): EventEntity {
|
fun map(event: Event, roomId: String): EventEntity {
|
||||||
val eventEntity = EventEntity()
|
val eventEntity = EventEntity()
|
||||||
// TODO change this as we shouldn't use event everywhere
|
// TODO change this as we shouldn't use event everywhere
|
||||||
eventEntity.eventId = event.eventId ?: "$$roomId-${System.currentTimeMillis()}-${event.hashCode()}"
|
eventEntity.eventId = event.eventId ?: "$$roomId-${Random.nextLong()}-${event.hashCode()}"
|
||||||
eventEntity.roomId = event.roomId ?: roomId
|
eventEntity.roomId = event.roomId ?: roomId
|
||||||
eventEntity.content = ContentMapper.map(event.content)
|
eventEntity.content = ContentMapper.map(event.content)
|
||||||
eventEntity.prevContent = ContentMapper.map(event.resolvedPrevContent())
|
eventEntity.prevContent = ContentMapper.map(event.resolvedPrevContent())
|
||||||
|
@ -126,7 +127,10 @@ internal fun EventEntity.asDomain(castJsonNumbers: Boolean = false): Event {
|
||||||
return EventMapper.map(this, castJsonNumbers)
|
return EventMapper.map(this, castJsonNumbers)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun Event.toEntity(roomId: String, sendState: SendState, ageLocalTs: Long?, contentToInject: String? = null): EventEntity {
|
internal fun Event.toEntity(roomId: String,
|
||||||
|
sendState: SendState,
|
||||||
|
ageLocalTs: Long?,
|
||||||
|
contentToInject: String? = null): EventEntity {
|
||||||
return EventMapper.map(this, roomId).apply {
|
return EventMapper.map(this, roomId).apply {
|
||||||
this.sendState = sendState
|
this.sendState = sendState
|
||||||
this.ageLocalTs = ageLocalTs
|
this.ageLocalTs = ageLocalTs
|
||||||
|
|
|
@ -38,6 +38,7 @@ import org.matrix.android.sdk.internal.di.SessionDownloadsDirectory
|
||||||
import org.matrix.android.sdk.internal.di.UnauthenticatedWithCertificateWithProgress
|
import org.matrix.android.sdk.internal.di.UnauthenticatedWithCertificateWithProgress
|
||||||
import org.matrix.android.sdk.internal.session.download.DownloadProgressInterceptor.Companion.DOWNLOAD_PROGRESS_INTERCEPTOR_HEADER
|
import org.matrix.android.sdk.internal.session.download.DownloadProgressInterceptor.Companion.DOWNLOAD_PROGRESS_INTERCEPTOR_HEADER
|
||||||
import org.matrix.android.sdk.internal.util.file.AtomicFileCreator
|
import org.matrix.android.sdk.internal.util.file.AtomicFileCreator
|
||||||
|
import org.matrix.android.sdk.internal.util.time.Clock
|
||||||
import org.matrix.android.sdk.internal.util.writeToFile
|
import org.matrix.android.sdk.internal.util.writeToFile
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
@ -51,7 +52,8 @@ internal class DefaultFileService @Inject constructor(
|
||||||
private val contentUrlResolver: ContentUrlResolver,
|
private val contentUrlResolver: ContentUrlResolver,
|
||||||
@UnauthenticatedWithCertificateWithProgress
|
@UnauthenticatedWithCertificateWithProgress
|
||||||
private val okHttpClient: OkHttpClient,
|
private val okHttpClient: OkHttpClient,
|
||||||
private val coroutineDispatchers: MatrixCoroutineDispatchers
|
private val coroutineDispatchers: MatrixCoroutineDispatchers,
|
||||||
|
private val clock: Clock,
|
||||||
) : FileService {
|
) : FileService {
|
||||||
|
|
||||||
// Legacy folder, will be deleted
|
// Legacy folder, will be deleted
|
||||||
|
@ -182,7 +184,8 @@ internal class DefaultFileService @Inject constructor(
|
||||||
MXEncryptedAttachments.decryptAttachment(
|
MXEncryptedAttachments.decryptAttachment(
|
||||||
inputStream,
|
inputStream,
|
||||||
elementToDecrypt,
|
elementToDecrypt,
|
||||||
outputStream
|
outputStream,
|
||||||
|
clock
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,6 +34,7 @@ import org.matrix.android.sdk.api.session.room.model.call.CallSelectAnswerConten
|
||||||
import org.matrix.android.sdk.api.session.room.model.call.CallSignalingContent
|
import org.matrix.android.sdk.api.session.room.model.call.CallSignalingContent
|
||||||
import org.matrix.android.sdk.internal.di.UserId
|
import org.matrix.android.sdk.internal.di.UserId
|
||||||
import org.matrix.android.sdk.internal.session.SessionScope
|
import org.matrix.android.sdk.internal.session.SessionScope
|
||||||
|
import org.matrix.android.sdk.internal.util.time.Clock
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@ -41,9 +42,12 @@ private val loggerTag = LoggerTag("CallSignalingHandler", LoggerTag.VOIP)
|
||||||
private const val MAX_AGE_TO_RING = 40_000
|
private const val MAX_AGE_TO_RING = 40_000
|
||||||
|
|
||||||
@SessionScope
|
@SessionScope
|
||||||
internal class CallSignalingHandler @Inject constructor(private val activeCallHandler: ActiveCallHandler,
|
internal class CallSignalingHandler @Inject constructor(
|
||||||
|
private val activeCallHandler: ActiveCallHandler,
|
||||||
private val mxCallFactory: MxCallFactory,
|
private val mxCallFactory: MxCallFactory,
|
||||||
@UserId private val userId: String) {
|
@UserId private val userId: String,
|
||||||
|
private val clock: Clock,
|
||||||
|
) {
|
||||||
|
|
||||||
private val invitedCallIds = mutableSetOf<String>()
|
private val invitedCallIds = mutableSetOf<String>()
|
||||||
private val callListeners = mutableSetOf<CallListener>()
|
private val callListeners = mutableSetOf<CallListener>()
|
||||||
|
@ -184,7 +188,7 @@ internal class CallSignalingHandler @Inject constructor(private val activeCallHa
|
||||||
if (event.roomId == null || event.senderId == null) {
|
if (event.roomId == null || event.senderId == null) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
val now = System.currentTimeMillis()
|
val now = clock.epochMillis()
|
||||||
val age = now - (event.ageLocalTs ?: now)
|
val age = now - (event.ageLocalTs ?: now)
|
||||||
if (age > MAX_AGE_TO_RING) {
|
if (age > MAX_AGE_TO_RING) {
|
||||||
Timber.tag(loggerTag.value).w("Call invite is too old to ring.")
|
Timber.tag(loggerTag.value).w("Call invite is too old to ring.")
|
||||||
|
|
|
@ -28,6 +28,7 @@ import org.matrix.android.sdk.internal.session.call.model.MxCallImpl
|
||||||
import org.matrix.android.sdk.internal.session.profile.GetProfileInfoTask
|
import org.matrix.android.sdk.internal.session.profile.GetProfileInfoTask
|
||||||
import org.matrix.android.sdk.internal.session.room.send.LocalEchoEventFactory
|
import org.matrix.android.sdk.internal.session.room.send.LocalEchoEventFactory
|
||||||
import org.matrix.android.sdk.internal.session.room.send.queue.EventSenderProcessor
|
import org.matrix.android.sdk.internal.session.room.send.queue.EventSenderProcessor
|
||||||
|
import org.matrix.android.sdk.internal.util.time.Clock
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
internal class MxCallFactory @Inject constructor(
|
internal class MxCallFactory @Inject constructor(
|
||||||
|
@ -36,7 +37,8 @@ internal class MxCallFactory @Inject constructor(
|
||||||
private val eventSenderProcessor: EventSenderProcessor,
|
private val eventSenderProcessor: EventSenderProcessor,
|
||||||
private val matrixConfiguration: MatrixConfiguration,
|
private val matrixConfiguration: MatrixConfiguration,
|
||||||
private val getProfileInfoTask: GetProfileInfoTask,
|
private val getProfileInfoTask: GetProfileInfoTask,
|
||||||
@UserId private val userId: String
|
@UserId private val userId: String,
|
||||||
|
private val clock: Clock,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
fun createIncomingCall(roomId: String, opponentUserId: String, content: CallInviteContent): MxCall? {
|
fun createIncomingCall(roomId: String, opponentUserId: String, content: CallInviteContent): MxCall? {
|
||||||
|
@ -51,7 +53,8 @@ internal class MxCallFactory @Inject constructor(
|
||||||
localEchoEventFactory = localEchoEventFactory,
|
localEchoEventFactory = localEchoEventFactory,
|
||||||
eventSenderProcessor = eventSenderProcessor,
|
eventSenderProcessor = eventSenderProcessor,
|
||||||
matrixConfiguration = matrixConfiguration,
|
matrixConfiguration = matrixConfiguration,
|
||||||
getProfileInfoTask = getProfileInfoTask
|
getProfileInfoTask = getProfileInfoTask,
|
||||||
|
clock = clock,
|
||||||
).apply {
|
).apply {
|
||||||
updateOpponentData(opponentUserId, content, content.capabilities)
|
updateOpponentData(opponentUserId, content, content.capabilities)
|
||||||
}
|
}
|
||||||
|
@ -68,7 +71,8 @@ internal class MxCallFactory @Inject constructor(
|
||||||
localEchoEventFactory = localEchoEventFactory,
|
localEchoEventFactory = localEchoEventFactory,
|
||||||
eventSenderProcessor = eventSenderProcessor,
|
eventSenderProcessor = eventSenderProcessor,
|
||||||
matrixConfiguration = matrixConfiguration,
|
matrixConfiguration = matrixConfiguration,
|
||||||
getProfileInfoTask = getProfileInfoTask
|
getProfileInfoTask = getProfileInfoTask,
|
||||||
|
clock = clock,
|
||||||
).apply {
|
).apply {
|
||||||
// Setup with this userId, might be updated when processing the Answer event
|
// Setup with this userId, might be updated when processing the Answer event
|
||||||
this.opponentUserId = opponentUserId
|
this.opponentUserId = opponentUserId
|
||||||
|
|
|
@ -46,6 +46,7 @@ import org.matrix.android.sdk.internal.session.call.DefaultCallSignalingService
|
||||||
import org.matrix.android.sdk.internal.session.profile.GetProfileInfoTask
|
import org.matrix.android.sdk.internal.session.profile.GetProfileInfoTask
|
||||||
import org.matrix.android.sdk.internal.session.room.send.LocalEchoEventFactory
|
import org.matrix.android.sdk.internal.session.room.send.LocalEchoEventFactory
|
||||||
import org.matrix.android.sdk.internal.session.room.send.queue.EventSenderProcessor
|
import org.matrix.android.sdk.internal.session.room.send.queue.EventSenderProcessor
|
||||||
|
import org.matrix.android.sdk.internal.util.time.Clock
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.math.BigDecimal
|
import java.math.BigDecimal
|
||||||
|
|
||||||
|
@ -61,7 +62,8 @@ internal class MxCallImpl(
|
||||||
private val localEchoEventFactory: LocalEchoEventFactory,
|
private val localEchoEventFactory: LocalEchoEventFactory,
|
||||||
private val eventSenderProcessor: EventSenderProcessor,
|
private val eventSenderProcessor: EventSenderProcessor,
|
||||||
private val matrixConfiguration: MatrixConfiguration,
|
private val matrixConfiguration: MatrixConfiguration,
|
||||||
private val getProfileInfoTask: GetProfileInfoTask
|
private val getProfileInfoTask: GetProfileInfoTask,
|
||||||
|
private val clock: Clock,
|
||||||
) : MxCall {
|
) : MxCall {
|
||||||
|
|
||||||
override var opponentPartyId: Optional<String>? = null
|
override var opponentPartyId: Optional<String>? = null
|
||||||
|
@ -250,7 +252,7 @@ internal class MxCallImpl(
|
||||||
private fun createEventAndLocalEcho(localId: String = LocalEcho.createLocalEchoId(), type: String, roomId: String, content: Content): Event {
|
private fun createEventAndLocalEcho(localId: String = LocalEcho.createLocalEchoId(), type: String, roomId: String, content: Content): Event {
|
||||||
return Event(
|
return Event(
|
||||||
roomId = roomId,
|
roomId = roomId,
|
||||||
originServerTs = System.currentTimeMillis(),
|
originServerTs = clock.epochMillis(),
|
||||||
senderId = userId,
|
senderId = userId,
|
||||||
eventId = localId,
|
eventId = localId,
|
||||||
type = type,
|
type = type,
|
||||||
|
|
|
@ -46,6 +46,7 @@ import org.matrix.android.sdk.internal.session.room.send.LocalEchoIdentifiers
|
||||||
import org.matrix.android.sdk.internal.session.room.send.LocalEchoRepository
|
import org.matrix.android.sdk.internal.session.room.send.LocalEchoRepository
|
||||||
import org.matrix.android.sdk.internal.session.room.send.MultipleEventSendingDispatcherWorker
|
import org.matrix.android.sdk.internal.session.room.send.MultipleEventSendingDispatcherWorker
|
||||||
import org.matrix.android.sdk.internal.util.TemporaryFileCreator
|
import org.matrix.android.sdk.internal.util.TemporaryFileCreator
|
||||||
|
import org.matrix.android.sdk.internal.util.time.Clock
|
||||||
import org.matrix.android.sdk.internal.util.toMatrixErrorStr
|
import org.matrix.android.sdk.internal.util.toMatrixErrorStr
|
||||||
import org.matrix.android.sdk.internal.worker.SessionSafeCoroutineWorker
|
import org.matrix.android.sdk.internal.worker.SessionSafeCoroutineWorker
|
||||||
import org.matrix.android.sdk.internal.worker.SessionWorkerParams
|
import org.matrix.android.sdk.internal.worker.SessionWorkerParams
|
||||||
|
@ -87,6 +88,7 @@ internal class UploadContentWorker(val context: Context, params: WorkerParameter
|
||||||
@Inject lateinit var thumbnailExtractor: ThumbnailExtractor
|
@Inject lateinit var thumbnailExtractor: ThumbnailExtractor
|
||||||
@Inject lateinit var localEchoRepository: LocalEchoRepository
|
@Inject lateinit var localEchoRepository: LocalEchoRepository
|
||||||
@Inject lateinit var temporaryFileCreator: TemporaryFileCreator
|
@Inject lateinit var temporaryFileCreator: TemporaryFileCreator
|
||||||
|
@Inject lateinit var clock: Clock
|
||||||
|
|
||||||
override fun injectWith(injector: SessionComponent) {
|
override fun injectWith(injector: SessionComponent) {
|
||||||
injector.inject(this)
|
injector.inject(this)
|
||||||
|
@ -243,7 +245,7 @@ internal class UploadContentWorker(val context: Context, params: WorkerParameter
|
||||||
.also { filesToDelete.add(it) }
|
.also { filesToDelete.add(it) }
|
||||||
|
|
||||||
uploadedFileEncryptedFileInfo =
|
uploadedFileEncryptedFileInfo =
|
||||||
MXEncryptedAttachments.encrypt(fileToUpload.inputStream(), encryptedFile) { read, total ->
|
MXEncryptedAttachments.encrypt(fileToUpload.inputStream(), encryptedFile, clock) { read, total ->
|
||||||
notifyTracker(params) {
|
notifyTracker(params) {
|
||||||
contentUploadStateTracker.setEncrypting(it, read.toLong(), total.toLong())
|
contentUploadStateTracker.setEncrypting(it, read.toLong(), total.toLong())
|
||||||
}
|
}
|
||||||
|
@ -329,7 +331,7 @@ internal class UploadContentWorker(val context: Context, params: WorkerParameter
|
||||||
if (params.isEncrypted) {
|
if (params.isEncrypted) {
|
||||||
Timber.v("Encrypt thumbnail")
|
Timber.v("Encrypt thumbnail")
|
||||||
notifyTracker(params) { contentUploadStateTracker.setEncryptingThumbnail(it) }
|
notifyTracker(params) { contentUploadStateTracker.setEncryptingThumbnail(it) }
|
||||||
val encryptionResult = MXEncryptedAttachments.encryptAttachment(thumbnailData.bytes.inputStream())
|
val encryptionResult = MXEncryptedAttachments.encryptAttachment(thumbnailData.bytes.inputStream(), clock)
|
||||||
val contentUploadResponse = fileUploader.uploadByteArray(
|
val contentUploadResponse = fileUploader.uploadByteArray(
|
||||||
byteArray = encryptionResult.encryptedByteArray,
|
byteArray = encryptionResult.encryptedByteArray,
|
||||||
filename = null,
|
filename = null,
|
||||||
|
|
|
@ -33,6 +33,7 @@ import org.matrix.android.sdk.internal.session.contentscanner.tasks.GetServerPub
|
||||||
import org.matrix.android.sdk.internal.session.contentscanner.tasks.ScanEncryptedTask
|
import org.matrix.android.sdk.internal.session.contentscanner.tasks.ScanEncryptedTask
|
||||||
import org.matrix.android.sdk.internal.session.contentscanner.tasks.ScanMediaTask
|
import org.matrix.android.sdk.internal.session.contentscanner.tasks.ScanMediaTask
|
||||||
import org.matrix.android.sdk.internal.task.TaskExecutor
|
import org.matrix.android.sdk.internal.task.TaskExecutor
|
||||||
|
import org.matrix.android.sdk.internal.util.time.Clock
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@ -46,7 +47,8 @@ internal class DefaultContentScannerService @Inject constructor(
|
||||||
private val getServerPublicKeyTask: GetServerPublicKeyTask,
|
private val getServerPublicKeyTask: GetServerPublicKeyTask,
|
||||||
private val scanEncryptedTask: ScanEncryptedTask,
|
private val scanEncryptedTask: ScanEncryptedTask,
|
||||||
private val scanMediaTask: ScanMediaTask,
|
private val scanMediaTask: ScanMediaTask,
|
||||||
private val taskExecutor: TaskExecutor
|
private val taskExecutor: TaskExecutor,
|
||||||
|
private val clock: Clock,
|
||||||
) : ContentScannerService {
|
) : ContentScannerService {
|
||||||
|
|
||||||
// Cache public key in memory
|
// Cache public key in memory
|
||||||
|
@ -71,11 +73,13 @@ internal class DefaultContentScannerService @Inject constructor(
|
||||||
|
|
||||||
override suspend fun getScanResultForAttachment(mxcUrl: String, fileInfo: ElementToDecrypt?): ScanStatusInfo {
|
override suspend fun getScanResultForAttachment(mxcUrl: String, fileInfo: ElementToDecrypt?): ScanStatusInfo {
|
||||||
val result = if (fileInfo != null) {
|
val result = if (fileInfo != null) {
|
||||||
scanEncryptedTask.execute(ScanEncryptedTask.Params(
|
scanEncryptedTask.execute(
|
||||||
|
ScanEncryptedTask.Params(
|
||||||
mxcUrl = mxcUrl,
|
mxcUrl = mxcUrl,
|
||||||
publicServerKey = getServerPublicKey(false),
|
publicServerKey = getServerPublicKey(false),
|
||||||
encryptedInfo = fileInfo
|
encryptedInfo = fileInfo
|
||||||
))
|
)
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
scanMediaTask.execute(ScanMediaTask.Params(mxcUrl))
|
scanMediaTask.execute(ScanMediaTask.Params(mxcUrl))
|
||||||
}
|
}
|
||||||
|
@ -83,7 +87,7 @@ internal class DefaultContentScannerService @Inject constructor(
|
||||||
return ScanStatusInfo(
|
return ScanStatusInfo(
|
||||||
state = if (result.clean) ScanState.TRUSTED else ScanState.INFECTED,
|
state = if (result.clean) ScanState.TRUSTED else ScanState.INFECTED,
|
||||||
humanReadableMessage = result.info,
|
humanReadableMessage = result.info,
|
||||||
scanDateTimestamp = System.currentTimeMillis()
|
scanDateTimestamp = clock.epochMillis()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,11 +31,14 @@ internal fun ContentScanResultEntity.Companion.get(realm: Realm, attachmentUrl:
|
||||||
.findFirst()
|
.findFirst()
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun ContentScanResultEntity.Companion.getOrCreate(realm: Realm, attachmentUrl: String, contentScannerUrl: String?): ContentScanResultEntity {
|
internal fun ContentScanResultEntity.Companion.getOrCreate(realm: Realm,
|
||||||
|
attachmentUrl: String,
|
||||||
|
contentScannerUrl: String?,
|
||||||
|
currentTimeMillis: Long): ContentScanResultEntity {
|
||||||
return ContentScanResultEntity.get(realm, attachmentUrl, contentScannerUrl)
|
return ContentScanResultEntity.get(realm, attachmentUrl, contentScannerUrl)
|
||||||
?: realm.createObject<ContentScanResultEntity>().also {
|
?: realm.createObject<ContentScanResultEntity>().also {
|
||||||
it.mediaUrl = attachmentUrl
|
it.mediaUrl = attachmentUrl
|
||||||
it.scanDateTimestamp = System.currentTimeMillis()
|
it.scanDateTimestamp = currentTimeMillis
|
||||||
it.scannerUrl = contentScannerUrl
|
it.scannerUrl = contentScannerUrl
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,12 +32,14 @@ import org.matrix.android.sdk.internal.di.ContentScannerDatabase
|
||||||
import org.matrix.android.sdk.internal.session.SessionScope
|
import org.matrix.android.sdk.internal.session.SessionScope
|
||||||
import org.matrix.android.sdk.internal.session.contentscanner.data.ContentScannerStore
|
import org.matrix.android.sdk.internal.session.contentscanner.data.ContentScannerStore
|
||||||
import org.matrix.android.sdk.internal.util.isValidUrl
|
import org.matrix.android.sdk.internal.util.isValidUrl
|
||||||
|
import org.matrix.android.sdk.internal.util.time.Clock
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@SessionScope
|
@SessionScope
|
||||||
internal class RealmContentScannerStore @Inject constructor(
|
internal class RealmContentScannerStore @Inject constructor(
|
||||||
@ContentScannerDatabase
|
@ContentScannerDatabase
|
||||||
private val realmConfiguration: RealmConfiguration
|
private val realmConfiguration: RealmConfiguration,
|
||||||
|
private val clock: Clock,
|
||||||
) : ContentScannerStore {
|
) : ContentScannerStore {
|
||||||
|
|
||||||
private val monarchy = Monarchy.Builder()
|
private val monarchy = Monarchy.Builder()
|
||||||
|
@ -82,15 +84,15 @@ internal class RealmContentScannerStore @Inject constructor(
|
||||||
|
|
||||||
override fun updateStateForContent(mxcUrl: String, state: ScanState, scannerUrl: String?) {
|
override fun updateStateForContent(mxcUrl: String, state: ScanState, scannerUrl: String?) {
|
||||||
monarchy.runTransactionSync {
|
monarchy.runTransactionSync {
|
||||||
ContentScanResultEntity.getOrCreate(it, mxcUrl, scannerUrl).scanResult = state
|
ContentScanResultEntity.getOrCreate(it, mxcUrl, scannerUrl, clock.epochMillis()).scanResult = state
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateScanResultForContent(mxcUrl: String, scannerUrl: String?, state: ScanState, humanReadable: String) {
|
override fun updateScanResultForContent(mxcUrl: String, scannerUrl: String?, state: ScanState, humanReadable: String) {
|
||||||
monarchy.runTransactionSync {
|
monarchy.runTransactionSync {
|
||||||
ContentScanResultEntity.getOrCreate(it, mxcUrl, scannerUrl).apply {
|
ContentScanResultEntity.getOrCreate(it, mxcUrl, scannerUrl, clock.epochMillis()).apply {
|
||||||
scanResult = state
|
scanResult = state
|
||||||
scanDateTimestamp = System.currentTimeMillis()
|
scanDateTimestamp = clock.epochMillis()
|
||||||
humanReadableMessage = humanReadable
|
humanReadableMessage = humanReadable
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -69,6 +69,7 @@ import org.matrix.android.sdk.internal.di.UserId
|
||||||
import org.matrix.android.sdk.internal.session.EventInsertLiveProcessor
|
import org.matrix.android.sdk.internal.session.EventInsertLiveProcessor
|
||||||
import org.matrix.android.sdk.internal.session.room.aggregation.livelocation.LiveLocationAggregationProcessor
|
import org.matrix.android.sdk.internal.session.room.aggregation.livelocation.LiveLocationAggregationProcessor
|
||||||
import org.matrix.android.sdk.internal.session.room.state.StateEventDataSource
|
import org.matrix.android.sdk.internal.session.room.state.StateEventDataSource
|
||||||
|
import org.matrix.android.sdk.internal.util.time.Clock
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@ -77,7 +78,8 @@ internal class EventRelationsAggregationProcessor @Inject constructor(
|
||||||
private val stateEventDataSource: StateEventDataSource,
|
private val stateEventDataSource: StateEventDataSource,
|
||||||
@SessionId private val sessionId: String,
|
@SessionId private val sessionId: String,
|
||||||
private val sessionManager: SessionManager,
|
private val sessionManager: SessionManager,
|
||||||
private val liveLocationAggregationProcessor: LiveLocationAggregationProcessor
|
private val liveLocationAggregationProcessor: LiveLocationAggregationProcessor,
|
||||||
|
private val clock: Clock,
|
||||||
) : EventInsertLiveProcessor {
|
) : EventInsertLiveProcessor {
|
||||||
|
|
||||||
private val allowedTypes = listOf(
|
private val allowedTypes = listOf(
|
||||||
|
@ -338,7 +340,7 @@ internal class EventRelationsAggregationProcessor @Inject constructor(
|
||||||
Timber.v("###REPLACE Receiving remote echo of edit (edit already done)")
|
Timber.v("###REPLACE Receiving remote echo of edit (edit already done)")
|
||||||
existingSummary.editions.firstOrNull { it.eventId == txId }?.let {
|
existingSummary.editions.firstOrNull { it.eventId == txId }?.let {
|
||||||
it.eventId = event.eventId
|
it.eventId = event.eventId
|
||||||
it.timestamp = event.originServerTs ?: System.currentTimeMillis()
|
it.timestamp = event.originServerTs ?: clock.epochMillis()
|
||||||
it.isLocalEcho = false
|
it.isLocalEcho = false
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -349,10 +351,10 @@ internal class EventRelationsAggregationProcessor @Inject constructor(
|
||||||
eventId = event.eventId,
|
eventId = event.eventId,
|
||||||
content = ContentMapper.map(newContent),
|
content = ContentMapper.map(newContent),
|
||||||
timestamp = if (isLocalEcho) {
|
timestamp = if (isLocalEcho) {
|
||||||
System.currentTimeMillis()
|
clock.epochMillis()
|
||||||
} else {
|
} else {
|
||||||
// Do not take local echo originServerTs here, could mess up ordering (keep old ts)
|
// Do not take local echo originServerTs here, could mess up ordering (keep old ts)
|
||||||
event.originServerTs ?: System.currentTimeMillis()
|
event.originServerTs ?: clock.epochMillis()
|
||||||
},
|
},
|
||||||
isLocalEcho = isLocalEcho
|
isLocalEcho = isLocalEcho
|
||||||
)
|
)
|
||||||
|
|
|
@ -41,6 +41,7 @@ import org.matrix.android.sdk.internal.session.user.accountdata.DirectChatsHelpe
|
||||||
import org.matrix.android.sdk.internal.session.user.accountdata.UpdateUserAccountDataTask
|
import org.matrix.android.sdk.internal.session.user.accountdata.UpdateUserAccountDataTask
|
||||||
import org.matrix.android.sdk.internal.task.Task
|
import org.matrix.android.sdk.internal.task.Task
|
||||||
import org.matrix.android.sdk.internal.util.awaitTransaction
|
import org.matrix.android.sdk.internal.util.awaitTransaction
|
||||||
|
import org.matrix.android.sdk.internal.util.time.Clock
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@ -56,7 +57,8 @@ internal class DefaultCreateRoomTask @Inject constructor(
|
||||||
@SessionDatabase
|
@SessionDatabase
|
||||||
private val realmConfiguration: RealmConfiguration,
|
private val realmConfiguration: RealmConfiguration,
|
||||||
private val createRoomBodyBuilder: CreateRoomBodyBuilder,
|
private val createRoomBodyBuilder: CreateRoomBodyBuilder,
|
||||||
private val globalErrorReceiver: GlobalErrorReceiver
|
private val globalErrorReceiver: GlobalErrorReceiver,
|
||||||
|
private val clock: Clock,
|
||||||
) : CreateRoomTask {
|
) : CreateRoomTask {
|
||||||
|
|
||||||
override suspend fun execute(params: CreateRoomParams): String {
|
override suspend fun execute(params: CreateRoomParams): String {
|
||||||
|
@ -106,7 +108,7 @@ internal class DefaultCreateRoomTask @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
awaitTransaction(realmConfiguration) {
|
awaitTransaction(realmConfiguration) {
|
||||||
RoomSummaryEntity.where(it, roomId).findFirst()?.lastActivityTime = System.currentTimeMillis()
|
RoomSummaryEntity.where(it, roomId).findFirst()?.lastActivityTime = clock.epochMillis()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (otherUserId != null) {
|
if (otherUserId != null) {
|
||||||
|
|
|
@ -42,6 +42,7 @@ import org.matrix.android.sdk.internal.session.room.summary.RoomSummaryUpdater
|
||||||
import org.matrix.android.sdk.internal.session.sync.SyncTokenStore
|
import org.matrix.android.sdk.internal.session.sync.SyncTokenStore
|
||||||
import org.matrix.android.sdk.internal.task.Task
|
import org.matrix.android.sdk.internal.task.Task
|
||||||
import org.matrix.android.sdk.internal.util.awaitTransaction
|
import org.matrix.android.sdk.internal.util.awaitTransaction
|
||||||
|
import org.matrix.android.sdk.internal.util.time.Clock
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@ -61,7 +62,8 @@ internal class DefaultLoadRoomMembersTask @Inject constructor(
|
||||||
private val roomMemberEventHandler: RoomMemberEventHandler,
|
private val roomMemberEventHandler: RoomMemberEventHandler,
|
||||||
private val cryptoSessionInfoProvider: CryptoSessionInfoProvider,
|
private val cryptoSessionInfoProvider: CryptoSessionInfoProvider,
|
||||||
private val deviceListManager: DeviceListManager,
|
private val deviceListManager: DeviceListManager,
|
||||||
private val globalErrorReceiver: GlobalErrorReceiver
|
private val globalErrorReceiver: GlobalErrorReceiver,
|
||||||
|
private val clock: Clock,
|
||||||
) : LoadRoomMembersTask {
|
) : LoadRoomMembersTask {
|
||||||
|
|
||||||
override suspend fun execute(params: LoadRoomMembersTask.Params) {
|
override suspend fun execute(params: LoadRoomMembersTask.Params) {
|
||||||
|
@ -107,7 +109,7 @@ internal class DefaultLoadRoomMembersTask @Inject constructor(
|
||||||
// We ignore all the already known members
|
// We ignore all the already known members
|
||||||
val roomEntity = RoomEntity.where(realm, roomId).findFirst()
|
val roomEntity = RoomEntity.where(realm, roomId).findFirst()
|
||||||
?: realm.createObject(roomId)
|
?: realm.createObject(roomId)
|
||||||
val now = System.currentTimeMillis()
|
val now = clock.epochMillis()
|
||||||
for (roomMemberEvent in response.roomMemberEvents) {
|
for (roomMemberEvent in response.roomMemberEvents) {
|
||||||
if (roomMemberEvent.eventId == null || roomMemberEvent.stateKey == null || roomMemberEvent.type == null) {
|
if (roomMemberEvent.eventId == null || roomMemberEvent.stateKey == null || roomMemberEvent.type == null) {
|
||||||
continue
|
continue
|
||||||
|
|
|
@ -35,6 +35,7 @@ import org.matrix.android.sdk.internal.session.room.RoomAPI
|
||||||
import org.matrix.android.sdk.internal.session.room.membership.RoomChangeMembershipStateDataSource
|
import org.matrix.android.sdk.internal.session.room.membership.RoomChangeMembershipStateDataSource
|
||||||
import org.matrix.android.sdk.internal.session.room.read.SetReadMarkersTask
|
import org.matrix.android.sdk.internal.session.room.read.SetReadMarkersTask
|
||||||
import org.matrix.android.sdk.internal.task.Task
|
import org.matrix.android.sdk.internal.task.Task
|
||||||
|
import org.matrix.android.sdk.internal.util.time.Clock
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@ -53,7 +54,8 @@ internal class DefaultJoinRoomTask @Inject constructor(
|
||||||
@SessionDatabase
|
@SessionDatabase
|
||||||
private val realmConfiguration: RealmConfiguration,
|
private val realmConfiguration: RealmConfiguration,
|
||||||
private val roomChangeMembershipStateDataSource: RoomChangeMembershipStateDataSource,
|
private val roomChangeMembershipStateDataSource: RoomChangeMembershipStateDataSource,
|
||||||
private val globalErrorReceiver: GlobalErrorReceiver
|
private val globalErrorReceiver: GlobalErrorReceiver,
|
||||||
|
private val clock: Clock,
|
||||||
) : JoinRoomTask {
|
) : JoinRoomTask {
|
||||||
|
|
||||||
override suspend fun execute(params: JoinRoomTask.Params) {
|
override suspend fun execute(params: JoinRoomTask.Params) {
|
||||||
|
@ -90,7 +92,7 @@ internal class DefaultJoinRoomTask @Inject constructor(
|
||||||
throw JoinRoomFailure.JoinedWithTimeout
|
throw JoinRoomFailure.JoinedWithTimeout
|
||||||
}
|
}
|
||||||
awaitTransaction(realmConfiguration) {
|
awaitTransaction(realmConfiguration) {
|
||||||
RoomSummaryEntity.where(it, roomId).findFirst()?.lastActivityTime = System.currentTimeMillis()
|
RoomSummaryEntity.where(it, roomId).findFirst()?.lastActivityTime = clock.epochMillis()
|
||||||
}
|
}
|
||||||
setReadMarkers(roomId)
|
setReadMarkers(roomId)
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,6 +34,7 @@ import org.matrix.android.sdk.internal.session.sync.handler.room.ReadReceiptHand
|
||||||
import org.matrix.android.sdk.internal.session.sync.handler.room.RoomFullyReadHandler
|
import org.matrix.android.sdk.internal.session.sync.handler.room.RoomFullyReadHandler
|
||||||
import org.matrix.android.sdk.internal.task.Task
|
import org.matrix.android.sdk.internal.task.Task
|
||||||
import org.matrix.android.sdk.internal.util.awaitTransaction
|
import org.matrix.android.sdk.internal.util.awaitTransaction
|
||||||
|
import org.matrix.android.sdk.internal.util.time.Clock
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import kotlin.collections.set
|
import kotlin.collections.set
|
||||||
|
@ -58,7 +59,8 @@ internal class DefaultSetReadMarkersTask @Inject constructor(
|
||||||
private val roomFullyReadHandler: RoomFullyReadHandler,
|
private val roomFullyReadHandler: RoomFullyReadHandler,
|
||||||
private val readReceiptHandler: ReadReceiptHandler,
|
private val readReceiptHandler: ReadReceiptHandler,
|
||||||
@UserId private val userId: String,
|
@UserId private val userId: String,
|
||||||
private val globalErrorReceiver: GlobalErrorReceiver
|
private val globalErrorReceiver: GlobalErrorReceiver,
|
||||||
|
private val clock: Clock,
|
||||||
) : SetReadMarkersTask {
|
) : SetReadMarkersTask {
|
||||||
|
|
||||||
override suspend fun execute(params: SetReadMarkersTask.Params) {
|
override suspend fun execute(params: SetReadMarkersTask.Params) {
|
||||||
|
@ -125,7 +127,7 @@ internal class DefaultSetReadMarkersTask @Inject constructor(
|
||||||
roomFullyReadHandler.handle(realm, roomId, FullyReadContent(readMarkerId))
|
roomFullyReadHandler.handle(realm, roomId, FullyReadContent(readMarkerId))
|
||||||
}
|
}
|
||||||
if (readReceiptId != null) {
|
if (readReceiptId != null) {
|
||||||
val readReceiptContent = ReadReceiptHandler.createContent(userId, readReceiptId)
|
val readReceiptContent = ReadReceiptHandler.createContent(userId, readReceiptId, clock.epochMillis())
|
||||||
readReceiptHandler.handle(realm, roomId, readReceiptContent, false, null)
|
readReceiptHandler.handle(realm, roomId, readReceiptContent, false, null)
|
||||||
}
|
}
|
||||||
if (shouldUpdateRoomSummary) {
|
if (shouldUpdateRoomSummary) {
|
||||||
|
|
|
@ -27,12 +27,16 @@ import org.matrix.android.sdk.internal.database.mapper.toEntity
|
||||||
import org.matrix.android.sdk.internal.session.room.send.LocalEchoEventFactory
|
import org.matrix.android.sdk.internal.session.room.send.LocalEchoEventFactory
|
||||||
import org.matrix.android.sdk.internal.session.room.send.LocalEchoRepository
|
import org.matrix.android.sdk.internal.session.room.send.LocalEchoRepository
|
||||||
import org.matrix.android.sdk.internal.session.room.send.queue.EventSenderProcessor
|
import org.matrix.android.sdk.internal.session.room.send.queue.EventSenderProcessor
|
||||||
|
import org.matrix.android.sdk.internal.util.time.Clock
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
internal class EventEditor @Inject constructor(private val eventSenderProcessor: EventSenderProcessor,
|
internal class EventEditor @Inject constructor(
|
||||||
|
private val eventSenderProcessor: EventSenderProcessor,
|
||||||
private val eventFactory: LocalEchoEventFactory,
|
private val eventFactory: LocalEchoEventFactory,
|
||||||
private val localEchoRepository: LocalEchoRepository) {
|
private val localEchoRepository: LocalEchoRepository,
|
||||||
|
private val clock: Clock,
|
||||||
|
) {
|
||||||
|
|
||||||
fun editTextMessage(targetEvent: TimelineEvent,
|
fun editTextMessage(targetEvent: TimelineEvent,
|
||||||
msgType: String,
|
msgType: String,
|
||||||
|
@ -126,7 +130,7 @@ internal class EventEditor @Inject constructor(private val eventSenderProcessor:
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateFailedEchoWithEvent(roomId: String, failedEchoEventId: String, editedEvent: Event) {
|
private fun updateFailedEchoWithEvent(roomId: String, failedEchoEventId: String, editedEvent: Event) {
|
||||||
val editedEventEntity = editedEvent.toEntity(roomId, SendState.UNSENT, System.currentTimeMillis())
|
val editedEventEntity = editedEvent.toEntity(roomId, SendState.UNSENT, clock.epochMillis())
|
||||||
localEchoRepository.updateEchoAsync(failedEchoEventId) { _, entity ->
|
localEchoRepository.updateEchoAsync(failedEchoEventId) { _, entity ->
|
||||||
entity.content = editedEventEntity.content
|
entity.content = editedEventEntity.content
|
||||||
entity.ageLocalTs = editedEventEntity.ageLocalTs
|
entity.ageLocalTs = editedEventEntity.ageLocalTs
|
||||||
|
|
|
@ -33,6 +33,7 @@ import org.matrix.android.sdk.internal.session.room.timeline.PaginationDirection
|
||||||
import org.matrix.android.sdk.internal.session.room.timeline.PaginationResponse
|
import org.matrix.android.sdk.internal.session.room.timeline.PaginationResponse
|
||||||
import org.matrix.android.sdk.internal.task.Task
|
import org.matrix.android.sdk.internal.task.Task
|
||||||
import org.matrix.android.sdk.internal.util.awaitTransaction
|
import org.matrix.android.sdk.internal.util.awaitTransaction
|
||||||
|
import org.matrix.android.sdk.internal.util.time.Clock
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@ -55,12 +56,14 @@ internal class DefaultFetchThreadSummariesTask @Inject constructor(
|
||||||
@SessionDatabase private val monarchy: Monarchy,
|
@SessionDatabase private val monarchy: Monarchy,
|
||||||
private val cryptoService: DefaultCryptoService,
|
private val cryptoService: DefaultCryptoService,
|
||||||
@UserId private val userId: String,
|
@UserId private val userId: String,
|
||||||
|
private val clock: Clock,
|
||||||
) : FetchThreadSummariesTask {
|
) : FetchThreadSummariesTask {
|
||||||
|
|
||||||
override suspend fun execute(params: FetchThreadSummariesTask.Params): Result {
|
override suspend fun execute(params: FetchThreadSummariesTask.Params): Result {
|
||||||
val filter = FilterFactory.createThreadsFilter(
|
val filter = FilterFactory.createThreadsFilter(
|
||||||
numberOfEvents = params.limit,
|
numberOfEvents = params.limit,
|
||||||
userId = if (params.isUserParticipating) userId else null).toJSONString()
|
userId = if (params.isUserParticipating) userId else null
|
||||||
|
).toJSONString()
|
||||||
|
|
||||||
val response = executeRequest(
|
val response = executeRequest(
|
||||||
globalErrorReceiver,
|
globalErrorReceiver,
|
||||||
|
@ -94,7 +97,9 @@ internal class DefaultFetchThreadSummariesTask @Inject constructor(
|
||||||
roomMemberContentsByUser = roomMemberContentsByUser,
|
roomMemberContentsByUser = roomMemberContentsByUser,
|
||||||
roomEntity = roomEntity,
|
roomEntity = roomEntity,
|
||||||
userId = userId,
|
userId = userId,
|
||||||
cryptoService = cryptoService)
|
cryptoService = cryptoService,
|
||||||
|
currentTimeMillis = clock.epochMillis(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Result.SUCCESS
|
return Result.SUCCESS
|
||||||
|
|
|
@ -23,7 +23,6 @@ import org.matrix.android.sdk.api.session.events.model.Event
|
||||||
import org.matrix.android.sdk.api.session.events.model.EventType
|
import org.matrix.android.sdk.api.session.events.model.EventType
|
||||||
import org.matrix.android.sdk.api.session.room.model.RoomMemberContent
|
import org.matrix.android.sdk.api.session.room.model.RoomMemberContent
|
||||||
import org.matrix.android.sdk.api.session.room.send.SendState
|
import org.matrix.android.sdk.api.session.room.send.SendState
|
||||||
import org.matrix.android.sdk.internal.crypto.CryptoSessionInfoProvider
|
|
||||||
import org.matrix.android.sdk.internal.crypto.DefaultCryptoService
|
import org.matrix.android.sdk.internal.crypto.DefaultCryptoService
|
||||||
import org.matrix.android.sdk.internal.database.helper.addTimelineEvent
|
import org.matrix.android.sdk.internal.database.helper.addTimelineEvent
|
||||||
import org.matrix.android.sdk.internal.database.mapper.asDomain
|
import org.matrix.android.sdk.internal.database.mapper.asDomain
|
||||||
|
@ -42,7 +41,6 @@ import org.matrix.android.sdk.internal.database.query.getOrCreate
|
||||||
import org.matrix.android.sdk.internal.database.query.getOrNull
|
import org.matrix.android.sdk.internal.database.query.getOrNull
|
||||||
import org.matrix.android.sdk.internal.database.query.where
|
import org.matrix.android.sdk.internal.database.query.where
|
||||||
import org.matrix.android.sdk.internal.di.SessionDatabase
|
import org.matrix.android.sdk.internal.di.SessionDatabase
|
||||||
import org.matrix.android.sdk.internal.di.UserId
|
|
||||||
import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
|
import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
|
||||||
import org.matrix.android.sdk.internal.network.executeRequest
|
import org.matrix.android.sdk.internal.network.executeRequest
|
||||||
import org.matrix.android.sdk.internal.session.events.getFixedRoomMemberContent
|
import org.matrix.android.sdk.internal.session.events.getFixedRoomMemberContent
|
||||||
|
@ -51,6 +49,7 @@ import org.matrix.android.sdk.internal.session.room.relation.RelationsResponse
|
||||||
import org.matrix.android.sdk.internal.session.room.timeline.PaginationDirection
|
import org.matrix.android.sdk.internal.session.room.timeline.PaginationDirection
|
||||||
import org.matrix.android.sdk.internal.task.Task
|
import org.matrix.android.sdk.internal.task.Task
|
||||||
import org.matrix.android.sdk.internal.util.awaitTransaction
|
import org.matrix.android.sdk.internal.util.awaitTransaction
|
||||||
|
import org.matrix.android.sdk.internal.util.time.Clock
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@ -85,10 +84,9 @@ internal interface FetchThreadTimelineTask : Task<FetchThreadTimelineTask.Params
|
||||||
internal class DefaultFetchThreadTimelineTask @Inject constructor(
|
internal class DefaultFetchThreadTimelineTask @Inject constructor(
|
||||||
private val roomAPI: RoomAPI,
|
private val roomAPI: RoomAPI,
|
||||||
private val globalErrorReceiver: GlobalErrorReceiver,
|
private val globalErrorReceiver: GlobalErrorReceiver,
|
||||||
private val cryptoSessionInfoProvider: CryptoSessionInfoProvider,
|
|
||||||
@SessionDatabase private val monarchy: Monarchy,
|
@SessionDatabase private val monarchy: Monarchy,
|
||||||
@UserId private val userId: String,
|
private val cryptoService: DefaultCryptoService,
|
||||||
private val cryptoService: DefaultCryptoService
|
private val clock: Clock,
|
||||||
) : FetchThreadTimelineTask {
|
) : FetchThreadTimelineTask {
|
||||||
|
|
||||||
enum class Result {
|
enum class Result {
|
||||||
|
@ -156,7 +154,8 @@ internal class DefaultFetchThreadTimelineTask @Inject constructor(
|
||||||
eventEntity = eventEntity,
|
eventEntity = eventEntity,
|
||||||
direction = PaginationDirection.FORWARDS,
|
direction = PaginationDirection.FORWARDS,
|
||||||
ownedByThreadChunk = true,
|
ownedByThreadChunk = true,
|
||||||
roomMemberContentsByUser = roomMemberContentsByUser)
|
roomMemberContentsByUser = roomMemberContentsByUser
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -178,7 +177,8 @@ internal class DefaultFetchThreadTimelineTask @Inject constructor(
|
||||||
eventEntity = eventEntity,
|
eventEntity = eventEntity,
|
||||||
direction = PaginationDirection.FORWARDS,
|
direction = PaginationDirection.FORWARDS,
|
||||||
ownedByThreadChunk = true,
|
ownedByThreadChunk = true,
|
||||||
roomMemberContentsByUser = roomMemberContentsByUser)
|
roomMemberContentsByUser = roomMemberContentsByUser
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -207,7 +207,7 @@ internal class DefaultFetchThreadTimelineTask @Inject constructor(
|
||||||
* Create an EventEntity to be added in the TimelineEventEntity
|
* Create an EventEntity to be added in the TimelineEventEntity
|
||||||
*/
|
*/
|
||||||
private fun createEventEntity(roomId: String, event: Event, realm: Realm): EventEntity {
|
private fun createEventEntity(roomId: String, event: Event, realm: Realm): EventEntity {
|
||||||
val ageLocalTs = event.unsignedData?.age?.let { System.currentTimeMillis() - it }
|
val ageLocalTs = event.unsignedData?.age?.let { clock.epochMillis() - it }
|
||||||
return event.toEntity(roomId, SendState.SYNCED, ageLocalTs).copyToRealmOrIgnore(realm, EventInsertType.PAGINATION)
|
return event.toEntity(roomId, SendState.SYNCED, ageLocalTs).copyToRealmOrIgnore(realm, EventInsertType.PAGINATION)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -70,6 +70,7 @@ import org.matrix.android.sdk.internal.di.UserId
|
||||||
import org.matrix.android.sdk.internal.session.content.ThumbnailExtractor
|
import org.matrix.android.sdk.internal.session.content.ThumbnailExtractor
|
||||||
import org.matrix.android.sdk.internal.session.permalinks.PermalinkFactory
|
import org.matrix.android.sdk.internal.session.permalinks.PermalinkFactory
|
||||||
import org.matrix.android.sdk.internal.session.room.send.pills.TextPillsUtils
|
import org.matrix.android.sdk.internal.session.room.send.pills.TextPillsUtils
|
||||||
|
import org.matrix.android.sdk.internal.util.time.Clock
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@ -90,7 +91,8 @@ internal class LocalEchoEventFactory @Inject constructor(
|
||||||
private val thumbnailExtractor: ThumbnailExtractor,
|
private val thumbnailExtractor: ThumbnailExtractor,
|
||||||
private val waveformSanitizer: WaveFormSanitizer,
|
private val waveformSanitizer: WaveFormSanitizer,
|
||||||
private val localEchoRepository: LocalEchoRepository,
|
private val localEchoRepository: LocalEchoRepository,
|
||||||
private val permalinkFactory: PermalinkFactory
|
private val permalinkFactory: PermalinkFactory,
|
||||||
|
private val clock: Clock,
|
||||||
) {
|
) {
|
||||||
fun createTextEvent(roomId: String, msgType: String, text: CharSequence, autoMarkdown: Boolean): Event {
|
fun createTextEvent(roomId: String, msgType: String, text: CharSequence, autoMarkdown: Boolean): Event {
|
||||||
if (msgType == MessageType.MSGTYPE_TEXT || msgType == MessageType.MSGTYPE_EMOTE) {
|
if (msgType == MessageType.MSGTYPE_TEXT || msgType == MessageType.MSGTYPE_EMOTE) {
|
||||||
|
@ -242,7 +244,7 @@ internal class LocalEchoEventFactory @Inject constructor(
|
||||||
body = geoUri,
|
body = geoUri,
|
||||||
unstableLocationInfo = LocationInfo(geoUri = geoUri, description = geoUri),
|
unstableLocationInfo = LocationInfo(geoUri = geoUri, description = geoUri),
|
||||||
unstableLocationAsset = LocationAsset(type = assetType),
|
unstableLocationAsset = LocationAsset(type = assetType),
|
||||||
unstableTimestampMillis = System.currentTimeMillis(),
|
unstableTimestampMillis = clock.epochMillis(),
|
||||||
unstableText = geoUri
|
unstableText = geoUri
|
||||||
)
|
)
|
||||||
return createMessageEvent(roomId, content)
|
return createMessageEvent(roomId, content)
|
||||||
|
@ -261,7 +263,7 @@ internal class LocalEchoEventFactory @Inject constructor(
|
||||||
eventId = beaconInfoEventId
|
eventId = beaconInfoEventId
|
||||||
),
|
),
|
||||||
unstableLocationInfo = LocationInfo(geoUri = geoUri, description = geoUri),
|
unstableLocationInfo = LocationInfo(geoUri = geoUri, description = geoUri),
|
||||||
unstableTimestampMillis = System.currentTimeMillis(),
|
unstableTimestampMillis = clock.epochMillis(),
|
||||||
)
|
)
|
||||||
val localId = LocalEcho.createLocalEchoId()
|
val localId = LocalEcho.createLocalEchoId()
|
||||||
return Event(
|
return Event(
|
||||||
|
@ -547,7 +549,7 @@ internal class LocalEchoEventFactory @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun dummyOriginServerTs(): Long {
|
private fun dummyOriginServerTs(): Long {
|
||||||
return System.currentTimeMillis()
|
return clock.epochMillis()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -43,16 +43,20 @@ import org.matrix.android.sdk.internal.session.room.summary.RoomSummaryUpdater
|
||||||
import org.matrix.android.sdk.internal.session.room.timeline.TimelineInput
|
import org.matrix.android.sdk.internal.session.room.timeline.TimelineInput
|
||||||
import org.matrix.android.sdk.internal.task.TaskExecutor
|
import org.matrix.android.sdk.internal.task.TaskExecutor
|
||||||
import org.matrix.android.sdk.internal.util.awaitTransaction
|
import org.matrix.android.sdk.internal.util.awaitTransaction
|
||||||
|
import org.matrix.android.sdk.internal.util.time.Clock
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
internal class LocalEchoRepository @Inject constructor(@SessionDatabase private val monarchy: Monarchy,
|
internal class LocalEchoRepository @Inject constructor(
|
||||||
|
@SessionDatabase private val monarchy: Monarchy,
|
||||||
private val taskExecutor: TaskExecutor,
|
private val taskExecutor: TaskExecutor,
|
||||||
private val realmSessionProvider: RealmSessionProvider,
|
private val realmSessionProvider: RealmSessionProvider,
|
||||||
private val roomSummaryUpdater: RoomSummaryUpdater,
|
private val roomSummaryUpdater: RoomSummaryUpdater,
|
||||||
private val timelineInput: TimelineInput,
|
private val timelineInput: TimelineInput,
|
||||||
private val timelineEventMapper: TimelineEventMapper) {
|
private val timelineEventMapper: TimelineEventMapper,
|
||||||
|
private val clock: Clock,
|
||||||
|
) {
|
||||||
|
|
||||||
fun createLocalEcho(event: Event) {
|
fun createLocalEcho(event: Event) {
|
||||||
val roomId = event.roomId ?: throw IllegalStateException("You should have set a roomId for your event")
|
val roomId = event.roomId ?: throw IllegalStateException("You should have set a roomId for your event")
|
||||||
|
@ -61,7 +65,7 @@ internal class LocalEchoRepository @Inject constructor(@SessionDatabase private
|
||||||
event.type ?: throw IllegalStateException("You should have set a type for your event")
|
event.type ?: throw IllegalStateException("You should have set a type for your event")
|
||||||
|
|
||||||
val timelineEventEntity = realmSessionProvider.withRealm { realm ->
|
val timelineEventEntity = realmSessionProvider.withRealm { realm ->
|
||||||
val eventEntity = event.toEntity(roomId, SendState.UNSENT, System.currentTimeMillis())
|
val eventEntity = event.toEntity(roomId, SendState.UNSENT, clock.epochMillis())
|
||||||
val roomMemberHelper = RoomMemberHelper(realm, roomId)
|
val roomMemberHelper = RoomMemberHelper(realm, roomId)
|
||||||
val myUser = roomMemberHelper.getLastRoomMember(senderId)
|
val myUser = roomMemberHelper.getLastRoomMember(senderId)
|
||||||
val localId = UUID.randomUUID().mostSignificantBits
|
val localId = UUID.randomUUID().mostSignificantBits
|
||||||
|
@ -88,7 +92,7 @@ internal class LocalEchoRepository @Inject constructor(@SessionDatabase private
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updateSendState(eventId: String, roomId: String?, sendState: SendState, sendStateDetails: String? = null) {
|
fun updateSendState(eventId: String, roomId: String?, sendState: SendState, sendStateDetails: String? = null) {
|
||||||
Timber.v("## SendEvent: [${System.currentTimeMillis()}] Update local state of $eventId to ${sendState.name}")
|
Timber.v("## SendEvent: [${clock.epochMillis()}] Update local state of $eventId to ${sendState.name}")
|
||||||
timelineInput.onLocalEchoUpdated(roomId = roomId ?: "", eventId = eventId, sendState = sendState)
|
timelineInput.onLocalEchoUpdated(roomId = roomId ?: "", eventId = eventId, sendState = sendState)
|
||||||
updateEchoAsync(eventId) { realm, sendingEventEntity ->
|
updateEchoAsync(eventId) { realm, sendingEventEntity ->
|
||||||
if (sendState == SendState.SENT && sendingEventEntity.sendState == SendState.SYNCED) {
|
if (sendState == SendState.SENT && sendingEventEntity.sendState == SendState.SYNCED) {
|
||||||
|
|
|
@ -77,7 +77,7 @@ internal class MultipleEventSendingDispatcherWorker(context: Context, params: Wo
|
||||||
val roomId = localEchoIds.roomId
|
val roomId = localEchoIds.roomId
|
||||||
val eventId = localEchoIds.eventId
|
val eventId = localEchoIds.eventId
|
||||||
localEchoRepository.updateSendState(eventId, roomId, SendState.SENDING)
|
localEchoRepository.updateSendState(eventId, roomId, SendState.SENDING)
|
||||||
Timber.v("## SendEvent: [${System.currentTimeMillis()}] Schedule send event $eventId")
|
Timber.v("## SendEvent: Schedule send event $eventId")
|
||||||
val sendWork = createSendEventWork(params.sessionId, eventId, true)
|
val sendWork = createSendEventWork(params.sessionId, eventId, true)
|
||||||
timelineSendEventWorkCommon.postWork(roomId, sendWork)
|
timelineSendEventWorkCommon.postWork(roomId, sendWork)
|
||||||
}
|
}
|
||||||
|
|
|
@ -89,13 +89,13 @@ internal class SendEventWorker(context: Context, params: WorkerParameters, sessi
|
||||||
.also { Timber.e("Work cancelled due to input error from parent") }
|
.also { Timber.e("Work cancelled due to input error from parent") }
|
||||||
}
|
}
|
||||||
|
|
||||||
Timber.v("## SendEvent: [${System.currentTimeMillis()}] Send event ${params.eventId}")
|
Timber.v("## SendEvent: Send event ${params.eventId}")
|
||||||
return try {
|
return try {
|
||||||
sendEventTask.execute(SendEventTask.Params(event, params.isEncrypted ?: cryptoService.isRoomEncrypted(event.roomId)))
|
sendEventTask.execute(SendEventTask.Params(event, params.isEncrypted ?: cryptoService.isRoomEncrypted(event.roomId)))
|
||||||
Result.success()
|
Result.success()
|
||||||
} catch (exception: Throwable) {
|
} catch (exception: Throwable) {
|
||||||
if (/*currentAttemptCount >= MAX_NUMBER_OF_RETRY_BEFORE_FAILING ||**/ !exception.shouldBeRetried()) {
|
if (/*currentAttemptCount >= MAX_NUMBER_OF_RETRY_BEFORE_FAILING ||**/ !exception.shouldBeRetried()) {
|
||||||
Timber.e("## SendEvent: [${System.currentTimeMillis()}] Send event Failed cannot retry ${params.eventId} > ${exception.localizedMessage}")
|
Timber.e("## SendEvent: Send event Failed cannot retry ${params.eventId} > ${exception.localizedMessage}")
|
||||||
localEchoRepository.updateSendState(
|
localEchoRepository.updateSendState(
|
||||||
eventId = event.eventId,
|
eventId = event.eventId,
|
||||||
roomId = event.roomId,
|
roomId = event.roomId,
|
||||||
|
@ -104,7 +104,7 @@ internal class SendEventWorker(context: Context, params: WorkerParameters, sessi
|
||||||
)
|
)
|
||||||
Result.success()
|
Result.success()
|
||||||
} else {
|
} else {
|
||||||
Timber.e("## SendEvent: [${System.currentTimeMillis()}] Send event Failed schedule retry ${params.eventId} > ${exception.localizedMessage}")
|
Timber.e("## SendEvent: Send event Failed schedule retry ${params.eventId} > ${exception.localizedMessage}")
|
||||||
Result.retry()
|
Result.retry()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -191,7 +191,7 @@ internal class EventSenderProcessorCoroutine @Inject constructor(
|
||||||
|
|
||||||
private suspend fun QueuedTask.waitForNetwork() = waitForNetworkSequencer.post {
|
private suspend fun QueuedTask.waitForNetwork() = waitForNetworkSequencer.post {
|
||||||
while (!canReachServer.get()) {
|
while (!canReachServer.get()) {
|
||||||
Timber.v("## $this cannot reach server wait ts:${System.currentTimeMillis()}")
|
Timber.v("## $this cannot reach server wait for $$RETRY_WAIT_TIME_MS ms")
|
||||||
delay(RETRY_WAIT_TIME_MS)
|
delay(RETRY_WAIT_TIME_MS)
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
val hostAvailable = HomeServerAvailabilityChecker(sessionParams).check()
|
val hostAvailable = HomeServerAvailabilityChecker(sessionParams).check()
|
||||||
|
|
|
@ -136,7 +136,7 @@ internal class EventSenderProcessorThread @Inject constructor(
|
||||||
private var retryNoNetworkTask: TimerTask? = null
|
private var retryNoNetworkTask: TimerTask? = null
|
||||||
|
|
||||||
override fun run() {
|
override fun run() {
|
||||||
Timber.v("## SendThread started ts:${System.currentTimeMillis()}")
|
Timber.v("## SendThread started")
|
||||||
try {
|
try {
|
||||||
while (!isInterrupted) {
|
while (!isInterrupted) {
|
||||||
Timber.v("## SendThread wait for task to process")
|
Timber.v("## SendThread wait for task to process")
|
||||||
|
@ -151,7 +151,7 @@ internal class EventSenderProcessorThread @Inject constructor(
|
||||||
}
|
}
|
||||||
// we check for network connectivity
|
// we check for network connectivity
|
||||||
while (!canReachServer) {
|
while (!canReachServer) {
|
||||||
Timber.v("## SendThread cannot reach server, wait ts:${System.currentTimeMillis()}")
|
Timber.v("## SendThread cannot reach server")
|
||||||
// schedule to retry
|
// schedule to retry
|
||||||
waitForNetwork()
|
waitForNetwork()
|
||||||
// if thread as been killed meanwhile
|
// if thread as been killed meanwhile
|
||||||
|
@ -175,7 +175,7 @@ internal class EventSenderProcessorThread @Inject constructor(
|
||||||
canReachServer = false
|
canReachServer = false
|
||||||
if (task.retryCount.getAndIncrement() >= 3) task.onTaskFailed()
|
if (task.retryCount.getAndIncrement() >= 3) task.onTaskFailed()
|
||||||
while (!canReachServer) {
|
while (!canReachServer) {
|
||||||
Timber.v("## SendThread retryLoop cannot reach server, wait ts:${System.currentTimeMillis()}")
|
Timber.v("## SendThread retryLoop cannot reach server")
|
||||||
// schedule to retry
|
// schedule to retry
|
||||||
waitForNetwork()
|
waitForNetwork()
|
||||||
}
|
}
|
||||||
|
@ -218,7 +218,7 @@ internal class EventSenderProcessorThread @Inject constructor(
|
||||||
// state = State.KILLED
|
// state = State.KILLED
|
||||||
// is this needed?
|
// is this needed?
|
||||||
retryNoNetworkTask?.cancel()
|
retryNoNetworkTask?.cancel()
|
||||||
Timber.w("## SendThread finished ${System.currentTimeMillis()}")
|
Timber.w("## SendThread finished")
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun waitForNetwork() {
|
private fun waitForNetwork() {
|
||||||
|
|
|
@ -43,19 +43,22 @@ import org.matrix.android.sdk.internal.session.sync.handler.room.ReadReceiptHand
|
||||||
import org.matrix.android.sdk.internal.session.sync.handler.room.ThreadsAwarenessHandler
|
import org.matrix.android.sdk.internal.session.sync.handler.room.ThreadsAwarenessHandler
|
||||||
import org.matrix.android.sdk.internal.task.SemaphoreCoroutineSequencer
|
import org.matrix.android.sdk.internal.task.SemaphoreCoroutineSequencer
|
||||||
import org.matrix.android.sdk.internal.util.createBackgroundHandler
|
import org.matrix.android.sdk.internal.util.createBackgroundHandler
|
||||||
|
import org.matrix.android.sdk.internal.util.time.Clock
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
import java.util.concurrent.CopyOnWriteArrayList
|
import java.util.concurrent.CopyOnWriteArrayList
|
||||||
import java.util.concurrent.atomic.AtomicBoolean
|
import java.util.concurrent.atomic.AtomicBoolean
|
||||||
import java.util.concurrent.atomic.AtomicReference
|
import java.util.concurrent.atomic.AtomicReference
|
||||||
|
|
||||||
internal class DefaultTimeline(private val roomId: String,
|
internal class DefaultTimeline(
|
||||||
|
private val roomId: String,
|
||||||
private val initialEventId: String?,
|
private val initialEventId: String?,
|
||||||
private val realmConfiguration: RealmConfiguration,
|
private val realmConfiguration: RealmConfiguration,
|
||||||
private val loadRoomMembersTask: LoadRoomMembersTask,
|
private val loadRoomMembersTask: LoadRoomMembersTask,
|
||||||
private val readReceiptHandler: ReadReceiptHandler,
|
private val readReceiptHandler: ReadReceiptHandler,
|
||||||
private val settings: TimelineSettings,
|
private val settings: TimelineSettings,
|
||||||
private val coroutineDispatchers: MatrixCoroutineDispatchers,
|
private val coroutineDispatchers: MatrixCoroutineDispatchers,
|
||||||
|
private val clock: Clock,
|
||||||
paginationTask: PaginationTask,
|
paginationTask: PaginationTask,
|
||||||
getEventTask: GetContextOfEventTask,
|
getEventTask: GetContextOfEventTask,
|
||||||
fetchTokenAndPaginateTask: FetchTokenAndPaginateTask,
|
fetchTokenAndPaginateTask: FetchTokenAndPaginateTask,
|
||||||
|
@ -64,7 +67,8 @@ internal class DefaultTimeline(private val roomId: String,
|
||||||
timelineInput: TimelineInput,
|
timelineInput: TimelineInput,
|
||||||
threadsAwarenessHandler: ThreadsAwarenessHandler,
|
threadsAwarenessHandler: ThreadsAwarenessHandler,
|
||||||
lightweightSettingsStorage: LightweightSettingsStorage,
|
lightweightSettingsStorage: LightweightSettingsStorage,
|
||||||
eventDecryptor: TimelineEventDecryptor) : Timeline {
|
eventDecryptor: TimelineEventDecryptor,
|
||||||
|
) : Timeline {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val BACKGROUND_HANDLER = createBackgroundHandler("DefaultTimeline_Thread")
|
val BACKGROUND_HANDLER = createBackgroundHandler("DefaultTimeline_Thread")
|
||||||
|
@ -370,7 +374,8 @@ internal class DefaultTimeline(private val roomId: String,
|
||||||
roomId = roomId,
|
roomId = roomId,
|
||||||
timelineId = timelineID,
|
timelineId = timelineID,
|
||||||
mode = mode,
|
mode = mode,
|
||||||
dependencies = strategyDependencies
|
dependencies = strategyDependencies,
|
||||||
|
clock = clock,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -34,6 +34,7 @@ import org.matrix.android.sdk.internal.session.room.membership.LoadRoomMembersTa
|
||||||
import org.matrix.android.sdk.internal.session.room.relation.threads.FetchThreadTimelineTask
|
import org.matrix.android.sdk.internal.session.room.relation.threads.FetchThreadTimelineTask
|
||||||
import org.matrix.android.sdk.internal.session.sync.handler.room.ReadReceiptHandler
|
import org.matrix.android.sdk.internal.session.sync.handler.room.ReadReceiptHandler
|
||||||
import org.matrix.android.sdk.internal.session.sync.handler.room.ThreadsAwarenessHandler
|
import org.matrix.android.sdk.internal.session.sync.handler.room.ThreadsAwarenessHandler
|
||||||
|
import org.matrix.android.sdk.internal.util.time.Clock
|
||||||
|
|
||||||
internal class DefaultTimelineService @AssistedInject constructor(
|
internal class DefaultTimelineService @AssistedInject constructor(
|
||||||
@Assisted private val roomId: String,
|
@Assisted private val roomId: String,
|
||||||
|
@ -50,7 +51,8 @@ internal class DefaultTimelineService @AssistedInject constructor(
|
||||||
private val lightweightSettingsStorage: LightweightSettingsStorage,
|
private val lightweightSettingsStorage: LightweightSettingsStorage,
|
||||||
private val readReceiptHandler: ReadReceiptHandler,
|
private val readReceiptHandler: ReadReceiptHandler,
|
||||||
private val coroutineDispatchers: MatrixCoroutineDispatchers,
|
private val coroutineDispatchers: MatrixCoroutineDispatchers,
|
||||||
private val timelineEventDataSource: TimelineEventDataSource
|
private val timelineEventDataSource: TimelineEventDataSource,
|
||||||
|
private val clock: Clock,
|
||||||
) : TimelineService {
|
) : TimelineService {
|
||||||
|
|
||||||
@AssistedFactory
|
@AssistedFactory
|
||||||
|
@ -75,7 +77,8 @@ internal class DefaultTimelineService @AssistedInject constructor(
|
||||||
readReceiptHandler = readReceiptHandler,
|
readReceiptHandler = readReceiptHandler,
|
||||||
getEventTask = contextOfEventTask,
|
getEventTask = contextOfEventTask,
|
||||||
threadsAwarenessHandler = threadsAwarenessHandler,
|
threadsAwarenessHandler = threadsAwarenessHandler,
|
||||||
lightweightSettingsStorage = lightweightSettingsStorage
|
lightweightSettingsStorage = lightweightSettingsStorage,
|
||||||
|
clock = clock
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,7 @@ import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
|
||||||
import org.matrix.android.sdk.internal.network.executeRequest
|
import org.matrix.android.sdk.internal.network.executeRequest
|
||||||
import org.matrix.android.sdk.internal.session.room.RoomAPI
|
import org.matrix.android.sdk.internal.session.room.RoomAPI
|
||||||
import org.matrix.android.sdk.internal.task.Task
|
import org.matrix.android.sdk.internal.task.Task
|
||||||
|
import org.matrix.android.sdk.internal.util.time.Clock
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
internal interface GetEventTask : Task<GetEventTask.Params, Event> {
|
internal interface GetEventTask : Task<GetEventTask.Params, Event> {
|
||||||
|
@ -36,7 +37,8 @@ internal interface GetEventTask : Task<GetEventTask.Params, Event> {
|
||||||
internal class DefaultGetEventTask @Inject constructor(
|
internal class DefaultGetEventTask @Inject constructor(
|
||||||
private val roomAPI: RoomAPI,
|
private val roomAPI: RoomAPI,
|
||||||
private val globalErrorReceiver: GlobalErrorReceiver,
|
private val globalErrorReceiver: GlobalErrorReceiver,
|
||||||
private val eventDecryptor: EventDecryptor
|
private val eventDecryptor: EventDecryptor,
|
||||||
|
private val clock: Clock,
|
||||||
) : GetEventTask {
|
) : GetEventTask {
|
||||||
|
|
||||||
override suspend fun execute(params: GetEventTask.Params): Event {
|
override suspend fun execute(params: GetEventTask.Params): Event {
|
||||||
|
@ -59,7 +61,7 @@ internal class DefaultGetEventTask @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
event.ageLocalTs = event.unsignedData?.age?.let { System.currentTimeMillis() - it }
|
event.ageLocalTs = event.unsignedData?.age?.let { clock.epochMillis() - it }
|
||||||
|
|
||||||
return event
|
return event
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,6 +42,7 @@ import org.matrix.android.sdk.internal.database.query.findLastForwardChunkOfThre
|
||||||
import org.matrix.android.sdk.internal.database.query.where
|
import org.matrix.android.sdk.internal.database.query.where
|
||||||
import org.matrix.android.sdk.internal.session.room.relation.threads.FetchThreadTimelineTask
|
import org.matrix.android.sdk.internal.session.room.relation.threads.FetchThreadTimelineTask
|
||||||
import org.matrix.android.sdk.internal.session.sync.handler.room.ThreadsAwarenessHandler
|
import org.matrix.android.sdk.internal.session.sync.handler.room.ThreadsAwarenessHandler
|
||||||
|
import org.matrix.android.sdk.internal.util.time.Clock
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.util.concurrent.atomic.AtomicReference
|
import java.util.concurrent.atomic.AtomicReference
|
||||||
|
|
||||||
|
@ -53,11 +54,13 @@ import java.util.concurrent.atomic.AtomicReference
|
||||||
* Once we got a ChunkEntity we wrap it with TimelineChunk class so we dispatch any methods for loading data.
|
* Once we got a ChunkEntity we wrap it with TimelineChunk class so we dispatch any methods for loading data.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
internal class LoadTimelineStrategy(
|
internal class LoadTimelineStrategy constructor(
|
||||||
private val roomId: String,
|
private val roomId: String,
|
||||||
private val timelineId: String,
|
private val timelineId: String,
|
||||||
private val mode: Mode,
|
private val mode: Mode,
|
||||||
private val dependencies: Dependencies) {
|
private val dependencies: Dependencies,
|
||||||
|
clock: Clock,
|
||||||
|
) {
|
||||||
|
|
||||||
sealed interface Mode {
|
sealed interface Mode {
|
||||||
object Live : Mode
|
object Live : Mode
|
||||||
|
@ -153,7 +156,7 @@ internal class LoadTimelineStrategy(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private val uiEchoManager = UIEchoManager(uiEchoManagerListener)
|
private val uiEchoManager = UIEchoManager(uiEchoManagerListener, clock)
|
||||||
private val sendingEventsDataSource: SendingEventsDataSource = RealmSendingEventsDataSource(
|
private val sendingEventsDataSource: SendingEventsDataSource = RealmSendingEventsDataSource(
|
||||||
roomId = roomId,
|
roomId = roomId,
|
||||||
realm = dependencies.realm,
|
realm = dependencies.realm,
|
||||||
|
|
|
@ -44,6 +44,7 @@ import org.matrix.android.sdk.internal.di.SessionDatabase
|
||||||
import org.matrix.android.sdk.internal.di.UserId
|
import org.matrix.android.sdk.internal.di.UserId
|
||||||
import org.matrix.android.sdk.internal.session.StreamEventsManager
|
import org.matrix.android.sdk.internal.session.StreamEventsManager
|
||||||
import org.matrix.android.sdk.internal.util.awaitTransaction
|
import org.matrix.android.sdk.internal.util.awaitTransaction
|
||||||
|
import org.matrix.android.sdk.internal.util.time.Clock
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@ -54,7 +55,9 @@ internal class TokenChunkEventPersistor @Inject constructor(
|
||||||
@SessionDatabase private val monarchy: Monarchy,
|
@SessionDatabase private val monarchy: Monarchy,
|
||||||
@UserId private val userId: String,
|
@UserId private val userId: String,
|
||||||
private val lightweightSettingsStorage: LightweightSettingsStorage,
|
private val lightweightSettingsStorage: LightweightSettingsStorage,
|
||||||
private val liveEventManager: Lazy<StreamEventsManager>) {
|
private val liveEventManager: Lazy<StreamEventsManager>,
|
||||||
|
private val clock: Clock,
|
||||||
|
) {
|
||||||
|
|
||||||
enum class Result {
|
enum class Result {
|
||||||
SHOULD_FETCH_MORE,
|
SHOULD_FETCH_MORE,
|
||||||
|
@ -166,7 +169,7 @@ internal class TokenChunkEventPersistor @Inject constructor(
|
||||||
val eventList = receivedChunk.events
|
val eventList = receivedChunk.events
|
||||||
val stateEvents = receivedChunk.stateEvents
|
val stateEvents = receivedChunk.stateEvents
|
||||||
|
|
||||||
val now = System.currentTimeMillis()
|
val now = clock.epochMillis()
|
||||||
|
|
||||||
stateEvents?.forEach { stateEvent ->
|
stateEvents?.forEach { stateEvent ->
|
||||||
val ageLocalTs = stateEvent.unsignedData?.age?.let { now - it }
|
val ageLocalTs = stateEvent.unsignedData?.age?.let { now - it }
|
||||||
|
|
|
@ -24,10 +24,14 @@ import org.matrix.android.sdk.api.session.room.model.ReactionAggregatedSummary
|
||||||
import org.matrix.android.sdk.api.session.room.model.relation.ReactionContent
|
import org.matrix.android.sdk.api.session.room.model.relation.ReactionContent
|
||||||
import org.matrix.android.sdk.api.session.room.send.SendState
|
import org.matrix.android.sdk.api.session.room.send.SendState
|
||||||
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
|
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
|
||||||
|
import org.matrix.android.sdk.internal.util.time.Clock
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.util.Collections
|
import java.util.Collections
|
||||||
|
|
||||||
internal class UIEchoManager(private val listener: Listener) {
|
internal class UIEchoManager(
|
||||||
|
private val listener: Listener,
|
||||||
|
private val clock: Clock,
|
||||||
|
) {
|
||||||
|
|
||||||
interface Listener {
|
interface Listener {
|
||||||
fun rebuildEvent(eventId: String, builder: (TimelineEvent) -> TimelineEvent?): Boolean
|
fun rebuildEvent(eventId: String, builder: (TimelineEvent) -> TimelineEvent?): Boolean
|
||||||
|
@ -109,7 +113,7 @@ internal class UIEchoManager(private val listener: Listener) {
|
||||||
key = uiEchoReaction.reaction,
|
key = uiEchoReaction.reaction,
|
||||||
count = 1,
|
count = 1,
|
||||||
addedByMe = true,
|
addedByMe = true,
|
||||||
firstTimestamp = System.currentTimeMillis(),
|
firstTimestamp = clock.epochMillis(),
|
||||||
sourceEvents = emptyList(),
|
sourceEvents = emptyList(),
|
||||||
localEchoEvents = listOf(uiEchoReaction.localEchoId)
|
localEchoEvents = listOf(uiEchoReaction.localEchoId)
|
||||||
).let { updateReactions.add(it) }
|
).let { updateReactions.add(it) }
|
||||||
|
@ -143,7 +147,7 @@ internal class UIEchoManager(private val listener: Listener) {
|
||||||
fun updateSentStateWithUiEcho(timelineEvent: TimelineEvent): TimelineEvent {
|
fun updateSentStateWithUiEcho(timelineEvent: TimelineEvent): TimelineEvent {
|
||||||
if (timelineEvent.root.sendState.isSent()) return timelineEvent
|
if (timelineEvent.root.sendState.isSent()) return timelineEvent
|
||||||
val inMemoryState = inMemorySendingStates[timelineEvent.eventId] ?: return timelineEvent
|
val inMemoryState = inMemorySendingStates[timelineEvent.eventId] ?: return timelineEvent
|
||||||
// Timber.v("## ${System.currentTimeMillis()} Send event refresh echo with live state $inMemoryState from state ${element.root.sendState}")
|
// Timber.v("## ${clock.epochMillis()} Send event refresh echo with live state $inMemoryState from state ${element.root.sendState}")
|
||||||
return timelineEvent.copy(
|
return timelineEvent.copy(
|
||||||
root = timelineEvent.root.copyAll()
|
root = timelineEvent.root.copyAll()
|
||||||
.also { it.sendState = inMemoryState }
|
.also { it.sendState = inMemoryState }
|
||||||
|
|
|
@ -20,6 +20,7 @@ import com.squareup.moshi.JsonClass
|
||||||
import okio.buffer
|
import okio.buffer
|
||||||
import okio.source
|
import okio.source
|
||||||
import org.matrix.android.sdk.internal.di.MoshiProvider
|
import org.matrix.android.sdk.internal.di.MoshiProvider
|
||||||
|
import org.matrix.android.sdk.internal.util.time.Clock
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
|
@ -46,7 +47,10 @@ internal interface InitialSyncStatusRepository {
|
||||||
/**
|
/**
|
||||||
* This class handle the current status of an initial sync and persist it on the disk, to be robust against crash
|
* This class handle the current status of an initial sync and persist it on the disk, to be robust against crash
|
||||||
*/
|
*/
|
||||||
internal class FileInitialSyncStatusRepository(directory: File) : InitialSyncStatusRepository {
|
internal class FileInitialSyncStatusRepository(
|
||||||
|
directory: File,
|
||||||
|
private val clock: Clock,
|
||||||
|
) : InitialSyncStatusRepository {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
// After 2 hours, we consider that the downloaded file is outdated:
|
// After 2 hours, we consider that the downloaded file is outdated:
|
||||||
|
@ -64,7 +68,7 @@ internal class FileInitialSyncStatusRepository(directory: File) : InitialSyncSta
|
||||||
ensureCache()
|
ensureCache()
|
||||||
val state = cache?.step ?: InitialSyncStatus.STEP_INIT
|
val state = cache?.step ?: InitialSyncStatus.STEP_INIT
|
||||||
return if (state >= InitialSyncStatus.STEP_DOWNLOADED &&
|
return if (state >= InitialSyncStatus.STEP_DOWNLOADED &&
|
||||||
System.currentTimeMillis() > (cache?.downloadedDate ?: 0) + INIT_SYNC_FILE_LIFETIME) {
|
clock.epochMillis() > (cache?.downloadedDate ?: 0) + INIT_SYNC_FILE_LIFETIME) {
|
||||||
Timber.d("INIT_SYNC downloaded file is outdated, download it again")
|
Timber.d("INIT_SYNC downloaded file is outdated, download it again")
|
||||||
// The downloaded file is outdated
|
// The downloaded file is outdated
|
||||||
setStep(InitialSyncStatus.STEP_INIT)
|
setStep(InitialSyncStatus.STEP_INIT)
|
||||||
|
@ -79,7 +83,7 @@ internal class FileInitialSyncStatusRepository(directory: File) : InitialSyncSta
|
||||||
if (step == InitialSyncStatus.STEP_DOWNLOADED) {
|
if (step == InitialSyncStatus.STEP_DOWNLOADED) {
|
||||||
// Also store the downloaded date
|
// Also store the downloaded date
|
||||||
newStatus = newStatus.copy(
|
newStatus = newStatus.copy(
|
||||||
downloadedDate = System.currentTimeMillis()
|
downloadedDate = clock.epochMillis()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
cache = newStatus
|
cache = newStatus
|
||||||
|
|
|
@ -44,6 +44,7 @@ import org.matrix.android.sdk.internal.session.sync.parsing.InitialSyncResponseP
|
||||||
import org.matrix.android.sdk.internal.session.user.UserStore
|
import org.matrix.android.sdk.internal.session.user.UserStore
|
||||||
import org.matrix.android.sdk.internal.task.Task
|
import org.matrix.android.sdk.internal.task.Task
|
||||||
import org.matrix.android.sdk.internal.util.logDuration
|
import org.matrix.android.sdk.internal.util.logDuration
|
||||||
|
import org.matrix.android.sdk.internal.util.time.Clock
|
||||||
import retrofit2.Response
|
import retrofit2.Response
|
||||||
import retrofit2.awaitResponse
|
import retrofit2.awaitResponse
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
@ -78,11 +79,12 @@ internal class DefaultSyncTask @Inject constructor(
|
||||||
@SessionFilesDirectory
|
@SessionFilesDirectory
|
||||||
private val fileDirectory: File,
|
private val fileDirectory: File,
|
||||||
private val syncResponseParser: InitialSyncResponseParser,
|
private val syncResponseParser: InitialSyncResponseParser,
|
||||||
private val roomSyncEphemeralTemporaryStore: RoomSyncEphemeralTemporaryStore
|
private val roomSyncEphemeralTemporaryStore: RoomSyncEphemeralTemporaryStore,
|
||||||
|
private val clock: Clock,
|
||||||
) : SyncTask {
|
) : SyncTask {
|
||||||
|
|
||||||
private val workingDir = File(fileDirectory, "is")
|
private val workingDir = File(fileDirectory, "is")
|
||||||
private val initialSyncStatusRepository: InitialSyncStatusRepository = FileInitialSyncStatusRepository(workingDir)
|
private val initialSyncStatusRepository: InitialSyncStatusRepository = FileInitialSyncStatusRepository(workingDir, clock)
|
||||||
|
|
||||||
override suspend fun execute(params: SyncTask.Params): SyncResponse {
|
override suspend fun execute(params: SyncTask.Params): SyncResponse {
|
||||||
return syncTaskSequencer.post {
|
return syncTaskSequencer.post {
|
||||||
|
@ -111,7 +113,8 @@ internal class DefaultSyncTask @Inject constructor(
|
||||||
userStore.createOrUpdate(
|
userStore.createOrUpdate(
|
||||||
userId = userId,
|
userId = userId,
|
||||||
displayName = user?.displayName,
|
displayName = user?.displayName,
|
||||||
avatarUrl = user?.avatarUrl)
|
avatarUrl = user?.avatarUrl
|
||||||
|
)
|
||||||
defaultSyncStatusService.startRoot(InitSyncStep.ImportingAccount, 100)
|
defaultSyncStatusService.startRoot(InitSyncStep.ImportingAccount, 100)
|
||||||
}
|
}
|
||||||
// Maybe refresh the homeserver capabilities data we know
|
// Maybe refresh the homeserver capabilities data we know
|
||||||
|
@ -124,7 +127,7 @@ internal class DefaultSyncTask @Inject constructor(
|
||||||
if (isInitialSync) {
|
if (isInitialSync) {
|
||||||
Timber.tag(loggerTag.value).d("INIT_SYNC with filter: ${requestParams["filter"]}")
|
Timber.tag(loggerTag.value).d("INIT_SYNC with filter: ${requestParams["filter"]}")
|
||||||
val initSyncStrategy = initialSyncStrategy
|
val initSyncStrategy = initialSyncStrategy
|
||||||
logDuration("INIT_SYNC strategy: $initSyncStrategy", loggerTag) {
|
logDuration("INIT_SYNC strategy: $initSyncStrategy", loggerTag, clock) {
|
||||||
if (initSyncStrategy is InitialSyncStrategy.Optimized) {
|
if (initSyncStrategy is InitialSyncStrategy.Optimized) {
|
||||||
roomSyncEphemeralTemporaryStore.reset()
|
roomSyncEphemeralTemporaryStore.reset()
|
||||||
workingDir.mkdirs()
|
workingDir.mkdirs()
|
||||||
|
@ -135,7 +138,7 @@ internal class DefaultSyncTask @Inject constructor(
|
||||||
// Delete all files
|
// Delete all files
|
||||||
workingDir.deleteRecursively()
|
workingDir.deleteRecursively()
|
||||||
} else {
|
} else {
|
||||||
val syncResponse = logDuration("INIT_SYNC Request", loggerTag) {
|
val syncResponse = logDuration("INIT_SYNC Request", loggerTag, clock) {
|
||||||
executeRequest(globalErrorReceiver) {
|
executeRequest(globalErrorReceiver) {
|
||||||
syncAPI.sync(
|
syncAPI.sync(
|
||||||
params = requestParams,
|
params = requestParams,
|
||||||
|
@ -146,7 +149,7 @@ internal class DefaultSyncTask @Inject constructor(
|
||||||
// We cannot distinguish request and download in this case.
|
// We cannot distinguish request and download in this case.
|
||||||
syncStatisticsData.requestInitSyncTime = SystemClock.elapsedRealtime()
|
syncStatisticsData.requestInitSyncTime = SystemClock.elapsedRealtime()
|
||||||
syncStatisticsData.downloadInitSyncTime = syncStatisticsData.requestInitSyncTime
|
syncStatisticsData.downloadInitSyncTime = syncStatisticsData.requestInitSyncTime
|
||||||
logDuration("INIT_SYNC Database insertion", loggerTag) {
|
logDuration("INIT_SYNC Database insertion", loggerTag, clock) {
|
||||||
syncResponseHandler.handleResponse(syncResponse, token, defaultSyncStatusService)
|
syncResponseHandler.handleResponse(syncResponse, token, defaultSyncStatusService)
|
||||||
}
|
}
|
||||||
syncResponseToReturn = syncResponse
|
syncResponseToReturn = syncResponse
|
||||||
|
@ -174,10 +177,12 @@ internal class DefaultSyncTask @Inject constructor(
|
||||||
Timber.tag(loggerTag.value).d(
|
Timber.tag(loggerTag.value).d(
|
||||||
"Incremental sync request parsing, $nbRooms room(s) $nbToDevice toDevice(s). Got nextBatch: $nextBatch"
|
"Incremental sync request parsing, $nbRooms room(s) $nbToDevice toDevice(s). Got nextBatch: $nextBatch"
|
||||||
)
|
)
|
||||||
defaultSyncStatusService.setStatus(SyncStatusService.Status.IncrementalSyncParsing(
|
defaultSyncStatusService.setStatus(
|
||||||
|
SyncStatusService.Status.IncrementalSyncParsing(
|
||||||
rooms = nbRooms,
|
rooms = nbRooms,
|
||||||
toDevice = nbToDevice
|
toDevice = nbToDevice
|
||||||
))
|
)
|
||||||
|
)
|
||||||
syncResponseHandler.handleResponse(syncResponse, token, null)
|
syncResponseHandler.handleResponse(syncResponse, token, null)
|
||||||
syncResponseToReturn = syncResponse
|
syncResponseToReturn = syncResponse
|
||||||
Timber.tag(loggerTag.value).d("Incremental sync done")
|
Timber.tag(loggerTag.value).d("Incremental sync done")
|
||||||
|
@ -201,14 +206,14 @@ internal class DefaultSyncTask @Inject constructor(
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
initialSyncStatusRepository.setStep(InitialSyncStatus.STEP_DOWNLOADING)
|
initialSyncStatusRepository.setStep(InitialSyncStatus.STEP_DOWNLOADING)
|
||||||
val syncResponse = logDuration("INIT_SYNC Perform server request", loggerTag) {
|
val syncResponse = logDuration("INIT_SYNC Perform server request", loggerTag, clock) {
|
||||||
reportSubtask(defaultSyncStatusService, InitSyncStep.ServerComputing, 1, 0.2f) {
|
reportSubtask(defaultSyncStatusService, InitSyncStep.ServerComputing, 1, 0.2f) {
|
||||||
getSyncResponse(requestParams, MAX_NUMBER_OF_RETRY_AFTER_TIMEOUT)
|
getSyncResponse(requestParams, MAX_NUMBER_OF_RETRY_AFTER_TIMEOUT)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
syncStatisticsData.requestInitSyncTime = SystemClock.elapsedRealtime()
|
syncStatisticsData.requestInitSyncTime = SystemClock.elapsedRealtime()
|
||||||
if (syncResponse.isSuccessful) {
|
if (syncResponse.isSuccessful) {
|
||||||
logDuration("INIT_SYNC Download and save to file", loggerTag) {
|
logDuration("INIT_SYNC Download and save to file", loggerTag, clock) {
|
||||||
reportSubtask(defaultSyncStatusService, InitSyncStep.Downloading, 1, 0.1f) {
|
reportSubtask(defaultSyncStatusService, InitSyncStep.Downloading, 1, 0.1f) {
|
||||||
syncResponse.body()?.byteStream()?.use { inputStream ->
|
syncResponse.body()?.byteStream()?.use { inputStream ->
|
||||||
workingFile.outputStream().use { outputStream ->
|
workingFile.outputStream().use { outputStream ->
|
||||||
|
@ -247,8 +252,8 @@ internal class DefaultSyncTask @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun handleSyncFile(workingFile: File, initSyncStrategy: InitialSyncStrategy.Optimized): SyncResponse {
|
private suspend fun handleSyncFile(workingFile: File, initSyncStrategy: InitialSyncStrategy.Optimized): SyncResponse {
|
||||||
return logDuration("INIT_SYNC handleSyncFile()", loggerTag) {
|
return logDuration("INIT_SYNC handleSyncFile()", loggerTag, clock) {
|
||||||
val syncResponse = logDuration("INIT_SYNC Read file and parse", loggerTag) {
|
val syncResponse = logDuration("INIT_SYNC Read file and parse", loggerTag, clock) {
|
||||||
syncResponseParser.parse(initSyncStrategy, workingFile)
|
syncResponseParser.parse(initSyncStrategy, workingFile)
|
||||||
}
|
}
|
||||||
initialSyncStatusRepository.setStep(InitialSyncStatus.STEP_PARSED)
|
initialSyncStatusRepository.setStep(InitialSyncStatus.STEP_PARSED)
|
||||||
|
@ -257,7 +262,7 @@ internal class DefaultSyncTask @Inject constructor(
|
||||||
val nbOfJoinedRoomsInFile = syncResponse.rooms?.join?.values?.count { it.ephemeral is LazyRoomSyncEphemeral.Stored }
|
val nbOfJoinedRoomsInFile = syncResponse.rooms?.join?.values?.count { it.ephemeral is LazyRoomSyncEphemeral.Stored }
|
||||||
Timber.tag(loggerTag.value).d("INIT_SYNC $nbOfJoinedRooms rooms, $nbOfJoinedRoomsInFile ephemeral stored into files")
|
Timber.tag(loggerTag.value).d("INIT_SYNC $nbOfJoinedRooms rooms, $nbOfJoinedRoomsInFile ephemeral stored into files")
|
||||||
|
|
||||||
logDuration("INIT_SYNC Database insertion", loggerTag) {
|
logDuration("INIT_SYNC Database insertion", loggerTag, clock) {
|
||||||
syncResponseHandler.handleResponse(syncResponse, null, defaultSyncStatusService)
|
syncResponseHandler.handleResponse(syncResponse, null, defaultSyncStatusService)
|
||||||
}
|
}
|
||||||
initialSyncStatusRepository.setStep(InitialSyncStatus.STEP_SUCCESS)
|
initialSyncStatusRepository.setStep(InitialSyncStatus.STEP_SUCCESS)
|
||||||
|
|
|
@ -44,12 +44,14 @@ internal class ReadReceiptHandler @Inject constructor(
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
fun createContent(userId: String, eventId: String): ReadReceiptContent {
|
fun createContent(userId: String,
|
||||||
|
eventId: String,
|
||||||
|
currentTimeMillis: Long): ReadReceiptContent {
|
||||||
return mapOf(
|
return mapOf(
|
||||||
eventId to mapOf(
|
eventId to mapOf(
|
||||||
READ_KEY to mapOf(
|
READ_KEY to mapOf(
|
||||||
userId to mapOf(
|
userId to mapOf(
|
||||||
TIMESTAMP_KEY to System.currentTimeMillis().toDouble()
|
TIMESTAMP_KEY to currentTimeMillis.toDouble()
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
|
@ -79,10 +79,12 @@ import org.matrix.android.sdk.internal.session.room.typing.TypingEventContent
|
||||||
import org.matrix.android.sdk.internal.session.sync.SyncResponsePostTreatmentAggregator
|
import org.matrix.android.sdk.internal.session.sync.SyncResponsePostTreatmentAggregator
|
||||||
import org.matrix.android.sdk.internal.session.sync.parsing.RoomSyncAccountDataHandler
|
import org.matrix.android.sdk.internal.session.sync.parsing.RoomSyncAccountDataHandler
|
||||||
import org.matrix.android.sdk.internal.util.computeBestChunkSize
|
import org.matrix.android.sdk.internal.util.computeBestChunkSize
|
||||||
|
import org.matrix.android.sdk.internal.util.time.Clock
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
internal class RoomSyncHandler @Inject constructor(private val readReceiptHandler: ReadReceiptHandler,
|
internal class RoomSyncHandler @Inject constructor(
|
||||||
|
private val readReceiptHandler: ReadReceiptHandler,
|
||||||
private val roomSummaryUpdater: RoomSummaryUpdater,
|
private val roomSummaryUpdater: RoomSummaryUpdater,
|
||||||
private val roomAccountDataHandler: RoomSyncAccountDataHandler,
|
private val roomAccountDataHandler: RoomSyncAccountDataHandler,
|
||||||
private val cryptoService: DefaultCryptoService,
|
private val cryptoService: DefaultCryptoService,
|
||||||
|
@ -94,7 +96,9 @@ internal class RoomSyncHandler @Inject constructor(private val readReceiptHandle
|
||||||
private val homeServerCapabilitiesService: HomeServerCapabilitiesService,
|
private val homeServerCapabilitiesService: HomeServerCapabilitiesService,
|
||||||
private val lightweightSettingsStorage: LightweightSettingsStorage,
|
private val lightweightSettingsStorage: LightweightSettingsStorage,
|
||||||
private val timelineInput: TimelineInput,
|
private val timelineInput: TimelineInput,
|
||||||
private val liveEventService: Lazy<StreamEventsManager>) {
|
private val liveEventService: Lazy<StreamEventsManager>,
|
||||||
|
private val clock: Clock,
|
||||||
|
) {
|
||||||
|
|
||||||
sealed class HandlingStrategy {
|
sealed class HandlingStrategy {
|
||||||
data class JOINED(val data: Map<String, RoomSync>) : HandlingStrategy()
|
data class JOINED(val data: Map<String, RoomSync>) : HandlingStrategy()
|
||||||
|
@ -130,7 +134,7 @@ internal class RoomSyncHandler @Inject constructor(private val readReceiptHandle
|
||||||
} else {
|
} else {
|
||||||
EventInsertType.INCREMENTAL_SYNC
|
EventInsertType.INCREMENTAL_SYNC
|
||||||
}
|
}
|
||||||
val syncLocalTimeStampMillis = System.currentTimeMillis()
|
val syncLocalTimeStampMillis = clock.epochMillis()
|
||||||
val rooms = when (handlingStrategy) {
|
val rooms = when (handlingStrategy) {
|
||||||
is HandlingStrategy.JOINED -> {
|
is HandlingStrategy.JOINED -> {
|
||||||
if (isInitialSync && initialSyncStrategy is InitialSyncStrategy.Optimized) {
|
if (isInitialSync && initialSyncStrategy is InitialSyncStrategy.Optimized) {
|
||||||
|
@ -440,7 +444,8 @@ internal class RoomSyncHandler @Inject constructor(private val readReceiptHandle
|
||||||
threadEventEntity = eventEntity,
|
threadEventEntity = eventEntity,
|
||||||
roomMemberContentsByUser = roomMemberContentsByUser,
|
roomMemberContentsByUser = roomMemberContentsByUser,
|
||||||
userId = userId,
|
userId = userId,
|
||||||
roomEntity = roomEntity
|
roomEntity = roomEntity,
|
||||||
|
currentTimeMillis = clock.epochMillis(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} ?: run {
|
} ?: run {
|
||||||
|
|
|
@ -18,6 +18,7 @@ package org.matrix.android.sdk.internal.util
|
||||||
|
|
||||||
import org.matrix.android.sdk.BuildConfig
|
import org.matrix.android.sdk.BuildConfig
|
||||||
import org.matrix.android.sdk.api.logger.LoggerTag
|
import org.matrix.android.sdk.api.logger.LoggerTag
|
||||||
|
import org.matrix.android.sdk.internal.util.time.Clock
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
|
||||||
internal fun <T> Collection<T>.logLimit(maxQuantity: Int = 5): String {
|
internal fun <T> Collection<T>.logLimit(maxQuantity: Int = 5): String {
|
||||||
|
@ -34,13 +35,14 @@ internal fun <T> Collection<T>.logLimit(maxQuantity: Int = 5): String {
|
||||||
|
|
||||||
internal suspend fun <T> logDuration(message: String,
|
internal suspend fun <T> logDuration(message: String,
|
||||||
loggerTag: LoggerTag,
|
loggerTag: LoggerTag,
|
||||||
|
clock: Clock,
|
||||||
block: suspend () -> T): T {
|
block: suspend () -> T): T {
|
||||||
Timber.tag(loggerTag.value).d("$message -- BEGIN")
|
Timber.tag(loggerTag.value).d("$message -- BEGIN")
|
||||||
val start = System.currentTimeMillis()
|
val start = clock.epochMillis()
|
||||||
val result = logRamUsage(message) {
|
val result = logRamUsage(message) {
|
||||||
block()
|
block()
|
||||||
}
|
}
|
||||||
val duration = System.currentTimeMillis() - start
|
val duration = clock.epochMillis() - start
|
||||||
Timber.tag(loggerTag.value).d("$message -- END duration: $duration ms")
|
Timber.tag(loggerTag.value).d("$message -- END duration: $duration ms")
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
|
@ -18,10 +18,15 @@ package org.matrix.android.sdk.internal.util.system
|
||||||
|
|
||||||
import dagger.Binds
|
import dagger.Binds
|
||||||
import dagger.Module
|
import dagger.Module
|
||||||
|
import org.matrix.android.sdk.internal.util.time.Clock
|
||||||
|
import org.matrix.android.sdk.internal.util.time.DefaultClock
|
||||||
|
|
||||||
@Module
|
@Module
|
||||||
internal abstract class SystemModule {
|
internal abstract class SystemModule {
|
||||||
|
|
||||||
@Binds
|
@Binds
|
||||||
abstract fun bindBuildVersionSdkIntProvider(provider: DefaultBuildVersionSdkIntProvider): BuildVersionSdkIntProvider
|
abstract fun bindBuildVersionSdkIntProvider(provider: DefaultBuildVersionSdkIntProvider): BuildVersionSdkIntProvider
|
||||||
|
|
||||||
|
@Binds
|
||||||
|
abstract fun bindClock(clock: DefaultClock): Clock
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022 The Matrix.org Foundation C.I.C.
|
||||||
|
*
|
||||||
|
* 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 org.matrix.android.sdk.internal.util.time
|
||||||
|
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
internal interface Clock {
|
||||||
|
fun epochMillis(): Long
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class DefaultClock @Inject constructor() : Clock {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides a UTC epoch in milliseconds
|
||||||
|
*
|
||||||
|
* This value is not guaranteed to be correct with reality
|
||||||
|
* as a User can override the system time and date to any values.
|
||||||
|
*/
|
||||||
|
override fun epochMillis(): Long {
|
||||||
|
return System.currentTimeMillis()
|
||||||
|
}
|
||||||
|
}
|
|
@ -174,3 +174,6 @@ PreferenceManager\.getDefaultSharedPreferences==2
|
||||||
|
|
||||||
### Do not use `template_` string. Please remove the prefix `template_` to use the generated resource instead.
|
### Do not use `template_` string. Please remove the prefix `template_` to use the generated resource instead.
|
||||||
R\.string\.template_
|
R\.string\.template_
|
||||||
|
|
||||||
|
### Use the Clock interface, or use `measureTimeMillis`
|
||||||
|
System\.currentTimeMillis\(\)===2
|
||||||
|
|
|
@ -43,6 +43,7 @@ import com.google.android.material.bottomsheet.BottomSheetBehavior
|
||||||
import com.google.android.material.bottomsheet.BottomSheetDialog
|
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
||||||
import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment
|
import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment
|
||||||
|
import im.vector.app.core.time.DefaultClock
|
||||||
import im.vector.app.espresso.tools.waitUntilViewVisible
|
import im.vector.app.espresso.tools.waitUntilViewVisible
|
||||||
import org.hamcrest.Matcher
|
import org.hamcrest.Matcher
|
||||||
import org.hamcrest.Matchers
|
import org.hamcrest.Matchers
|
||||||
|
@ -52,6 +53,7 @@ import org.matrix.android.sdk.api.session.crypto.crosssigning.PrivateKeysInfo
|
||||||
import org.matrix.android.sdk.api.session.sync.SyncState
|
import org.matrix.android.sdk.api.session.sync.SyncState
|
||||||
import org.matrix.android.sdk.api.util.Optional
|
import org.matrix.android.sdk.api.util.Optional
|
||||||
import java.util.concurrent.TimeoutException
|
import java.util.concurrent.TimeoutException
|
||||||
|
import kotlin.random.Random
|
||||||
|
|
||||||
object EspressoHelper {
|
object EspressoHelper {
|
||||||
fun getCurrentActivity(): Activity? {
|
fun getCurrentActivity(): Activity? {
|
||||||
|
@ -89,6 +91,8 @@ fun getString(@StringRes id: Int): String {
|
||||||
|
|
||||||
fun waitForView(viewMatcher: Matcher<View>, timeout: Long = 10_000, waitForDisplayed: Boolean = true): ViewAction {
|
fun waitForView(viewMatcher: Matcher<View>, timeout: Long = 10_000, waitForDisplayed: Boolean = true): ViewAction {
|
||||||
return object : ViewAction {
|
return object : ViewAction {
|
||||||
|
private val clock = DefaultClock()
|
||||||
|
|
||||||
override fun getConstraints(): Matcher<View> {
|
override fun getConstraints(): Matcher<View> {
|
||||||
return Matchers.any(View::class.java)
|
return Matchers.any(View::class.java)
|
||||||
}
|
}
|
||||||
|
@ -102,14 +106,14 @@ fun waitForView(viewMatcher: Matcher<View>, timeout: Long = 10_000, waitForDispl
|
||||||
override fun perform(uiController: UiController, view: View) {
|
override fun perform(uiController: UiController, view: View) {
|
||||||
println("*** waitForView 1 $view")
|
println("*** waitForView 1 $view")
|
||||||
uiController.loopMainThreadUntilIdle()
|
uiController.loopMainThreadUntilIdle()
|
||||||
val startTime = System.currentTimeMillis()
|
val startTime = clock.epochMillis()
|
||||||
val endTime = startTime + timeout
|
val endTime = startTime + timeout
|
||||||
val visibleMatcher = isDisplayed()
|
val visibleMatcher = isDisplayed()
|
||||||
|
|
||||||
uiController.loopMainThreadForAtLeast(100)
|
uiController.loopMainThreadForAtLeast(100)
|
||||||
|
|
||||||
do {
|
do {
|
||||||
println("*** waitForView loop $view end:$endTime current:${System.currentTimeMillis()}")
|
println("*** waitForView loop $view end:$endTime current:${clock.epochMillis()}")
|
||||||
val viewVisible = TreeIterables.breadthFirstViewTraversal(view)
|
val viewVisible = TreeIterables.breadthFirstViewTraversal(view)
|
||||||
.any { viewMatcher.matches(it) && visibleMatcher.matches(it) }
|
.any { viewMatcher.matches(it) && visibleMatcher.matches(it) }
|
||||||
|
|
||||||
|
@ -118,7 +122,7 @@ fun waitForView(viewMatcher: Matcher<View>, timeout: Long = 10_000, waitForDispl
|
||||||
println("*** waitForView loop loopMainThreadForAtLeast...")
|
println("*** waitForView loop loopMainThreadForAtLeast...")
|
||||||
uiController.loopMainThreadForAtLeast(50)
|
uiController.loopMainThreadForAtLeast(50)
|
||||||
println("*** waitForView loop ...loopMainThreadForAtLeast")
|
println("*** waitForView loop ...loopMainThreadForAtLeast")
|
||||||
} while (System.currentTimeMillis() < endTime)
|
} while (clock.epochMillis() < endTime)
|
||||||
|
|
||||||
println("*** waitForView timeout $view")
|
println("*** waitForView timeout $view")
|
||||||
// Timeout happens.
|
// Timeout happens.
|
||||||
|
@ -168,9 +172,9 @@ fun activityIdlingResource(activityClass: Class<*>): IdlingResource {
|
||||||
val res = object : IdlingResource, ActivityLifecycleCallback {
|
val res = object : IdlingResource, ActivityLifecycleCallback {
|
||||||
private var callback: IdlingResource.ResourceCallback? = null
|
private var callback: IdlingResource.ResourceCallback? = null
|
||||||
private var resumedActivity: Activity? = null
|
private var resumedActivity: Activity? = null
|
||||||
private val uniqTS = System.currentTimeMillis()
|
private val uniqueSuffix = Random.nextLong()
|
||||||
|
|
||||||
override fun getName() = "activityIdlingResource_${activityClass.name}_$uniqTS"
|
override fun getName() = "activityIdlingResource_${activityClass.name}_$uniqueSuffix"
|
||||||
|
|
||||||
override fun isIdleNow(): Boolean {
|
override fun isIdleNow(): Boolean {
|
||||||
val activity = resumedActivity ?: ActivityLifecycleMonitorRegistry.getInstance().getActivitiesInStage(Stage.RESUMED).firstOrNull {
|
val activity = resumedActivity ?: ActivityLifecycleMonitorRegistry.getInstance().getActivitiesInStage(Stage.RESUMED).firstOrNull {
|
||||||
|
|
|
@ -34,6 +34,7 @@ import org.hamcrest.CoreMatchers.not
|
||||||
import org.junit.Rule
|
import org.junit.Rule
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.runner.RunWith
|
import org.junit.runner.RunWith
|
||||||
|
import kotlin.random.Random
|
||||||
|
|
||||||
@RunWith(AndroidJUnit4::class)
|
@RunWith(AndroidJUnit4::class)
|
||||||
@LargeTest
|
@LargeTest
|
||||||
|
@ -44,7 +45,7 @@ class RegistrationTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun simpleRegister() {
|
fun simpleRegister() {
|
||||||
val userId: String = "UiAutoTest_${System.currentTimeMillis()}"
|
val userId: String = "UiAutoTest_${Random.nextLong()}"
|
||||||
val password: String = "password"
|
val password: String = "password"
|
||||||
val homeServerUrl: String = "http://10.0.2.2:8080"
|
val homeServerUrl: String = "http://10.0.2.2:8080"
|
||||||
|
|
||||||
|
|
|
@ -48,6 +48,7 @@ import org.junit.Rule
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.runner.RunWith
|
import org.junit.runner.RunWith
|
||||||
import org.matrix.android.sdk.api.session.Session
|
import org.matrix.android.sdk.api.session.Session
|
||||||
|
import kotlin.random.Random
|
||||||
|
|
||||||
@RunWith(AndroidJUnit4::class)
|
@RunWith(AndroidJUnit4::class)
|
||||||
@LargeTest
|
@LargeTest
|
||||||
|
@ -61,7 +62,7 @@ class SecurityBootstrapTest : VerificationTestBase() {
|
||||||
@Before
|
@Before
|
||||||
fun createSessionWithCrossSigning() {
|
fun createSessionWithCrossSigning() {
|
||||||
val matrix = getMatrixInstance()
|
val matrix = getMatrixInstance()
|
||||||
val userName = "foobar_${System.currentTimeMillis()}"
|
val userName = "foobar_${Random.nextLong()}"
|
||||||
existingSession = createAccountAndSync(matrix, userName, password, true)
|
existingSession = createAccountAndSync(matrix, userName, password, true)
|
||||||
stubAllExternalIntents()
|
stubAllExternalIntents()
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,6 +53,7 @@ import org.matrix.android.sdk.api.session.crypto.verification.VerificationTransa
|
||||||
import org.matrix.android.sdk.api.session.crypto.verification.VerificationTxState
|
import org.matrix.android.sdk.api.session.crypto.verification.VerificationTxState
|
||||||
import kotlin.coroutines.Continuation
|
import kotlin.coroutines.Continuation
|
||||||
import kotlin.coroutines.resume
|
import kotlin.coroutines.resume
|
||||||
|
import kotlin.random.Random
|
||||||
|
|
||||||
@RunWith(AndroidJUnit4::class)
|
@RunWith(AndroidJUnit4::class)
|
||||||
@LargeTest
|
@LargeTest
|
||||||
|
@ -66,7 +67,7 @@ class VerifySessionInteractiveTest : VerificationTestBase() {
|
||||||
@Before
|
@Before
|
||||||
fun createSessionWithCrossSigning() {
|
fun createSessionWithCrossSigning() {
|
||||||
val matrix = getMatrixInstance()
|
val matrix = getMatrixInstance()
|
||||||
val userName = "foobar_${System.currentTimeMillis()}"
|
val userName = "foobar_${Random.nextLong()}"
|
||||||
existingSession = createAccountAndSync(matrix, userName, password, true)
|
existingSession = createAccountAndSync(matrix, userName, password, true)
|
||||||
doSync<Unit> {
|
doSync<Unit> {
|
||||||
existingSession!!.cryptoService().crossSigningService()
|
existingSession!!.cryptoService().crossSigningService()
|
||||||
|
|
|
@ -53,6 +53,7 @@ import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse
|
||||||
import org.matrix.android.sdk.api.session.Session
|
import org.matrix.android.sdk.api.session.Session
|
||||||
import kotlin.coroutines.Continuation
|
import kotlin.coroutines.Continuation
|
||||||
import kotlin.coroutines.resume
|
import kotlin.coroutines.resume
|
||||||
|
import kotlin.random.Random
|
||||||
|
|
||||||
@RunWith(AndroidJUnit4::class)
|
@RunWith(AndroidJUnit4::class)
|
||||||
@LargeTest
|
@LargeTest
|
||||||
|
@ -68,7 +69,7 @@ class VerifySessionPassphraseTest : VerificationTestBase() {
|
||||||
fun createSessionWithCrossSigningAnd4S() {
|
fun createSessionWithCrossSigningAnd4S() {
|
||||||
val context = InstrumentationRegistry.getInstrumentation().targetContext
|
val context = InstrumentationRegistry.getInstrumentation().targetContext
|
||||||
val matrix = getMatrixInstance()
|
val matrix = getMatrixInstance()
|
||||||
val userName = "foobar_${System.currentTimeMillis()}"
|
val userName = "foobar_${Random.nextLong()}"
|
||||||
existingSession = createAccountAndSync(matrix, userName, password, true)
|
existingSession = createAccountAndSync(matrix, userName, password, true)
|
||||||
doSync<Unit> {
|
doSync<Unit> {
|
||||||
existingSession!!.cryptoService().crossSigningService()
|
existingSession!!.cryptoService().crossSigningService()
|
||||||
|
|
|
@ -24,6 +24,7 @@ import android.os.Build
|
||||||
import android.os.Environment
|
import android.os.Environment
|
||||||
import android.provider.MediaStore
|
import android.provider.MediaStore
|
||||||
import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
|
import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
|
||||||
|
import im.vector.app.core.time.DefaultClock
|
||||||
import org.junit.rules.TestWatcher
|
import org.junit.rules.TestWatcher
|
||||||
import org.junit.runner.Description
|
import org.junit.runner.Description
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
@ -54,7 +55,7 @@ private fun storeFailureScreenshot(bitmap: Bitmap, screenshotName: String) {
|
||||||
|
|
||||||
val contentValues = ContentValues().apply {
|
val contentValues = ContentValues().apply {
|
||||||
put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg")
|
put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg")
|
||||||
put(MediaStore.Images.Media.DATE_TAKEN, System.currentTimeMillis())
|
put(MediaStore.Images.Media.DATE_TAKEN, DefaultClock().epochMillis())
|
||||||
}
|
}
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||||
useMediaStoreScreenshotStorage(
|
useMediaStoreScreenshotStorage(
|
||||||
|
|
|
@ -29,6 +29,7 @@ import im.vector.app.R
|
||||||
import im.vector.app.core.di.ActiveSessionHolder
|
import im.vector.app.core.di.ActiveSessionHolder
|
||||||
import im.vector.app.core.extensions.registerStartForActivityResult
|
import im.vector.app.core.extensions.registerStartForActivityResult
|
||||||
import im.vector.app.core.platform.VectorBaseActivity
|
import im.vector.app.core.platform.VectorBaseActivity
|
||||||
|
import im.vector.app.core.time.Clock
|
||||||
import im.vector.app.core.utils.PERMISSIONS_FOR_TAKING_PHOTO
|
import im.vector.app.core.utils.PERMISSIONS_FOR_TAKING_PHOTO
|
||||||
import im.vector.app.core.utils.checkPermissions
|
import im.vector.app.core.utils.checkPermissions
|
||||||
import im.vector.app.core.utils.registerForPermissionsResult
|
import im.vector.app.core.utils.registerForPermissionsResult
|
||||||
|
@ -59,6 +60,8 @@ class DebugMenuActivity : VectorBaseActivity<ActivityDebugMenuBinding>() {
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var activeSessionHolder: ActiveSessionHolder
|
lateinit var activeSessionHolder: ActiveSessionHolder
|
||||||
|
@Inject
|
||||||
|
lateinit var clock: Clock
|
||||||
|
|
||||||
private lateinit var buffer: ByteArray
|
private lateinit var buffer: ByteArray
|
||||||
|
|
||||||
|
@ -165,7 +168,7 @@ class DebugMenuActivity : VectorBaseActivity<ActivityDebugMenuBinding>() {
|
||||||
}
|
}
|
||||||
|
|
||||||
val builder = NotificationCompat.Builder(this, "CHAN")
|
val builder = NotificationCompat.Builder(this, "CHAN")
|
||||||
.setWhen(System.currentTimeMillis())
|
.setWhen(clock.epochMillis())
|
||||||
.setContentTitle("Title")
|
.setContentTitle("Title")
|
||||||
.setContentText("Content")
|
.setContentText("Content")
|
||||||
// No effect because it's a group summary notif
|
// No effect because it's a group summary notif
|
||||||
|
@ -180,16 +183,16 @@ class DebugMenuActivity : VectorBaseActivity<ActivityDebugMenuBinding>() {
|
||||||
.setName("User name")
|
.setName("User name")
|
||||||
.build()
|
.build()
|
||||||
)
|
)
|
||||||
.addMessage("Message 1 - 1", System.currentTimeMillis(), Person.Builder().setName("user 1-1").build())
|
.addMessage("Message 1 - 1", clock.epochMillis(), Person.Builder().setName("user 1-1").build())
|
||||||
.addMessage("Message 1 - 2", System.currentTimeMillis(), Person.Builder().setName("user 1-2").build())
|
.addMessage("Message 1 - 2", clock.epochMillis(), Person.Builder().setName("user 1-2").build())
|
||||||
|
|
||||||
val messagingStyle2 = NotificationCompat.MessagingStyle(
|
val messagingStyle2 = NotificationCompat.MessagingStyle(
|
||||||
Person.Builder()
|
Person.Builder()
|
||||||
.setName("User name 2")
|
.setName("User name 2")
|
||||||
.build()
|
.build()
|
||||||
)
|
)
|
||||||
.addMessage("Message 2 - 1", System.currentTimeMillis(), Person.Builder().setName("user 1-1").build())
|
.addMessage("Message 2 - 1", clock.epochMillis(), Person.Builder().setName("user 1-1").build())
|
||||||
.addMessage("Message 2 - 2", System.currentTimeMillis(), Person.Builder().setName("user 1-2").build())
|
.addMessage("Message 2 - 2", clock.epochMillis(), Person.Builder().setName("user 1-2").build())
|
||||||
|
|
||||||
notificationManager.notify(10, builder.build())
|
notificationManager.notify(10, builder.build())
|
||||||
|
|
||||||
|
@ -197,7 +200,7 @@ class DebugMenuActivity : VectorBaseActivity<ActivityDebugMenuBinding>() {
|
||||||
11,
|
11,
|
||||||
NotificationCompat.Builder(this, "CHAN")
|
NotificationCompat.Builder(this, "CHAN")
|
||||||
.setChannelId("CHAN")
|
.setChannelId("CHAN")
|
||||||
.setWhen(System.currentTimeMillis())
|
.setWhen(clock.epochMillis())
|
||||||
.setContentTitle("Title 1")
|
.setContentTitle("Title 1")
|
||||||
.setContentText("Content 1")
|
.setContentText("Content 1")
|
||||||
// For shortcut on long press on launcher icon
|
// For shortcut on long press on launcher icon
|
||||||
|
@ -211,7 +214,7 @@ class DebugMenuActivity : VectorBaseActivity<ActivityDebugMenuBinding>() {
|
||||||
notificationManager.notify(
|
notificationManager.notify(
|
||||||
12,
|
12,
|
||||||
NotificationCompat.Builder(this, "CHAN2")
|
NotificationCompat.Builder(this, "CHAN2")
|
||||||
.setWhen(System.currentTimeMillis())
|
.setWhen(clock.epochMillis())
|
||||||
.setContentTitle("Title 2")
|
.setContentTitle("Title 2")
|
||||||
.setContentText("Content 2")
|
.setContentText("Content 2")
|
||||||
.setStyle(messagingStyle2)
|
.setStyle(messagingStyle2)
|
||||||
|
|
|
@ -18,13 +18,17 @@ package im.vector.app.fdroid
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import im.vector.app.core.di.ActiveSessionHolder
|
import im.vector.app.core.di.ActiveSessionHolder
|
||||||
|
import im.vector.app.core.time.Clock
|
||||||
import im.vector.app.fdroid.receiver.AlarmSyncBroadcastReceiver
|
import im.vector.app.fdroid.receiver.AlarmSyncBroadcastReceiver
|
||||||
import im.vector.app.features.settings.BackgroundSyncMode
|
import im.vector.app.features.settings.BackgroundSyncMode
|
||||||
import im.vector.app.features.settings.VectorPreferences
|
import im.vector.app.features.settings.VectorPreferences
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
|
||||||
object BackgroundSyncStarter {
|
object BackgroundSyncStarter {
|
||||||
fun start(context: Context, vectorPreferences: VectorPreferences, activeSessionHolder: ActiveSessionHolder) {
|
fun start(context: Context,
|
||||||
|
vectorPreferences: VectorPreferences,
|
||||||
|
activeSessionHolder: ActiveSessionHolder,
|
||||||
|
clock: Clock) {
|
||||||
if (vectorPreferences.areNotificationEnabledForDevice()) {
|
if (vectorPreferences.areNotificationEnabledForDevice()) {
|
||||||
val activeSession = activeSessionHolder.getSafeActiveSession() ?: return
|
val activeSession = activeSessionHolder.getSafeActiveSession() ?: return
|
||||||
when (vectorPreferences.getFdroidSyncBackgroundMode()) {
|
when (vectorPreferences.getFdroidSyncBackgroundMode()) {
|
||||||
|
@ -38,7 +42,12 @@ object BackgroundSyncStarter {
|
||||||
}
|
}
|
||||||
BackgroundSyncMode.FDROID_BACKGROUND_SYNC_MODE_FOR_REALTIME -> {
|
BackgroundSyncMode.FDROID_BACKGROUND_SYNC_MODE_FOR_REALTIME -> {
|
||||||
// We need to use alarm in this mode
|
// We need to use alarm in this mode
|
||||||
AlarmSyncBroadcastReceiver.scheduleAlarm(context, activeSession.sessionId, vectorPreferences.backgroundSyncDelay())
|
AlarmSyncBroadcastReceiver.scheduleAlarm(
|
||||||
|
context,
|
||||||
|
activeSession.sessionId,
|
||||||
|
vectorPreferences.backgroundSyncDelay(),
|
||||||
|
clock
|
||||||
|
)
|
||||||
Timber.i("## Sync: Alarm scheduled to start syncing")
|
Timber.i("## Sync: Alarm scheduled to start syncing")
|
||||||
}
|
}
|
||||||
BackgroundSyncMode.FDROID_BACKGROUND_SYNC_MODE_DISABLED -> {
|
BackgroundSyncMode.FDROID_BACKGROUND_SYNC_MODE_DISABLED -> {
|
||||||
|
|
|
@ -27,6 +27,7 @@ import androidx.core.content.getSystemService
|
||||||
import im.vector.app.core.extensions.singletonEntryPoint
|
import im.vector.app.core.extensions.singletonEntryPoint
|
||||||
import im.vector.app.core.platform.PendingIntentCompat
|
import im.vector.app.core.platform.PendingIntentCompat
|
||||||
import im.vector.app.core.services.VectorSyncService
|
import im.vector.app.core.services.VectorSyncService
|
||||||
|
import im.vector.app.core.time.Clock
|
||||||
import org.matrix.android.sdk.api.session.sync.job.SyncService
|
import org.matrix.android.sdk.api.session.sync.job.SyncService
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
|
||||||
|
@ -34,10 +35,13 @@ class AlarmSyncBroadcastReceiver : BroadcastReceiver() {
|
||||||
|
|
||||||
override fun onReceive(context: Context, intent: Intent) {
|
override fun onReceive(context: Context, intent: Intent) {
|
||||||
Timber.d("## Sync: AlarmSyncBroadcastReceiver received intent")
|
Timber.d("## Sync: AlarmSyncBroadcastReceiver received intent")
|
||||||
val vectorPreferences = context.singletonEntryPoint()
|
val singletonEntryPoint = context.singletonEntryPoint()
|
||||||
.takeIf { it.activeSessionHolder().getSafeActiveSession() != null }
|
if (singletonEntryPoint.activeSessionHolder().getSafeActiveSession() == null) {
|
||||||
?.vectorPreferences()
|
Timber.v("No active session, so don't launch sync service.")
|
||||||
?: return Unit.also { Timber.v("No active session, so don't launch sync service.") }
|
return
|
||||||
|
}
|
||||||
|
val vectorPreferences = singletonEntryPoint.vectorPreferences()
|
||||||
|
val clock = singletonEntryPoint.clock()
|
||||||
|
|
||||||
val sessionId = intent.getStringExtra(SyncService.EXTRA_SESSION_ID) ?: return
|
val sessionId = intent.getStringExtra(SyncService.EXTRA_SESSION_ID) ?: return
|
||||||
VectorSyncService.newPeriodicIntent(
|
VectorSyncService.newPeriodicIntent(
|
||||||
|
@ -52,7 +56,7 @@ class AlarmSyncBroadcastReceiver : BroadcastReceiver() {
|
||||||
ContextCompat.startForegroundService(context, it)
|
ContextCompat.startForegroundService(context, it)
|
||||||
} catch (ex: Throwable) {
|
} catch (ex: Throwable) {
|
||||||
Timber.i("## Sync: Failed to start service, Alarm scheduled to restart service")
|
Timber.i("## Sync: Failed to start service, Alarm scheduled to restart service")
|
||||||
scheduleAlarm(context, sessionId, vectorPreferences.backgroundSyncDelay())
|
scheduleAlarm(context, sessionId, vectorPreferences.backgroundSyncDelay(), clock)
|
||||||
Timber.e(ex)
|
Timber.e(ex)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -61,7 +65,7 @@ class AlarmSyncBroadcastReceiver : BroadcastReceiver() {
|
||||||
companion object {
|
companion object {
|
||||||
private const val REQUEST_CODE = 0
|
private const val REQUEST_CODE = 0
|
||||||
|
|
||||||
fun scheduleAlarm(context: Context, sessionId: String, delayInSeconds: Int) {
|
fun scheduleAlarm(context: Context, sessionId: String, delayInSeconds: Int, clock: Clock) {
|
||||||
// Reschedule
|
// Reschedule
|
||||||
Timber.v("## Sync: Scheduling alarm for background sync in $delayInSeconds seconds")
|
Timber.v("## Sync: Scheduling alarm for background sync in $delayInSeconds seconds")
|
||||||
val intent = Intent(context, AlarmSyncBroadcastReceiver::class.java).apply {
|
val intent = Intent(context, AlarmSyncBroadcastReceiver::class.java).apply {
|
||||||
|
@ -74,7 +78,7 @@ class AlarmSyncBroadcastReceiver : BroadcastReceiver() {
|
||||||
intent,
|
intent,
|
||||||
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE
|
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE
|
||||||
)
|
)
|
||||||
val firstMillis = System.currentTimeMillis() + delayInSeconds * 1000L
|
val firstMillis = clock.epochMillis() + delayInSeconds * 1000L
|
||||||
val alarmMgr = context.getSystemService<AlarmManager>()!!
|
val alarmMgr = context.getSystemService<AlarmManager>()!!
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||||
alarmMgr.setAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, firstMillis, pIntent)
|
alarmMgr.setAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, firstMillis, pIntent)
|
||||||
|
|
|
@ -32,7 +32,8 @@ class OnApplicationUpgradeOrRebootReceiver : BroadcastReceiver() {
|
||||||
BackgroundSyncStarter.start(
|
BackgroundSyncStarter.start(
|
||||||
context,
|
context,
|
||||||
singletonEntryPoint.vectorPreferences(),
|
singletonEntryPoint.vectorPreferences(),
|
||||||
singletonEntryPoint.activeSessionHolder()
|
singletonEntryPoint.activeSessionHolder(),
|
||||||
|
singletonEntryPoint.clock()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@ import android.app.Activity
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import im.vector.app.core.di.ActiveSessionHolder
|
import im.vector.app.core.di.ActiveSessionHolder
|
||||||
import im.vector.app.core.pushers.PushersManager
|
import im.vector.app.core.pushers.PushersManager
|
||||||
|
import im.vector.app.core.time.Clock
|
||||||
import im.vector.app.fdroid.BackgroundSyncStarter
|
import im.vector.app.fdroid.BackgroundSyncStarter
|
||||||
import im.vector.app.fdroid.receiver.AlarmSyncBroadcastReceiver
|
import im.vector.app.fdroid.receiver.AlarmSyncBroadcastReceiver
|
||||||
import im.vector.app.features.settings.VectorPreferences
|
import im.vector.app.features.settings.VectorPreferences
|
||||||
|
@ -66,7 +67,10 @@ object FcmHelper {
|
||||||
AlarmSyncBroadcastReceiver.cancelAlarm(context)
|
AlarmSyncBroadcastReceiver.cancelAlarm(context)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onEnterBackground(context: Context, vectorPreferences: VectorPreferences, activeSessionHolder: ActiveSessionHolder) {
|
fun onEnterBackground(context: Context,
|
||||||
BackgroundSyncStarter.start(context, vectorPreferences, activeSessionHolder)
|
vectorPreferences: VectorPreferences,
|
||||||
|
activeSessionHolder: ActiveSessionHolder,
|
||||||
|
clock: Clock) {
|
||||||
|
BackgroundSyncStarter.start(context, vectorPreferences, activeSessionHolder, clock)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,7 @@ import im.vector.app.R
|
||||||
import im.vector.app.core.di.ActiveSessionHolder
|
import im.vector.app.core.di.ActiveSessionHolder
|
||||||
import im.vector.app.core.di.DefaultSharedPreferences
|
import im.vector.app.core.di.DefaultSharedPreferences
|
||||||
import im.vector.app.core.pushers.PushersManager
|
import im.vector.app.core.pushers.PushersManager
|
||||||
|
import im.vector.app.core.time.Clock
|
||||||
import im.vector.app.features.settings.VectorPreferences
|
import im.vector.app.features.settings.VectorPreferences
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
|
||||||
|
@ -107,7 +108,10 @@ object FcmHelper {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("UNUSED_PARAMETER")
|
@Suppress("UNUSED_PARAMETER")
|
||||||
fun onEnterBackground(context: Context, vectorPreferences: VectorPreferences, activeSessionHolder: ActiveSessionHolder) {
|
fun onEnterBackground(context: Context,
|
||||||
|
vectorPreferences: VectorPreferences,
|
||||||
|
activeSessionHolder: ActiveSessionHolder,
|
||||||
|
clock: Clock) {
|
||||||
// No op
|
// No op
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,6 +43,7 @@ import dagger.hilt.android.HiltAndroidApp
|
||||||
import im.vector.app.core.di.ActiveSessionHolder
|
import im.vector.app.core.di.ActiveSessionHolder
|
||||||
import im.vector.app.core.extensions.configureAndStart
|
import im.vector.app.core.extensions.configureAndStart
|
||||||
import im.vector.app.core.extensions.startSyncing
|
import im.vector.app.core.extensions.startSyncing
|
||||||
|
import im.vector.app.core.time.Clock
|
||||||
import im.vector.app.features.analytics.VectorAnalytics
|
import im.vector.app.features.analytics.VectorAnalytics
|
||||||
import im.vector.app.features.call.webrtc.WebRtcCallManager
|
import im.vector.app.features.call.webrtc.WebRtcCallManager
|
||||||
import im.vector.app.features.configuration.VectorConfiguration
|
import im.vector.app.features.configuration.VectorConfiguration
|
||||||
|
@ -85,6 +86,7 @@ class VectorApplication :
|
||||||
@Inject lateinit var emojiCompatWrapper: EmojiCompatWrapper
|
@Inject lateinit var emojiCompatWrapper: EmojiCompatWrapper
|
||||||
@Inject lateinit var vectorUncaughtExceptionHandler: VectorUncaughtExceptionHandler
|
@Inject lateinit var vectorUncaughtExceptionHandler: VectorUncaughtExceptionHandler
|
||||||
@Inject lateinit var activeSessionHolder: ActiveSessionHolder
|
@Inject lateinit var activeSessionHolder: ActiveSessionHolder
|
||||||
|
@Inject lateinit var clock: Clock
|
||||||
@Inject lateinit var notificationDrawerManager: NotificationDrawerManager
|
@Inject lateinit var notificationDrawerManager: NotificationDrawerManager
|
||||||
@Inject lateinit var vectorPreferences: VectorPreferences
|
@Inject lateinit var vectorPreferences: VectorPreferences
|
||||||
@Inject lateinit var versionProvider: VersionProvider
|
@Inject lateinit var versionProvider: VersionProvider
|
||||||
|
@ -180,7 +182,7 @@ class VectorApplication :
|
||||||
|
|
||||||
override fun onPause(owner: LifecycleOwner) {
|
override fun onPause(owner: LifecycleOwner) {
|
||||||
Timber.i("App entered background")
|
Timber.i("App entered background")
|
||||||
FcmHelper.onEnterBackground(appContext, vectorPreferences, activeSessionHolder)
|
FcmHelper.onEnterBackground(appContext, vectorPreferences, activeSessionHolder, clock)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
ProcessLifecycleOwner.get().lifecycle.addObserver(appStateHandler)
|
ProcessLifecycleOwner.get().lifecycle.addObserver(appStateHandler)
|
||||||
|
|
|
@ -22,15 +22,19 @@ import android.text.format.DateUtils
|
||||||
import im.vector.app.core.resources.DateProvider
|
import im.vector.app.core.resources.DateProvider
|
||||||
import im.vector.app.core.resources.LocaleProvider
|
import im.vector.app.core.resources.LocaleProvider
|
||||||
import im.vector.app.core.resources.toTimestamp
|
import im.vector.app.core.resources.toTimestamp
|
||||||
|
import im.vector.app.core.time.Clock
|
||||||
import org.threeten.bp.LocalDateTime
|
import org.threeten.bp.LocalDateTime
|
||||||
import org.threeten.bp.Period
|
import org.threeten.bp.Period
|
||||||
import org.threeten.bp.format.DateTimeFormatter
|
import org.threeten.bp.format.DateTimeFormatter
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import kotlin.math.absoluteValue
|
import kotlin.math.absoluteValue
|
||||||
|
|
||||||
class VectorDateFormatter @Inject constructor(private val context: Context,
|
class VectorDateFormatter @Inject constructor(
|
||||||
|
private val context: Context,
|
||||||
private val localeProvider: LocaleProvider,
|
private val localeProvider: LocaleProvider,
|
||||||
private val dateFormatterProviders: DateFormatterProviders) {
|
private val dateFormatterProviders: DateFormatterProviders,
|
||||||
|
private val clock: Clock,
|
||||||
|
) {
|
||||||
|
|
||||||
private val hourFormatter by lazy {
|
private val hourFormatter by lazy {
|
||||||
if (DateFormat.is24HourFormat(context)) {
|
if (DateFormat.is24HourFormat(context)) {
|
||||||
|
@ -158,8 +162,9 @@ class VectorDateFormatter @Inject constructor(private val context: Context,
|
||||||
private fun getRelativeDay(ts: Long): String {
|
private fun getRelativeDay(ts: Long): String {
|
||||||
return DateUtils.getRelativeTimeSpanString(
|
return DateUtils.getRelativeTimeSpanString(
|
||||||
ts,
|
ts,
|
||||||
System.currentTimeMillis(),
|
clock.epochMillis(),
|
||||||
DateUtils.DAY_IN_MILLIS,
|
DateUtils.DAY_IN_MILLIS,
|
||||||
DateUtils.FORMAT_SHOW_WEEKDAY).toString()
|
DateUtils.FORMAT_SHOW_WEEKDAY
|
||||||
|
).toString()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@ import dagger.hilt.InstallIn
|
||||||
import dagger.hilt.components.SingletonComponent
|
import dagger.hilt.components.SingletonComponent
|
||||||
import im.vector.app.core.dialogs.UnrecognizedCertificateDialog
|
import im.vector.app.core.dialogs.UnrecognizedCertificateDialog
|
||||||
import im.vector.app.core.error.ErrorFormatter
|
import im.vector.app.core.error.ErrorFormatter
|
||||||
|
import im.vector.app.core.time.Clock
|
||||||
import im.vector.app.features.analytics.AnalyticsTracker
|
import im.vector.app.features.analytics.AnalyticsTracker
|
||||||
import im.vector.app.features.call.webrtc.WebRtcCallManager
|
import im.vector.app.features.call.webrtc.WebRtcCallManager
|
||||||
import im.vector.app.features.home.AvatarRenderer
|
import im.vector.app.features.home.AvatarRenderer
|
||||||
|
@ -46,6 +47,8 @@ interface SingletonEntryPoint {
|
||||||
|
|
||||||
fun navigator(): Navigator
|
fun navigator(): Navigator
|
||||||
|
|
||||||
|
fun clock(): Clock
|
||||||
|
|
||||||
fun errorFormatter(): ErrorFormatter
|
fun errorFormatter(): ErrorFormatter
|
||||||
|
|
||||||
fun bugReporter(): BugReporter
|
fun bugReporter(): BugReporter
|
||||||
|
|
|
@ -27,6 +27,7 @@ import im.vector.app.core.dialogs.GalleryOrCameraDialogHelper.Listener
|
||||||
import im.vector.app.core.extensions.insertBeforeLast
|
import im.vector.app.core.extensions.insertBeforeLast
|
||||||
import im.vector.app.core.extensions.registerStartForActivityResult
|
import im.vector.app.core.extensions.registerStartForActivityResult
|
||||||
import im.vector.app.core.resources.ColorProvider
|
import im.vector.app.core.resources.ColorProvider
|
||||||
|
import im.vector.app.core.time.Clock
|
||||||
import im.vector.app.core.utils.PERMISSIONS_FOR_TAKING_PHOTO
|
import im.vector.app.core.utils.PERMISSIONS_FOR_TAKING_PHOTO
|
||||||
import im.vector.app.core.utils.checkPermissions
|
import im.vector.app.core.utils.checkPermissions
|
||||||
import im.vector.app.core.utils.onPermissionDeniedDialog
|
import im.vector.app.core.utils.onPermissionDeniedDialog
|
||||||
|
@ -45,7 +46,8 @@ import java.io.File
|
||||||
class GalleryOrCameraDialogHelper(
|
class GalleryOrCameraDialogHelper(
|
||||||
// must implement GalleryOrCameraDialogHelper.Listener
|
// must implement GalleryOrCameraDialogHelper.Listener
|
||||||
private val fragment: Fragment,
|
private val fragment: Fragment,
|
||||||
private val colorProvider: ColorProvider
|
private val colorProvider: ColorProvider,
|
||||||
|
private val clock: Clock,
|
||||||
) {
|
) {
|
||||||
interface Listener {
|
interface Listener {
|
||||||
fun onImageReady(uri: Uri?)
|
fun onImageReady(uri: Uri?)
|
||||||
|
@ -91,7 +93,7 @@ class GalleryOrCameraDialogHelper(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun startUCrop(image: MultiPickerImageType) {
|
private fun startUCrop(image: MultiPickerImageType) {
|
||||||
val destinationFile = File(activity.cacheDir, image.displayName.insertBeforeLast("_e_${System.currentTimeMillis()}"))
|
val destinationFile = File(activity.cacheDir, image.displayName.insertBeforeLast("_e_${clock.epochMillis()}"))
|
||||||
val uri = image.contentUri
|
val uri = image.contentUri
|
||||||
createUCropWithDefaultSettings(colorProvider, uri, destinationFile.toUri(), fragment.getString(R.string.rotate_and_crop_screen_title))
|
createUCropWithDefaultSettings(colorProvider, uri, destinationFile.toUri(), fragment.getString(R.string.rotate_and_crop_screen_title))
|
||||||
.withAspectRatio(1f, 1f)
|
.withAspectRatio(1f, 1f)
|
||||||
|
|
|
@ -33,6 +33,8 @@ import androidx.work.WorkerParameters
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import im.vector.app.R
|
import im.vector.app.R
|
||||||
import im.vector.app.core.platform.PendingIntentCompat
|
import im.vector.app.core.platform.PendingIntentCompat
|
||||||
|
import im.vector.app.core.time.Clock
|
||||||
|
import im.vector.app.core.time.DefaultClock
|
||||||
import im.vector.app.features.notifications.NotificationUtils
|
import im.vector.app.features.notifications.NotificationUtils
|
||||||
import im.vector.app.features.settings.BackgroundSyncMode
|
import im.vector.app.features.settings.BackgroundSyncMode
|
||||||
import org.matrix.android.sdk.api.Matrix
|
import org.matrix.android.sdk.api.Matrix
|
||||||
|
@ -77,6 +79,7 @@ class VectorSyncService : SyncService() {
|
||||||
|
|
||||||
@Inject lateinit var notificationUtils: NotificationUtils
|
@Inject lateinit var notificationUtils: NotificationUtils
|
||||||
@Inject lateinit var matrix: Matrix
|
@Inject lateinit var matrix: Matrix
|
||||||
|
@Inject lateinit var clock: Clock
|
||||||
|
|
||||||
override fun provideMatrix() = matrix
|
override fun provideMatrix() = matrix
|
||||||
|
|
||||||
|
@ -102,7 +105,8 @@ class VectorSyncService : SyncService() {
|
||||||
syncTimeoutSeconds = syncTimeoutSeconds,
|
syncTimeoutSeconds = syncTimeoutSeconds,
|
||||||
syncDelaySeconds = syncDelaySeconds,
|
syncDelaySeconds = syncDelaySeconds,
|
||||||
isPeriodic = true,
|
isPeriodic = true,
|
||||||
isNetworkBack = false
|
isNetworkBack = false,
|
||||||
|
currentTimeMillis = clock.epochMillis()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -114,7 +118,8 @@ class VectorSyncService : SyncService() {
|
||||||
val rescheduleSyncWorkRequest: WorkRequest =
|
val rescheduleSyncWorkRequest: WorkRequest =
|
||||||
OneTimeWorkRequestBuilder<RestartWhenNetworkOn>()
|
OneTimeWorkRequestBuilder<RestartWhenNetworkOn>()
|
||||||
.setInputData(RestartWhenNetworkOn.createInputData(sessionId, syncTimeoutSeconds, syncDelaySeconds, isPeriodic))
|
.setInputData(RestartWhenNetworkOn.createInputData(sessionId, syncTimeoutSeconds, syncDelaySeconds, isPeriodic))
|
||||||
.setConstraints(Constraints.Builder()
|
.setConstraints(
|
||||||
|
Constraints.Builder()
|
||||||
.setRequiredNetworkType(NetworkType.CONNECTED)
|
.setRequiredNetworkType(NetworkType.CONNECTED)
|
||||||
.build()
|
.build()
|
||||||
)
|
)
|
||||||
|
@ -137,20 +142,27 @@ class VectorSyncService : SyncService() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// I do not move or rename this class, since I'm not sure about the side effect regarding the WorkManager
|
// I do not move or rename this class, since I'm not sure about the side effect regarding the WorkManager
|
||||||
class RestartWhenNetworkOn(appContext: Context, workerParams: WorkerParameters) :
|
class RestartWhenNetworkOn(
|
||||||
Worker(appContext, workerParams) {
|
appContext: Context,
|
||||||
|
workerParams: WorkerParameters
|
||||||
|
) : Worker(appContext, workerParams) {
|
||||||
|
|
||||||
override fun doWork(): Result {
|
override fun doWork(): Result {
|
||||||
Timber.d("## Sync: RestartWhenNetworkOn.doWork()")
|
Timber.d("## Sync: RestartWhenNetworkOn.doWork()")
|
||||||
val sessionId = inputData.getString(KEY_SESSION_ID) ?: return Result.failure()
|
val sessionId = inputData.getString(KEY_SESSION_ID) ?: return Result.failure()
|
||||||
val syncTimeoutSeconds = inputData.getInt(KEY_SYNC_TIMEOUT_SECONDS, BackgroundSyncMode.DEFAULT_SYNC_TIMEOUT_SECONDS)
|
val syncTimeoutSeconds = inputData.getInt(KEY_SYNC_TIMEOUT_SECONDS, BackgroundSyncMode.DEFAULT_SYNC_TIMEOUT_SECONDS)
|
||||||
val syncDelaySeconds = inputData.getInt(KEY_SYNC_DELAY_SECONDS, BackgroundSyncMode.DEFAULT_SYNC_DELAY_SECONDS)
|
val syncDelaySeconds = inputData.getInt(KEY_SYNC_DELAY_SECONDS, BackgroundSyncMode.DEFAULT_SYNC_DELAY_SECONDS)
|
||||||
val isPeriodic = inputData.getBoolean(KEY_IS_PERIODIC, false)
|
val isPeriodic = inputData.getBoolean(KEY_IS_PERIODIC, false)
|
||||||
|
|
||||||
|
// Not sure how to inject a Clock here
|
||||||
|
val clock = DefaultClock()
|
||||||
applicationContext.rescheduleSyncService(
|
applicationContext.rescheduleSyncService(
|
||||||
sessionId = sessionId,
|
sessionId = sessionId,
|
||||||
syncTimeoutSeconds = syncTimeoutSeconds,
|
syncTimeoutSeconds = syncTimeoutSeconds,
|
||||||
syncDelaySeconds = syncDelaySeconds,
|
syncDelaySeconds = syncDelaySeconds,
|
||||||
isPeriodic = isPeriodic,
|
isPeriodic = isPeriodic,
|
||||||
isNetworkBack = true
|
isNetworkBack = true,
|
||||||
|
currentTimeMillis = clock.epochMillis()
|
||||||
)
|
)
|
||||||
// Indicate whether the work finished successfully with the Result
|
// Indicate whether the work finished successfully with the Result
|
||||||
return Result.success()
|
return Result.success()
|
||||||
|
@ -182,7 +194,8 @@ private fun Context.rescheduleSyncService(sessionId: String,
|
||||||
syncTimeoutSeconds: Int,
|
syncTimeoutSeconds: Int,
|
||||||
syncDelaySeconds: Int,
|
syncDelaySeconds: Int,
|
||||||
isPeriodic: Boolean,
|
isPeriodic: Boolean,
|
||||||
isNetworkBack: Boolean) {
|
isNetworkBack: Boolean,
|
||||||
|
currentTimeMillis: Long) {
|
||||||
Timber.d("## Sync: rescheduleSyncService")
|
Timber.d("## Sync: rescheduleSyncService")
|
||||||
val intent = if (isPeriodic) {
|
val intent = if (isPeriodic) {
|
||||||
VectorSyncService.newPeriodicIntent(
|
VectorSyncService.newPeriodicIntent(
|
||||||
|
@ -208,7 +221,7 @@ private fun Context.rescheduleSyncService(sessionId: String,
|
||||||
} else {
|
} else {
|
||||||
PendingIntent.getService(this, 0, intent, PendingIntentCompat.FLAG_IMMUTABLE)
|
PendingIntent.getService(this, 0, intent, PendingIntentCompat.FLAG_IMMUTABLE)
|
||||||
}
|
}
|
||||||
val firstMillis = System.currentTimeMillis() + syncDelaySeconds * 1000L
|
val firstMillis = currentTimeMillis + syncDelaySeconds * 1000L
|
||||||
val alarmMgr = getSystemService<AlarmManager>()!!
|
val alarmMgr = getSystemService<AlarmManager>()!!
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||||
alarmMgr.setAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, firstMillis, pendingIntent)
|
alarmMgr.setAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, firstMillis, pendingIntent)
|
||||||
|
|
|
@ -147,8 +147,11 @@ fun openFileSelection(activity: Activity,
|
||||||
* Send an email to address with optional subject and message
|
* Send an email to address with optional subject and message
|
||||||
*/
|
*/
|
||||||
fun sendMailTo(address: String, subject: String? = null, message: String? = null, activity: Activity) {
|
fun sendMailTo(address: String, subject: String? = null, message: String? = null, activity: Activity) {
|
||||||
val intent = Intent(Intent.ACTION_SENDTO, Uri.fromParts(
|
val intent = Intent(
|
||||||
"mailto", address, null))
|
Intent.ACTION_SENDTO, Uri.fromParts(
|
||||||
|
"mailto", address, null
|
||||||
|
)
|
||||||
|
)
|
||||||
intent.putExtra(Intent.EXTRA_SUBJECT, subject)
|
intent.putExtra(Intent.EXTRA_SUBJECT, subject)
|
||||||
intent.putExtra(Intent.EXTRA_TEXT, message)
|
intent.putExtra(Intent.EXTRA_TEXT, message)
|
||||||
|
|
||||||
|
@ -248,7 +251,12 @@ private fun appendTimeToFilename(name: String): String {
|
||||||
return """${filename}_$dateExtension.$fileExtension"""
|
return """${filename}_$dateExtension.$fileExtension"""
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun saveMedia(context: Context, file: File, title: String, mediaMimeType: String?, notificationUtils: NotificationUtils) {
|
suspend fun saveMedia(context: Context,
|
||||||
|
file: File,
|
||||||
|
title: String,
|
||||||
|
mediaMimeType: String?,
|
||||||
|
notificationUtils: NotificationUtils,
|
||||||
|
currentTimeMillis: Long) {
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||||
val filename = appendTimeToFilename(title)
|
val filename = appendTimeToFilename(title)
|
||||||
|
@ -257,8 +265,8 @@ suspend fun saveMedia(context: Context, file: File, title: String, mediaMimeType
|
||||||
put(MediaStore.Images.Media.TITLE, filename)
|
put(MediaStore.Images.Media.TITLE, filename)
|
||||||
put(MediaStore.Images.Media.DISPLAY_NAME, filename)
|
put(MediaStore.Images.Media.DISPLAY_NAME, filename)
|
||||||
put(MediaStore.Images.Media.MIME_TYPE, mediaMimeType)
|
put(MediaStore.Images.Media.MIME_TYPE, mediaMimeType)
|
||||||
put(MediaStore.Images.Media.DATE_ADDED, System.currentTimeMillis())
|
put(MediaStore.Images.Media.DATE_ADDED, currentTimeMillis)
|
||||||
put(MediaStore.Images.Media.DATE_TAKEN, System.currentTimeMillis())
|
put(MediaStore.Images.Media.DATE_TAKEN, currentTimeMillis)
|
||||||
}
|
}
|
||||||
val externalContentUri = when {
|
val externalContentUri = when {
|
||||||
mediaMimeType?.isMimeTypeImage() == true -> MediaStore.Images.Media.EXTERNAL_CONTENT_URI
|
mediaMimeType?.isMimeTypeImage() == true -> MediaStore.Images.Media.EXTERNAL_CONTENT_URI
|
||||||
|
@ -289,7 +297,7 @@ suspend fun saveMedia(context: Context, file: File, title: String, mediaMimeType
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
saveMediaLegacy(context, mediaMimeType, title, file)
|
saveMediaLegacy(context, mediaMimeType, title, file, currentTimeMillis)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -298,7 +306,8 @@ suspend fun saveMedia(context: Context, file: File, title: String, mediaMimeType
|
||||||
private fun saveMediaLegacy(context: Context,
|
private fun saveMediaLegacy(context: Context,
|
||||||
mediaMimeType: String?,
|
mediaMimeType: String?,
|
||||||
title: String,
|
title: String,
|
||||||
file: File) {
|
file: File,
|
||||||
|
currentTimeMillis: Long) {
|
||||||
val state = Environment.getExternalStorageState()
|
val state = Environment.getExternalStorageState()
|
||||||
if (Environment.MEDIA_MOUNTED != state) {
|
if (Environment.MEDIA_MOUNTED != state) {
|
||||||
context.toast(context.getString(R.string.error_saving_media_file))
|
context.toast(context.getString(R.string.error_saving_media_file))
|
||||||
|
@ -319,7 +328,7 @@ private fun saveMediaLegacy(context: Context,
|
||||||
} else {
|
} else {
|
||||||
title
|
title
|
||||||
}
|
}
|
||||||
val savedFile = saveFileIntoLegacy(file, downloadDir, outputFilename)
|
val savedFile = saveFileIntoLegacy(file, downloadDir, outputFilename, currentTimeMillis)
|
||||||
if (savedFile != null) {
|
if (savedFile != null) {
|
||||||
val downloadManager = context.getSystemService<DownloadManager>()
|
val downloadManager = context.getSystemService<DownloadManager>()
|
||||||
downloadManager?.addCompletedDownload(
|
downloadManager?.addCompletedDownload(
|
||||||
|
@ -329,7 +338,8 @@ private fun saveMediaLegacy(context: Context,
|
||||||
mediaMimeType ?: MimeTypes.OctetStream,
|
mediaMimeType ?: MimeTypes.OctetStream,
|
||||||
savedFile.absolutePath,
|
savedFile.absolutePath,
|
||||||
savedFile.length(),
|
savedFile.length(),
|
||||||
true)
|
true
|
||||||
|
)
|
||||||
addToGallery(savedFile, mediaMimeType, context)
|
addToGallery(savedFile, mediaMimeType, context)
|
||||||
}
|
}
|
||||||
} catch (error: Throwable) {
|
} catch (error: Throwable) {
|
||||||
|
@ -408,10 +418,11 @@ fun selectTxtFileToWrite(
|
||||||
* @param sourceFile the file source path
|
* @param sourceFile the file source path
|
||||||
* @param dstDirPath the dst path
|
* @param dstDirPath the dst path
|
||||||
* @param outputFilename optional the output filename
|
* @param outputFilename optional the output filename
|
||||||
|
* @param currentTimeMillis the current time in milliseconds
|
||||||
* @return the created file
|
* @return the created file
|
||||||
*/
|
*/
|
||||||
@Suppress("DEPRECATION")
|
@Suppress("DEPRECATION")
|
||||||
fun saveFileIntoLegacy(sourceFile: File, dstDirPath: File, outputFilename: String?): File? {
|
fun saveFileIntoLegacy(sourceFile: File, dstDirPath: File, outputFilename: String?, currentTimeMillis: Long): File? {
|
||||||
// defines another name for the external media
|
// defines another name for the external media
|
||||||
var dstFileName: String
|
var dstFileName: String
|
||||||
|
|
||||||
|
@ -423,7 +434,7 @@ fun saveFileIntoLegacy(sourceFile: File, dstDirPath: File, outputFilename: Strin
|
||||||
if (dotPos > 0) {
|
if (dotPos > 0) {
|
||||||
fileExt = sourceFile.name.substring(dotPos)
|
fileExt = sourceFile.name.substring(dotPos)
|
||||||
}
|
}
|
||||||
dstFileName = "vector_" + System.currentTimeMillis() + fileExt
|
dstFileName = "vector_$currentTimeMillis$fileExt"
|
||||||
} else {
|
} else {
|
||||||
dstFileName = outputFilename
|
dstFileName = outputFilename
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,6 +45,7 @@ import im.vector.app.core.extensions.insertBeforeLast
|
||||||
import im.vector.app.core.extensions.registerStartForActivityResult
|
import im.vector.app.core.extensions.registerStartForActivityResult
|
||||||
import im.vector.app.core.platform.VectorBaseFragment
|
import im.vector.app.core.platform.VectorBaseFragment
|
||||||
import im.vector.app.core.resources.ColorProvider
|
import im.vector.app.core.resources.ColorProvider
|
||||||
|
import im.vector.app.core.time.Clock
|
||||||
import im.vector.app.core.utils.OnSnapPositionChangeListener
|
import im.vector.app.core.utils.OnSnapPositionChangeListener
|
||||||
import im.vector.app.core.utils.SnapOnScrollListener
|
import im.vector.app.core.utils.SnapOnScrollListener
|
||||||
import im.vector.app.core.utils.attachSnapHelperWithListener
|
import im.vector.app.core.utils.attachSnapHelperWithListener
|
||||||
|
@ -64,7 +65,8 @@ data class AttachmentsPreviewArgs(
|
||||||
class AttachmentsPreviewFragment @Inject constructor(
|
class AttachmentsPreviewFragment @Inject constructor(
|
||||||
private val attachmentMiniaturePreviewController: AttachmentMiniaturePreviewController,
|
private val attachmentMiniaturePreviewController: AttachmentMiniaturePreviewController,
|
||||||
private val attachmentBigPreviewController: AttachmentBigPreviewController,
|
private val attachmentBigPreviewController: AttachmentBigPreviewController,
|
||||||
private val colorProvider: ColorProvider
|
private val colorProvider: ColorProvider,
|
||||||
|
private val clock: Clock,
|
||||||
) : VectorBaseFragment<FragmentAttachmentsPreviewBinding>(), AttachmentMiniaturePreviewController.Callback {
|
) : VectorBaseFragment<FragmentAttachmentsPreviewBinding>(), AttachmentMiniaturePreviewController.Callback {
|
||||||
|
|
||||||
private val fragmentArgs: AttachmentsPreviewArgs by args()
|
private val fragmentArgs: AttachmentsPreviewArgs by args()
|
||||||
|
@ -192,7 +194,7 @@ class AttachmentsPreviewFragment @Inject constructor(
|
||||||
|
|
||||||
private fun handleEditAction() = withState(viewModel) {
|
private fun handleEditAction() = withState(viewModel) {
|
||||||
val currentAttachment = it.attachments.getOrNull(it.currentAttachmentIndex) ?: return@withState
|
val currentAttachment = it.attachments.getOrNull(it.currentAttachmentIndex) ?: return@withState
|
||||||
val destinationFile = File(requireContext().cacheDir, currentAttachment.name.insertBeforeLast("_edited_image_${System.currentTimeMillis()}"))
|
val destinationFile = File(requireContext().cacheDir, currentAttachment.name.insertBeforeLast("_edited_image_${clock.epochMillis()}"))
|
||||||
val uri = currentAttachment.queryUri
|
val uri = currentAttachment.queryUri
|
||||||
createUCropWithDefaultSettings(colorProvider, uri, destinationFile.toUri(), currentAttachment.name)
|
createUCropWithDefaultSettings(colorProvider, uri, destinationFile.toUri(), currentAttachment.name)
|
||||||
.getIntent(requireContext())
|
.getIntent(requireContext())
|
||||||
|
|
|
@ -19,6 +19,7 @@ package im.vector.app.features.call.conference
|
||||||
import im.vector.app.R
|
import im.vector.app.R
|
||||||
import im.vector.app.core.network.await
|
import im.vector.app.core.network.await
|
||||||
import im.vector.app.core.resources.StringProvider
|
import im.vector.app.core.resources.StringProvider
|
||||||
|
import im.vector.app.core.time.Clock
|
||||||
import im.vector.app.core.utils.ensureProtocol
|
import im.vector.app.core.utils.ensureProtocol
|
||||||
import im.vector.app.core.utils.toBase32String
|
import im.vector.app.core.utils.toBase32String
|
||||||
import im.vector.app.features.call.conference.jwt.JitsiJWTFactory
|
import im.vector.app.features.call.conference.jwt.JitsiJWTFactory
|
||||||
|
@ -46,7 +47,9 @@ class JitsiService @Inject constructor(
|
||||||
private val rawService: RawService,
|
private val rawService: RawService,
|
||||||
private val stringProvider: StringProvider,
|
private val stringProvider: StringProvider,
|
||||||
private val themeProvider: ThemeProvider,
|
private val themeProvider: ThemeProvider,
|
||||||
private val jitsiJWTFactory: JitsiJWTFactory) {
|
private val jitsiJWTFactory: JitsiJWTFactory,
|
||||||
|
private val clock: Clock,
|
||||||
|
) {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val JITSI_OPEN_ID_TOKEN_JWT_AUTH = "openidtoken-jwt"
|
const val JITSI_OPEN_ID_TOKEN_JWT_AUTH = "openidtoken-jwt"
|
||||||
|
@ -60,7 +63,7 @@ class JitsiService @Inject constructor(
|
||||||
|
|
||||||
suspend fun createJitsiWidget(roomId: String, withVideo: Boolean): Widget {
|
suspend fun createJitsiWidget(roomId: String, withVideo: Boolean): Widget {
|
||||||
// Build data for a jitsi widget
|
// Build data for a jitsi widget
|
||||||
val widgetId: String = WidgetType.Jitsi.preferred + "_" + session.myUserId + "_" + System.currentTimeMillis()
|
val widgetId: String = WidgetType.Jitsi.preferred + "_" + session.myUserId + "_" + clock.epochMillis()
|
||||||
val preferredJitsiDomain = tryOrNull {
|
val preferredJitsiDomain = tryOrNull {
|
||||||
rawService.getElementWellknown(session.sessionParams)
|
rawService.getElementWellknown(session.sessionParams)
|
||||||
?.jitsiServer
|
?.jitsiServer
|
||||||
|
|
|
@ -21,6 +21,7 @@ import android.os.Binder
|
||||||
import android.os.IBinder
|
import android.os.IBinder
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import im.vector.app.core.services.VectorService
|
import im.vector.app.core.services.VectorService
|
||||||
|
import im.vector.app.core.time.Clock
|
||||||
import im.vector.app.features.notifications.NotificationUtils
|
import im.vector.app.features.notifications.NotificationUtils
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@ -28,6 +29,7 @@ import javax.inject.Inject
|
||||||
class ScreenCaptureService : VectorService() {
|
class ScreenCaptureService : VectorService() {
|
||||||
|
|
||||||
@Inject lateinit var notificationUtils: NotificationUtils
|
@Inject lateinit var notificationUtils: NotificationUtils
|
||||||
|
@Inject lateinit var clock: Clock
|
||||||
private val binder = LocalBinder()
|
private val binder = LocalBinder()
|
||||||
|
|
||||||
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
||||||
|
@ -37,7 +39,7 @@ class ScreenCaptureService : VectorService() {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun showStickyNotification() {
|
private fun showStickyNotification() {
|
||||||
val notificationId = System.currentTimeMillis().toInt()
|
val notificationId = clock.epochMillis().toInt()
|
||||||
val notification = notificationUtils.buildScreenSharingNotification()
|
val notification = notificationUtils.buildScreenSharingNotification()
|
||||||
startForeground(notificationId, notification)
|
startForeground(notificationId, notification)
|
||||||
}
|
}
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue