Merge pull request #5907 from vector-im/feature/bma/currentTimeMillis

Use Clock interface
This commit is contained in:
Benoit Marty 2022-05-04 18:27:04 +02:00 committed by GitHub
commit 330d802079
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
134 changed files with 1109 additions and 610 deletions

1
changelog.d/5907.sdk Normal file
View file

@ -0,0 +1 @@
Replace usage of `System.currentTimeMillis()` by a `Clock` interface

View file

@ -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
) )

View file

@ -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
)
)
} }
} }

View file

@ -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(),
) )
} }
} }

View file

@ -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

View file

@ -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(

View file

@ -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)

View file

@ -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)
} }
} }

View file

@ -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
) )
} }
} }

View file

@ -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
) )
} }
} }

View file

@ -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
) )
} }
} }

View file

@ -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
} }
} }

View file

@ -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 ->

View file

@ -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

View file

@ -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 olmDevice: MXOlmDevice, private val cryptoStore: IMXCryptoStore,
private val syncTokenStore: SyncTokenStore, private val olmDevice: MXOlmDevice,
private val credentials: Credentials, private val syncTokenStore: SyncTokenStore,
private val downloadKeysForUsersTask: DownloadKeysForUsersTask, private val credentials: Credentials,
private val cryptoSessionInfoProvider: CryptoSessionInfoProvider, private val downloadKeysForUsersTask: DownloadKeysForUsersTask,
coroutineDispatchers: MatrixCoroutineDispatchers, private val cryptoSessionInfoProvider: CryptoSessionInfoProvider,
private val taskExecutor: TaskExecutor) { coroutineDispatchers: MatrixCoroutineDispatchers,
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(
deviceKeys.deviceId + " has changed : " + "## CRYPTO | validateDeviceKeys() : WARNING:Ed25519 key for device " + userId + ":" +
previouslyStoredDeviceKeys.fingerprint() + " -> " + signKey) deviceKeys.deviceId + " has changed : " +
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}")

View file

@ -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) {

View file

@ -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

View file

@ -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)
} }
} }

View file

@ -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,40 +311,40 @@ 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()
// 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.
// noticed as dklen/hlen
val prf = Mac.getInstance("HmacSHA512")
prf.init(SecretKeySpec(password.toByteArray(Charsets.UTF_8), "HmacSHA512"))
// 512 bits key length
val key = ByteArray(64) val key = ByteArray(64)
val uc = ByteArray(64) measureTimeMillis {
// 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.
// noticed as dklen/hlen
val prf = Mac.getInstance("HmacSHA512")
prf.init(SecretKeySpec(password.toByteArray(Charsets.UTF_8), "HmacSHA512"))
// U1 = PRF(Password, Salt || INT_32_BE(i)) // 512 bits key length
prf.update(salt) val uc = ByteArray(64)
val int32BE = ByteArray(4) { 0.toByte() }
int32BE[3] = 1.toByte()
prf.update(int32BE)
prf.doFinal(uc, 0)
// copy to the key // U1 = PRF(Password, Salt || INT_32_BE(i))
System.arraycopy(uc, 0, key, 0, uc.size) prf.update(salt)
val int32BE = ByteArray(4) { 0.toByte() }
for (index in 2..iterations) { int32BE[3] = 1.toByte()
// Uc = PRF(Password, Uc-1) prf.update(int32BE)
prf.update(uc)
prf.doFinal(uc, 0) prf.doFinal(uc, 0)
// F(Password, Salt, c, i) = U1 ^ U2 ^ ... ^ Uc // copy to the key
for (byteIndex in uc.indices) { System.arraycopy(uc, 0, key, 0, uc.size)
key[byteIndex] = key[byteIndex] xor uc[byteIndex]
}
}
Timber.v("## deriveKeys() : $iterations in ${System.currentTimeMillis() - t0} ms") for (index in 2..iterations) {
// Uc = PRF(Password, Uc-1)
prf.update(uc)
prf.doFinal(uc, 0)
// F(Password, Salt, c, i) = U1 ^ U2 ^ ... ^ Uc
for (byteIndex in uc.indices) {
key[byteIndex] = key[byteIndex] xor uc[byteIndex]
}
}
}.also {
Timber.v("## deriveKeys() : $iterations in $it ms")
}
return key return key
} }

