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) {
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
@ -167,7 +168,8 @@ class CommonTestHelper(context: Context) {
if (rootThreadEventId != null) {
room.relationService().replyInThread(
rootThreadEventId = rootThreadEventId,
replyInThreadText = formattedMessage)
replyInThreadText = formattedMessage
)
} else {
room.sendService().sendTextMessage(formattedMessage)
}
@ -237,7 +239,7 @@ class CommonTestHelper(context: Context) {
password: String,
testParams: SessionTestParams): Session {
val session = createAccountAndSync(
userNamePrefix + "_" + System.currentTimeMillis() + UUID.randomUUID(),
userNamePrefix + "_" + accountNumber++ + "_" + UUID.randomUUID(),
password,
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.EncryptedFileKey
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.InputStream
import java.util.UUID
/**
* Unit tests AttachmentEncryptionTest.
@ -48,13 +50,18 @@ class AttachmentEncryptionTest {
inputStream = if (inputAsByteArray.isEmpty()) {
inputAsByteArray.inputStream()
} else {
val memoryFile = MemoryFile("file" + System.currentTimeMillis(), inputAsByteArray.size)
val memoryFile = MemoryFile("file_" + UUID.randomUUID(), inputAsByteArray.size)
memoryFile.outputStream.write(inputAsByteArray)
memoryFile.inputStream
}
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)
@ -117,9 +124,13 @@ class AttachmentEncryptionTest {
url = "dummyUrl"
)
assertEquals("YWxwaGFudW1lcmljYWxseWFscGhhbnVtZXJpY2FsbHlhbHBoYW51bWVyaWNhbGx5YWxwaGFudW1lcmljYWxseQ",
checkDecryption("zhtFStAeFx0s+9L/sSQO+WQMtldqYEHqTxMduJrCIpnkyer09kxJJuA4K+adQE4w+7jZe/vR9kIcqj9rOhDR8Q",
encryptedFileInfo))
assertEquals(
"YWxwaGFudW1lcmljYWxseWFscGhhbnVtZXJpY2FsbHlhbHBoYW51bWVyaWNhbGx5YWxwaGFudW1lcmljYWxseQ",
checkDecryption(
"zhtFStAeFx0s+9L/sSQO+WQMtldqYEHqTxMduJrCIpnkyer09kxJJuA4K+adQE4w+7jZe/vR9kIcqj9rOhDR8Q",
encryptedFileInfo
)
)
}
@Test
@ -138,8 +149,12 @@ class AttachmentEncryptionTest {
url = "dummyUrl"
)
assertNotEquals("YWxwaGFudW1lcmljYWxseWFscGhhbnVtZXJpY2FsbHlhbHBoYW51bWVyaWNhbGx5YWxwaGFudW1lcmljYWxseQ",
checkDecryption("tJVNBVJ/vl36UQt4Y5e5m84bRUrQHhcdLPvS/7EkDvlkDLZXamBB6k8THbiawiKZ5Mnq9PZMSSbgOCvmnUBOMA",
encryptedFileInfo))
assertNotEquals(
"YWxwaGFudW1lcmljYWxseWFscGhhbnVtZXJpY2FsbHlhbHBoYW51bWVyaWNhbGx5YWxwaGFudW1lcmljYWxseQ",
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.mapper.CrossSigningKeysMapper
import org.matrix.android.sdk.internal.di.MoshiProvider
import org.matrix.android.sdk.internal.util.time.DefaultClock
import kotlin.random.Random
internal class CryptoStoreHelper {
@ -34,7 +35,8 @@ internal class CryptoStoreHelper {
.build(),
crossSigningKeysMapper = CrossSigningKeysMapper(MoshiProvider.providesMoshi()),
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.internal.crypto.model.OlmSessionWrapper
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.OlmManager
import org.matrix.olm.OlmSession
@ -37,6 +38,7 @@ private const val DUMMY_DEVICE_KEY = "DeviceKey"
class CryptoStoreTest : InstrumentedTest {
private val cryptoStoreHelper = CryptoStoreHelper()
private val clock = DefaultClock()
@Before
fun setup() {
@ -106,7 +108,7 @@ class CryptoStoreTest : InstrumentedTest {
// 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)
// sessionId2 is returned now
@ -114,7 +116,7 @@ class CryptoStoreTest : InstrumentedTest {
Thread.sleep(2)
olmSessionWrapper1.onMessageReceived()
olmSessionWrapper1.onMessageReceived(clock.epochMillis())
cryptoStore.storeSession(olmSessionWrapper1, DUMMY_DEVICE_KEY)
// 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.SessionRealmModule
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.createFakeMessageEvent
@ -42,6 +43,7 @@ import org.matrix.android.sdk.session.room.timeline.RoomDataHelper.createFakeMes
internal class ChunkEntityTest : InstrumentedTest {
private lateinit var monarchy: Monarchy
private val clock = DefaultClock()
@Before
fun setup() {
@ -59,7 +61,7 @@ internal class ChunkEntityTest : InstrumentedTest {
monarchy.runTransactionSync { realm ->
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)
}
chunk.addTimelineEvent(
@ -75,7 +77,7 @@ internal class ChunkEntityTest : InstrumentedTest {
fun add_shouldNotAdd_whenAlreadyIncluded() {
monarchy.runTransactionSync { realm ->
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)
}
chunk.addTimelineEvent(
@ -153,7 +155,7 @@ internal class ChunkEntityTest : InstrumentedTest {
events: List<Event>,
direction: PaginationDirection) {
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)
}
addTimelineEvent(

View File

@ -26,8 +26,8 @@ internal class FakeGetContextOfEventTask constructor(private val tokenChunkEvent
override suspend fun execute(params: GetContextOfEventTask.Params): TokenChunkEventPersistor.Result {
val fakeEvents = RoomDataHelper.createFakeListOfEvents(30)
val tokenChunkEvent = FakeTokenChunkEvent(
Random.nextLong(System.currentTimeMillis()).toString(),
Random.nextLong(System.currentTimeMillis()).toString(),
Random.nextLong().toString(),
Random.nextLong().toString(),
fakeEvents
)
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 {
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)
}
}

View File

@ -46,8 +46,9 @@ data class IncomingRequestCancellation(
* Factory
*
* @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()
.toModel<ShareRequestCancellation>()
?.let {
@ -55,7 +56,7 @@ data class IncomingRequestCancellation(
userId = event.senderId,
deviceId = it.requestingDeviceId,
requestId = it.requestId,
localCreationTimestamp = event.ageLocalTs ?: System.currentTimeMillis()
localCreationTimestamp = event.ageLocalTs ?: currentTimeMillis
)
}
}

View File

@ -64,8 +64,9 @@ data class IncomingRoomKeyRequest(
* Factory
*
* @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()
.toModel<RoomKeyShareRequest>()
?.let {
@ -74,7 +75,7 @@ data class IncomingRoomKeyRequest(
deviceId = it.requestingDeviceId,
requestId = it.requestId,
requestBody = it.body ?: RoomKeyRequestBody(),
localCreationTimestamp = event.ageLocalTs ?: System.currentTimeMillis()
localCreationTimestamp = event.ageLocalTs ?: currentTimeMillis
)
}
}

View File

@ -64,8 +64,9 @@ data class IncomingSecretShareRequest(
* Factory
*
* @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()
.toModel<SecretShareRequest>()
?.let {
@ -74,7 +75,7 @@ data class IncomingSecretShareRequest(
deviceId = it.requestingDeviceId,
requestId = it.requestId,
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 FIVE_MINUTES_IN_MILLIS = 5 * 60 * 1000
fun isValidRequest(age: Long?): Boolean {
fun isValidRequest(age: Long?, currentTimeMillis: Long): Boolean {
if (age == null) return false
val now = System.currentTimeMillis()
val tooInThePast = now - TEN_MINUTES_IN_MILLIS
val tooInTheFuture = now + FIVE_MINUTES_IN_MILLIS
val tooInThePast = currentTimeMillis - TEN_MINUTES_IN_MILLIS
val tooInTheFuture = currentTimeMillis + FIVE_MINUTES_IN_MILLIS
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.createUniqueTxnId
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.SessionWorkerParams
import javax.inject.Inject
@ -65,6 +66,7 @@ internal class CancelGossipRequestWorker(context: Context, params: WorkerParamet
@Inject lateinit var sendToDeviceTask: SendToDeviceTask
@Inject lateinit var cryptoStore: IMXCryptoStore
@Inject lateinit var credentials: Credentials
@Inject lateinit var clock: Clock
override fun injectWith(injector: SessionComponent) {
injector.inject(this)
@ -85,7 +87,7 @@ internal class CancelGossipRequestWorker(context: Context, params: WorkerParamet
content = toDeviceContent.toContent(),
senderId = credentials.userId
).also {
it.ageLocalTs = System.currentTimeMillis()
it.ageLocalTs = clock.epochMillis()
})
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.launchToCallback
import org.matrix.android.sdk.internal.util.JsonCanonicalizer
import org.matrix.android.sdk.internal.util.time.Clock
import org.matrix.olm.OlmManager
import timber.log.Timber
import java.util.concurrent.atomic.AtomicBoolean
@ -130,6 +131,7 @@ internal class DefaultCryptoService @Inject constructor(
private val userId: String,
@DeviceId
private val deviceId: String?,
private val clock: Clock,
private val myDeviceInfoHolder: Lazy<MyDeviceInfoHolder>,
// the crypto store
private val cryptoStore: IMXCryptoStore,
@ -701,11 +703,11 @@ internal class DefaultCryptoService @Inject constructor(
}
val safeAlgorithm = alg
if (safeAlgorithm != null) {
val t0 = System.currentTimeMillis()
val t0 = clock.epochMillis()
Timber.tag(loggerTag.value).v("encryptEventContent() starts")
runCatching {
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)
}.foldToCallback(callback)
} else {
@ -1022,9 +1024,9 @@ internal class DefaultCryptoService @Inject constructor(
return withContext(coroutineDispatchers.crypto) {
Timber.tag(loggerTag.value).v("importRoomKeys starts")
val t0 = System.currentTimeMillis()
val t0 = clock.epochMillis()
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")
@ -1032,7 +1034,7 @@ internal class DefaultCryptoService @Inject constructor(
.adapter<List<MegolmSessionData>>(Types.newParameterizedType(List::class.java, MegolmSessionData::class.java))
.fromJson(roomKeys)
val t2 = System.currentTimeMillis()
val t2 = clock.epochMillis()
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 lastForcedDate = lastNewSessionForcedDates.getObject(senderId, deviceKey) ?: 0
// val now = System.currentTimeMillis()
// val now = clock.epochMillis()
// if (now - lastForcedDate < CRYPTO_MIN_FORCE_SESSION_PERIOD_MILLIS) {
// Timber.d("## CRYPTO | markOlmSessionForUnwedging: New session already forced with device at $lastForcedDate. Not forcing another")
// 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.task.TaskExecutor
import org.matrix.android.sdk.internal.util.logLimit
import org.matrix.android.sdk.internal.util.time.Clock
import timber.log.Timber
import javax.inject.Inject
// Legacy name: MXDeviceList
@SessionScope
internal class DeviceListManager @Inject constructor(private val cryptoStore: IMXCryptoStore,
private val olmDevice: MXOlmDevice,
private val syncTokenStore: SyncTokenStore,
private val credentials: Credentials,
private val downloadKeysForUsersTask: DownloadKeysForUsersTask,
private val cryptoSessionInfoProvider: CryptoSessionInfoProvider,
coroutineDispatchers: MatrixCoroutineDispatchers,
private val taskExecutor: TaskExecutor) {
internal class DeviceListManager @Inject constructor(
private val cryptoStore: IMXCryptoStore,
private val olmDevice: MXOlmDevice,
private val syncTokenStore: SyncTokenStore,
private val credentials: Credentials,
private val downloadKeysForUsersTask: DownloadKeysForUsersTask,
private val cryptoSessionInfoProvider: CryptoSessionInfoProvider,
coroutineDispatchers: MatrixCoroutineDispatchers,
private val taskExecutor: TaskExecutor,
private val clock: Clock,
) {
interface UserDevicesUpdateListener {
fun onUsersDeviceUpdate(userIds: List<String>)
@ -310,9 +314,9 @@ internal class DeviceListManager @Inject constructor(private val cryptoStore: IM
stored
} else {
Timber.v("## CRYPTO | downloadKeys() : starts")
val t0 = System.currentTimeMillis()
val t0 = clock.epochMillis()
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 {
it.addEntriesFromMap(stored)
}
@ -475,8 +479,10 @@ internal class DeviceListManager @Inject constructor(private val cryptoStore: IM
}
if (!isVerified) {
Timber.e("## CRYPTO | validateDeviceKeys() : Unable to verify signature on device " + userId + ":" +
deviceKeys.deviceId + " with error " + errorMessage)
Timber.e(
"## CRYPTO | validateDeviceKeys() : Unable to verify signature on device " + userId + ":" +
deviceKeys.deviceId + " with error " + errorMessage
)
return false
}
@ -486,9 +492,11 @@ internal class DeviceListManager @Inject constructor(private val cryptoStore: IM
// best off sticking with the original keys.
//
// Should we warn the user about it somehow?
Timber.e("## CRYPTO | validateDeviceKeys() : WARNING:Ed25519 key for device " + userId + ":" +
deviceKeys.deviceId + " has changed : " +
previouslyStoredDeviceKeys.fingerprint() + " -> " + signKey)
Timber.e(
"## CRYPTO | validateDeviceKeys() : WARNING:Ed25519 key for device " + userId + ":" +
deviceKeys.deviceId + " has changed : " +
previouslyStoredDeviceKeys.fingerprint() + " -> " + signKey
)
Timber.e("## CRYPTO | validateDeviceKeys() : $previouslyStoredDeviceKeys -> $deviceKeys")
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.extensions.foldToCallback
import org.matrix.android.sdk.internal.session.SessionScope
import org.matrix.android.sdk.internal.util.time.Clock
import timber.log.Timber
import javax.inject.Inject
@ -47,6 +48,7 @@ private val loggerTag = LoggerTag("CryptoSyncHandler", LoggerTag.CRYPTO)
internal class EventDecryptor @Inject constructor(
private val cryptoCoroutineScope: CoroutineScope,
private val coroutineDispatchers: MatrixCoroutineDispatchers,
private val clock: Clock,
private val roomDecryptorProvider: RoomDecryptorProvider,
private val messageEncrypter: MessageEncrypter,
private val sendToDeviceTask: SendToDeviceTask,
@ -153,7 +155,7 @@ internal class EventDecryptor @Inject constructor(
// we should force start a new session for those
Timber.tag(loggerTag.value).v("Unwedging: ${wedgedDevices.size} are wedged")
// get the one that should be retried according to rate limit
val now = System.currentTimeMillis()
val now = clock.epochMillis()
val toUnwedge = wedgedDevices.filter {
val lastForcedDate = lastNewSessionForcedDates[it] ?: 0
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.util.CancelableWork
import org.matrix.android.sdk.internal.worker.startChain
import java.util.UUID
import java.util.concurrent.TimeUnit
import javax.inject.Inject
@ -44,8 +45,8 @@ internal class GossipingWorkManager @Inject constructor(
}
// Prevent sending queue to stay broken after app restart
// The unique queue id will stay the same as long as this object is instanciated
val queueSuffixApp = System.currentTimeMillis()
// The unique queue id will stay the same as long as this object is instantiated
private val queueSuffixApp = UUID.randomUUID()
fun postWork(workRequest: OneTimeWorkRequest, policy: ExistingWorkPolicy = ExistingWorkPolicy.APPEND): Cancelable {
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.di.SessionId
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 timber.log.Timber
import java.util.concurrent.Executors
@ -59,7 +60,9 @@ internal class IncomingGossipingRequestManager @Inject constructor(
private val roomEncryptorsStore: RoomEncryptorsStore,
private val roomDecryptorProvider: RoomDecryptorProvider,
private val coroutineDispatchers: MatrixCoroutineDispatchers,
private val cryptoCoroutineScope: CoroutineScope) {
private val cryptoCoroutineScope: CoroutineScope,
private val clock: Clock,
) {
private val executor = Executors.newSingleThreadExecutor()
@ -89,7 +92,7 @@ internal class IncomingGossipingRequestManager @Inject constructor(
fun onVerificationCompleteForDevice(deviceId: String) {
// For now we just keep an in memory cache
synchronized(recentlyVerifiedDevices) {
recentlyVerifiedDevices[deviceId] = System.currentTimeMillis()
recentlyVerifiedDevices[deviceId] = clock.epochMillis()
}
}
@ -100,7 +103,7 @@ internal class IncomingGossipingRequestManager @Inject constructor(
}
if (verifTimestamp == null) return false
val age = System.currentTimeMillis() - verifTimestamp
val age = clock.epochMillis() - verifTimestamp
return age < FIVE_MINUTES_IN_MILLIS
}
@ -114,11 +117,11 @@ internal class IncomingGossipingRequestManager @Inject constructor(
fun onGossipingRequestEvent(event: Event) {
val roomKeyShare = event.getClearContent().toModel<GossipingDefaultContent>()
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) {
GossipingToDeviceObject.ACTION_SHARE_REQUEST -> {
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) {
// ignore, it was sent by me as *
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) {
IncomingRoomKeyRequest.fromEvent(event)?.let {
IncomingRoomKeyRequest.fromEvent(event, clock.epochMillis())?.let {
if (event.senderId == credentials.userId && it.deviceId == credentials.deviceId) {
// ignore, it was sent by me as *
Timber.v("## GOSSIP onGossipingRequestEvent type ${event.type} ignore remote echo")
@ -141,7 +144,7 @@ internal class IncomingGossipingRequestManager @Inject constructor(
}
}
GossipingToDeviceObject.ACTION_SHARE_CANCELLATION -> {
IncomingRequestCancellation.fromEvent(event)?.let {
IncomingRequestCancellation.fromEvent(event, clock.epochMillis())?.let {
receivedRequestCancellations.add(it)
}
}

View File

@ -29,6 +29,7 @@ import javax.crypto.spec.SecretKeySpec
import kotlin.experimental.and
import kotlin.experimental.xor
import kotlin.math.min
import kotlin.system.measureTimeMillis
/**
* Utility class to import/export the crypto data
@ -310,40 +311,40 @@ internal object MXMegolmExportEncryption {
*/
@Throws(Exception::class)
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 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))
prf.update(salt)
val int32BE = ByteArray(4) { 0.toByte() }
int32BE[3] = 1.toByte()
prf.update(int32BE)
prf.doFinal(uc, 0)
// 512 bits key length
val uc = ByteArray(64)
// copy to the key
System.arraycopy(uc, 0, key, 0, uc.size)
for (index in 2..iterations) {
// Uc = PRF(Password, Uc-1)
prf.update(uc)
// U1 = PRF(Password, Salt || INT_32_BE(i))
prf.update(salt)
val int32BE = ByteArray(4) { 0.toByte() }
int32BE[3] = 1.toByte()
prf.update(int32BE)
prf.doFinal(uc, 0)
// F(Password, Salt, c, i) = U1 ^ U2 ^ ... ^ Uc
for (byteIndex in uc.indices) {
key[byteIndex] = key[byteIndex] xor uc[byteIndex]
}
}
// copy to the key
System.arraycopy(uc, 0, key, 0, uc.size)
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
}

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.convertFromUTF8
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.OlmException
import org.matrix.olm.OlmMessage
@ -55,7 +56,8 @@ internal class MXOlmDevice @Inject constructor(
*/
private val store: IMXCryptoStore,
private val olmSessionStore: OlmSessionStore,
private val inboundGroupSessionStore: InboundGroupSessionStore
private val inboundGroupSessionStore: InboundGroupSessionStore,
private val clock: Clock,
) {
val mutex = Mutex()
@ -277,7 +279,7 @@ internal class MXOlmDevice @Inject constructor(
// Pretend we've received a message at this point, otherwise
// if we try to send a message to the device, it won't use
// this session
olmSessionWrapper.onMessageReceived()
olmSessionWrapper.onMessageReceived(clock.epochMillis())
olmSessionStore.storeSession(olmSessionWrapper, theirIdentityKey)
@ -348,7 +350,7 @@ internal class MXOlmDevice @Inject constructor(
val olmSessionWrapper = OlmSessionWrapper(olmSession, 0)
// This counts as a received message: set last received message time to now
olmSessionWrapper.onMessageReceived()
olmSessionWrapper.onMessageReceived(clock.epochMillis())
olmSessionStore.storeSession(olmSessionWrapper, theirDeviceIdentityKey)
} catch (e: Exception) {
@ -454,7 +456,7 @@ internal class MXOlmDevice @Inject constructor(
payloadString =
olmSessionWrapper.mutex.withLock {
olmSessionWrapper.olmSession.decryptMessage(olmMessage).also {
olmSessionWrapper.onMessageReceived()
olmSessionWrapper.onMessageReceived(clock.epochMillis())
}
}
olmSessionStore.storeSession(olmSessionWrapper, theirDeviceIdentityKey)
@ -520,6 +522,7 @@ internal class MXOlmDevice @Inject constructor(
return MXOutboundSessionInfo(
sessionId = sessionId,
sharedWithHelper = SharedWithHelper(roomId, sessionId, store),
clock,
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.session.SessionScope
import org.matrix.android.sdk.internal.util.JsonCanonicalizer
import org.matrix.android.sdk.internal.util.time.Clock
import org.matrix.olm.OlmAccount
import timber.log.Timber
import javax.inject.Inject
@ -38,6 +39,7 @@ internal class OneTimeKeysUploader @Inject constructor(
private val olmDevice: MXOlmDevice,
private val objectSigner: ObjectSigner,
private val uploadKeysTask: UploadKeysTask,
private val clock: Clock,
context: Context
) {
// tell if there is a OTK check in progress
@ -77,7 +79,7 @@ internal class OneTimeKeysUploader @Inject constructor(
Timber.v("maybeUploadOneTimeKeys: already in progress")
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.
Timber.v("maybeUploadOneTimeKeys: executed too recently")
return
@ -94,7 +96,7 @@ internal class OneTimeKeysUploader @Inject constructor(
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.
val maxOneTimeKeys = olmDevice.getMaxNumberOfOneTimeKeys()
@ -126,7 +128,7 @@ internal class OneTimeKeysUploader @Inject constructor(
// Check if we need to forget a fallback key
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
// that use the old fallback key
Timber.d("## forgetFallbackKey()")
@ -168,7 +170,7 @@ internal class OneTimeKeysUploader @Inject constructor(
olmDevice.markKeysAsPublished()
if (hadUnpublishedFallbackKey) {
// 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)) {

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.createUniqueTxnId
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.SessionWorkerParams
import timber.log.Timber
@ -57,6 +58,7 @@ internal class SendGossipRequestWorker(context: Context, params: WorkerParameter
@Inject lateinit var sendToDeviceTask: SendToDeviceTask
@Inject lateinit var cryptoStore: IMXCryptoStore
@Inject lateinit var credentials: Credentials
@Inject lateinit var clock: Clock
override fun injectWith(injector: SessionComponent) {
injector.inject(this)
@ -85,7 +87,7 @@ internal class SendGossipRequestWorker(context: Context, params: WorkerParameter
content = toDeviceContent.toContent(),
senderId = credentials.userId
).also {
it.ageLocalTs = System.currentTimeMillis()
it.ageLocalTs = clock.epochMillis()
})
params.keyShareRequest.recipients.forEach { userToDeviceMap ->
@ -109,7 +111,7 @@ internal class SendGossipRequestWorker(context: Context, params: WorkerParameter
content = toDeviceContent.toContent(),
senderId = credentials.userId
).also {
it.ageLocalTs = System.currentTimeMillis()
it.ageLocalTs = clock.epochMillis()
})
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.createUniqueTxnId
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.SessionWorkerParams
import timber.log.Timber
@ -63,6 +64,7 @@ internal class SendGossipWorker(
@Inject lateinit var credentials: Credentials
@Inject lateinit var messageEncrypter: MessageEncrypter
@Inject lateinit var ensureOlmSessionsForDevicesAction: EnsureOlmSessionsForDevicesAction
@Inject lateinit var clock: Clock
override fun injectWith(injector: SessionComponent) {
injector.inject(this)
@ -129,7 +131,7 @@ internal class SendGossipWorker(
content = toDeviceContent.toContent(),
senderId = credentials.userId
).also {
it.ageLocalTs = System.currentTimeMillis()
it.ageLocalTs = clock.epochMillis()
})
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.algorithms.megolm.MXMegolmDecryption
import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore
import org.matrix.android.sdk.internal.util.time.Clock
import timber.log.Timber
import javax.inject.Inject
internal class MegolmSessionDataImporter @Inject constructor(private val olmDevice: MXOlmDevice,
private val roomDecryptorProvider: RoomDecryptorProvider,
private val outgoingGossipingRequestManager: OutgoingGossipingRequestManager,
private val cryptoStore: IMXCryptoStore) {
internal class MegolmSessionDataImporter @Inject constructor(
private val olmDevice: MXOlmDevice,
private val roomDecryptorProvider: RoomDecryptorProvider,
private val outgoingGossipingRequestManager: OutgoingGossipingRequestManager,
private val cryptoStore: IMXCryptoStore,
private val clock: Clock,
) {
/**
* Import a list of megolm session keys.
@ -47,7 +51,7 @@ internal class MegolmSessionDataImporter @Inject constructor(private val olmDevi
fun handle(megolmSessionsData: List<MegolmSessionData>,
fromBackup: Boolean,
progressListener: ProgressListener?): ImportRoomKeysResult {
val t0 = System.currentTimeMillis()
val t0 = clock.epochMillis()
val totalNumbersOfKeys = megolmSessionsData.size
var lastProgress = 0
@ -103,7 +107,7 @@ internal class MegolmSessionDataImporter @Inject constructor(private val olmDevi
cryptoStore.markBackupDoneForInboundGroupSessions(olmInboundGroupSessionWrappers)
}
val t1 = System.currentTimeMillis()
val t1 = clock.epochMillis()
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.util.JsonCanonicalizer
import org.matrix.android.sdk.internal.util.convertToUTF8
import org.matrix.android.sdk.internal.util.time.Clock
import timber.log.Timber
private val loggerTag = LoggerTag("MXMegolmEncryption", LoggerTag.CRYPTO)
@ -64,7 +65,8 @@ internal class MXMegolmEncryption(
private val messageEncrypter: MessageEncrypter,
private val warnOnUnknownDevicesRepository: WarnOnUnknownDeviceRepository,
private val coroutineDispatchers: MatrixCoroutineDispatchers,
private val cryptoCoroutineScope: CoroutineScope
private val cryptoCoroutineScope: CoroutineScope,
private val clock: Clock,
) : IMXEncrypting, IMXGroupEncryption {
// 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,
eventType: String,
userIds: List<String>): Content {
val ts = System.currentTimeMillis()
val ts = clock.epochMillis()
Timber.tag(loggerTag.value).v("encryptEventContent : getDevicesInRoom")
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).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)
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 :/
// if not we would see duplicate message index errors
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>) {
val ts = System.currentTimeMillis()
val ts = clock.epochMillis()
Timber.tag(loggerTag.value).d("preshareKey started in $roomId ...")
val devices = getDevicesInRoom(userIds)
val outboundSession = ensureOutboundSession(devices.allowedDevices)
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!!
)
olmDevice.addInboundGroupSession(sessionId!!, olmDevice.getSessionKey(sessionId)!!, roomId, olmDevice.deviceCurve25519Key!!,
emptyList(), keysClaimedMap, false)
olmDevice.addInboundGroupSession(
sessionId!!, olmDevice.getSessionKey(sessionId)!!, roomId, olmDevice.deviceCurve25519Key!!,
emptyList(), keysClaimedMap, false
)
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["content"] = submap
var t0 = System.currentTimeMillis()
var t0 = clock.epochMillis()
Timber.tag(loggerTag.value).v("shareUserDevicesKey() : starts")
val results = ensureOlmSessionsForDevicesAction.handle(devicesByUser)
Timber.tag(loggerTag.value).v(
"""shareUserDevicesKey(): ensureOlmSessionsForDevices succeeds after ${System.currentTimeMillis() - t0} ms"""
"""shareUserDevicesKey(): ensureOlmSessionsForDevices succeeds after ${clock.epochMillis() - t0} ms"""
.trimMargin()
)
val contentMap = MXUsersDevicesMap<Any>()
@ -301,7 +305,7 @@ internal class MXMegolmEncryption(
cryptoStore.saveGossipingEvents(gossipingEventBuffer)
if (haveTargets) {
t0 = System.currentTimeMillis()
t0 = clock.epochMillis()
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()}")
val sendToDeviceParams = SendToDeviceTask.Params(EventType.ENCRYPTED, contentMap)
@ -309,7 +313,7 @@ internal class MXMegolmEncryption(
withContext(coroutineDispatchers.io) {
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) {
// What to do here...
Timber.tag(loggerTag.value).e("shareUserDevicesKey() : Failed to share <${session.sessionId}>")
@ -334,7 +338,8 @@ internal class MXMegolmEncryption(
senderKey: String?,
code: WithHeldCode) {
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(
roomId = roomId,
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.di.DeviceId
import org.matrix.android.sdk.internal.di.UserId
import org.matrix.android.sdk.internal.util.time.Clock
import javax.inject.Inject
internal class MXMegolmEncryptionFactory @Inject constructor(
@ -42,7 +43,9 @@ internal class MXMegolmEncryptionFactory @Inject constructor(
private val messageEncrypter: MessageEncrypter,
private val warnOnUnknownDevicesRepository: WarnOnUnknownDeviceRepository,
private val coroutineDispatchers: MatrixCoroutineDispatchers,
private val cryptoCoroutineScope: CoroutineScope) {
private val cryptoCoroutineScope: CoroutineScope,
private val clock: Clock,
) {
fun create(roomId: String): MXMegolmEncryption {
return MXMegolmEncryption(
@ -58,7 +61,8 @@ internal class MXMegolmEncryptionFactory @Inject constructor(
messageEncrypter = messageEncrypter,
warnOnUnknownDevicesRepository = warnOnUnknownDevicesRepository,
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.MXUsersDevicesMap
import org.matrix.android.sdk.internal.util.time.Clock
import timber.log.Timber
internal class MXOutboundSessionInfo(
// The id of the session
val sessionId: String,
val sharedWithHelper: SharedWithHelper,
private val clock: Clock,
// 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
var useCount: Int = 0
fun needsRotation(rotationPeriodMsgs: Int, rotationPeriodMs: Int): Boolean {
var needsRotation = false
val sessionLifetime = System.currentTimeMillis() - creationTime
val sessionLifetime = clock.epochMillis() - creationTime
if (useCount >= rotationPeriodMsgs || sessionLifetime >= rotationPeriodMs) {
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.base64ToUnpaddedBase64
import org.matrix.android.sdk.internal.util.base64UrlToBase64
import org.matrix.android.sdk.internal.util.time.Clock
import timber.log.Timber
import java.io.ByteArrayOutputStream
import java.io.File
@ -42,8 +43,9 @@ internal object MXEncryptedAttachments {
fun encrypt(clearStream: InputStream,
outputFile: File,
clock: Clock,
progress: ((current: Int, total: Int) -> Unit)): EncryptedFileInfo {
val t0 = System.currentTimeMillis()
val t0 = clock.epochMillis()
val secureRandom = SecureRandom()
val initVectorBytes = ByteArray(16) { 0.toByte() }
@ -100,7 +102,7 @@ internal object MXEncryptedAttachments {
hashes = mapOf("sha256" to base64ToUnpaddedBase64(Base64.encodeToString(messageDigest.digest(), Base64.DEFAULT))),
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> {
@ -159,8 +161,8 @@ internal object MXEncryptedAttachments {
* @param attachmentStream the attachment stream. Will be closed after this method call.
* @return the encryption file info
*/
fun encryptAttachment(attachmentStream: InputStream): EncryptionResult {
val t0 = System.currentTimeMillis()
fun encryptAttachment(attachmentStream: InputStream, clock: Clock): EncryptionResult {
val t0 = clock.epochMillis()
val secureRandom = SecureRandom()
// generate a random iv key
@ -221,7 +223,7 @@ internal object MXEncryptedAttachments {
),
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?,
elementToDecrypt: ElementToDecrypt?,
outputStream: OutputStream): Boolean {
outputStream: OutputStream,
clock: Clock
): Boolean {
// sanity checks
if (null == attachmentStream || elementToDecrypt == null) {
Timber.e("## decryptAttachment() : null stream")
return false
}
val t0 = System.currentTimeMillis()
val t0 = clock.epochMillis()
try {
val key = Base64.decode(base64UrlToBase64(elementToDecrypt.k), Base64.DEFAULT)
@ -279,7 +283,8 @@ internal object MXEncryptedAttachments {
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) {
Timber.e(oom, "## decryptAttachment() failed: OOM")
} catch (e: Exception) {

View File

@ -26,6 +26,7 @@ import java.util.UUID
import javax.crypto.Mac
import javax.crypto.spec.SecretKeySpec
import kotlin.experimental.xor
import kotlin.system.measureTimeMillis
private const val SALT_LENGTH = 32
private const val DEFAULT_ITERATION = 500_000
@ -91,52 +92,53 @@ internal fun deriveKey(password: String,
iterations: Int,
progressListener: ProgressListener?): ByteArray {
// Note: copied and adapted from MXMegolmExportEncryption
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
// dklen = 256
// hlen = 512
val prf = Mac.getInstance("HmacSHA512")
prf.init(SecretKeySpec(password.toByteArray(), "HmacSHA512"))
// 256 bits key length
val dk = ByteArray(32)
val uc = ByteArray(64)
// U1 = PRF(Password, Salt || INT_32_BE(i)) with i goes from 1 to dklen/hlen
prf.update(salt.toByteArray())
val int32BE = byteArrayOf(0, 0, 0, 1)
prf.update(int32BE)
prf.doFinal(uc, 0)
measureTimeMillis {
// dklen = 256
// hlen = 512
val prf = Mac.getInstance("HmacSHA512")
// copy to the key
System.arraycopy(uc, 0, dk, 0, dk.size)
prf.init(SecretKeySpec(password.toByteArray(), "HmacSHA512"))
var lastProgress = -1
val uc = ByteArray(64)
for (index in 2..iterations) {
// Uc = PRF(Password, Uc-1)
prf.update(uc)
// U1 = PRF(Password, Salt || INT_32_BE(i)) with i goes from 1 to dklen/hlen
prf.update(salt.toByteArray())
val int32BE = byteArrayOf(0, 0, 0, 1)
prf.update(int32BE)
prf.doFinal(uc, 0)
// F(Password, Salt, c, i) = U1 ^ U2 ^ ... ^ Uc
for (byteIndex in dk.indices) {
dk[byteIndex] = dk[byteIndex] xor uc[byteIndex]
}
// copy to the key
System.arraycopy(uc, 0, dk, 0, dk.size)
val progress = (index + 1) * 100 / iterations
if (progress != lastProgress) {
lastProgress = progress
progressListener?.onProgress(lastProgress, 100)
var lastProgress = -1
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 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
}

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`
*/
fun onMessageReceived() {
lastReceivedMessageTs = System.currentTimeMillis()
fun onMessageReceived(currentTimeMillis: Long) {
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.extensions.clearWith
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.OlmException
import org.matrix.olm.OlmOutboundGroupSession
@ -110,7 +111,8 @@ internal class RealmCryptoStore @Inject constructor(
@CryptoDatabase private val realmConfiguration: RealmConfiguration,
private val crossSigningKeysMapper: CrossSigningKeysMapper,
@UserId private val userId: String,
@DeviceId private val deviceId: String?
@DeviceId private val deviceId: String?,
private val clock: Clock,
) : IMXCryptoStore {
/* ==========================================================================================
@ -307,7 +309,7 @@ internal class RealmCryptoStore @Inject constructor(
// Add the device
Timber.d("Add device ${cryptoDeviceInfo.deviceId} of user $userId")
val newEntity = CryptoMapper.mapToEntity(cryptoDeviceInfo)
newEntity.firstTimeSeenLocalTs = System.currentTimeMillis()
newEntity.firstTimeSeenLocalTs = clock.epochMillis()
userEntity.devices.add(newEntity)
} else {
// Update the device
@ -792,7 +794,7 @@ internal class RealmCryptoStore @Inject constructor(
if (outboundGroupSession != null) {
val info = realm.createObject(OutboundGroupSessionInfoEntity::class.java).apply {
creationTime = System.currentTimeMillis()
creationTime = clock.epochMillis()
putOutboundGroupSession(outboundGroupSession)
}
entity.outboundSessionInfo = info
@ -882,7 +884,8 @@ internal class RealmCryptoStore @Inject constructor(
try {
val key = OlmInboundGroupSessionEntity.createPrimaryKey(
olmInboundGroupSessionWrapper.olmInboundGroupSession?.sessionIdentifier(),
olmInboundGroupSessionWrapper.senderKey)
olmInboundGroupSessionWrapper.senderKey
)
it.where<OlmInboundGroupSessionEntity>()
.equalTo(OlmInboundGroupSessionEntityFields.PRIMARY_KEY, key)
@ -1057,13 +1060,16 @@ internal class RealmCryptoStore @Inject constructor(
localCreationTimestamp = 0
)
}
return monarchy.findAllPagedWithChanges(realmDataSourceFactory,
LivePagedListBuilder(dataSourceFactory,
return monarchy.findAllPagedWithChanges(
realmDataSourceFactory,
LivePagedListBuilder(
dataSourceFactory,
PagedList.Config.Builder()
.setPageSize(20)
.setEnablePlaceholders(false)
.setPrefetchDistance(1)
.build())
.build()
)
)
}
@ -1072,13 +1078,16 @@ internal class RealmCryptoStore @Inject constructor(
realm.where<GossipingEventEntity>().sort(GossipingEventEntityFields.AGE_LOCAL_TS, Sort.DESCENDING)
}
val dataSourceFactory = realmDataSourceFactory.map { it.toModel() }
val trail = monarchy.findAllPagedWithChanges(realmDataSourceFactory,
LivePagedListBuilder(dataSourceFactory,
val trail = monarchy.findAllPagedWithChanges(
realmDataSourceFactory,
LivePagedListBuilder(
dataSourceFactory,
PagedList.Config.Builder()
.setPageSize(20)
.setEnablePlaceholders(false)
.setPrefetchDistance(1)
.build())
.build()
)
)
return trail
}
@ -1153,7 +1162,7 @@ internal class RealmCryptoStore @Inject constructor(
override fun saveGossipingEvents(events: List<Event>) {
monarchy.writeAsync { realm ->
val now = System.currentTimeMillis()
val now = clock.epochMillis()
events.forEach { event ->
val ageLocalTs = event.unsignedData?.age?.let { now - it } ?: now
val entity = GossipingEventEntity(
@ -1326,7 +1335,7 @@ internal class RealmCryptoStore @Inject constructor(
.findAll()
.mapNotNull { entity ->
when (entity.type) {
GossipRequestType.KEY -> {
GossipRequestType.KEY -> {
IncomingRoomKeyRequest(
userId = entity.otherUserId,
deviceId = entity.otherDeviceId,
@ -1359,7 +1368,7 @@ internal class RealmCryptoStore @Inject constructor(
it.otherUserId = request.userId
it.requestId = request.requestId ?: ""
it.requestState = GossipingRequestState.PENDING
it.localCreationTimestamp = ageLocalTS ?: System.currentTimeMillis()
it.localCreationTimestamp = ageLocalTS ?: clock.epochMillis()
if (request is IncomingSecretShareRequest) {
it.type = GossipRequestType.SECRET
it.requestedInfoStr = request.secretName
@ -1380,7 +1389,7 @@ internal class RealmCryptoStore @Inject constructor(
it.otherUserId = request.userId
it.requestId = request.requestId ?: ""
it.requestState = GossipingRequestState.PENDING
it.localCreationTimestamp = request.localCreationTimestamp ?: System.currentTimeMillis()
it.localCreationTimestamp = request.localCreationTimestamp ?: clock.epochMillis()
if (request is IncomingSecretShareRequest) {
it.type = GossipRequestType.SECRET
it.requestedInfoStr = request.secretName
@ -1536,13 +1545,16 @@ internal class RealmCryptoStore @Inject constructor(
it.toOutgoingGossipingRequest() as? OutgoingRoomKeyRequest
?: OutgoingRoomKeyRequest(requestBody = null, requestId = "?", recipients = emptyMap(), state = OutgoingGossipingRequestState.CANCELLED)
}
val trail = monarchy.findAllPagedWithChanges(realmDataSourceFactory,
LivePagedListBuilder(dataSourceFactory,
val trail = monarchy.findAllPagedWithChanges(
realmDataSourceFactory,
LivePagedListBuilder(
dataSourceFactory,
PagedList.Config.Builder()
.setPageSize(20)
.setEnablePlaceholders(false)
.setPrefetchDistance(1)
.build())
.build()
)
)
return trail
}
@ -1707,7 +1719,7 @@ internal class RealmCryptoStore @Inject constructor(
* So we need to tidy up a bit
*/
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 ->
// 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.MigrateCryptoTo014
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 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
* 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 < 6) MigrateCryptoTo006(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 < 10) MigrateCryptoTo010(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.MyDeviceLastSeenInfoEntityFields
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) {
realm.schema.create("MyDeviceLastSeenInfoEntity")
@ -33,7 +37,7 @@ internal class MigrateCryptoTo008(realm: DynamicRealm) : RealmMigrator(realm, 8)
.addField(MyDeviceLastSeenInfoEntityFields.LAST_SEEN_TS, Long::class.java)
.setNullable(MyDeviceLastSeenInfoEntityFields.LAST_SEEN_TS, true)
val now = System.currentTimeMillis()
val now = clock.epochMillis()
realm.schema.get("DeviceInfoEntity")
?.addField(DeviceInfoEntityFields.FIRST_TIME_SEEN_LOCAL_TS, Long::class.java)
?.setNullable(DeviceInfoEntityFields.FIRST_TIME_SEEN_LOCAL_TS, true)

View File

@ -123,7 +123,7 @@ internal class DefaultOutgoingSASDefaultVerificationTransaction(
// val requestMessage = KeyVerificationRequest(
// fromDevice = session.sessionParams.deviceId ?: "",
// methods = listOf(KeyVerificationStart.VERIF_METHOD_SAS),
// timestamp = System.currentTimeMillis().toInt(),
// timestamp = clock.epochMillis().toInt(),
// 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.session.SessionScope
import org.matrix.android.sdk.internal.task.TaskExecutor
import org.matrix.android.sdk.internal.util.time.Clock
import timber.log.Timber
import java.util.UUID
import javax.inject.Inject
@ -104,7 +105,8 @@ internal class DefaultVerificationService @Inject constructor(
private val verificationTransportToDeviceFactory: VerificationTransportToDeviceFactory,
private val crossSigningService: CrossSigningService,
private val cryptoCoroutineScope: CoroutineScope,
private val taskExecutor: TaskExecutor
private val taskExecutor: TaskExecutor,
private val clock: Clock,
) : DefaultVerificationTransaction.Listener, VerificationService {
private val uiHandler = Handler(Looper.getMainLooper())
@ -261,9 +263,11 @@ internal class DefaultVerificationService @Inject constructor(
}
override fun markedLocallyAsManuallyVerified(userId: String, deviceID: String) {
setDeviceVerificationAction.handle(DeviceTrustLevel(false, true),
setDeviceVerificationAction.handle(
DeviceTrustLevel(crossSigningVerified = false, locallyVerified = true),
userId,
deviceID)
deviceID
)
listeners.forEach {
try {
@ -313,7 +317,7 @@ internal class DefaultVerificationService @Inject constructor(
val requestsForUser = pendingRequests.getOrPut(senderId) { mutableListOf() }
val pendingVerificationRequest = PendingVerificationRequest(
ageLocalTs = event.ageLocalTs ?: System.currentTimeMillis(),
ageLocalTs = event.ageLocalTs ?: clock.epochMillis(),
isIncoming = true,
otherUserId = senderId, // requestInfo.toUserId,
roomId = null,
@ -352,7 +356,7 @@ internal class DefaultVerificationService @Inject constructor(
val requestsForUser = pendingRequests.getOrPut(senderId) { mutableListOf() }
val pendingVerificationRequest = PendingVerificationRequest(
ageLocalTs = event.ageLocalTs ?: System.currentTimeMillis(),
ageLocalTs = event.ageLocalTs ?: clock.epochMillis(),
isIncoming = true,
otherUserId = senderId, // requestInfo.toUserId,
roomId = event.roomId,
@ -552,7 +556,8 @@ internal class DefaultVerificationService @Inject constructor(
myDeviceInfoHolder.get().myDevice.fingerprint()!!,
startReq.transactionId,
otherUserId,
autoAccept).also { txConfigure(it) }
autoAccept
).also { txConfigure(it) }
addTransaction(tx)
tx.onVerificationStart(startReq)
return null
@ -644,9 +649,11 @@ internal class DefaultVerificationService @Inject constructor(
if (existingRequest != null) {
// Mark this request as cancelled
updatePendingRequest(existingRequest.copy(
cancelConclusion = safeValueOf(cancelReq.code)
))
updatePendingRequest(
existingRequest.copy(
cancelConclusion = safeValueOf(cancelReq.code)
)
)
}
existingTransaction?.state = VerificationTxState.Cancelled(safeValueOf(cancelReq.code), false)
@ -809,15 +816,19 @@ internal class DefaultVerificationService @Inject constructor(
?.let { vt ->
val otherDeviceId = vt.otherDeviceId
if (!crossSigningService.canCrossSign()) {
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(USER_SIGNING_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(
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,
userId = userId,
deviceId = deviceId ?: "",
isIncoming = false)
isIncoming = false
)
tx.transport = transportCreator.invoke(tx)
addTransaction(tx)
}
updatePendingRequest(existingRequest.copy(
readyInfo = readyReq
))
updatePendingRequest(
existingRequest.copy(
readyInfo = readyReq
)
)
}
private fun createQrCodeData(requestId: String?, otherUserId: String, otherDeviceId: String?): QrCodeData? {
@ -1115,7 +1129,8 @@ internal class DefaultVerificationService @Inject constructor(
myDeviceInfoHolder.get().myDevice.fingerprint()!!,
txID,
otherUserId,
otherDeviceId)
otherDeviceId
)
tx.transport = verificationTransportToDeviceFactory.createTransport(tx)
addTransaction(tx)
@ -1150,7 +1165,7 @@ internal class DefaultVerificationService @Inject constructor(
val validLocalId = localId ?: LocalEcho.createLocalEchoId()
val verificationRequest = PendingVerificationRequest(
ageLocalTs = System.currentTimeMillis(),
ageLocalTs = clock.epochMillis(),
isIncoming = false,
roomId = roomId,
localId = validLocalId,
@ -1175,11 +1190,13 @@ internal class DefaultVerificationService @Inject constructor(
transport.sendVerificationRequest(methodValues, validLocalId, otherUserId, roomId, null) { syncedId, info ->
// We need to update with the syncedID
updatePendingRequest(verificationRequest.copy(
transactionId = syncedId,
// localId stays different
requestInfo = info
))
updatePendingRequest(
verificationRequest.copy(
transactionId = syncedId,
// localId stays different
requestInfo = info
)
)
}
requestsForUser.add(verificationRequest)
@ -1228,7 +1245,7 @@ internal class DefaultVerificationService @Inject constructor(
val verificationRequest = PendingVerificationRequest(
transactionId = localId,
ageLocalTs = System.currentTimeMillis(),
ageLocalTs = clock.epochMillis(),
isIncoming = false,
roomId = null,
localId = localId,
@ -1254,10 +1271,12 @@ internal class DefaultVerificationService @Inject constructor(
transport.sendVerificationRequest(methodValues, localId, otherUserId, null, targetDevices) { _, info ->
// Nothing special to do in to device mode
updatePendingRequest(verificationRequest.copy(
// localId stays different
requestInfo = info
))
updatePendingRequest(
verificationRequest.copy(
// localId stays different
requestInfo = info
)
)
}
requestsForUser.add(verificationRequest)
@ -1271,9 +1290,11 @@ internal class DefaultVerificationService @Inject constructor(
.cancelTransaction(transactionId, otherUserId, null, CancelCode.User)
getExistingVerificationRequest(otherUserId, transactionId)?.let {
updatePendingRequest(it.copy(
cancelConclusion = CancelCode.User
))
updatePendingRequest(
it.copy(
cancelConclusion = CancelCode.User
)
)
}
}
@ -1307,7 +1328,8 @@ internal class DefaultVerificationService @Inject constructor(
myDeviceInfoHolder.get().myDevice.fingerprint()!!,
transactionId,
otherUserId,
otherDeviceId)
otherDeviceId
)
tx.transport = verificationTransportRoomMessageFactory.createTransport(roomId, tx)
addTransaction(tx)
@ -1333,7 +1355,8 @@ internal class DefaultVerificationService @Inject constructor(
otherUserId,
existingRequest.requestInfo?.fromDevice ?: "",
existingRequest.requestInfo?.methods,
methods) {
methods
) {
verificationTransportRoomMessageFactory.createTransport(roomId, it)
}
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?
val readyMsg = transport.createReady(transactionId, deviceId ?: "", computedMethods)
transport.sendToOther(EventType.KEY_VERIFICATION_READY,
transport.sendToOther(
EventType.KEY_VERIFICATION_READY,
readyMsg,
VerificationTxState.None,
CancelCode.User,
@ -1372,7 +1396,8 @@ internal class DefaultVerificationService @Inject constructor(
otherUserId,
existingRequest.requestInfo?.fromDevice ?: "",
existingRequest.requestInfo?.methods,
methods) {
methods
) {
verificationTransportToDeviceFactory.createTransport(it)
}
if (methods.isNullOrEmpty()) {
@ -1446,7 +1471,8 @@ internal class DefaultVerificationService @Inject constructor(
qrCodeData = qrCodeData,
userId = userId,
deviceId = deviceId ?: "",
isIncoming = false)
isIncoming = false
)
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.UserId
import org.matrix.android.sdk.internal.session.EventInsertLiveProcessor
import org.matrix.android.sdk.internal.util.time.Clock
import timber.log.Timber
import javax.inject.Inject
internal class VerificationMessageProcessor @Inject constructor(
private val eventDecryptor: EventDecryptor,
private val clock: Clock,
private val verificationService: DefaultVerificationService,
@UserId private val userId: 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,
// the message should be ignored by the receiver.
if (!VerificationService.isValidRequest(event.ageLocalTs
?: event.originServerTs)) return Unit.also {
if (!VerificationService.isValidRequest(event.ageLocalTs ?: event.originServerTs, clock.epochMillis())) return Unit.also {
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.di.WorkManagerProvider
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.WorkerParamsFactory
import timber.log.Timber
@ -61,7 +62,8 @@ internal class VerificationTransportRoomMessage(
private val roomId: String,
private val localEchoEventFactory: LocalEchoEventFactory,
private val tx: DefaultVerificationTransaction?,
private val coroutineScope: CoroutineScope
private val coroutineScope: CoroutineScope,
private val clock: Clock,
) : VerificationTransport {
override fun <T> sendToOther(type: String,
@ -77,10 +79,12 @@ internal class VerificationTransportRoomMessage(
content = verificationInfo.toEventContent()!!
)
val workerParams = WorkerParamsFactory.toData(SendVerificationMessageWorker.Params(
sessionId = sessionId,
eventId = event.eventId ?: ""
))
val workerParams = WorkerParamsFactory.toData(
SendVerificationMessageWorker.Params(
sessionId = sessionId,
eventId = event.eventId ?: ""
)
)
val enqueueInfo = enqueueSendWork(workerParams)
// I cannot just listen to the given work request, because when used in a uniqueWork,
@ -155,7 +159,7 @@ internal class VerificationTransportRoomMessage(
transactionId = "",
fromDevice = userDeviceId ?: "",
methods = supportedMethods,
timestamp = System.currentTimeMillis()
timestamp = clock.epochMillis()
)
val info = MessageVerificationRequestContent(
@ -175,10 +179,12 @@ internal class VerificationTransportRoomMessage(
content
)
val workerParams = WorkerParamsFactory.toData(SendVerificationMessageWorker.Params(
sessionId = sessionId,
eventId = event.eventId ?: ""
))
val workerParams = WorkerParamsFactory.toData(
SendVerificationMessageWorker.Params(
sessionId = sessionId,
eventId = event.eventId ?: ""
)
)
val workRequest = workManagerProvider.matrixOneTimeWorkRequestBuilder<SendVerificationMessageWorker>()
.setConstraints(WorkManagerProvider.workConstraints)
@ -230,10 +236,12 @@ internal class VerificationTransportRoomMessage(
roomId = roomId,
content = MessageVerificationCancelContent.create(transactionId, code).toContent()
)
val workerParams = WorkerParamsFactory.toData(SendVerificationMessageWorker.Params(
sessionId = sessionId,
eventId = event.eventId ?: ""
))
val workerParams = WorkerParamsFactory.toData(
SendVerificationMessageWorker.Params(
sessionId = sessionId,
eventId = event.eventId ?: ""
)
)
enqueueSendWork(workerParams)
}
@ -250,10 +258,12 @@ internal class VerificationTransportRoomMessage(
)
).toContent()
)
val workerParams = WorkerParamsFactory.toData(SendVerificationMessageWorker.Params(
sessionId = sessionId,
eventId = event.eventId ?: ""
))
val workerParams = WorkerParamsFactory.toData(
SendVerificationMessageWorker.Params(
sessionId = sessionId,
eventId = event.eventId ?: ""
)
)
val enqueueInfo = enqueueSendWork(workerParams)
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 {
return Event(
roomId = roomId,
originServerTs = System.currentTimeMillis(),
originServerTs = clock.epochMillis(),
senderId = userId,
eventId = localId,
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.session.room.send.LocalEchoEventFactory
import org.matrix.android.sdk.internal.task.TaskExecutor
import org.matrix.android.sdk.internal.util.time.Clock
import javax.inject.Inject
internal class VerificationTransportRoomMessageFactory @Inject constructor(
@ -33,17 +34,21 @@ internal class VerificationTransportRoomMessageFactory @Inject constructor(
@DeviceId
private val deviceId: String?,
private val localEchoEventFactory: LocalEchoEventFactory,
private val taskExecutor: TaskExecutor
private val taskExecutor: TaskExecutor,
private val clock: Clock,
) {
fun createTransport(roomId: String, tx: DefaultVerificationTransaction?): VerificationTransportRoomMessage {
return VerificationTransportRoomMessage(workManagerProvider,
return VerificationTransportRoomMessage(
workManagerProvider,
sessionId,
userId,
deviceId,
roomId,
localEchoEventFactory,
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.task.TaskExecutor
import org.matrix.android.sdk.internal.task.configureWith
import org.matrix.android.sdk.internal.util.time.Clock
import timber.log.Timber
// TODO var could be val
internal class VerificationTransportToDevice(
private var tx: DefaultVerificationTransaction?,
private var sendToDeviceTask: SendToDeviceTask,
private val myDeviceId: String?,
private var taskExecutor: TaskExecutor
private var taskExecutor: TaskExecutor,
private val clock: Clock,
) : VerificationTransport {
override fun sendVerificationRequest(supportedMethods: List<String>,
@ -56,7 +59,7 @@ internal class VerificationTransportToDevice(
transactionId = localId,
fromDevice = myDeviceId ?: "",
methods = supportedMethods,
timestamp = System.currentTimeMillis()
timestamp = clock.epochMillis()
)
val keyReq = KeyVerificationRequest(
fromDevice = validKeyReq.fromDevice,
@ -201,7 +204,8 @@ internal class VerificationTransportToDevice(
hash,
commitment,
messageAuthenticationCode,
shortAuthenticationStrings)
shortAuthenticationStrings
)
override fun createKey(tid: String, pubKey: String): VerificationInfoKey = KeyVerificationKey.create(tid, pubKey)
@ -221,7 +225,8 @@ internal class VerificationTransportToDevice(
hashes,
messageAuthenticationCodes,
shortAuthenticationStrings,
null)
null
)
}
override fun createStartForQrCode(fromDevice: String,
@ -235,7 +240,8 @@ internal class VerificationTransportToDevice(
null,
null,
null,
sharedSecret)
sharedSecret
)
}
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.di.DeviceId
import org.matrix.android.sdk.internal.task.TaskExecutor
import org.matrix.android.sdk.internal.util.time.Clock
import javax.inject.Inject
internal class VerificationTransportToDeviceFactory @Inject constructor(
private val sendToDeviceTask: SendToDeviceTask,
@DeviceId val myDeviceId: String?,
private val taskExecutor: TaskExecutor) {
private val taskExecutor: TaskExecutor,
private val clock: Clock,
) {
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.withContext
import timber.log.Timber
import kotlin.system.measureTimeMillis
internal fun <T> CoroutineScope.asyncTransaction(monarchy: Monarchy, transaction: suspend (realm: Realm) -> T) {
asyncTransaction(monarchy.realmConfiguration, transaction)
@ -41,13 +42,13 @@ internal suspend fun <T> awaitTransaction(config: RealmConfiguration, transactio
bgRealm.beginTransaction()
val result: T
try {
val start = System.currentTimeMillis()
result = transaction(bgRealm)
if (isActive) {
bgRealm.commitTransaction()
val end = System.currentTimeMillis()
val time = end - start
Timber.v("Execute transaction in $time millis")
measureTimeMillis {
result = transaction(bgRealm)
if (isActive) {
bgRealm.commitTransaction()
}
}.also {
Timber.v("Execute transaction in $it millis")
}
} finally {
if (bgRealm.isInTransaction) {

View File

@ -137,7 +137,8 @@ internal fun ThreadSummaryEntity.Companion.createOrUpdate(
roomMemberContentsByUser: HashMap<String, RoomMemberContent?>,
roomEntity: RoomEntity,
userId: String,
cryptoService: CryptoService? = null
cryptoService: CryptoService? = null,
currentTimeMillis: Long,
) {
when (threadSummaryType) {
ThreadSummaryUpdateType.REPLACE -> {
@ -153,14 +154,14 @@ internal fun ThreadSummaryEntity.Companion.createOrUpdate(
Timber.i("###THREADS ThreadSummaryHelper REPLACE eventId:${it.rootThreadEventId} ")
}
val rootThreadEventEntity = createEventEntity(roomId, rootThreadEvent, realm).also {
val rootThreadEventEntity = createEventEntity(realm, roomId, rootThreadEvent, currentTimeMillis).also {
try {
decryptIfNeeded(cryptoService, it, roomId)
} catch (e: InterruptedException) {
Timber.i("Decryption got interrupted")
}
}
val latestThreadEventEntity = createLatestEventEntity(roomId, rootThreadEvent, roomMemberContentsByUser, realm)?.also {
val latestThreadEventEntity = createLatestEventEntity(realm, roomId, rootThreadEvent, roomMemberContentsByUser, currentTimeMillis)?.also {
try {
decryptIfNeeded(cryptoService, it, roomId)
} 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
*/
private fun createEventEntity(roomId: String, event: Event, realm: Realm): EventEntity {
val ageLocalTs = event.unsignedData?.age?.let { System.currentTimeMillis() - it }
private fun createEventEntity(realm: Realm, roomId: String, event: Event, currentTimeMillis: Long): EventEntity {
val ageLocalTs = event.unsignedData?.age?.let { currentTimeMillis - it }
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
*/
private fun createLatestEventEntity(
realm: Realm,
roomId: String,
rootThreadEvent: Event,
roomMemberContentsByUser: HashMap<String, RoomMemberContent?>,
realm: Realm): EventEntity? {
currentTimeMillis: Long,
): EventEntity? {
return getLatestEvent(rootThreadEvent)?.let {
it.senderId?.let { 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.di.MoshiProvider
import timber.log.Timber
import kotlin.random.Random
internal object EventMapper {
fun map(event: Event, roomId: String): EventEntity {
val eventEntity = EventEntity()
// 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.content = ContentMapper.map(event.content)
eventEntity.prevContent = ContentMapper.map(event.resolvedPrevContent())
@ -126,7 +127,10 @@ internal fun EventEntity.asDomain(castJsonNumbers: Boolean = false): Event {
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 {
this.sendState = sendState
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.session.download.DownloadProgressInterceptor.Companion.DOWNLOAD_PROGRESS_INTERCEPTOR_HEADER
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 timber.log.Timber
import java.io.File
@ -51,7 +52,8 @@ internal class DefaultFileService @Inject constructor(
private val contentUrlResolver: ContentUrlResolver,
@UnauthenticatedWithCertificateWithProgress
private val okHttpClient: OkHttpClient,
private val coroutineDispatchers: MatrixCoroutineDispatchers
private val coroutineDispatchers: MatrixCoroutineDispatchers,
private val clock: Clock,
) : FileService {
// Legacy folder, will be deleted
@ -182,7 +184,8 @@ internal class DefaultFileService @Inject constructor(
MXEncryptedAttachments.decryptAttachment(
inputStream,
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.internal.di.UserId
import org.matrix.android.sdk.internal.session.SessionScope
import org.matrix.android.sdk.internal.util.time.Clock
import timber.log.Timber
import javax.inject.Inject
@ -41,9 +42,12 @@ private val loggerTag = LoggerTag("CallSignalingHandler", LoggerTag.VOIP)
private const val MAX_AGE_TO_RING = 40_000
@SessionScope
internal class CallSignalingHandler @Inject constructor(private val activeCallHandler: ActiveCallHandler,
private val mxCallFactory: MxCallFactory,
@UserId private val userId: String) {
internal class CallSignalingHandler @Inject constructor(
private val activeCallHandler: ActiveCallHandler,
private val mxCallFactory: MxCallFactory,
@UserId private val userId: String,
private val clock: Clock,
) {
private val invitedCallIds = mutableSetOf<String>()
private val callListeners = mutableSetOf<CallListener>()
@ -184,7 +188,7 @@ internal class CallSignalingHandler @Inject constructor(private val activeCallHa
if (event.roomId == null || event.senderId == null) {
return
}
val now = System.currentTimeMillis()
val now = clock.epochMillis()
val age = now - (event.ageLocalTs ?: now)
if (age > MAX_AGE_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.room.send.LocalEchoEventFactory
import org.matrix.android.sdk.internal.session.room.send.queue.EventSenderProcessor
import org.matrix.android.sdk.internal.util.time.Clock
import javax.inject.Inject
internal class MxCallFactory @Inject constructor(
@ -36,7 +37,8 @@ internal class MxCallFactory @Inject constructor(
private val eventSenderProcessor: EventSenderProcessor,
private val matrixConfiguration: MatrixConfiguration,
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? {
@ -51,7 +53,8 @@ internal class MxCallFactory @Inject constructor(
localEchoEventFactory = localEchoEventFactory,
eventSenderProcessor = eventSenderProcessor,
matrixConfiguration = matrixConfiguration,
getProfileInfoTask = getProfileInfoTask
getProfileInfoTask = getProfileInfoTask,
clock = clock,
).apply {
updateOpponentData(opponentUserId, content, content.capabilities)
}
@ -68,7 +71,8 @@ internal class MxCallFactory @Inject constructor(
localEchoEventFactory = localEchoEventFactory,
eventSenderProcessor = eventSenderProcessor,
matrixConfiguration = matrixConfiguration,
getProfileInfoTask = getProfileInfoTask
getProfileInfoTask = getProfileInfoTask,
clock = clock,
).apply {
// Setup with this userId, might be updated when processing the Answer event
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.room.send.LocalEchoEventFactory
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 java.math.BigDecimal
@ -61,7 +62,8 @@ internal class MxCallImpl(
private val localEchoEventFactory: LocalEchoEventFactory,
private val eventSenderProcessor: EventSenderProcessor,
private val matrixConfiguration: MatrixConfiguration,
private val getProfileInfoTask: GetProfileInfoTask
private val getProfileInfoTask: GetProfileInfoTask,
private val clock: Clock,
) : MxCall {
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 {
return Event(
roomId = roomId,
originServerTs = System.currentTimeMillis(),
originServerTs = clock.epochMillis(),
senderId = userId,
eventId = localId,
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.MultipleEventSendingDispatcherWorker
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.worker.SessionSafeCoroutineWorker
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 localEchoRepository: LocalEchoRepository
@Inject lateinit var temporaryFileCreator: TemporaryFileCreator
@Inject lateinit var clock: Clock
override fun injectWith(injector: SessionComponent) {
injector.inject(this)
@ -243,7 +245,7 @@ internal class UploadContentWorker(val context: Context, params: WorkerParameter
.also { filesToDelete.add(it) }
uploadedFileEncryptedFileInfo =
MXEncryptedAttachments.encrypt(fileToUpload.inputStream(), encryptedFile) { read, total ->
MXEncryptedAttachments.encrypt(fileToUpload.inputStream(), encryptedFile, clock) { read, total ->
notifyTracker(params) {
contentUploadStateTracker.setEncrypting(it, read.toLong(), total.toLong())
}
@ -329,7 +331,7 @@ internal class UploadContentWorker(val context: Context, params: WorkerParameter
if (params.isEncrypted) {
Timber.v("Encrypt thumbnail")
notifyTracker(params) { contentUploadStateTracker.setEncryptingThumbnail(it) }
val encryptionResult = MXEncryptedAttachments.encryptAttachment(thumbnailData.bytes.inputStream())
val encryptionResult = MXEncryptedAttachments.encryptAttachment(thumbnailData.bytes.inputStream(), clock)
val contentUploadResponse = fileUploader.uploadByteArray(
byteArray = encryptionResult.encryptedByteArray,
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.ScanMediaTask
import org.matrix.android.sdk.internal.task.TaskExecutor
import org.matrix.android.sdk.internal.util.time.Clock
import timber.log.Timber
import javax.inject.Inject
@ -46,7 +47,8 @@ internal class DefaultContentScannerService @Inject constructor(
private val getServerPublicKeyTask: GetServerPublicKeyTask,
private val scanEncryptedTask: ScanEncryptedTask,
private val scanMediaTask: ScanMediaTask,
private val taskExecutor: TaskExecutor
private val taskExecutor: TaskExecutor,
private val clock: Clock,
) : ContentScannerService {
// Cache public key in memory
@ -71,11 +73,13 @@ internal class DefaultContentScannerService @Inject constructor(
override suspend fun getScanResultForAttachment(mxcUrl: String, fileInfo: ElementToDecrypt?): ScanStatusInfo {
val result = if (fileInfo != null) {
scanEncryptedTask.execute(ScanEncryptedTask.Params(
mxcUrl = mxcUrl,
publicServerKey = getServerPublicKey(false),
encryptedInfo = fileInfo
))
scanEncryptedTask.execute(
ScanEncryptedTask.Params(
mxcUrl = mxcUrl,
publicServerKey = getServerPublicKey(false),
encryptedInfo = fileInfo
)
)
} else {
scanMediaTask.execute(ScanMediaTask.Params(mxcUrl))
}
@ -83,7 +87,7 @@ internal class DefaultContentScannerService @Inject constructor(
return ScanStatusInfo(
state = if (result.clean) ScanState.TRUSTED else ScanState.INFECTED,
humanReadableMessage = result.info,
scanDateTimestamp = System.currentTimeMillis()
scanDateTimestamp = clock.epochMillis()
)
}

View File

@ -31,11 +31,14 @@ internal fun ContentScanResultEntity.Companion.get(realm: Realm, attachmentUrl:
.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)
?: realm.createObject<ContentScanResultEntity>().also {
it.mediaUrl = attachmentUrl
it.scanDateTimestamp = System.currentTimeMillis()
it.scanDateTimestamp = currentTimeMillis
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.contentscanner.data.ContentScannerStore
import org.matrix.android.sdk.internal.util.isValidUrl
import org.matrix.android.sdk.internal.util.time.Clock
import javax.inject.Inject
@SessionScope
internal class RealmContentScannerStore @Inject constructor(
@ContentScannerDatabase
private val realmConfiguration: RealmConfiguration
private val realmConfiguration: RealmConfiguration,
private val clock: Clock,
) : ContentScannerStore {
private val monarchy = Monarchy.Builder()
@ -82,15 +84,15 @@ internal class RealmContentScannerStore @Inject constructor(
override fun updateStateForContent(mxcUrl: String, state: ScanState, scannerUrl: String?) {
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) {
monarchy.runTransactionSync {
ContentScanResultEntity.getOrCreate(it, mxcUrl, scannerUrl).apply {
ContentScanResultEntity.getOrCreate(it, mxcUrl, scannerUrl, clock.epochMillis()).apply {
scanResult = state
scanDateTimestamp = System.currentTimeMillis()
scanDateTimestamp = clock.epochMillis()
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.room.aggregation.livelocation.LiveLocationAggregationProcessor
import org.matrix.android.sdk.internal.session.room.state.StateEventDataSource
import org.matrix.android.sdk.internal.util.time.Clock
import timber.log.Timber
import javax.inject.Inject
@ -77,7 +78,8 @@ internal class EventRelationsAggregationProcessor @Inject constructor(
private val stateEventDataSource: StateEventDataSource,
@SessionId private val sessionId: String,
private val sessionManager: SessionManager,
private val liveLocationAggregationProcessor: LiveLocationAggregationProcessor
private val liveLocationAggregationProcessor: LiveLocationAggregationProcessor,
private val clock: Clock,
) : EventInsertLiveProcessor {
private val allowedTypes = listOf(
@ -338,7 +340,7 @@ internal class EventRelationsAggregationProcessor @Inject constructor(
Timber.v("###REPLACE Receiving remote echo of edit (edit already done)")
existingSummary.editions.firstOrNull { it.eventId == txId }?.let {
it.eventId = event.eventId
it.timestamp = event.originServerTs ?: System.currentTimeMillis()
it.timestamp = event.originServerTs ?: clock.epochMillis()
it.isLocalEcho = false
}
} else {
@ -349,10 +351,10 @@ internal class EventRelationsAggregationProcessor @Inject constructor(
eventId = event.eventId,
content = ContentMapper.map(newContent),
timestamp = if (isLocalEcho) {
System.currentTimeMillis()
clock.epochMillis()
} else {
// Do not take local echo originServerTs here, could mess up ordering (keep old ts)
event.originServerTs ?: System.currentTimeMillis()
event.originServerTs ?: clock.epochMillis()
},
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.task.Task
import org.matrix.android.sdk.internal.util.awaitTransaction
import org.matrix.android.sdk.internal.util.time.Clock
import java.util.concurrent.TimeUnit
import javax.inject.Inject
@ -56,7 +57,8 @@ internal class DefaultCreateRoomTask @Inject constructor(
@SessionDatabase
private val realmConfiguration: RealmConfiguration,
private val createRoomBodyBuilder: CreateRoomBodyBuilder,
private val globalErrorReceiver: GlobalErrorReceiver
private val globalErrorReceiver: GlobalErrorReceiver,
private val clock: Clock,
) : CreateRoomTask {
override suspend fun execute(params: CreateRoomParams): String {
@ -106,7 +108,7 @@ internal class DefaultCreateRoomTask @Inject constructor(
}
awaitTransaction(realmConfiguration) {
RoomSummaryEntity.where(it, roomId).findFirst()?.lastActivityTime = System.currentTimeMillis()
RoomSummaryEntity.where(it, roomId).findFirst()?.lastActivityTime = clock.epochMillis()
}
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.task.Task
import org.matrix.android.sdk.internal.util.awaitTransaction
import org.matrix.android.sdk.internal.util.time.Clock
import java.util.concurrent.TimeUnit
import javax.inject.Inject
@ -61,7 +62,8 @@ internal class DefaultLoadRoomMembersTask @Inject constructor(
private val roomMemberEventHandler: RoomMemberEventHandler,
private val cryptoSessionInfoProvider: CryptoSessionInfoProvider,
private val deviceListManager: DeviceListManager,
private val globalErrorReceiver: GlobalErrorReceiver
private val globalErrorReceiver: GlobalErrorReceiver,
private val clock: Clock,
) : LoadRoomMembersTask {
override suspend fun execute(params: LoadRoomMembersTask.Params) {
@ -107,7 +109,7 @@ internal class DefaultLoadRoomMembersTask @Inject constructor(
// We ignore all the already known members
val roomEntity = RoomEntity.where(realm, roomId).findFirst()
?: realm.createObject(roomId)
val now = System.currentTimeMillis()
val now = clock.epochMillis()
for (roomMemberEvent in response.roomMemberEvents) {
if (roomMemberEvent.eventId == null || roomMemberEvent.stateKey == null || roomMemberEvent.type == null) {
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.read.SetReadMarkersTask
import org.matrix.android.sdk.internal.task.Task
import org.matrix.android.sdk.internal.util.time.Clock
import java.util.concurrent.TimeUnit
import javax.inject.Inject
@ -53,7 +54,8 @@ internal class DefaultJoinRoomTask @Inject constructor(
@SessionDatabase
private val realmConfiguration: RealmConfiguration,
private val roomChangeMembershipStateDataSource: RoomChangeMembershipStateDataSource,
private val globalErrorReceiver: GlobalErrorReceiver
private val globalErrorReceiver: GlobalErrorReceiver,
private val clock: Clock,
) : JoinRoomTask {
override suspend fun execute(params: JoinRoomTask.Params) {
@ -90,7 +92,7 @@ internal class DefaultJoinRoomTask @Inject constructor(
throw JoinRoomFailure.JoinedWithTimeout
}
awaitTransaction(realmConfiguration) {
RoomSummaryEntity.where(it, roomId).findFirst()?.lastActivityTime = System.currentTimeMillis()
RoomSummaryEntity.where(it, roomId).findFirst()?.lastActivityTime = clock.epochMillis()
}
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.task.Task
import org.matrix.android.sdk.internal.util.awaitTransaction
import org.matrix.android.sdk.internal.util.time.Clock
import timber.log.Timber
import javax.inject.Inject
import kotlin.collections.set
@ -58,7 +59,8 @@ internal class DefaultSetReadMarkersTask @Inject constructor(
private val roomFullyReadHandler: RoomFullyReadHandler,
private val readReceiptHandler: ReadReceiptHandler,
@UserId private val userId: String,
private val globalErrorReceiver: GlobalErrorReceiver
private val globalErrorReceiver: GlobalErrorReceiver,
private val clock: Clock,
) : SetReadMarkersTask {
override suspend fun execute(params: SetReadMarkersTask.Params) {
@ -125,7 +127,7 @@ internal class DefaultSetReadMarkersTask @Inject constructor(
roomFullyReadHandler.handle(realm, roomId, FullyReadContent(readMarkerId))
}
if (readReceiptId != null) {
val readReceiptContent = ReadReceiptHandler.createContent(userId, readReceiptId)
val readReceiptContent = ReadReceiptHandler.createContent(userId, readReceiptId, clock.epochMillis())
readReceiptHandler.handle(realm, roomId, readReceiptContent, false, null)
}
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.LocalEchoRepository
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 javax.inject.Inject
internal class EventEditor @Inject constructor(private val eventSenderProcessor: EventSenderProcessor,
private val eventFactory: LocalEchoEventFactory,
private val localEchoRepository: LocalEchoRepository) {
internal class EventEditor @Inject constructor(
private val eventSenderProcessor: EventSenderProcessor,
private val eventFactory: LocalEchoEventFactory,
private val localEchoRepository: LocalEchoRepository,
private val clock: Clock,
) {
fun editTextMessage(targetEvent: TimelineEvent,
msgType: String,
@ -126,7 +130,7 @@ internal class EventEditor @Inject constructor(private val eventSenderProcessor:
}
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 ->
entity.content = editedEventEntity.content
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.task.Task
import org.matrix.android.sdk.internal.util.awaitTransaction
import org.matrix.android.sdk.internal.util.time.Clock
import timber.log.Timber
import javax.inject.Inject
@ -55,12 +56,14 @@ internal class DefaultFetchThreadSummariesTask @Inject constructor(
@SessionDatabase private val monarchy: Monarchy,
private val cryptoService: DefaultCryptoService,
@UserId private val userId: String,
private val clock: Clock,
) : FetchThreadSummariesTask {
override suspend fun execute(params: FetchThreadSummariesTask.Params): Result {
val filter = FilterFactory.createThreadsFilter(
numberOfEvents = params.limit,
userId = if (params.isUserParticipating) userId else null).toJSONString()
userId = if (params.isUserParticipating) userId else null
).toJSONString()
val response = executeRequest(
globalErrorReceiver,
@ -94,7 +97,9 @@ internal class DefaultFetchThreadSummariesTask @Inject constructor(
roomMemberContentsByUser = roomMemberContentsByUser,
roomEntity = roomEntity,
userId = userId,
cryptoService = cryptoService)
cryptoService = cryptoService,
currentTimeMillis = clock.epochMillis(),
)
}
}
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.room.model.RoomMemberContent
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.database.helper.addTimelineEvent
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.where
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.executeRequest
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.task.Task
import org.matrix.android.sdk.internal.util.awaitTransaction
import org.matrix.android.sdk.internal.util.time.Clock
import timber.log.Timber
import javax.inject.Inject
@ -85,10 +84,9 @@ internal interface FetchThreadTimelineTask : Task<FetchThreadTimelineTask.Params
internal class DefaultFetchThreadTimelineTask @Inject constructor(
private val roomAPI: RoomAPI,
private val globalErrorReceiver: GlobalErrorReceiver,
private val cryptoSessionInfoProvider: CryptoSessionInfoProvider,
@SessionDatabase private val monarchy: Monarchy,
@UserId private val userId: String,
private val cryptoService: DefaultCryptoService
private val cryptoService: DefaultCryptoService,
private val clock: Clock,
) : FetchThreadTimelineTask {
enum class Result {
@ -156,7 +154,8 @@ internal class DefaultFetchThreadTimelineTask @Inject constructor(
eventEntity = eventEntity,
direction = PaginationDirection.FORWARDS,
ownedByThreadChunk = true,
roomMemberContentsByUser = roomMemberContentsByUser)
roomMemberContentsByUser = roomMemberContentsByUser
)
}
}
@ -178,7 +177,8 @@ internal class DefaultFetchThreadTimelineTask @Inject constructor(
eventEntity = eventEntity,
direction = PaginationDirection.FORWARDS,
ownedByThreadChunk = true,
roomMemberContentsByUser = roomMemberContentsByUser)
roomMemberContentsByUser = roomMemberContentsByUser
)
}
}
}
@ -207,7 +207,7 @@ internal class DefaultFetchThreadTimelineTask @Inject constructor(
* Create an EventEntity to be added in the TimelineEventEntity
*/
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)
}

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.permalinks.PermalinkFactory
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 javax.inject.Inject
@ -90,7 +91,8 @@ internal class LocalEchoEventFactory @Inject constructor(
private val thumbnailExtractor: ThumbnailExtractor,
private val waveformSanitizer: WaveFormSanitizer,
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 {
if (msgType == MessageType.MSGTYPE_TEXT || msgType == MessageType.MSGTYPE_EMOTE) {
@ -242,7 +244,7 @@ internal class LocalEchoEventFactory @Inject constructor(
body = geoUri,
unstableLocationInfo = LocationInfo(geoUri = geoUri, description = geoUri),
unstableLocationAsset = LocationAsset(type = assetType),
unstableTimestampMillis = System.currentTimeMillis(),
unstableTimestampMillis = clock.epochMillis(),
unstableText = geoUri
)
return createMessageEvent(roomId, content)
@ -261,7 +263,7 @@ internal class LocalEchoEventFactory @Inject constructor(
eventId = beaconInfoEventId
),
unstableLocationInfo = LocationInfo(geoUri = geoUri, description = geoUri),
unstableTimestampMillis = System.currentTimeMillis(),
unstableTimestampMillis = clock.epochMillis(),
)
val localId = LocalEcho.createLocalEchoId()
return Event(
@ -547,7 +549,7 @@ internal class LocalEchoEventFactory @Inject constructor(
}
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.task.TaskExecutor
import org.matrix.android.sdk.internal.util.awaitTransaction
import org.matrix.android.sdk.internal.util.time.Clock
import timber.log.Timber
import java.util.UUID
import javax.inject.Inject
internal class LocalEchoRepository @Inject constructor(@SessionDatabase private val monarchy: Monarchy,
private val taskExecutor: TaskExecutor,
private val realmSessionProvider: RealmSessionProvider,
private val roomSummaryUpdater: RoomSummaryUpdater,
private val timelineInput: TimelineInput,
private val timelineEventMapper: TimelineEventMapper) {
internal class LocalEchoRepository @Inject constructor(
@SessionDatabase private val monarchy: Monarchy,
private val taskExecutor: TaskExecutor,
private val realmSessionProvider: RealmSessionProvider,
private val roomSummaryUpdater: RoomSummaryUpdater,
private val timelineInput: TimelineInput,
private val timelineEventMapper: TimelineEventMapper,
private val clock: Clock,
) {
fun createLocalEcho(event: 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")
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 myUser = roomMemberHelper.getLastRoomMember(senderId)
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) {
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)
updateEchoAsync(eventId) { realm, sendingEventEntity ->
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 eventId = localEchoIds.eventId
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)
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") }
}
Timber.v("## SendEvent: [${System.currentTimeMillis()}] Send event ${params.eventId}")
Timber.v("## SendEvent: Send event ${params.eventId}")
return try {
sendEventTask.execute(SendEventTask.Params(event, params.isEncrypted ?: cryptoService.isRoomEncrypted(event.roomId)))
Result.success()
} catch (exception: Throwable) {
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(
eventId = event.eventId,
roomId = event.roomId,
@ -104,7 +104,7 @@ internal class SendEventWorker(context: Context, params: WorkerParameters, sessi
)
Result.success()
} 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()
}
}

View File

@ -191,7 +191,7 @@ internal class EventSenderProcessorCoroutine @Inject constructor(
private suspend fun QueuedTask.waitForNetwork() = waitForNetworkSequencer.post {
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)
withContext(Dispatchers.IO) {
val hostAvailable = HomeServerAvailabilityChecker(sessionParams).check()

View File

@ -136,7 +136,7 @@ internal class EventSenderProcessorThread @Inject constructor(
private var retryNoNetworkTask: TimerTask? = null
override fun run() {
Timber.v("## SendThread started ts:${System.currentTimeMillis()}")
Timber.v("## SendThread started")
try {
while (!isInterrupted) {
Timber.v("## SendThread wait for task to process")
@ -151,7 +151,7 @@ internal class EventSenderProcessorThread @Inject constructor(
}
// we check for network connectivity
while (!canReachServer) {
Timber.v("## SendThread cannot reach server, wait ts:${System.currentTimeMillis()}")
Timber.v("## SendThread cannot reach server")
// schedule to retry
waitForNetwork()
// if thread as been killed meanwhile
@ -175,7 +175,7 @@ internal class EventSenderProcessorThread @Inject constructor(
canReachServer = false
if (task.retryCount.getAndIncrement() >= 3) task.onTaskFailed()
while (!canReachServer) {
Timber.v("## SendThread retryLoop cannot reach server, wait ts:${System.currentTimeMillis()}")
Timber.v("## SendThread retryLoop cannot reach server")
// schedule to retry
waitForNetwork()
}
@ -218,7 +218,7 @@ internal class EventSenderProcessorThread @Inject constructor(
// state = State.KILLED
// is this needed?
retryNoNetworkTask?.cancel()
Timber.w("## SendThread finished ${System.currentTimeMillis()}")
Timber.w("## SendThread finished")
}
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.task.SemaphoreCoroutineSequencer
import org.matrix.android.sdk.internal.util.createBackgroundHandler
import org.matrix.android.sdk.internal.util.time.Clock
import timber.log.Timber
import java.util.UUID
import java.util.concurrent.CopyOnWriteArrayList
import java.util.concurrent.atomic.AtomicBoolean
import java.util.concurrent.atomic.AtomicReference
internal class DefaultTimeline(private val roomId: String,
private val initialEventId: String?,
private val realmConfiguration: RealmConfiguration,
private val loadRoomMembersTask: LoadRoomMembersTask,
private val readReceiptHandler: ReadReceiptHandler,
private val settings: TimelineSettings,
private val coroutineDispatchers: MatrixCoroutineDispatchers,
paginationTask: PaginationTask,
getEventTask: GetContextOfEventTask,
fetchTokenAndPaginateTask: FetchTokenAndPaginateTask,
fetchThreadTimelineTask: FetchThreadTimelineTask,
timelineEventMapper: TimelineEventMapper,
timelineInput: TimelineInput,
threadsAwarenessHandler: ThreadsAwarenessHandler,
lightweightSettingsStorage: LightweightSettingsStorage,
eventDecryptor: TimelineEventDecryptor) : Timeline {
internal class DefaultTimeline(
private val roomId: String,
private val initialEventId: String?,
private val realmConfiguration: RealmConfiguration,
private val loadRoomMembersTask: LoadRoomMembersTask,
private val readReceiptHandler: ReadReceiptHandler,
private val settings: TimelineSettings,
private val coroutineDispatchers: MatrixCoroutineDispatchers,
private val clock: Clock,
paginationTask: PaginationTask,
getEventTask: GetContextOfEventTask,
fetchTokenAndPaginateTask: FetchTokenAndPaginateTask,
fetchThreadTimelineTask: FetchThreadTimelineTask,
timelineEventMapper: TimelineEventMapper,
timelineInput: TimelineInput,
threadsAwarenessHandler: ThreadsAwarenessHandler,
lightweightSettingsStorage: LightweightSettingsStorage,
eventDecryptor: TimelineEventDecryptor,
) : Timeline {
companion object {
val BACKGROUND_HANDLER = createBackgroundHandler("DefaultTimeline_Thread")
@ -370,7 +374,8 @@ internal class DefaultTimeline(private val roomId: String,
roomId = roomId,
timelineId = timelineID,
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.sync.handler.room.ReadReceiptHandler
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(
@Assisted private val roomId: String,
@ -50,8 +51,9 @@ internal class DefaultTimelineService @AssistedInject constructor(
private val lightweightSettingsStorage: LightweightSettingsStorage,
private val readReceiptHandler: ReadReceiptHandler,
private val coroutineDispatchers: MatrixCoroutineDispatchers,
private val timelineEventDataSource: TimelineEventDataSource
) : TimelineService {
private val timelineEventDataSource: TimelineEventDataSource,
private val clock: Clock,
) : TimelineService {
@AssistedFactory
interface Factory {
@ -75,7 +77,8 @@ internal class DefaultTimelineService @AssistedInject constructor(
readReceiptHandler = readReceiptHandler,
getEventTask = contextOfEventTask,
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.session.room.RoomAPI
import org.matrix.android.sdk.internal.task.Task
import org.matrix.android.sdk.internal.util.time.Clock
import javax.inject.Inject
internal interface GetEventTask : Task<GetEventTask.Params, Event> {
@ -36,7 +37,8 @@ internal interface GetEventTask : Task<GetEventTask.Params, Event> {
internal class DefaultGetEventTask @Inject constructor(
private val roomAPI: RoomAPI,
private val globalErrorReceiver: GlobalErrorReceiver,
private val eventDecryptor: EventDecryptor
private val eventDecryptor: EventDecryptor,
private val clock: Clock,
) : GetEventTask {
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
}

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.session.room.relation.threads.FetchThreadTimelineTask
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 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.
*/
internal class LoadTimelineStrategy(
internal class LoadTimelineStrategy constructor(
private val roomId: String,
private val timelineId: String,
private val mode: Mode,
private val dependencies: Dependencies) {
private val dependencies: Dependencies,
clock: Clock,
) {
sealed interface 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(
roomId = roomId,
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.session.StreamEventsManager
import org.matrix.android.sdk.internal.util.awaitTransaction
import org.matrix.android.sdk.internal.util.time.Clock
import timber.log.Timber
import javax.inject.Inject
@ -54,7 +55,9 @@ internal class TokenChunkEventPersistor @Inject constructor(
@SessionDatabase private val monarchy: Monarchy,
@UserId private val userId: String,
private val lightweightSettingsStorage: LightweightSettingsStorage,
private val liveEventManager: Lazy<StreamEventsManager>) {
private val liveEventManager: Lazy<StreamEventsManager>,
private val clock: Clock,
) {
enum class Result {
SHOULD_FETCH_MORE,
@ -166,7 +169,7 @@ internal class TokenChunkEventPersistor @Inject constructor(
val eventList = receivedChunk.events
val stateEvents = receivedChunk.stateEvents
val now = System.currentTimeMillis()
val now = clock.epochMillis()
stateEvents?.forEach { stateEvent ->
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.send.SendState
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
import org.matrix.android.sdk.internal.util.time.Clock
import timber.log.Timber
import java.util.Collections
internal class UIEchoManager(private val listener: Listener) {
internal class UIEchoManager(
private val listener: Listener,
private val clock: Clock,
) {
interface Listener {
fun rebuildEvent(eventId: String, builder: (TimelineEvent) -> TimelineEvent?): Boolean
@ -109,7 +113,7 @@ internal class UIEchoManager(private val listener: Listener) {
key = uiEchoReaction.reaction,
count = 1,
addedByMe = true,
firstTimestamp = System.currentTimeMillis(),
firstTimestamp = clock.epochMillis(),
sourceEvents = emptyList(),
localEchoEvents = listOf(uiEchoReaction.localEchoId)
).let { updateReactions.add(it) }
@ -143,7 +147,7 @@ internal class UIEchoManager(private val listener: Listener) {
fun updateSentStateWithUiEcho(timelineEvent: TimelineEvent): TimelineEvent {
if (timelineEvent.root.sendState.isSent()) 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(
root = timelineEvent.root.copyAll()
.also { it.sendState = inMemoryState }

View File

@ -20,6 +20,7 @@ import com.squareup.moshi.JsonClass
import okio.buffer
import okio.source
import org.matrix.android.sdk.internal.di.MoshiProvider
import org.matrix.android.sdk.internal.util.time.Clock
import timber.log.Timber
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
*/
internal class FileInitialSyncStatusRepository(directory: File) : InitialSyncStatusRepository {
internal class FileInitialSyncStatusRepository(
directory: File,
private val clock: Clock,
) : InitialSyncStatusRepository {
companion object {
// After 2 hours, we consider that the downloaded file is outdated:
@ -64,7 +68,7 @@ internal class FileInitialSyncStatusRepository(directory: File) : InitialSyncSta
ensureCache()
val state = cache?.step ?: InitialSyncStatus.STEP_INIT
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")
// The downloaded file is outdated
setStep(InitialSyncStatus.STEP_INIT)
@ -79,7 +83,7 @@ internal class FileInitialSyncStatusRepository(directory: File) : InitialSyncSta
if (step == InitialSyncStatus.STEP_DOWNLOADED) {
// Also store the downloaded date
newStatus = newStatus.copy(
downloadedDate = System.currentTimeMillis()
downloadedDate = clock.epochMillis()
)
}
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.task.Task
import org.matrix.android.sdk.internal.util.logDuration
import org.matrix.android.sdk.internal.util.time.Clock
import retrofit2.Response
import retrofit2.awaitResponse
import timber.log.Timber
@ -78,11 +79,12 @@ internal class DefaultSyncTask @Inject constructor(
@SessionFilesDirectory
private val fileDirectory: File,
private val syncResponseParser: InitialSyncResponseParser,
private val roomSyncEphemeralTemporaryStore: RoomSyncEphemeralTemporaryStore
private val roomSyncEphemeralTemporaryStore: RoomSyncEphemeralTemporaryStore,
private val clock: Clock,
) : SyncTask {
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 {
return syncTaskSequencer.post {
@ -111,7 +113,8 @@ internal class DefaultSyncTask @Inject constructor(
userStore.createOrUpdate(
userId = userId,
displayName = user?.displayName,
avatarUrl = user?.avatarUrl)
avatarUrl = user?.avatarUrl
)
defaultSyncStatusService.startRoot(InitSyncStep.ImportingAccount, 100)
}
// Maybe refresh the homeserver capabilities data we know
@ -124,7 +127,7 @@ internal class DefaultSyncTask @Inject constructor(
if (isInitialSync) {
Timber.tag(loggerTag.value).d("INIT_SYNC with filter: ${requestParams["filter"]}")
val initSyncStrategy = initialSyncStrategy
logDuration("INIT_SYNC strategy: $initSyncStrategy", loggerTag) {
logDuration("INIT_SYNC strategy: $initSyncStrategy", loggerTag, clock) {
if (initSyncStrategy is InitialSyncStrategy.Optimized) {
roomSyncEphemeralTemporaryStore.reset()
workingDir.mkdirs()
@ -135,7 +138,7 @@ internal class DefaultSyncTask @Inject constructor(
// Delete all files
workingDir.deleteRecursively()
} else {
val syncResponse = logDuration("INIT_SYNC Request", loggerTag) {
val syncResponse = logDuration("INIT_SYNC Request", loggerTag, clock) {
executeRequest(globalErrorReceiver) {
syncAPI.sync(
params = requestParams,
@ -146,7 +149,7 @@ internal class DefaultSyncTask @Inject constructor(
// We cannot distinguish request and download in this case.
syncStatisticsData.requestInitSyncTime = SystemClock.elapsedRealtime()
syncStatisticsData.downloadInitSyncTime = syncStatisticsData.requestInitSyncTime
logDuration("INIT_SYNC Database insertion", loggerTag) {
logDuration("INIT_SYNC Database insertion", loggerTag, clock) {
syncResponseHandler.handleResponse(syncResponse, token, defaultSyncStatusService)
}
syncResponseToReturn = syncResponse
@ -174,10 +177,12 @@ internal class DefaultSyncTask @Inject constructor(
Timber.tag(loggerTag.value).d(
"Incremental sync request parsing, $nbRooms room(s) $nbToDevice toDevice(s). Got nextBatch: $nextBatch"
)
defaultSyncStatusService.setStatus(SyncStatusService.Status.IncrementalSyncParsing(
rooms = nbRooms,
toDevice = nbToDevice
))
defaultSyncStatusService.setStatus(
SyncStatusService.Status.IncrementalSyncParsing(
rooms = nbRooms,
toDevice = nbToDevice
)
)
syncResponseHandler.handleResponse(syncResponse, token, null)
syncResponseToReturn = syncResponse
Timber.tag(loggerTag.value).d("Incremental sync done")
@ -201,14 +206,14 @@ internal class DefaultSyncTask @Inject constructor(
}
} else {
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) {
getSyncResponse(requestParams, MAX_NUMBER_OF_RETRY_AFTER_TIMEOUT)
}
}
syncStatisticsData.requestInitSyncTime = SystemClock.elapsedRealtime()
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) {
syncResponse.body()?.byteStream()?.use { inputStream ->
workingFile.outputStream().use { outputStream ->
@ -247,8 +252,8 @@ internal class DefaultSyncTask @Inject constructor(
}
private suspend fun handleSyncFile(workingFile: File, initSyncStrategy: InitialSyncStrategy.Optimized): SyncResponse {
return logDuration("INIT_SYNC handleSyncFile()", loggerTag) {
val syncResponse = logDuration("INIT_SYNC Read file and parse", loggerTag) {
return logDuration("INIT_SYNC handleSyncFile()", loggerTag, clock) {
val syncResponse = logDuration("INIT_SYNC Read file and parse", loggerTag, clock) {
syncResponseParser.parse(initSyncStrategy, workingFile)
}
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 }
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)
}
initialSyncStatusRepository.setStep(InitialSyncStatus.STEP_SUCCESS)

View File

@ -44,12 +44,14 @@ internal class ReadReceiptHandler @Inject constructor(
companion object {
fun createContent(userId: String, eventId: String): ReadReceiptContent {
fun createContent(userId: String,
eventId: String,
currentTimeMillis: Long): ReadReceiptContent {
return mapOf(
eventId to mapOf(
READ_KEY 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.parsing.RoomSyncAccountDataHandler
import org.matrix.android.sdk.internal.util.computeBestChunkSize
import org.matrix.android.sdk.internal.util.time.Clock
import timber.log.Timber
import javax.inject.Inject
internal class RoomSyncHandler @Inject constructor(private val readReceiptHandler: ReadReceiptHandler,
private val roomSummaryUpdater: RoomSummaryUpdater,
private val roomAccountDataHandler: RoomSyncAccountDataHandler,
private val cryptoService: DefaultCryptoService,
private val roomMemberEventHandler: RoomMemberEventHandler,
private val roomTypingUsersHandler: RoomTypingUsersHandler,
private val threadsAwarenessHandler: ThreadsAwarenessHandler,
private val roomChangeMembershipStateDataSource: RoomChangeMembershipStateDataSource,
@UserId private val userId: String,
private val homeServerCapabilitiesService: HomeServerCapabilitiesService,
private val lightweightSettingsStorage: LightweightSettingsStorage,
private val timelineInput: TimelineInput,
private val liveEventService: Lazy<StreamEventsManager>) {
internal class RoomSyncHandler @Inject constructor(
private val readReceiptHandler: ReadReceiptHandler,
private val roomSummaryUpdater: RoomSummaryUpdater,
private val roomAccountDataHandler: RoomSyncAccountDataHandler,
private val cryptoService: DefaultCryptoService,
private val roomMemberEventHandler: RoomMemberEventHandler,
private val roomTypingUsersHandler: RoomTypingUsersHandler,
private val threadsAwarenessHandler: ThreadsAwarenessHandler,
private val roomChangeMembershipStateDataSource: RoomChangeMembershipStateDataSource,
@UserId private val userId: String,
private val homeServerCapabilitiesService: HomeServerCapabilitiesService,
private val lightweightSettingsStorage: LightweightSettingsStorage,
private val timelineInput: TimelineInput,
private val liveEventService: Lazy<StreamEventsManager>,
private val clock: Clock,
) {
sealed class HandlingStrategy {
data class JOINED(val data: Map<String, RoomSync>) : HandlingStrategy()
@ -130,7 +134,7 @@ internal class RoomSyncHandler @Inject constructor(private val readReceiptHandle
} else {
EventInsertType.INCREMENTAL_SYNC
}
val syncLocalTimeStampMillis = System.currentTimeMillis()
val syncLocalTimeStampMillis = clock.epochMillis()
val rooms = when (handlingStrategy) {
is HandlingStrategy.JOINED -> {
if (isInitialSync && initialSyncStrategy is InitialSyncStrategy.Optimized) {
@ -440,7 +444,8 @@ internal class RoomSyncHandler @Inject constructor(private val readReceiptHandle
threadEventEntity = eventEntity,
roomMemberContentsByUser = roomMemberContentsByUser,
userId = userId,
roomEntity = roomEntity
roomEntity = roomEntity,
currentTimeMillis = clock.epochMillis(),
)
}
} ?: 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.api.logger.LoggerTag
import org.matrix.android.sdk.internal.util.time.Clock
import timber.log.Timber
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,
loggerTag: LoggerTag,
clock: Clock,
block: suspend () -> T): T {
Timber.tag(loggerTag.value).d("$message -- BEGIN")
val start = System.currentTimeMillis()
val start = clock.epochMillis()
val result = logRamUsage(message) {
block()
}
val duration = System.currentTimeMillis() - start
val duration = clock.epochMillis() - start
Timber.tag(loggerTag.value).d("$message -- END duration: $duration ms")
return result

View File

@ -18,10 +18,15 @@ package org.matrix.android.sdk.internal.util.system
import dagger.Binds
import dagger.Module
import org.matrix.android.sdk.internal.util.time.Clock
import org.matrix.android.sdk.internal.util.time.DefaultClock
@Module
internal abstract class SystemModule {
@Binds
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
### 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.BottomSheetDialogFragment
import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment
import im.vector.app.core.time.DefaultClock
import im.vector.app.espresso.tools.waitUntilViewVisible
import org.hamcrest.Matcher
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.util.Optional
import java.util.concurrent.TimeoutException
import kotlin.random.Random
object EspressoHelper {
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 {
return object : ViewAction {
private val clock = DefaultClock()
override fun getConstraints(): Matcher<View> {
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) {
println("*** waitForView 1 $view")
uiController.loopMainThreadUntilIdle()
val startTime = System.currentTimeMillis()
val startTime = clock.epochMillis()
val endTime = startTime + timeout
val visibleMatcher = isDisplayed()
uiController.loopMainThreadForAtLeast(100)
do {
println("*** waitForView loop $view end:$endTime current:${System.currentTimeMillis()}")
println("*** waitForView loop $view end:$endTime current:${clock.epochMillis()}")
val viewVisible = TreeIterables.breadthFirstViewTraversal(view)
.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...")
uiController.loopMainThreadForAtLeast(50)
println("*** waitForView loop ...loopMainThreadForAtLeast")
} while (System.currentTimeMillis() < endTime)
} while (clock.epochMillis() < endTime)
println("*** waitForView timeout $view")
// Timeout happens.
@ -168,9 +172,9 @@ fun activityIdlingResource(activityClass: Class<*>): IdlingResource {
val res = object : IdlingResource, ActivityLifecycleCallback {
private var callback: IdlingResource.ResourceCallback? = 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 {
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.Test
import org.junit.runner.RunWith
import kotlin.random.Random
@RunWith(AndroidJUnit4::class)
@LargeTest
@ -44,7 +45,7 @@ class RegistrationTest {
@Test
fun simpleRegister() {
val userId: String = "UiAutoTest_${System.currentTimeMillis()}"
val userId: String = "UiAutoTest_${Random.nextLong()}"
val password: String = "password"
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.runner.RunWith
import org.matrix.android.sdk.api.session.Session
import kotlin.random.Random
@RunWith(AndroidJUnit4::class)
@LargeTest
@ -61,7 +62,7 @@ class SecurityBootstrapTest : VerificationTestBase() {
@Before
fun createSessionWithCrossSigning() {
val matrix = getMatrixInstance()
val userName = "foobar_${System.currentTimeMillis()}"
val userName = "foobar_${Random.nextLong()}"
existingSession = createAccountAndSync(matrix, userName, password, true)
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 kotlin.coroutines.Continuation
import kotlin.coroutines.resume
import kotlin.random.Random
@RunWith(AndroidJUnit4::class)
@LargeTest
@ -66,7 +67,7 @@ class VerifySessionInteractiveTest : VerificationTestBase() {
@Before
fun createSessionWithCrossSigning() {
val matrix = getMatrixInstance()
val userName = "foobar_${System.currentTimeMillis()}"
val userName = "foobar_${Random.nextLong()}"
existingSession = createAccountAndSync(matrix, userName, password, true)
doSync<Unit> {
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 kotlin.coroutines.Continuation
import kotlin.coroutines.resume
import kotlin.random.Random
@RunWith(AndroidJUnit4::class)
@LargeTest
@ -68,7 +69,7 @@ class VerifySessionPassphraseTest : VerificationTestBase() {
fun createSessionWithCrossSigningAnd4S() {
val context = InstrumentationRegistry.getInstrumentation().targetContext
val matrix = getMatrixInstance()
val userName = "foobar_${System.currentTimeMillis()}"
val userName = "foobar_${Random.nextLong()}"
existingSession = createAccountAndSync(matrix, userName, password, true)
doSync<Unit> {
existingSession!!.cryptoService().crossSigningService()

View File

@ -24,6 +24,7 @@ import android.os.Build
import android.os.Environment
import android.provider.MediaStore
import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
import im.vector.app.core.time.DefaultClock
import org.junit.rules.TestWatcher
import org.junit.runner.Description
import timber.log.Timber
@ -54,7 +55,7 @@ private fun storeFailureScreenshot(bitmap: Bitmap, screenshotName: String) {
val contentValues = ContentValues().apply {
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) {
useMediaStoreScreenshotStorage(

View File

@ -29,6 +29,7 @@ import im.vector.app.R
import im.vector.app.core.di.ActiveSessionHolder
import im.vector.app.core.extensions.registerStartForActivityResult
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.checkPermissions
import im.vector.app.core.utils.registerForPermissionsResult
@ -59,6 +60,8 @@ class DebugMenuActivity : VectorBaseActivity<ActivityDebugMenuBinding>() {
@Inject
lateinit var activeSessionHolder: ActiveSessionHolder
@Inject
lateinit var clock: Clock
private lateinit var buffer: ByteArray
@ -165,7 +168,7 @@ class DebugMenuActivity : VectorBaseActivity<ActivityDebugMenuBinding>() {
}
val builder = NotificationCompat.Builder(this, "CHAN")
.setWhen(System.currentTimeMillis())
.setWhen(clock.epochMillis())
.setContentTitle("Title")
.setContentText("Content")
// No effect because it's a group summary notif
@ -180,16 +183,16 @@ class DebugMenuActivity : VectorBaseActivity<ActivityDebugMenuBinding>() {
.setName("User name")
.build()
)
.addMessage("Message 1 - 1", System.currentTimeMillis(), Person.Builder().setName("user 1-1").build())
.addMessage("Message 1 - 2", System.currentTimeMillis(), Person.Builder().setName("user 1-2").build())
.addMessage("Message 1 - 1", clock.epochMillis(), Person.Builder().setName("user 1-1").build())
.addMessage("Message 1 - 2", clock.epochMillis(), Person.Builder().setName("user 1-2").build())
val messagingStyle2 = NotificationCompat.MessagingStyle(
Person.Builder()
.setName("User name 2")
.build()
)
.addMessage("Message 2 - 1", System.currentTimeMillis(), Person.Builder().setName("user 1-1").build())
.addMessage("Message 2 - 2", System.currentTimeMillis(), Person.Builder().setName("user 1-2").build())
.addMessage("Message 2 - 1", clock.epochMillis(), Person.Builder().setName("user 1-1").build())
.addMessage("Message 2 - 2", clock.epochMillis(), Person.Builder().setName("user 1-2").build())
notificationManager.notify(10, builder.build())
@ -197,7 +200,7 @@ class DebugMenuActivity : VectorBaseActivity<ActivityDebugMenuBinding>() {
11,
NotificationCompat.Builder(this, "CHAN")
.setChannelId("CHAN")
.setWhen(System.currentTimeMillis())
.setWhen(clock.epochMillis())
.setContentTitle("Title 1")
.setContentText("Content 1")
// For shortcut on long press on launcher icon
@ -211,7 +214,7 @@ class DebugMenuActivity : VectorBaseActivity<ActivityDebugMenuBinding>() {
notificationManager.notify(
12,
NotificationCompat.Builder(this, "CHAN2")
.setWhen(System.currentTimeMillis())
.setWhen(clock.epochMillis())
.setContentTitle("Title 2")
.setContentText("Content 2")
.setStyle(messagingStyle2)

View File

@ -18,13 +18,17 @@ package im.vector.app.fdroid
import android.content.Context
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.features.settings.BackgroundSyncMode
import im.vector.app.features.settings.VectorPreferences
import timber.log.Timber
object BackgroundSyncStarter {
fun start(context: Context, vectorPreferences: VectorPreferences, activeSessionHolder: ActiveSessionHolder) {
fun start(context: Context,
vectorPreferences: VectorPreferences,
activeSessionHolder: ActiveSessionHolder,
clock: Clock) {
if (vectorPreferences.areNotificationEnabledForDevice()) {
val activeSession = activeSessionHolder.getSafeActiveSession() ?: return
when (vectorPreferences.getFdroidSyncBackgroundMode()) {
@ -38,7 +42,12 @@ object BackgroundSyncStarter {
}
BackgroundSyncMode.FDROID_BACKGROUND_SYNC_MODE_FOR_REALTIME -> {
// 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")
}
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.platform.PendingIntentCompat
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 timber.log.Timber
@ -34,10 +35,13 @@ class AlarmSyncBroadcastReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
Timber.d("## Sync: AlarmSyncBroadcastReceiver received intent")
val vectorPreferences = context.singletonEntryPoint()
.takeIf { it.activeSessionHolder().getSafeActiveSession() != null }
?.vectorPreferences()
?: return Unit.also { Timber.v("No active session, so don't launch sync service.") }
val singletonEntryPoint = context.singletonEntryPoint()
if (singletonEntryPoint.activeSessionHolder().getSafeActiveSession() == null) {
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
VectorSyncService.newPeriodicIntent(
@ -52,7 +56,7 @@ class AlarmSyncBroadcastReceiver : BroadcastReceiver() {
ContextCompat.startForegroundService(context, it)
} catch (ex: Throwable) {
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)
}
}
@ -61,7 +65,7 @@ class AlarmSyncBroadcastReceiver : BroadcastReceiver() {
companion object {
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
Timber.v("## Sync: Scheduling alarm for background sync in $delayInSeconds seconds")
val intent = Intent(context, AlarmSyncBroadcastReceiver::class.java).apply {
@ -74,7 +78,7 @@ class AlarmSyncBroadcastReceiver : BroadcastReceiver() {
intent,
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>()!!
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
alarmMgr.setAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, firstMillis, pIntent)

View File

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

View File

@ -21,6 +21,7 @@ import android.app.Activity
import android.content.Context
import im.vector.app.core.di.ActiveSessionHolder
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.receiver.AlarmSyncBroadcastReceiver
import im.vector.app.features.settings.VectorPreferences
@ -66,7 +67,10 @@ object FcmHelper {
AlarmSyncBroadcastReceiver.cancelAlarm(context)
}
fun onEnterBackground(context: Context, vectorPreferences: VectorPreferences, activeSessionHolder: ActiveSessionHolder) {
BackgroundSyncStarter.start(context, vectorPreferences, activeSessionHolder)
fun onEnterBackground(context: Context,
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.DefaultSharedPreferences
import im.vector.app.core.pushers.PushersManager
import im.vector.app.core.time.Clock
import im.vector.app.features.settings.VectorPreferences
import timber.log.Timber
@ -107,7 +108,10 @@ object FcmHelper {
}
@Suppress("UNUSED_PARAMETER")
fun onEnterBackground(context: Context, vectorPreferences: VectorPreferences, activeSessionHolder: ActiveSessionHolder) {
fun onEnterBackground(context: Context,
vectorPreferences: VectorPreferences,
activeSessionHolder: ActiveSessionHolder,
clock: Clock) {
// 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.extensions.configureAndStart
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.call.webrtc.WebRtcCallManager
import im.vector.app.features.configuration.VectorConfiguration
@ -85,6 +86,7 @@ class VectorApplication :
@Inject lateinit var emojiCompatWrapper: EmojiCompatWrapper
@Inject lateinit var vectorUncaughtExceptionHandler: VectorUncaughtExceptionHandler
@Inject lateinit var activeSessionHolder: ActiveSessionHolder
@Inject lateinit var clock: Clock
@Inject lateinit var notificationDrawerManager: NotificationDrawerManager
@Inject lateinit var vectorPreferences: VectorPreferences
@Inject lateinit var versionProvider: VersionProvider
@ -180,7 +182,7 @@ class VectorApplication :
override fun onPause(owner: LifecycleOwner) {
Timber.i("App entered background")
FcmHelper.onEnterBackground(appContext, vectorPreferences, activeSessionHolder)
FcmHelper.onEnterBackground(appContext, vectorPreferences, activeSessionHolder, clock)
}
})
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.LocaleProvider
import im.vector.app.core.resources.toTimestamp
import im.vector.app.core.time.Clock
import org.threeten.bp.LocalDateTime
import org.threeten.bp.Period
import org.threeten.bp.format.DateTimeFormatter
import javax.inject.Inject
import kotlin.math.absoluteValue
class VectorDateFormatter @Inject constructor(private val context: Context,
private val localeProvider: LocaleProvider,
private val dateFormatterProviders: DateFormatterProviders) {
class VectorDateFormatter @Inject constructor(
private val context: Context,
private val localeProvider: LocaleProvider,
private val dateFormatterProviders: DateFormatterProviders,
private val clock: Clock,
) {
private val hourFormatter by lazy {
if (DateFormat.is24HourFormat(context)) {
@ -158,8 +162,9 @@ class VectorDateFormatter @Inject constructor(private val context: Context,
private fun getRelativeDay(ts: Long): String {
return DateUtils.getRelativeTimeSpanString(
ts,
System.currentTimeMillis(),
clock.epochMillis(),
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 im.vector.app.core.dialogs.UnrecognizedCertificateDialog
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.call.webrtc.WebRtcCallManager
import im.vector.app.features.home.AvatarRenderer
@ -46,6 +47,8 @@ interface SingletonEntryPoint {
fun navigator(): Navigator
fun clock(): Clock
fun errorFormatter(): ErrorFormatter
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.registerStartForActivityResult
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.checkPermissions
import im.vector.app.core.utils.onPermissionDeniedDialog
@ -45,7 +46,8 @@ import java.io.File
class GalleryOrCameraDialogHelper(
// must implement GalleryOrCameraDialogHelper.Listener
private val fragment: Fragment,
private val colorProvider: ColorProvider
private val colorProvider: ColorProvider,
private val clock: Clock,
) {
interface Listener {
fun onImageReady(uri: Uri?)
@ -91,7 +93,7 @@ class GalleryOrCameraDialogHelper(
}
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
createUCropWithDefaultSettings(colorProvider, uri, destinationFile.toUri(), fragment.getString(R.string.rotate_and_crop_screen_title))
.withAspectRatio(1f, 1f)

View File

@ -33,6 +33,8 @@ import androidx.work.WorkerParameters
import dagger.hilt.android.AndroidEntryPoint
import im.vector.app.R
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.settings.BackgroundSyncMode
import org.matrix.android.sdk.api.Matrix
@ -77,6 +79,7 @@ class VectorSyncService : SyncService() {
@Inject lateinit var notificationUtils: NotificationUtils
@Inject lateinit var matrix: Matrix
@Inject lateinit var clock: Clock
override fun provideMatrix() = matrix
@ -102,7 +105,8 @@ class VectorSyncService : SyncService() {
syncTimeoutSeconds = syncTimeoutSeconds,
syncDelaySeconds = syncDelaySeconds,
isPeriodic = true,
isNetworkBack = false
isNetworkBack = false,
currentTimeMillis = clock.epochMillis()
)
}
@ -114,9 +118,10 @@ class VectorSyncService : SyncService() {
val rescheduleSyncWorkRequest: WorkRequest =
OneTimeWorkRequestBuilder<RestartWhenNetworkOn>()
.setInputData(RestartWhenNetworkOn.createInputData(sessionId, syncTimeoutSeconds, syncDelaySeconds, isPeriodic))
.setConstraints(Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.build()
.setConstraints(
Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.build()
)
.build()
@ -137,20 +142,27 @@ class VectorSyncService : SyncService() {
}
// I do not move or rename this class, since I'm not sure about the side effect regarding the WorkManager
class RestartWhenNetworkOn(appContext: Context, workerParams: WorkerParameters) :
Worker(appContext, workerParams) {
class RestartWhenNetworkOn(
appContext: Context,
workerParams: WorkerParameters
) : Worker(appContext, workerParams) {
override fun doWork(): Result {
Timber.d("## Sync: RestartWhenNetworkOn.doWork()")
val sessionId = inputData.getString(KEY_SESSION_ID) ?: return Result.failure()
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 isPeriodic = inputData.getBoolean(KEY_IS_PERIODIC, false)
// Not sure how to inject a Clock here
val clock = DefaultClock()
applicationContext.rescheduleSyncService(
sessionId = sessionId,
syncTimeoutSeconds = syncTimeoutSeconds,
syncDelaySeconds = syncDelaySeconds,
isPeriodic = isPeriodic,
isNetworkBack = true
isNetworkBack = true,
currentTimeMillis = clock.epochMillis()
)
// Indicate whether the work finished successfully with the Result
return Result.success()
@ -182,7 +194,8 @@ private fun Context.rescheduleSyncService(sessionId: String,
syncTimeoutSeconds: Int,
syncDelaySeconds: Int,
isPeriodic: Boolean,
isNetworkBack: Boolean) {
isNetworkBack: Boolean,
currentTimeMillis: Long) {
Timber.d("## Sync: rescheduleSyncService")
val intent = if (isPeriodic) {
VectorSyncService.newPeriodicIntent(
@ -208,7 +221,7 @@ private fun Context.rescheduleSyncService(sessionId: String,
} else {
PendingIntent.getService(this, 0, intent, PendingIntentCompat.FLAG_IMMUTABLE)
}
val firstMillis = System.currentTimeMillis() + syncDelaySeconds * 1000L
val firstMillis = currentTimeMillis + syncDelaySeconds * 1000L
val alarmMgr = getSystemService<AlarmManager>()!!
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
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
*/
fun sendMailTo(address: String, subject: String? = null, message: String? = null, activity: Activity) {
val intent = Intent(Intent.ACTION_SENDTO, Uri.fromParts(
"mailto", address, null))
val intent = Intent(
Intent.ACTION_SENDTO, Uri.fromParts(
"mailto", address, null
)
)
intent.putExtra(Intent.EXTRA_SUBJECT, subject)
intent.putExtra(Intent.EXTRA_TEXT, message)
@ -248,7 +251,12 @@ private fun appendTimeToFilename(name: String): String {
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) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
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.DISPLAY_NAME, filename)
put(MediaStore.Images.Media.MIME_TYPE, mediaMimeType)
put(MediaStore.Images.Media.DATE_ADDED, System.currentTimeMillis())
put(MediaStore.Images.Media.DATE_TAKEN, System.currentTimeMillis())
put(MediaStore.Images.Media.DATE_ADDED, currentTimeMillis)
put(MediaStore.Images.Media.DATE_TAKEN, currentTimeMillis)
}
val externalContentUri = when {
mediaMimeType?.isMimeTypeImage() == true -> MediaStore.Images.Media.EXTERNAL_CONTENT_URI
@ -289,7 +297,7 @@ suspend fun saveMedia(context: Context, file: File, title: String, mediaMimeType
}
}
} 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,
mediaMimeType: String?,
title: String,
file: File) {
file: File,
currentTimeMillis: Long) {
val state = Environment.getExternalStorageState()
if (Environment.MEDIA_MOUNTED != state) {
context.toast(context.getString(R.string.error_saving_media_file))
@ -319,7 +328,7 @@ private fun saveMediaLegacy(context: Context,
} else {
title
}
val savedFile = saveFileIntoLegacy(file, downloadDir, outputFilename)
val savedFile = saveFileIntoLegacy(file, downloadDir, outputFilename, currentTimeMillis)
if (savedFile != null) {
val downloadManager = context.getSystemService<DownloadManager>()
downloadManager?.addCompletedDownload(
@ -329,7 +338,8 @@ private fun saveMediaLegacy(context: Context,
mediaMimeType ?: MimeTypes.OctetStream,
savedFile.absolutePath,
savedFile.length(),
true)
true
)
addToGallery(savedFile, mediaMimeType, context)
}
} catch (error: Throwable) {
@ -408,10 +418,11 @@ fun selectTxtFileToWrite(
* @param sourceFile the file source path
* @param dstDirPath the dst path
* @param outputFilename optional the output filename
* @param currentTimeMillis the current time in milliseconds
* @return the created file
*/
@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
var dstFileName: String
@ -423,7 +434,7 @@ fun saveFileIntoLegacy(sourceFile: File, dstDirPath: File, outputFilename: Strin
if (dotPos > 0) {
fileExt = sourceFile.name.substring(dotPos)
}
dstFileName = "vector_" + System.currentTimeMillis() + fileExt
dstFileName = "vector_$currentTimeMillis$fileExt"
} else {
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.platform.VectorBaseFragment
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.SnapOnScrollListener
import im.vector.app.core.utils.attachSnapHelperWithListener
@ -64,7 +65,8 @@ data class AttachmentsPreviewArgs(
class AttachmentsPreviewFragment @Inject constructor(
private val attachmentMiniaturePreviewController: AttachmentMiniaturePreviewController,
private val attachmentBigPreviewController: AttachmentBigPreviewController,
private val colorProvider: ColorProvider
private val colorProvider: ColorProvider,
private val clock: Clock,
) : VectorBaseFragment<FragmentAttachmentsPreviewBinding>(), AttachmentMiniaturePreviewController.Callback {
private val fragmentArgs: AttachmentsPreviewArgs by args()
@ -192,7 +194,7 @@ class AttachmentsPreviewFragment @Inject constructor(
private fun handleEditAction() = withState(viewModel) {
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
createUCropWithDefaultSettings(colorProvider, uri, destinationFile.toUri(), currentAttachment.name)
.getIntent(requireContext())

View File

@ -19,6 +19,7 @@ package im.vector.app.features.call.conference
import im.vector.app.R
import im.vector.app.core.network.await
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.toBase32String
import im.vector.app.features.call.conference.jwt.JitsiJWTFactory
@ -46,7 +47,9 @@ class JitsiService @Inject constructor(
private val rawService: RawService,
private val stringProvider: StringProvider,
private val themeProvider: ThemeProvider,
private val jitsiJWTFactory: JitsiJWTFactory) {
private val jitsiJWTFactory: JitsiJWTFactory,
private val clock: Clock,
) {
companion object {
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 {
// 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 {
rawService.getElementWellknown(session.sessionParams)
?.jitsiServer

View File

@ -21,6 +21,7 @@ import android.os.Binder
import android.os.IBinder
import dagger.hilt.android.AndroidEntryPoint
import im.vector.app.core.services.VectorService
import im.vector.app.core.time.Clock
import im.vector.app.features.notifications.NotificationUtils
import javax.inject.Inject
@ -28,6 +29,7 @@ import javax.inject.Inject
class ScreenCaptureService : VectorService() {
@Inject lateinit var notificationUtils: NotificationUtils
@Inject lateinit var clock: Clock
private val binder = LocalBinder()
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
@ -37,7 +39,7 @@ class ScreenCaptureService : VectorService() {
}
private fun showStickyNotification() {
val notificationId = System.currentTimeMillis().toInt()
val notificationId = clock.epochMillis().toInt()
val notification = notificationUtils.buildScreenSharingNotification()
startForeground(notificationId, notification)
}

Some files were not shown because too many files have changed in this diff Show More