View file

@ -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
) )
} }

View file

@ -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)) {

View file

@ -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 ->

View file

@ -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 {

View file

@ -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 roomDecryptorProvider: RoomDecryptorProvider, private val olmDevice: MXOlmDevice,
private val outgoingGossipingRequestManager: OutgoingGossipingRequestManager, private val roomDecryptorProvider: RoomDecryptorProvider,
private val cryptoStore: IMXCryptoStore) { private val outgoingGossipingRequestManager: OutgoingGossipingRequestManager,
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)")

View file

@ -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,

View file

@ -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,
) )
} }
} }

View file

@ -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")

View file

@ -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) {

View file

@ -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,52 +92,53 @@ 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
// dklen = 256
// hlen = 512
val prf = Mac.getInstance("HmacSHA512")
prf.init(SecretKeySpec(password.toByteArray(), "HmacSHA512"))
// 256 bits key length // 256 bits key length
val dk = ByteArray(32) val dk = ByteArray(32)
val uc = ByteArray(64)
// U1 = PRF(Password, Salt || INT_32_BE(i)) with i goes from 1 to dklen/hlen measureTimeMillis {
prf.update(salt.toByteArray()) // dklen = 256
val int32BE = byteArrayOf(0, 0, 0, 1) // hlen = 512
prf.update(int32BE) val prf = Mac.getInstance("HmacSHA512")
prf.doFinal(uc, 0)
// copy to the key prf.init(SecretKeySpec(password.toByteArray(), "HmacSHA512"))
System.arraycopy(uc, 0, dk, 0, dk.size)
var lastProgress = -1 val uc = ByteArray(64)
for (index in 2..iterations) { // U1 = PRF(Password, Salt || INT_32_BE(i)) with i goes from 1 to dklen/hlen
// Uc = PRF(Password, Uc-1) prf.update(salt.toByteArray())
prf.update(uc) val int32BE = byteArrayOf(0, 0, 0, 1)
prf.update(int32BE)
prf.doFinal(uc, 0) prf.doFinal(uc, 0)
// F(Password, Salt, c, i) = U1 ^ U2 ^ ... ^ Uc // copy to the key
for (byteIndex in dk.indices) { System.arraycopy(uc, 0, dk, 0, dk.size)
dk[byteIndex] = dk[byteIndex] xor uc[byteIndex]
}
val progress = (index + 1) * 100 / iterations var lastProgress = -1
if (progress != lastProgress) {
lastProgress = progress for (index in 2..iterations) {
progressListener?.onProgress(lastProgress, 100) // Uc = PRF(Password, Uc-1)
prf.update(uc)
prf.doFinal(uc, 0)
// F(Password, Salt, c, i) = U1 ^ U2 ^ ... ^ Uc
for (byteIndex in dk.indices) {
dk[byteIndex] = dk[byteIndex] xor uc[byteIndex]
}
val progress = (index + 1) * 100 / iterations
if (progress != lastProgress) {
lastProgress = progress
progressListener?.onProgress(lastProgress, 100)
}
} }
}.also {
Timber.v("KeysBackupPassword: deriveKeys() : $iterations in $it ms")
} }
Timber.v("KeysBackupPassword: deriveKeys() : " + iterations + " in " + (System.currentTimeMillis() - t0) + " ms")
return dk return dk
} }

View file

@ -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
} }
} }

View file

@ -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(
@ -1326,7 +1335,7 @@ internal class RealmCryptoStore @Inject constructor(
.findAll() .findAll()
.mapNotNull { entity -> .mapNotNull { entity ->
when (entity.type) { when (entity.type) {
GossipRequestType.KEY -> { GossipRequestType.KEY -> {
IncomingRoomKeyRequest( IncomingRoomKeyRequest(
userId = entity.otherUserId, userId = entity.otherUserId,
deviceId = entity.otherDeviceId, deviceId = entity.otherDeviceId,
@ -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

View file

@ -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()

View file

@ -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)

View file

@ -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
// ) // )
// //

View file

@ -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(
cancelConclusion = safeValueOf(cancelReq.code) existingRequest.copy(
)) 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(
readyInfo = readyReq existingRequest.copy(
)) 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(
transactionId = syncedId, verificationRequest.copy(
// localId stays different transactionId = syncedId,
requestInfo = info // localId stays different
)) 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(
// localId stays different verificationRequest.copy(
requestInfo = info // localId stays different
)) 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(
cancelConclusion = CancelCode.User it.copy(
)) 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)

View file

@ -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")
} }

View file

@ -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(
sessionId = sessionId, SendVerificationMessageWorker.Params(
eventId = event.eventId ?: "" sessionId = sessionId,
)) 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(
sessionId = sessionId, SendVerificationMessageWorker.Params(
eventId = event.eventId ?: "" sessionId = sessionId,
)) 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(
sessionId = sessionId, SendVerificationMessageWorker.Params(
eventId = event.eventId ?: "" sessionId = sessionId,
)) 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(
sessionId = sessionId, SendVerificationMessageWorker.Params(
eventId = event.eventId ?: "" sessionId = sessionId,
)) 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,

View file

@ -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
)
} }
} }

View file

@ -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 {

View file

@ -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)
} }
} }

View file

@ -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) {

View file

@ -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)
} }
} }

View file

@ -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

View file

@ -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
) )
} }
} }

View file

@ -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 mxCallFactory: MxCallFactory, private val activeCallHandler: ActiveCallHandler,
@UserId private val userId: String) { private val mxCallFactory: MxCallFactory,
@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.")

View file

@ -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

View file

@ -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,

View file

@ -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,

View file

@ -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(
mxcUrl = mxcUrl, ScanEncryptedTask.Params(
publicServerKey = getServerPublicKey(false), mxcUrl = mxcUrl,
encryptedInfo = fileInfo publicServerKey = getServerPublicKey(false),
)) 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()
) )
} }

View file

@ -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
} }
} }

View file

@ -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
} }
} }

View file

@ -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
) )

View file

@ -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) {

View file

@ -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

View file

@ -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)
} }

View file

@ -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) {

View file

@ -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 eventFactory: LocalEchoEventFactory, private val eventSenderProcessor: EventSenderProcessor,
private val localEchoRepository: LocalEchoRepository) { private val eventFactory: LocalEchoEventFactory,
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

View file

@ -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

View file

@ -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)
} }

View file

@ -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()
} }
/** /**

View file

@ -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(
private val taskExecutor: TaskExecutor, @SessionDatabase private val monarchy: Monarchy,
private val realmSessionProvider: RealmSessionProvider, private val taskExecutor: TaskExecutor,
private val roomSummaryUpdater: RoomSummaryUpdater, private val realmSessionProvider: RealmSessionProvider,
private val timelineInput: TimelineInput, private val roomSummaryUpdater: RoomSummaryUpdater,
private val timelineEventMapper: TimelineEventMapper) { private val timelineInput: TimelineInput,
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) {

View file

@ -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)
} }

View file

@ -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()
} }
} }

View file

@ -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()

View file

@ -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() {

View file

@ -43,28 +43,32 @@ 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 initialEventId: String?, private val roomId: String,
private val realmConfiguration: RealmConfiguration, private val initialEventId: String?,
private val loadRoomMembersTask: LoadRoomMembersTask, private val realmConfiguration: RealmConfiguration,
private val readReceiptHandler: ReadReceiptHandler, private val loadRoomMembersTask: LoadRoomMembersTask,
private val settings: TimelineSettings, private val readReceiptHandler: ReadReceiptHandler,
private val coroutineDispatchers: MatrixCoroutineDispatchers, private val settings: TimelineSettings,
paginationTask: PaginationTask, private val coroutineDispatchers: MatrixCoroutineDispatchers,
getEventTask: GetContextOfEventTask, private val clock: Clock,
fetchTokenAndPaginateTask: FetchTokenAndPaginateTask, paginationTask: PaginationTask,
fetchThreadTimelineTask: FetchThreadTimelineTask, getEventTask: GetContextOfEventTask,
timelineEventMapper: TimelineEventMapper, fetchTokenAndPaginateTask: FetchTokenAndPaginateTask,
timelineInput: TimelineInput, fetchThreadTimelineTask: FetchThreadTimelineTask,
threadsAwarenessHandler: ThreadsAwarenessHandler, timelineEventMapper: TimelineEventMapper,
lightweightSettingsStorage: LightweightSettingsStorage, timelineInput: TimelineInput,
eventDecryptor: TimelineEventDecryptor) : Timeline { threadsAwarenessHandler: ThreadsAwarenessHandler,
lightweightSettingsStorage: LightweightSettingsStorage,
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,
) )
} }

View file

@ -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,8 +51,9 @@ 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,
) : TimelineService { private val clock: Clock,
) : TimelineService {
@AssistedFactory @AssistedFactory
interface Factory { interface Factory {
@ -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
) )
} }

View file

@ -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
} }

View file

@ -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,

View file

@ -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 }

View file

@ -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 }

View file

@ -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

View file

@ -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(
rooms = nbRooms, SyncStatusService.Status.IncrementalSyncParsing(
toDevice = nbToDevice rooms = nbRooms,
)) 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)

View file

@ -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()
) )
) )
) )

View file

@ -79,22 +79,26 @@ 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 roomSummaryUpdater: RoomSummaryUpdater, private val readReceiptHandler: ReadReceiptHandler,
private val roomAccountDataHandler: RoomSyncAccountDataHandler, private val roomSummaryUpdater: RoomSummaryUpdater,
private val cryptoService: DefaultCryptoService, private val roomAccountDataHandler: RoomSyncAccountDataHandler,
private val roomMemberEventHandler: RoomMemberEventHandler, private val cryptoService: DefaultCryptoService,
private val roomTypingUsersHandler: RoomTypingUsersHandler, private val roomMemberEventHandler: RoomMemberEventHandler,
private val threadsAwarenessHandler: ThreadsAwarenessHandler, private val roomTypingUsersHandler: RoomTypingUsersHandler,
private val roomChangeMembershipStateDataSource: RoomChangeMembershipStateDataSource, private val threadsAwarenessHandler: ThreadsAwarenessHandler,
@UserId private val userId: String, private val roomChangeMembershipStateDataSource: RoomChangeMembershipStateDataSource,
private val homeServerCapabilitiesService: HomeServerCapabilitiesService, @UserId private val userId: String,
private val lightweightSettingsStorage: LightweightSettingsStorage, private val homeServerCapabilitiesService: HomeServerCapabilitiesService,
private val timelineInput: TimelineInput, private val lightweightSettingsStorage: LightweightSettingsStorage,
private val liveEventService: Lazy<StreamEventsManager>) { private val timelineInput: TimelineInput,
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 {

View file

@ -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

View file

@ -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
} }

View file

@ -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()
}
}

View file

@ -173,4 +173,7 @@ PreferenceManager\.getDefaultSharedPreferences==2
# findViewById # findViewById
### 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

View file

@ -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 {

View file

@ -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"

View file

@ -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()
} }

View file

@ -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()

View file

@ -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()

View file

@ -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(

View file

@ -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)

View file

@ -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 -> {

View file

@ -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)

View file

@ -32,7 +32,8 @@ class OnApplicationUpgradeOrRebootReceiver : BroadcastReceiver() {
BackgroundSyncStarter.start( BackgroundSyncStarter.start(
context, context,
singletonEntryPoint.vectorPreferences(), singletonEntryPoint.vectorPreferences(),
singletonEntryPoint.activeSessionHolder() singletonEntryPoint.activeSessionHolder(),
singletonEntryPoint.clock()
) )
} }
} }

View file

@ -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)
} }
} }

View file

@ -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
} }
} }

View file

@ -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)

View file

@ -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 localeProvider: LocaleProvider, private val context: Context,
private val dateFormatterProviders: DateFormatterProviders) { private val localeProvider: LocaleProvider,
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()
} }
} }

View file

@ -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

View file

@ -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)

View file

@ -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,9 +118,10 @@ 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(
.setRequiredNetworkType(NetworkType.CONNECTED) Constraints.Builder()
.build() .setRequiredNetworkType(NetworkType.CONNECTED)
.build()
) )
.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)

View file

@ -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
} }

View file

@ -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())

View file

@ -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

View file

@ -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