From 40e26900b0edc59dba93ce7b1b9e96fabff57389 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 2 May 2022 17:21:26 +0200 Subject: [PATCH 01/11] Create a Clock SDK side (#4562) --- .../sdk/internal/util/system/SystemModule.kt | 5 +++ .../android/sdk/internal/util/time/Clock.kt | 36 +++++++++++++++++++ 2 files changed, 41 insertions(+) create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/time/Clock.kt diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/system/SystemModule.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/system/SystemModule.kt index 8a7b50175a..396d12f369 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/system/SystemModule.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/system/SystemModule.kt @@ -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 } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/time/Clock.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/time/Clock.kt new file mode 100644 index 0000000000..4fe9069b49 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/time/Clock.kt @@ -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() + } +} From 6a61e639e0705cacd19c4f9397fe5882af387ba6 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 3 May 2022 09:31:43 +0200 Subject: [PATCH 02/11] SDK: Replace usage of `System.currentTimeMillis()` by a `Clock` interface (#4562) Sometimes move to UUID or Random numbers instead. --- .../android/sdk/common/CommonTestHelper.kt | 8 +- .../crypto/AttachmentEncryptionTest.kt | 31 +++-- .../sdk/internal/crypto/CryptoStoreHelper.kt | 4 +- .../sdk/internal/crypto/CryptoStoreTest.kt | 6 +- .../session/room/timeline/ChunkEntityTest.kt | 8 +- .../timeline/FakeGetContextOfEventTask.kt | 4 +- .../room/timeline/FakePaginationTask.kt | 2 +- .../model/IncomingRequestCancellation.kt | 4 +- .../crypto/model/IncomingRoomKeyRequest.kt | 4 +- .../model/IncomingSecretShareRequest.kt | 4 +- .../verification/VerificationService.kt | 3 +- .../crypto/CancelGossipRequestWorker.kt | 4 +- .../internal/crypto/DefaultCryptoService.kt | 14 ++- .../sdk/internal/crypto/DeviceListManager.kt | 38 +++--- .../sdk/internal/crypto/EventDecryptor.kt | 4 +- .../internal/crypto/GossipingWorkManager.kt | 5 +- .../crypto/IncomingGossipingRequestManager.kt | 17 +-- .../crypto/MXMegolmExportEncryption.kt | 57 ++++----- .../sdk/internal/crypto/MXOlmDevice.kt | 11 +- .../internal/crypto/OneTimeKeysUploader.kt | 10 +- .../crypto/SendGossipRequestWorker.kt | 6 +- .../sdk/internal/crypto/SendGossipWorker.kt | 4 +- .../actions/MegolmSessionDataImporter.kt | 16 ++- .../algorithms/megolm/MXMegolmEncryption.kt | 33 +++--- .../megolm/MXMegolmEncryptionFactory.kt | 8 +- .../megolm/MXOutboundSessionInfo.kt | 7 +- .../attachments/MXEncryptedAttachments.kt | 21 ++-- .../crypto/keysbackup/KeysBackupPassword.kt | 62 +++++----- .../crypto/model/OlmSessionWrapper.kt | 4 +- .../crypto/store/db/RealmCryptoStore.kt | 48 +++++--- .../store/db/RealmCryptoStoreMigration.kt | 7 +- .../store/db/migration/MigrateCryptoTo008.kt | 8 +- ...tgoingSASDefaultVerificationTransaction.kt | 2 +- .../DefaultVerificationService.kt | 108 +++++++++++------- .../VerificationMessageProcessor.kt | 5 +- .../VerificationTransportRoomMessage.kt | 48 +++++--- ...VerificationTransportRoomMessageFactory.kt | 11 +- .../VerificationTransportToDevice.kt | 16 ++- .../VerificationTransportToDeviceFactory.kt | 7 +- .../sdk/internal/database/AsyncTransaction.kt | 15 +-- .../database/helper/ThreadSummaryHelper.kt | 17 +-- .../internal/database/mapper/EventMapper.kt | 8 +- .../internal/session/DefaultFileService.kt | 7 +- .../session/call/CallSignalingHandler.kt | 12 +- .../internal/session/call/MxCallFactory.kt | 10 +- .../internal/session/call/model/MxCallImpl.kt | 6 +- .../session/content/UploadContentWorker.kt | 6 +- .../DefaultContentScannerService.kt | 18 +-- .../db/ContentScannerEntityQueries.kt | 7 +- .../db/RealmContentScannerStore.kt | 10 +- .../EventRelationsAggregationProcessor.kt | 25 ++-- .../session/room/create/CreateRoomTask.kt | 6 +- .../room/membership/LoadRoomMembersTask.kt | 6 +- .../room/membership/joining/JoinRoomTask.kt | 6 +- .../session/room/read/SetReadMarkersTask.kt | 6 +- .../session/room/relation/EventEditor.kt | 12 +- .../threads/FetchThreadSummariesTask.kt | 9 +- .../threads/FetchThreadTimelineTask.kt | 16 +-- .../room/send/LocalEchoEventFactory.kt | 56 ++++++--- .../session/room/send/LocalEchoRepository.kt | 20 ++-- .../MultipleEventSendingDispatcherWorker.kt | 2 +- .../session/room/send/SendEventWorker.kt | 6 +- .../queue/EventSenderProcessorCoroutine.kt | 2 +- .../send/queue/EventSenderProcessorThread.kt | 8 +- .../session/room/timeline/DefaultTimeline.kt | 39 ++++--- .../room/timeline/DefaultTimelineService.kt | 9 +- .../session/room/timeline/GetEventTask.kt | 6 +- .../room/timeline/LoadTimelineStrategy.kt | 9 +- .../room/timeline/TokenChunkEventPersistor.kt | 7 +- .../session/room/timeline/UIEchoManager.kt | 10 +- .../sync/InitialSyncStatusRepository.kt | 10 +- .../sdk/internal/session/sync/SyncTask.kt | 35 +++--- .../sync/handler/room/ReadReceiptHandler.kt | 4 +- .../sync/handler/room/RoomSyncHandler.kt | 35 +++--- .../android/sdk/internal/util/LogUtil.kt | 6 +- 75 files changed, 690 insertions(+), 435 deletions(-) diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CommonTestHelper.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CommonTestHelper.kt index 63922f0226..733e62301e 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CommonTestHelper.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CommonTestHelper.kt @@ -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.replyInThread( rootThreadEventId = rootThreadEventId, - replyInThreadText = formattedMessage) + replyInThreadText = formattedMessage + ) } else { room.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 ) diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/AttachmentEncryptionTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/AttachmentEncryptionTest.kt index 732f4f7dce..f5f585a1e0 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/AttachmentEncryptionTest.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/AttachmentEncryptionTest.kt @@ -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 + ) + ) } } diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/CryptoStoreHelper.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/CryptoStoreHelper.kt index c717c8e33f..ba1afd4758 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/CryptoStoreHelper.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/CryptoStoreHelper.kt @@ -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(), ) } } diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/CryptoStoreTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/CryptoStoreTest.kt index f43c425cc9..3f75aa0979 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/CryptoStoreTest.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/CryptoStoreTest.kt @@ -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 diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/ChunkEntityTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/ChunkEntityTest.kt index 5c011c8b2f..27d3fdc856 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/ChunkEntityTest.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/ChunkEntityTest.kt @@ -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, 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( diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/FakeGetContextOfEventTask.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/FakeGetContextOfEventTask.kt index b86c86c0c7..ccf1c7c2c9 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/FakeGetContextOfEventTask.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/FakeGetContextOfEventTask.kt @@ -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) diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/FakePaginationTask.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/FakePaginationTask.kt index d09bfb18c6..f241be0c5c 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/FakePaginationTask.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/FakePaginationTask.kt @@ -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) } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/model/IncomingRequestCancellation.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/model/IncomingRequestCancellation.kt index 74ca7304f7..15f663d30d 100755 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/model/IncomingRequestCancellation.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/model/IncomingRequestCancellation.kt @@ -47,7 +47,7 @@ data class IncomingRequestCancellation( * * @param event the event */ - fun fromEvent(event: Event): IncomingRequestCancellation? { + fun fromEvent(event: Event, now: Long): IncomingRequestCancellation? { return event.getClearContent() .toModel() ?.let { @@ -55,7 +55,7 @@ data class IncomingRequestCancellation( userId = event.senderId, deviceId = it.requestingDeviceId, requestId = it.requestId, - localCreationTimestamp = event.ageLocalTs ?: System.currentTimeMillis() + localCreationTimestamp = event.ageLocalTs ?: now ) } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/model/IncomingRoomKeyRequest.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/model/IncomingRoomKeyRequest.kt index 45b0926d89..7012dd1d15 100755 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/model/IncomingRoomKeyRequest.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/model/IncomingRoomKeyRequest.kt @@ -65,7 +65,7 @@ data class IncomingRoomKeyRequest( * * @param event the event */ - fun fromEvent(event: Event): IncomingRoomKeyRequest? { + fun fromEvent(event: Event, now: Long): IncomingRoomKeyRequest? { return event.getClearContent() .toModel() ?.let { @@ -74,7 +74,7 @@ data class IncomingRoomKeyRequest( deviceId = it.requestingDeviceId, requestId = it.requestId, requestBody = it.body ?: RoomKeyRequestBody(), - localCreationTimestamp = event.ageLocalTs ?: System.currentTimeMillis() + localCreationTimestamp = event.ageLocalTs ?: now ) } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/model/IncomingSecretShareRequest.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/model/IncomingSecretShareRequest.kt index 5afffef1ae..4c20bf5769 100755 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/model/IncomingSecretShareRequest.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/model/IncomingSecretShareRequest.kt @@ -65,7 +65,7 @@ data class IncomingSecretShareRequest( * * @param event the event */ - fun fromEvent(event: Event): IncomingSecretShareRequest? { + fun fromEvent(event: Event, now: Long): IncomingSecretShareRequest? { return event.getClearContent() .toModel() ?.let { @@ -74,7 +74,7 @@ data class IncomingSecretShareRequest( deviceId = it.requestingDeviceId, requestId = it.requestId, secretName = it.secretName, - localCreationTimestamp = event.ageLocalTs ?: System.currentTimeMillis() + localCreationTimestamp = event.ageLocalTs ?: now ) } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/verification/VerificationService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/verification/VerificationService.kt index b9d0c0ad2c..027cdbd70c 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/verification/VerificationService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/verification/VerificationService.kt @@ -129,9 +129,8 @@ 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?, now: 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 return age in tooInThePast..tooInTheFuture diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/CancelGossipRequestWorker.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/CancelGossipRequestWorker.kt index 4380e31bff..aaf23d17b3 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/CancelGossipRequestWorker.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/CancelGossipRequestWorker.kt @@ -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 -> diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/DefaultCryptoService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/DefaultCryptoService.kt index 6a57d94677..54c9990bf6 100755 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/DefaultCryptoService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/DefaultCryptoService.kt @@ -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, // 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>(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 diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/DeviceListManager.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/DeviceListManager.kt index 6cae2d0935..535999373b 100755 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/DeviceListManager.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/DeviceListManager.kt @@ -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) @@ -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}") diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/EventDecryptor.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/EventDecryptor.kt index 1c8bce7377..a094189645 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/EventDecryptor.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/EventDecryptor.kt @@ -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) { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/GossipingWorkManager.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/GossipingWorkManager.kt index 0013c31eea..a2c85e5ceb 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/GossipingWorkManager.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/GossipingWorkManager.kt @@ -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 diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/IncomingGossipingRequestManager.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/IncomingGossipingRequestManager.kt index b907b57f82..1612caba9f 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/IncomingGossipingRequestManager.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/IncomingGossipingRequestManager.kt @@ -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() 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) } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/MXMegolmExportEncryption.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/MXMegolmExportEncryption.kt index f8235bf344..89e38cb7cf 100755 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/MXMegolmExportEncryption.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/MXMegolmExportEncryption.kt @@ -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 } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/MXOlmDevice.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/MXOlmDevice.kt index 4947761f05..7eec83abdd 100755 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/MXOlmDevice.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/MXOlmDevice.kt @@ -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 ) } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/OneTimeKeysUploader.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/OneTimeKeysUploader.kt index 792c9a25dc..8143e36892 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/OneTimeKeysUploader.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/OneTimeKeysUploader.kt @@ -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)) { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/SendGossipRequestWorker.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/SendGossipRequestWorker.kt index 69b405aedc..3b43ad672b 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/SendGossipRequestWorker.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/SendGossipRequestWorker.kt @@ -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 -> diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/SendGossipWorker.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/SendGossipWorker.kt index fd472fe73b..113d71d387 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/SendGossipWorker.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/SendGossipWorker.kt @@ -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 { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/actions/MegolmSessionDataImporter.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/actions/MegolmSessionDataImporter.kt index f9bcdf2c68..86674b4ac5 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/actions/MegolmSessionDataImporter.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/actions/MegolmSessionDataImporter.kt @@ -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, 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)") diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmEncryption.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmEncryption.kt index f052194230..b31b5e8a64 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmEncryption.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmEncryption.kt @@ -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): 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) { - 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() @@ -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, diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmEncryptionFactory.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmEncryptionFactory.kt index 136fdc05f5..4225d604aa 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmEncryptionFactory.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmEncryptionFactory.kt @@ -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, ) } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXOutboundSessionInfo.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXOutboundSessionInfo.kt index 091abd4974..28d925d8fd 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXOutboundSessionInfo.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXOutboundSessionInfo.kt @@ -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") diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/attachments/MXEncryptedAttachments.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/attachments/MXEncryptedAttachments.kt index 91b6af6fc3..65bbb0c412 100755 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/attachments/MXEncryptedAttachments.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/attachments/MXEncryptedAttachments.kt @@ -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 { @@ -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) { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/KeysBackupPassword.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/KeysBackupPassword.kt index c12879dbee..4d5b38acbf 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/KeysBackupPassword.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/KeysBackupPassword.kt @@ -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 } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/model/OlmSessionWrapper.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/model/OlmSessionWrapper.kt index 927d049eca..4636089364 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/model/OlmSessionWrapper.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/model/OlmSessionWrapper.kt @@ -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(now: Long) { + lastReceivedMessageTs = now } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStore.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStore.kt index fad901f95d..a509315e7a 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStore.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStore.kt @@ -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() .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().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) { 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 diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStoreMigration.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStoreMigration.kt index cac6499486..32f24c5d2e 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStoreMigration.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStoreMigration.kt @@ -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() diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/migration/MigrateCryptoTo008.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/migration/MigrateCryptoTo008.kt index 785e6a04f4..ad195e6e55 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/migration/MigrateCryptoTo008.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/migration/MigrateCryptoTo008.kt @@ -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) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/DefaultOutgoingSASDefaultVerificationTransaction.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/DefaultOutgoingSASDefaultVerificationTransaction.kt index e203f03b06..e0d912a9a6 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/DefaultOutgoingSASDefaultVerificationTransaction.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/DefaultOutgoingSASDefaultVerificationTransaction.kt @@ -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 // ) // diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/DefaultVerificationService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/DefaultVerificationService.kt index 28bf1d70f7..d62ca5503d 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/DefaultVerificationService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/DefaultVerificationService.kt @@ -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) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/VerificationMessageProcessor.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/VerificationMessageProcessor.kt index 52166761ab..ec4e1aa65c 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/VerificationMessageProcessor.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/VerificationMessageProcessor.kt @@ -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") } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/VerificationTransportRoomMessage.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/VerificationTransportRoomMessage.kt index 49235c5744..325a6f0ba2 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/VerificationTransportRoomMessage.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/VerificationTransportRoomMessage.kt @@ -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 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() .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, diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/VerificationTransportRoomMessageFactory.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/VerificationTransportRoomMessageFactory.kt index f89127273b..b1b7ad7a98 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/VerificationTransportRoomMessageFactory.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/VerificationTransportRoomMessageFactory.kt @@ -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 + ) } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/VerificationTransportToDevice.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/VerificationTransportToDevice.kt index 40deda2745..bc24ef2966 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/VerificationTransportToDevice.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/VerificationTransportToDevice.kt @@ -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, @@ -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): VerificationInfoReady { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/VerificationTransportToDeviceFactory.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/VerificationTransportToDeviceFactory.kt index e9a2c65ef7..312d911822 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/VerificationTransportToDeviceFactory.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/VerificationTransportToDeviceFactory.kt @@ -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) } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/AsyncTransaction.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/AsyncTransaction.kt index 315d77d932..7d263f1937 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/AsyncTransaction.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/AsyncTransaction.kt @@ -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 CoroutineScope.asyncTransaction(monarchy: Monarchy, transaction: suspend (realm: Realm) -> T) { asyncTransaction(monarchy.realmConfiguration, transaction) @@ -41,13 +42,13 @@ internal suspend fun 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) { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/helper/ThreadSummaryHelper.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/helper/ThreadSummaryHelper.kt index 89474a1bc5..76f0164ac6 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/helper/ThreadSummaryHelper.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/helper/ThreadSummaryHelper.kt @@ -137,7 +137,8 @@ internal fun ThreadSummaryEntity.Companion.createOrUpdate( roomMemberContentsByUser: HashMap, roomEntity: RoomEntity, userId: String, - cryptoService: CryptoService? = null + cryptoService: CryptoService? = null, + now: 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, now).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, now)?.also { try { decryptIfNeeded(cryptoService, it, roomId) } catch (e: InterruptedException) { @@ -268,8 +269,8 @@ private fun HashMap.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, now: Long): EventEntity { + val ageLocalTs = event.unsignedData?.age?.let { now - 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, - realm: Realm): EventEntity? { + now: Long, +): EventEntity? { return getLatestEvent(rootThreadEvent)?.let { it.senderId?.let { senderId -> roomMemberContentsByUser.addSenderState(realm, roomId, senderId) } - createEventEntity(roomId, it, realm) + createEventEntity(realm, roomId, it, now) } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/mapper/EventMapper.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/mapper/EventMapper.kt index 3083df062e..bc7d40bf6f 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/mapper/EventMapper.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/mapper/EventMapper.kt @@ -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 diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/DefaultFileService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/DefaultFileService.kt index 9b83cca558..78f1c84f3d 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/DefaultFileService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/DefaultFileService.kt @@ -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 ) } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/call/CallSignalingHandler.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/call/CallSignalingHandler.kt index 59058bf976..c4f711a9e6 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/call/CallSignalingHandler.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/call/CallSignalingHandler.kt @@ -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() private val callListeners = mutableSetOf() @@ -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.") diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/call/MxCallFactory.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/call/MxCallFactory.kt index 547be2253f..9ec892b65d 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/call/MxCallFactory.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/call/MxCallFactory.kt @@ -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 diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/call/model/MxCallImpl.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/call/model/MxCallImpl.kt index a89713870a..796e83311f 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/call/model/MxCallImpl.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/call/model/MxCallImpl.kt @@ -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? = 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, diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/UploadContentWorker.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/UploadContentWorker.kt index 75606f2e7a..75a79abcdb 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/UploadContentWorker.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/UploadContentWorker.kt @@ -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, diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/contentscanner/DefaultContentScannerService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/contentscanner/DefaultContentScannerService.kt index da7e2d102e..5aaf058757 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/contentscanner/DefaultContentScannerService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/contentscanner/DefaultContentScannerService.kt @@ -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() ) } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/contentscanner/db/ContentScannerEntityQueries.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/contentscanner/db/ContentScannerEntityQueries.kt index b47be235c6..d41e4cb326 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/contentscanner/db/ContentScannerEntityQueries.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/contentscanner/db/ContentScannerEntityQueries.kt @@ -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?, + now: Long): ContentScanResultEntity { return ContentScanResultEntity.get(realm, attachmentUrl, contentScannerUrl) ?: realm.createObject().also { it.mediaUrl = attachmentUrl - it.scanDateTimestamp = System.currentTimeMillis() + it.scanDateTimestamp = now it.scannerUrl = contentScannerUrl } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/contentscanner/db/RealmContentScannerStore.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/contentscanner/db/RealmContentScannerStore.kt index 947a66c8b9..27729d38c7 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/contentscanner/db/RealmContentScannerStore.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/contentscanner/db/RealmContentScannerStore.kt @@ -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 } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/EventRelationsAggregationProcessor.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/EventRelationsAggregationProcessor.kt index ac944ea8a7..2ac7d6d710 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/EventRelationsAggregationProcessor.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/EventRelationsAggregationProcessor.kt @@ -67,6 +67,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 @@ -75,7 +76,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( @@ -325,7 +327,8 @@ internal class EventRelationsAggregationProcessor @Inject constructor( totalVotes = 0, winnerVoteCount = 0, ) - .toContent()) + .toContent() + ) } val txId = event.unsignedData?.transactionId @@ -335,7 +338,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 { @@ -346,10 +349,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 ) @@ -729,11 +732,13 @@ internal class EventRelationsAggregationProcessor @Inject constructor( EventType.KEY_VERIFICATION_READY, EventType.KEY_VERIFICATION_KEY, EventType.KEY_VERIFICATION_MAC -> currentState.toState(VerificationState.WAITING) - EventType.KEY_VERIFICATION_CANCEL -> currentState.toState(if (event.senderId == userId) { - VerificationState.CANCELED_BY_ME - } else { - VerificationState.CANCELED_BY_OTHER - }) + EventType.KEY_VERIFICATION_CANCEL -> currentState.toState( + if (event.senderId == userId) { + VerificationState.CANCELED_BY_ME + } else { + VerificationState.CANCELED_BY_OTHER + } + ) EventType.KEY_VERIFICATION_DONE -> currentState.toState(VerificationState.DONE) else -> VerificationState.REQUEST } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/create/CreateRoomTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/create/CreateRoomTask.kt index 9bd15a0267..6dd2c91048 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/create/CreateRoomTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/create/CreateRoomTask.kt @@ -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) { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/LoadRoomMembersTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/LoadRoomMembersTask.kt index 70ba9287a2..d3d1cb856a 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/LoadRoomMembersTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/LoadRoomMembersTask.kt @@ -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 diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/joining/JoinRoomTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/joining/JoinRoomTask.kt index f883cc33ec..6306f3c6fe 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/joining/JoinRoomTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/joining/JoinRoomTask.kt @@ -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) } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/read/SetReadMarkersTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/read/SetReadMarkersTask.kt index 64f1cc34f1..a124a8a4c2 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/read/SetReadMarkersTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/read/SetReadMarkersTask.kt @@ -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) { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/EventEditor.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/EventEditor.kt index b54cd71e50..7bf7d6b587 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/EventEditor.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/EventEditor.kt @@ -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 diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/threads/FetchThreadSummariesTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/threads/FetchThreadSummariesTask.kt index b596f2288e..e82a3a5895 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/threads/FetchThreadSummariesTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/threads/FetchThreadSummariesTask.kt @@ -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, + now = clock.epochMillis(), + ) } } return Result.SUCCESS diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/threads/FetchThreadTimelineTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/threads/FetchThreadTimelineTask.kt index 116d4aa0a1..8d35a8fea4 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/threads/FetchThreadTimelineTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/threads/FetchThreadTimelineTask.kt @@ -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 - 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) { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/MultipleEventSendingDispatcherWorker.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/MultipleEventSendingDispatcherWorker.kt index a54ce833d4..ecc8149255 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/MultipleEventSendingDispatcherWorker.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/MultipleEventSendingDispatcherWorker.kt @@ -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) } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/SendEventWorker.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/SendEventWorker.kt index 669891c822..ddbe8a61a0 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/SendEventWorker.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/SendEventWorker.kt @@ -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() } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/EventSenderProcessorCoroutine.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/EventSenderProcessorCoroutine.kt index 5b4efa5df6..a1d3e2c0ac 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/EventSenderProcessorCoroutine.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/EventSenderProcessorCoroutine.kt @@ -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() diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/EventSenderProcessorThread.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/EventSenderProcessorThread.kt index 1ee3139194..301f8cb9d6 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/EventSenderProcessorThread.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/EventSenderProcessorThread.kt @@ -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() { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimeline.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimeline.kt index 08b2700a43..18fbe73c09 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimeline.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimeline.kt @@ -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, ) } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimelineService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimelineService.kt index 826c9d7c48..849d7bd7ab 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimelineService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimelineService.kt @@ -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 ) } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/GetEventTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/GetEventTask.kt index 5bca5118b8..aef9e24c8b 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/GetEventTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/GetEventTask.kt @@ -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 { @@ -36,7 +37,8 @@ internal interface GetEventTask : Task { 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 } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/LoadTimelineStrategy.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/LoadTimelineStrategy.kt index 8819ffe69f..bcf202962c 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/LoadTimelineStrategy.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/LoadTimelineStrategy.kt @@ -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, diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/TokenChunkEventPersistor.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/TokenChunkEventPersistor.kt index d3f24a8568..e5dd8aab30 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/TokenChunkEventPersistor.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/TokenChunkEventPersistor.kt @@ -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) { + private val liveEventManager: Lazy, + 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 } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/UIEchoManager.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/UIEchoManager.kt index bb92623249..b6247c961f 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/UIEchoManager.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/UIEchoManager.kt @@ -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 @@ -111,7 +115,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) } @@ -145,7 +149,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 } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/InitialSyncStatusRepository.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/InitialSyncStatusRepository.kt index bd20ada28b..8e0c3422b9 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/InitialSyncStatusRepository.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/InitialSyncStatusRepository.kt @@ -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 diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/SyncTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/SyncTask.kt index 197037f1cf..f88d973101 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/SyncTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/SyncTask.kt @@ -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) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/handler/room/ReadReceiptHandler.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/handler/room/ReadReceiptHandler.kt index 2c84f1e692..5843e727a4 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/handler/room/ReadReceiptHandler.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/handler/room/ReadReceiptHandler.kt @@ -44,12 +44,12 @@ internal class ReadReceiptHandler @Inject constructor( companion object { - fun createContent(userId: String, eventId: String): ReadReceiptContent { + fun createContent(userId: String, eventId: String, now: Long): ReadReceiptContent { return mapOf( eventId to mapOf( READ_KEY to mapOf( userId to mapOf( - TIMESTAMP_KEY to System.currentTimeMillis().toDouble() + TIMESTAMP_KEY to now.toDouble() ) ) ) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/handler/room/RoomSyncHandler.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/handler/room/RoomSyncHandler.kt index 05dad983da..0aff191ecc 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/handler/room/RoomSyncHandler.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/handler/room/RoomSyncHandler.kt @@ -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) { +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, + private val clock: Clock, +) { sealed class HandlingStrategy { data class JOINED(val data: Map) : 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, + now = clock.epochMillis(), ) } } ?: run { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/LogUtil.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/LogUtil.kt index 6fd907d397..ffad0b856c 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/LogUtil.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/LogUtil.kt @@ -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 Collection.logLimit(maxQuantity: Int = 5): String { @@ -34,13 +35,14 @@ internal fun Collection.logLimit(maxQuantity: Int = 5): String { internal suspend fun 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 From 45526c0e3a93fa3e50c5ded59ea3f9c10e0c447d Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 3 May 2022 09:32:44 +0200 Subject: [PATCH 03/11] Use Clock (SDK API change) --- .../home/room/detail/timeline/factory/MessageItemFactory.kt | 3 +++ .../room/detail/timeline/item/VerificationRequestItem.kt | 6 +++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt index 186b34dc29..3aa66c7e44 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt @@ -31,6 +31,7 @@ import im.vector.app.core.epoxy.VectorEpoxyModel import im.vector.app.core.files.LocalFilesHelper import im.vector.app.core.resources.ColorProvider import im.vector.app.core.resources.StringProvider +import im.vector.app.core.time.Clock import im.vector.app.core.utils.DimensionConverter import im.vector.app.core.utils.containsOnlyEmojis import im.vector.app.features.home.room.detail.timeline.TimelineEventController @@ -142,6 +143,7 @@ class MessageItemFactory @Inject constructor( private val lightweightSettingsStorage: LightweightSettingsStorage, private val spanUtils: SpanUtils, private val session: Session, + private val clock: Clock, private val audioMessagePlaybackTracker: AudioMessagePlaybackTracker, private val locationPinProvider: LocationPinProvider, private val vectorPreferences: VectorPreferences, @@ -457,6 +459,7 @@ class MessageItemFactory @Inject constructor( reactionsSummaryEvents = attributes.reactionsSummaryEvents, ) ) + .clock(clock) .callback(callback) .highlighted(highlight) .leftGuideline(avatarSizeProvider.leftGuideline) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/VerificationRequestItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/VerificationRequestItem.kt index 0e6530fdca..fc4c55d1f3 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/VerificationRequestItem.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/VerificationRequestItem.kt @@ -31,6 +31,7 @@ import com.airbnb.epoxy.EpoxyModelClass import im.vector.app.R import im.vector.app.core.epoxy.ClickListener import im.vector.app.core.epoxy.onClick +import im.vector.app.core.time.Clock import im.vector.app.features.home.AvatarRenderer import im.vector.app.features.home.room.detail.RoomDetailAction import im.vector.app.features.home.room.detail.timeline.MessageColorProvider @@ -47,6 +48,9 @@ abstract class VerificationRequestItem : AbsBaseMessageItem Date: Tue, 3 May 2022 10:32:48 +0200 Subject: [PATCH 04/11] Use Clock interface app side --- .../java/im/vector/app/EspressoExt.kt | 12 ++-- .../java/im/vector/app/RegistrationTest.kt | 3 +- .../im/vector/app/SecurityBootstrapTest.kt | 3 +- .../app/VerifySessionInteractiveTest.kt | 3 +- .../vector/app/VerifySessionPassphraseTest.kt | 3 +- .../espresso/tools/ScreenshotFailureRule.kt | 3 +- .../app/features/debug/DebugMenuActivity.kt | 17 +++-- .../receiver/AlarmSyncBroadcastReceiver.kt | 2 +- .../app/core/date/VectorDateFormatter.kt | 15 +++-- .../dialogs/GalleryOrCameraDialogHelper.kt | 6 +- .../app/core/services/VectorSyncService.kt | 31 ++++++--- .../core/utils/ExternalApplicationsUtil.kt | 32 ++++++--- .../preview/AttachmentsPreviewFragment.kt | 6 +- .../features/call/conference/JitsiService.kt | 7 +- .../call/webrtc/ScreenCaptureService.kt | 4 +- .../setup/KeysBackupSetupSharedViewModel.kt | 13 ++-- .../IncomingVerificationRequestHandler.kt | 9 ++- .../UnknownDeviceDetectorSharedViewModel.kt | 14 ++-- .../home/room/detail/ChatEffectManager.kt | 7 +- .../detail/RoomMessageTouchHelperCallback.kt | 12 ++-- .../home/room/detail/TimelineFragment.kt | 7 +- .../composer/MessageComposerViewState.kt | 6 +- .../detail/search/SearchResultController.kt | 9 ++- .../timeline/TimelineEventController.kt | 67 ++++++++++++------- .../ViewEditHistoryEpoxyController.kt | 6 +- .../TimelineControllerInterceptorHelper.kt | 5 +- .../login2/created/AccountCreatedFragment.kt | 6 +- .../domain/usecase/DownloadMediaUseCase.kt | 7 +- .../notifications/NotifiableEventResolver.kt | 18 +++-- .../NotificationBroadcastReceiver.kt | 6 +- .../notifications/NotificationUtils.kt | 36 +++++----- .../FtueAuthChooseProfilePictureFragment.kt | 6 +- .../app/features/popup/PopupAlertManager.kt | 9 ++- .../createroom/CreateRoomFragment.kt | 9 ++- .../settings/RoomSettingsFragment.kt | 9 ++- .../uploads/RoomUploadsFragment.kt | 7 +- .../features/settings/VectorPreferences.kt | 24 ++++--- .../settings/VectorSettingsGeneralFragment.kt | 6 +- .../settings/devtools/KeyRequestsFragment.kt | 7 +- .../create/CreateSpaceDetailsFragment.kt | 6 +- .../spaces/manage/SpaceSettingsFragment.kt | 11 +-- .../userdirectory/UserListViewModel.kt | 3 +- .../app/features/voice/VoicePlayerHelper.kt | 6 +- .../app/features/voice/VoiceRecorderL.kt | 10 ++- .../features/voice/VoiceRecorderProvider.kt | 6 +- 45 files changed, 320 insertions(+), 174 deletions(-) diff --git a/vector/src/androidTest/java/im/vector/app/EspressoExt.kt b/vector/src/androidTest/java/im/vector/app/EspressoExt.kt index 35554ae75a..899f268176 100644 --- a/vector/src/androidTest/java/im/vector/app/EspressoExt.kt +++ b/vector/src/androidTest/java/im/vector/app/EspressoExt.kt @@ -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, timeout: Long = 10_000, waitForDisplayed: Boolean = true): ViewAction { return object : ViewAction { + private val clock = DefaultClock() + override fun getConstraints(): Matcher { return Matchers.any(View::class.java) } @@ -102,14 +106,14 @@ fun waitForView(viewMatcher: Matcher, 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, 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,7 +172,7 @@ 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 uniqTS = Random.nextLong() override fun getName() = "activityIdlingResource_${activityClass.name}_$uniqTS" diff --git a/vector/src/androidTest/java/im/vector/app/RegistrationTest.kt b/vector/src/androidTest/java/im/vector/app/RegistrationTest.kt index fcec2a9fb5..344a2ecfb1 100644 --- a/vector/src/androidTest/java/im/vector/app/RegistrationTest.kt +++ b/vector/src/androidTest/java/im/vector/app/RegistrationTest.kt @@ -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" diff --git a/vector/src/androidTest/java/im/vector/app/SecurityBootstrapTest.kt b/vector/src/androidTest/java/im/vector/app/SecurityBootstrapTest.kt index a84aae9994..21fd226317 100644 --- a/vector/src/androidTest/java/im/vector/app/SecurityBootstrapTest.kt +++ b/vector/src/androidTest/java/im/vector/app/SecurityBootstrapTest.kt @@ -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() } diff --git a/vector/src/androidTest/java/im/vector/app/VerifySessionInteractiveTest.kt b/vector/src/androidTest/java/im/vector/app/VerifySessionInteractiveTest.kt index c82b543a08..76f09638be 100644 --- a/vector/src/androidTest/java/im/vector/app/VerifySessionInteractiveTest.kt +++ b/vector/src/androidTest/java/im/vector/app/VerifySessionInteractiveTest.kt @@ -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 { existingSession!!.cryptoService().crossSigningService() diff --git a/vector/src/androidTest/java/im/vector/app/VerifySessionPassphraseTest.kt b/vector/src/androidTest/java/im/vector/app/VerifySessionPassphraseTest.kt index 80d8315a0e..76d5717000 100644 --- a/vector/src/androidTest/java/im/vector/app/VerifySessionPassphraseTest.kt +++ b/vector/src/androidTest/java/im/vector/app/VerifySessionPassphraseTest.kt @@ -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 { existingSession!!.cryptoService().crossSigningService() diff --git a/vector/src/androidTest/java/im/vector/app/espresso/tools/ScreenshotFailureRule.kt b/vector/src/androidTest/java/im/vector/app/espresso/tools/ScreenshotFailureRule.kt index 2939dcf4e0..b01c1a895f 100644 --- a/vector/src/androidTest/java/im/vector/app/espresso/tools/ScreenshotFailureRule.kt +++ b/vector/src/androidTest/java/im/vector/app/espresso/tools/ScreenshotFailureRule.kt @@ -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( diff --git a/vector/src/debug/java/im/vector/app/features/debug/DebugMenuActivity.kt b/vector/src/debug/java/im/vector/app/features/debug/DebugMenuActivity.kt index cc69cfd426..07b7c9ebf9 100644 --- a/vector/src/debug/java/im/vector/app/features/debug/DebugMenuActivity.kt +++ b/vector/src/debug/java/im/vector/app/features/debug/DebugMenuActivity.kt @@ -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() { @Inject lateinit var activeSessionHolder: ActiveSessionHolder + @Inject + lateinit var clock: Clock private lateinit var buffer: ByteArray @@ -165,7 +168,7 @@ class DebugMenuActivity : VectorBaseActivity() { } 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() { .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() { 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() { notificationManager.notify( 12, NotificationCompat.Builder(this, "CHAN2") - .setWhen(System.currentTimeMillis()) + .setWhen(clock.epochMillis()) .setContentTitle("Title 2") .setContentText("Content 2") .setStyle(messagingStyle2) diff --git a/vector/src/fdroid/java/im/vector/app/fdroid/receiver/AlarmSyncBroadcastReceiver.kt b/vector/src/fdroid/java/im/vector/app/fdroid/receiver/AlarmSyncBroadcastReceiver.kt index 4be36d7de3..a847f8fc45 100644 --- a/vector/src/fdroid/java/im/vector/app/fdroid/receiver/AlarmSyncBroadcastReceiver.kt +++ b/vector/src/fdroid/java/im/vector/app/fdroid/receiver/AlarmSyncBroadcastReceiver.kt @@ -74,7 +74,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()!! if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { alarmMgr.setAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, firstMillis, pIntent) diff --git a/vector/src/main/java/im/vector/app/core/date/VectorDateFormatter.kt b/vector/src/main/java/im/vector/app/core/date/VectorDateFormatter.kt index eb6f6a94f7..029fce7423 100644 --- a/vector/src/main/java/im/vector/app/core/date/VectorDateFormatter.kt +++ b/vector/src/main/java/im/vector/app/core/date/VectorDateFormatter.kt @@ -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() } } diff --git a/vector/src/main/java/im/vector/app/core/dialogs/GalleryOrCameraDialogHelper.kt b/vector/src/main/java/im/vector/app/core/dialogs/GalleryOrCameraDialogHelper.kt index 8f70808087..757b415ce5 100644 --- a/vector/src/main/java/im/vector/app/core/dialogs/GalleryOrCameraDialogHelper.kt +++ b/vector/src/main/java/im/vector/app/core/dialogs/GalleryOrCameraDialogHelper.kt @@ -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) diff --git a/vector/src/main/java/im/vector/app/core/services/VectorSyncService.kt b/vector/src/main/java/im/vector/app/core/services/VectorSyncService.kt index 5dbea8dcc4..8051900bcb 100644 --- a/vector/src/main/java/im/vector/app/core/services/VectorSyncService.kt +++ b/vector/src/main/java/im/vector/app/core/services/VectorSyncService.kt @@ -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, + now = clock.epochMillis() ) } @@ -114,9 +118,10 @@ class VectorSyncService : SyncService() { val rescheduleSyncWorkRequest: WorkRequest = OneTimeWorkRequestBuilder() .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, + now = 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, + now: 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 = now + syncDelaySeconds * 1000L val alarmMgr = getSystemService()!! if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { alarmMgr.setAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, firstMillis, pendingIntent) diff --git a/vector/src/main/java/im/vector/app/core/utils/ExternalApplicationsUtil.kt b/vector/src/main/java/im/vector/app/core/utils/ExternalApplicationsUtil.kt index a9375b6545..f34260c941 100644 --- a/vector/src/main/java/im/vector/app/core/utils/ExternalApplicationsUtil.kt +++ b/vector/src/main/java/im/vector/app/core/utils/ExternalApplicationsUtil.kt @@ -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, + now: 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, now) + put(MediaStore.Images.Media.DATE_TAKEN, now) } 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, now) } } } @@ -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, + now: 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, now) if (savedFile != null) { val downloadManager = context.getSystemService() 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) { @@ -411,7 +421,7 @@ fun selectTxtFileToWrite( * @return the created file */ @Suppress("DEPRECATION") -fun saveFileIntoLegacy(sourceFile: File, dstDirPath: File, outputFilename: String?): File? { +fun saveFileIntoLegacy(sourceFile: File, dstDirPath: File, outputFilename: String?, now: Long): File? { // defines another name for the external media var dstFileName: String @@ -423,7 +433,7 @@ fun saveFileIntoLegacy(sourceFile: File, dstDirPath: File, outputFilename: Strin if (dotPos > 0) { fileExt = sourceFile.name.substring(dotPos) } - dstFileName = "vector_" + System.currentTimeMillis() + fileExt + dstFileName = "vector_$now$fileExt" } else { dstFileName = outputFilename } diff --git a/vector/src/main/java/im/vector/app/features/attachments/preview/AttachmentsPreviewFragment.kt b/vector/src/main/java/im/vector/app/features/attachments/preview/AttachmentsPreviewFragment.kt index 5b9c3f7fb4..729ac10d4b 100644 --- a/vector/src/main/java/im/vector/app/features/attachments/preview/AttachmentsPreviewFragment.kt +++ b/vector/src/main/java/im/vector/app/features/attachments/preview/AttachmentsPreviewFragment.kt @@ -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(), 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()) diff --git a/vector/src/main/java/im/vector/app/features/call/conference/JitsiService.kt b/vector/src/main/java/im/vector/app/features/call/conference/JitsiService.kt index 07062fc732..b0da4f0e18 100644 --- a/vector/src/main/java/im/vector/app/features/call/conference/JitsiService.kt +++ b/vector/src/main/java/im/vector/app/features/call/conference/JitsiService.kt @@ -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 diff --git a/vector/src/main/java/im/vector/app/features/call/webrtc/ScreenCaptureService.kt b/vector/src/main/java/im/vector/app/features/call/webrtc/ScreenCaptureService.kt index f1a4975751..489b2d1eae 100644 --- a/vector/src/main/java/im/vector/app/features/call/webrtc/ScreenCaptureService.kt +++ b/vector/src/main/java/im/vector/app/features/call/webrtc/ScreenCaptureService.kt @@ -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) } diff --git a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/setup/KeysBackupSetupSharedViewModel.kt b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/setup/KeysBackupSetupSharedViewModel.kt index 34aa7ba0ee..dfa7d1aaa3 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/setup/KeysBackupSetupSharedViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/setup/KeysBackupSetupSharedViewModel.kt @@ -22,6 +22,7 @@ import androidx.lifecycle.ViewModel import com.nulabinc.zxcvbn.Strength import im.vector.app.R import im.vector.app.core.platform.WaitingViewData +import im.vector.app.core.time.Clock import im.vector.app.core.utils.LiveEvent import org.matrix.android.sdk.api.MatrixCallback import org.matrix.android.sdk.api.listeners.ProgressListener @@ -37,7 +38,9 @@ import javax.inject.Inject /** * The shared view model between all fragments. */ -class KeysBackupSetupSharedViewModel @Inject constructor() : ViewModel() { +class KeysBackupSetupSharedViewModel @Inject constructor( + private val clock: Clock, +) : ViewModel() { companion object { const val NAVIGATE_TO_STEP_2 = "NAVIGATE_TO_STEP_2" @@ -85,7 +88,7 @@ class KeysBackupSetupSharedViewModel @Inject constructor() : ViewModel() { fun prepareRecoveryKey(context: Context, withPassphrase: String?) { // Update requestId - currentRequestId.value = System.currentTimeMillis() + currentRequestId.value = clock.epochMillis() isCreatingBackupVersion.value = true recoveryKey.value = null @@ -101,9 +104,11 @@ class KeysBackupSetupSharedViewModel @Inject constructor() : ViewModel() { return } - loadingStatus.value = WaitingViewData(context.getString(R.string.keys_backup_setup_step3_generating_key_status), + loadingStatus.value = WaitingViewData( + context.getString(R.string.keys_backup_setup_step3_generating_key_status), progress, - total) + total + ) } }, object : MatrixCallback { diff --git a/vector/src/main/java/im/vector/app/features/crypto/verification/IncomingVerificationRequestHandler.kt b/vector/src/main/java/im/vector/app/features/crypto/verification/IncomingVerificationRequestHandler.kt index 2e9fe1bcf9..e8f0bd344f 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/verification/IncomingVerificationRequestHandler.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/verification/IncomingVerificationRequestHandler.kt @@ -18,6 +18,7 @@ package im.vector.app.features.crypto.verification import android.content.Context import im.vector.app.R import im.vector.app.core.platform.VectorBaseActivity +import im.vector.app.core.time.Clock import im.vector.app.features.displayname.getBestName import im.vector.app.features.home.AvatarRenderer import im.vector.app.features.home.room.detail.RoomDetailActivity @@ -42,7 +43,9 @@ import javax.inject.Singleton class IncomingVerificationRequestHandler @Inject constructor( private val context: Context, private var avatarRenderer: Provider, - private val popupAlertManager: PopupAlertManager) : VerificationService.Listener { + private val popupAlertManager: PopupAlertManager, + private val clock: Clock, +) : VerificationService.Listener { private var session: Session? = null @@ -104,7 +107,7 @@ class IncomingVerificationRequestHandler @Inject constructor( } ) // 10mn expiration - expirationTimestamp = System.currentTimeMillis() + (10 * 60 * 1000L) + expirationTimestamp = clock.epochMillis() + (10 * 60 * 1000L) } popupAlertManager.postVectorAlert(alert) } @@ -168,7 +171,7 @@ class IncomingVerificationRequestHandler @Inject constructor( } colorAttribute = R.attr.vctr_notice_secondary // 5mn expiration - expirationTimestamp = System.currentTimeMillis() + (5 * 60 * 1000L) + expirationTimestamp = clock.epochMillis() + (5 * 60 * 1000L) } popupAlertManager.postVectorAlert(alert) } diff --git a/vector/src/main/java/im/vector/app/features/home/UnknownDeviceDetectorSharedViewModel.kt b/vector/src/main/java/im/vector/app/features/home/UnknownDeviceDetectorSharedViewModel.kt index 3ca19b39f9..34bdc5fcd3 100644 --- a/vector/src/main/java/im/vector/app/features/home/UnknownDeviceDetectorSharedViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/UnknownDeviceDetectorSharedViewModel.kt @@ -29,6 +29,7 @@ import im.vector.app.core.di.hiltMavericksViewModelFactory import im.vector.app.core.platform.EmptyViewEvents import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.platform.VectorViewModelAction +import im.vector.app.core.time.Clock import im.vector.app.features.settings.VectorPreferences import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged @@ -56,10 +57,12 @@ data class DeviceDetectionInfo( val currentSessionTrust: Boolean ) -class UnknownDeviceDetectorSharedViewModel @AssistedInject constructor(@Assisted initialState: UnknownDevicesState, - session: Session, - private val vectorPreferences: VectorPreferences) : - VectorViewModel(initialState) { +class UnknownDeviceDetectorSharedViewModel @AssistedInject constructor( + @Assisted initialState: UnknownDevicesState, + session: Session, + private val vectorPreferences: VectorPreferences, + clock: Clock, +) : VectorViewModel(initialState) { sealed class Action : VectorViewModelAction { data class IgnoreDevice(val deviceIds: List) : Action() @@ -75,11 +78,10 @@ class UnknownDeviceDetectorSharedViewModel @AssistedInject constructor(@Assisted private val ignoredDeviceList = ArrayList() init { - val currentSessionTs = session.cryptoService().getCryptoDeviceInfo(session.myUserId) .firstOrNull { it.deviceId == session.sessionParams.deviceId } ?.firstTimeSeenLocalTs - ?: System.currentTimeMillis() + ?: clock.epochMillis() Timber.v("## Detector - Current Session first time seen $currentSessionTs") ignoredDeviceList.addAll( diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/ChatEffectManager.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/ChatEffectManager.kt index f95baae36b..fa19e39ae7 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/ChatEffectManager.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/ChatEffectManager.kt @@ -16,6 +16,7 @@ package im.vector.app.features.home.room.detail +import im.vector.app.core.time.Clock import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.api.session.room.model.message.MessageContent import org.matrix.android.sdk.api.session.room.model.message.MessageType @@ -45,7 +46,9 @@ fun ChatEffect.toMessageType(): String { * precisely an event decrypted with a few delay won't trigger an effect; it's acceptable) * Events that are more that 10s old won't trigger effects */ -class ChatEffectManager @Inject constructor() { +class ChatEffectManager @Inject constructor( + private val clock: Clock, +) { interface Delegate { fun stopEffects() @@ -61,7 +64,7 @@ class ChatEffectManager @Inject constructor() { fun checkForEffect(event: TimelineEvent) { val age = event.root.ageLocalTs ?: 0 - val now = System.currentTimeMillis() + val now = clock.epochMillis() // messages older than 10s should not trigger any effect if ((now - age) >= 10_000) return val content = event.root.getClearContent()?.toModel() ?: return diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomMessageTouchHelperCallback.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomMessageTouchHelperCallback.kt index 9f10805f95..1f1124b8c0 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomMessageTouchHelperCallback.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomMessageTouchHelperCallback.kt @@ -33,14 +33,18 @@ import com.airbnb.epoxy.EpoxyModel import com.airbnb.epoxy.EpoxyTouchHelperCallback import com.airbnb.epoxy.EpoxyViewHolder import im.vector.app.R +import im.vector.app.core.time.Clock import im.vector.app.features.themes.ThemeUtils import timber.log.Timber import kotlin.math.abs import kotlin.math.min -class RoomMessageTouchHelperCallback(private val context: Context, - @DrawableRes actionIcon: Int, - private val handler: QuickReplayHandler) : EpoxyTouchHelperCallback() { +class RoomMessageTouchHelperCallback( + private val context: Context, + @DrawableRes actionIcon: Int, + private val handler: QuickReplayHandler, + private val clock: Clock, +) : EpoxyTouchHelperCallback() { interface QuickReplayHandler { fun performQuickReplyOnHolder(model: EpoxyModel<*>) @@ -141,7 +145,7 @@ class RoomMessageTouchHelperCallback(private val context: Context, private fun drawReplyButton(canvas: Canvas, itemView: View) { // Timber.v("drawReplyButton") val translationX = abs(itemView.translationX) - val newTime = System.currentTimeMillis() + val newTime = clock.epochMillis() val dt = min(17, newTime - lastReplyButtonAnimationTime) lastReplyButtonAnimationTime = newTime val showing = translationX >= minShowDistance diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt index 4603793bd5..60a1ddcc62 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt @@ -296,7 +296,7 @@ class TimelineFragment @Inject constructor( private const val ircPattern = " (IRC)" } - private val galleryOrCameraDialogHelper = GalleryOrCameraDialogHelper(this, colorProvider) + private val galleryOrCameraDialogHelper = GalleryOrCameraDialogHelper(this, colorProvider, clock) private val timelineArgs: TimelineArgs by args() private val glideRequests by lazy { @@ -1443,7 +1443,7 @@ class TimelineFragment @Inject constructor( } } } - val swipeCallback = RoomMessageTouchHelperCallback(requireContext(), R.drawable.ic_reply, quickReplyHandler) + val swipeCallback = RoomMessageTouchHelperCallback(requireContext(), R.drawable.ic_reply, quickReplyHandler, clock) val touchHelper = ItemTouchHelper(swipeCallback) touchHelper.attachToRecyclerView(views.timelineRecyclerView) } @@ -2186,7 +2186,8 @@ class TimelineFragment @Inject constructor( file = it, title = action.messageContent.body, mediaMimeType = action.messageContent.mimeType ?: getMimeTypeFromUri(requireContext(), it.toUri()), - notificationUtils = notificationUtils + notificationUtils = notificationUtils, + now = clock.epochMillis() ) } .onFailure { diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerViewState.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerViewState.kt index 95553eb1cd..10263c1abc 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerViewState.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerViewState.kt @@ -21,6 +21,7 @@ import im.vector.app.features.home.room.detail.arguments.TimelineArgs import im.vector.app.features.home.room.detail.composer.voice.VoiceMessageRecorderView import org.matrix.android.sdk.api.extensions.orFalse import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent +import kotlin.random.Random /** * Describes the current send mode: @@ -35,7 +36,7 @@ sealed interface SendMode { val text: String, val fromSharing: Boolean, // This is necessary for forcing refresh on selectSubscribe - private val ts: Long = System.currentTimeMillis() + private val ts: Int = Random.nextInt() ) : SendMode data class Quote(val timelineEvent: TimelineEvent, val text: String) : SendMode @@ -83,7 +84,8 @@ data class MessageComposerViewState( constructor(args: TimelineArgs) : this( roomId = args.roomId, startsThread = args.threadTimelineArgs?.startsThread.orFalse(), - rootThreadEventId = args.threadTimelineArgs?.rootThreadEventId) + rootThreadEventId = args.threadTimelineArgs?.rootThreadEventId + ) fun isInThreadTimeline(): Boolean = rootThreadEventId != null } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchResultController.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchResultController.kt index 4f951dfecb..c77cdceed0 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchResultController.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchResultController.kt @@ -30,6 +30,7 @@ import im.vector.app.core.epoxy.loadingItem import im.vector.app.core.epoxy.noResultItem import im.vector.app.core.resources.StringProvider import im.vector.app.core.resources.UserPreferencesProvider +import im.vector.app.core.time.Clock import im.vector.app.core.ui.list.GenericHeaderItem_ import im.vector.app.features.home.AvatarRenderer import im.vector.app.features.home.room.detail.timeline.format.DisplayableEventFormatter @@ -47,7 +48,8 @@ class SearchResultController @Inject constructor( private val stringProvider: StringProvider, private val dateFormatter: VectorDateFormatter, private val displayableEventFormatter: DisplayableEventFormatter, - private val userPreferencesProvider: UserPreferencesProvider + private val userPreferencesProvider: UserPreferencesProvider, + private val clock: Clock, ) : TypedEpoxyController() { var listener: Listener? = null @@ -109,7 +111,7 @@ class SearchResultController @Inject constructor( val spannable = setHighLightedText(text, data.highlights) ?: return@forEach val eventDate = Calendar.getInstance().apply { - timeInMillis = eventAndSender.event.originServerTs ?: System.currentTimeMillis() + timeInMillis = eventAndSender.event.originServerTs ?: clock.epochMillis() } if (lastDate?.get(Calendar.DAY_OF_YEAR) != eventDate.get(Calendar.DAY_OF_YEAR)) { GenericHeaderItem_() @@ -125,7 +127,8 @@ class SearchResultController @Inject constructor( .formattedDate(dateFormatter.format(event.originServerTs, DateFormatKind.MESSAGE_SIMPLE)) .spannable(spannable.toEpoxyCharSequence()) .sender(eventAndSender.sender - ?: eventAndSender.event.senderId?.let { session.roomService().getRoomMember(it, data.roomId) }?.toMatrixItem()) + ?: eventAndSender.event.senderId?.let { session.roomService().getRoomMember(it, data.roomId) }?.toMatrixItem() + ) .threadDetails(event.threadDetails) .threadSummaryFormatted(displayableEventFormatter.formatThreadSummary(event.threadDetails?.threadSummaryLatestEvent).toString()) .areThreadMessagesEnabled(userPreferencesProvider.areThreadMessagesEnabled()) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/TimelineEventController.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/TimelineEventController.kt index 8cea57399a..6c9f7ac4ff 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/TimelineEventController.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/TimelineEventController.kt @@ -31,6 +31,7 @@ import im.vector.app.core.epoxy.LoadingItem_ import im.vector.app.core.extensions.localDateTime import im.vector.app.core.extensions.nextOrNull import im.vector.app.core.extensions.prevOrNull +import im.vector.app.core.time.Clock import im.vector.app.features.home.room.detail.JitsiState import im.vector.app.features.home.room.detail.RoomDetailAction import im.vector.app.features.home.room.detail.RoomDetailViewState @@ -78,19 +79,21 @@ import javax.inject.Inject import kotlin.math.min import kotlin.system.measureTimeMillis -class TimelineEventController @Inject constructor(private val dateFormatter: VectorDateFormatter, - private val vectorPreferences: VectorPreferences, - private val contentUploadStateTrackerBinder: ContentUploadStateTrackerBinder, - private val contentDownloadStateTrackerBinder: ContentDownloadStateTrackerBinder, - private val timelineItemFactory: TimelineItemFactory, - private val timelineMediaSizeProvider: TimelineMediaSizeProvider, - private val mergedHeaderItemFactory: MergedHeaderItemFactory, - private val session: Session, - @TimelineEventControllerHandler - private val backgroundHandler: Handler, - private val timelineEventVisibilityHelper: TimelineEventVisibilityHelper, - private val readReceiptsItemFactory: ReadReceiptsItemFactory, - private val reactionListFactory: ReactionsSummaryFactory +class TimelineEventController @Inject constructor( + private val dateFormatter: VectorDateFormatter, + private val vectorPreferences: VectorPreferences, + private val contentUploadStateTrackerBinder: ContentUploadStateTrackerBinder, + private val contentDownloadStateTrackerBinder: ContentDownloadStateTrackerBinder, + private val timelineItemFactory: TimelineItemFactory, + private val timelineMediaSizeProvider: TimelineMediaSizeProvider, + private val mergedHeaderItemFactory: MergedHeaderItemFactory, + private val session: Session, + @TimelineEventControllerHandler + private val backgroundHandler: Handler, + private val timelineEventVisibilityHelper: TimelineEventVisibilityHelper, + private val readReceiptsItemFactory: ReadReceiptsItemFactory, + private val reactionListFactory: ReactionsSummaryFactory, + private val clock: Clock, ) : EpoxyController(backgroundHandler, backgroundHandler), Timeline.Listener, EpoxyController.Interceptor { /** @@ -209,8 +212,10 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec override fun onChanged(position: Int, count: Int, payload: Any?) { synchronized(modelCache) { assertUpdateCallbacksAllowed() - Timber.v("listUpdateCallback.onChanged(position: $position, count: $count). " + - "\ncurrentSnapshot has size of ${currentSnapshot.size} items") + Timber.v( + "listUpdateCallback.onChanged(position: $position, count: $count). " + + "\ncurrentSnapshot has size of ${currentSnapshot.size} items" + ) (position until position + count).forEach { // Invalidate cache modelCache[it] = null @@ -238,8 +243,10 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec override fun onMoved(fromPosition: Int, toPosition: Int) { synchronized(modelCache) { assertUpdateCallbacksAllowed() - Timber.v("listUpdateCallback.onMoved(fromPosition: $fromPosition, toPosition: $toPosition). " + - "\ncurrentSnapshot has size of ${currentSnapshot.size} items") + Timber.v( + "listUpdateCallback.onMoved(fromPosition: $fromPosition, toPosition: $toPosition). " + + "\ncurrentSnapshot has size of ${currentSnapshot.size} items" + ) val model = modelCache.removeAt(fromPosition) modelCache.add(toPosition, model) requestModelBuild() @@ -249,8 +256,10 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec override fun onInserted(position: Int, count: Int) { synchronized(modelCache) { assertUpdateCallbacksAllowed() - Timber.v("listUpdateCallback.onInserted(position: $position, count: $count). " + - "\ncurrentSnapshot has size of ${currentSnapshot.size} items") + Timber.v( + "listUpdateCallback.onInserted(position: $position, count: $count). " + + "\ncurrentSnapshot has size of ${currentSnapshot.size} items" + ) repeat(count) { modelCache.add(position, null) } @@ -261,8 +270,10 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec override fun onRemoved(position: Int, count: Int) { synchronized(modelCache) { assertUpdateCallbacksAllowed() - Timber.v("listUpdateCallback.onRemoved(position: $position, count: $count). " + - "\ncurrentSnapshot has size of ${currentSnapshot.size} items") + Timber.v( + "listUpdateCallback.onRemoved(position: $position, count: $count). " + + "\ncurrentSnapshot has size of ${currentSnapshot.size} items" + ) repeat(count) { modelCache.removeAt(position) } @@ -314,7 +325,7 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec if (partialState.roomSummary?.membership != Membership.JOIN) { return } - val timestamp = System.currentTimeMillis() + val timestamp = clock.epochMillis() val showingForwardLoader = LoadingItem_() .id("forward_loading_item_$timestamp") @@ -406,14 +417,16 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec timelineEvent = it, highlightedEventId = partialState.highlightedEventId, isFromThreadTimeline = partialState.isFromThreadTimeline(), - rootThreadEventId = partialState.rootThreadEventId) + rootThreadEventId = partialState.rootThreadEventId + ) } val nextDisplayableEvent = currentSnapshot.subList(position + 1, currentSnapshot.size).firstOrNull { timelineEventVisibilityHelper.shouldShowEvent( timelineEvent = it, highlightedEventId = partialState.highlightedEventId, isFromThreadTimeline = partialState.isFromThreadTimeline(), - rootThreadEventId = partialState.rootThreadEventId) + rootThreadEventId = partialState.rootThreadEventId + ) } val timelineEventsGroup = timelineEventsGroups.getOrNull(event) val params = TimelineItemFactoryParams( @@ -466,7 +479,8 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec position: Int, receiptsByEvents: Map>): CacheItemData { val wantsDateSeparator = wantsDateSeparator(event, nextEvent) - val mergedHeaderModel = mergedHeaderItemFactory.create(event, + val mergedHeaderModel = mergedHeaderItemFactory.create( + event, nextEvent = nextEvent, partialState = partialState, items = this@TimelineEventController.currentSnapshot, @@ -537,7 +551,8 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec timelineEvent = event, highlightedEventId = partialState.highlightedEventId, isFromThreadTimeline = partialState.isFromThreadTimeline(), - rootThreadEventId = partialState.rootThreadEventId)) { + rootThreadEventId = partialState.rootThreadEventId + )) { lastShownEventId = event.eventId } if (lastShownEventId == null) { diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/edithistory/ViewEditHistoryEpoxyController.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/edithistory/ViewEditHistoryEpoxyController.kt index 63a34fe713..f96ee7eee2 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/edithistory/ViewEditHistoryEpoxyController.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/edithistory/ViewEditHistoryEpoxyController.kt @@ -26,6 +26,7 @@ import im.vector.app.core.date.DateFormatKind import im.vector.app.core.date.VectorDateFormatter import im.vector.app.core.resources.ColorProvider import im.vector.app.core.resources.StringProvider +import im.vector.app.core.time.Clock import im.vector.app.core.ui.list.genericFooterItem import im.vector.app.core.ui.list.genericHeaderItem import im.vector.app.core.ui.list.genericItem @@ -49,7 +50,8 @@ class ViewEditHistoryEpoxyController @Inject constructor( private val stringProvider: StringProvider, private val colorProvider: ColorProvider, private val eventHtmlRenderer: EventHtmlRenderer, - private val dateFormatter: VectorDateFormatter + private val dateFormatter: VectorDateFormatter, + private val clock: Clock, ) : TypedEpoxyController() { override fun buildModels(state: ViewEditHistoryViewState) { @@ -86,7 +88,7 @@ class ViewEditHistoryEpoxyController @Inject constructor( val evDate = Calendar.getInstance().apply { timeInMillis = timelineEvent.originServerTs - ?: System.currentTimeMillis() + ?: clock.epochMillis() } if (lastDate?.get(Calendar.DAY_OF_YEAR) != evDate.get(Calendar.DAY_OF_YEAR)) { // need to display header with day diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/TimelineControllerInterceptorHelper.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/TimelineControllerInterceptorHelper.kt index 8a0e1e18fd..e9361e564c 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/TimelineControllerInterceptorHelper.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/TimelineControllerInterceptorHelper.kt @@ -25,6 +25,7 @@ import im.vector.app.features.home.room.detail.timeline.item.DaySeparatorItem import im.vector.app.features.home.room.detail.timeline.item.ItemWithEvents import im.vector.app.features.home.room.detail.timeline.item.TimelineReadMarkerItem_ import org.matrix.android.sdk.api.session.room.timeline.Timeline +import kotlin.random.Random import kotlin.reflect.KMutableProperty0 private const val DEFAULT_PREFETCH_THRESHOLD = 30 @@ -104,7 +105,7 @@ class TimelineControllerInterceptorHelper(private val positionOfReadMarker: KMut .coerceAtLeast(0) val loadingItem = LoadingItem_() - .id("prefetch_backward_loading${System.currentTimeMillis()}") + .id("prefetch_backward_loading${Random.nextLong()}") .showLoader(false) .setVisibilityStateChangedListener(Timeline.Direction.BACKWARDS, callback) @@ -120,7 +121,7 @@ class TimelineControllerInterceptorHelper(private val positionOfReadMarker: KMut .coerceAtLeast(0) val loadingItem = LoadingItem_() - .id("prefetch_forward_loading${System.currentTimeMillis()}") + .id("prefetch_forward_loading${Random.nextLong()}") .showLoader(false) .setVisibilityStateChangedListener(Timeline.Direction.FORWARDS, callback) add(indexOfPrefetchForward, loadingItem) diff --git a/vector/src/main/java/im/vector/app/features/login2/created/AccountCreatedFragment.kt b/vector/src/main/java/im/vector/app/features/login2/created/AccountCreatedFragment.kt index 8223053ad8..b0caf80d82 100644 --- a/vector/src/main/java/im/vector/app/features/login2/created/AccountCreatedFragment.kt +++ b/vector/src/main/java/im/vector/app/features/login2/created/AccountCreatedFragment.kt @@ -31,6 +31,7 @@ import im.vector.app.core.date.VectorDateFormatter import im.vector.app.core.dialogs.GalleryOrCameraDialogHelper import im.vector.app.core.intent.getFilenameFromUri import im.vector.app.core.resources.ColorProvider +import im.vector.app.core.time.Clock import im.vector.app.databinding.DialogBaseEditTextBinding import im.vector.app.databinding.FragmentLoginAccountCreatedBinding import im.vector.app.features.displayname.getBestName @@ -52,13 +53,14 @@ class AccountCreatedFragment @Inject constructor( private val avatarRenderer: AvatarRenderer, private val dateFormatter: VectorDateFormatter, private val matrixItemColorProvider: MatrixItemColorProvider, + private val clock: Clock, colorProvider: ColorProvider ) : AbstractLoginFragment2(), GalleryOrCameraDialogHelper.Listener { private val viewModel: AccountCreatedViewModel by fragmentViewModel() - private val galleryOrCameraDialogHelper = GalleryOrCameraDialogHelper(this, colorProvider) + private val galleryOrCameraDialogHelper = GalleryOrCameraDialogHelper(this, colorProvider, clock) override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentLoginAccountCreatedBinding { return FragmentLoginAccountCreatedBinding.inflate(inflater, container, false) @@ -73,7 +75,7 @@ class AccountCreatedFragment @Inject constructor( viewModel.onEach { invalidateState(it) } - views.loginAccountCreatedTime.text = dateFormatter.format(System.currentTimeMillis(), DateFormatKind.MESSAGE_SIMPLE) + views.loginAccountCreatedTime.text = dateFormatter.format(clock.epochMillis(), DateFormatKind.MESSAGE_SIMPLE) } private fun observeViewEvents() { diff --git a/vector/src/main/java/im/vector/app/features/media/domain/usecase/DownloadMediaUseCase.kt b/vector/src/main/java/im/vector/app/features/media/domain/usecase/DownloadMediaUseCase.kt index b0401ccd30..32b804e43b 100644 --- a/vector/src/main/java/im/vector/app/features/media/domain/usecase/DownloadMediaUseCase.kt +++ b/vector/src/main/java/im/vector/app/features/media/domain/usecase/DownloadMediaUseCase.kt @@ -20,6 +20,7 @@ import android.content.Context import androidx.core.net.toUri import dagger.hilt.android.qualifiers.ApplicationContext import im.vector.app.core.intent.getMimeTypeFromUri +import im.vector.app.core.time.Clock import im.vector.app.core.utils.saveMedia import im.vector.app.features.notifications.NotificationUtils import kotlinx.coroutines.withContext @@ -30,7 +31,8 @@ import javax.inject.Inject class DownloadMediaUseCase @Inject constructor( @ApplicationContext private val appContext: Context, private val session: Session, - private val notificationUtils: NotificationUtils + private val notificationUtils: NotificationUtils, + private val clock: Clock, ) { suspend fun execute(input: File): Result = withContext(session.coroutineDispatchers.io) { @@ -40,7 +42,8 @@ class DownloadMediaUseCase @Inject constructor( file = input, title = input.name, mediaMimeType = getMimeTypeFromUri(appContext, input.toUri()), - notificationUtils = notificationUtils + notificationUtils = notificationUtils, + now = clock.epochMillis() ) } } diff --git a/vector/src/main/java/im/vector/app/features/notifications/NotifiableEventResolver.kt b/vector/src/main/java/im/vector/app/features/notifications/NotifiableEventResolver.kt index 43eab0c1f2..4ac7461f5a 100644 --- a/vector/src/main/java/im/vector/app/features/notifications/NotifiableEventResolver.kt +++ b/vector/src/main/java/im/vector/app/features/notifications/NotifiableEventResolver.kt @@ -20,6 +20,7 @@ import im.vector.app.BuildConfig import im.vector.app.R import im.vector.app.core.extensions.takeAs import im.vector.app.core.resources.StringProvider +import im.vector.app.core.time.Clock import im.vector.app.features.displayname.getBestName import im.vector.app.features.home.room.detail.timeline.format.DisplayableEventFormatter import im.vector.app.features.home.room.detail.timeline.format.NoticeEventFormatter @@ -58,7 +59,8 @@ import javax.inject.Inject class NotifiableEventResolver @Inject constructor( private val stringProvider: StringProvider, private val noticeEventFormatter: NoticeEventFormatter, - private val displayableEventFormatter: DisplayableEventFormatter + private val displayableEventFormatter: DisplayableEventFormatter, + private val clock: Clock, ) { // private val eventDisplay = RiotEventDisplay(context) @@ -86,7 +88,7 @@ class NotifiableEventResolver @Inject constructor( eventId = event.eventId!!, editedEventId = timelineEvent.getEditedEventId(), noisy = false, // will be updated - timestamp = event.originServerTs ?: System.currentTimeMillis(), + timestamp = event.originServerTs ?: clock.epochMillis(), description = bodyPreview, title = stringProvider.getString(R.string.notification_unknown_new_event), soundName = null, @@ -178,15 +180,19 @@ class NotifiableEventResolver @Inject constructor( roomName = roomName, roomIsDirect = room.roomSummary()?.isDirect ?: false, roomAvatarPath = session.contentUrlResolver() - .resolveThumbnail(room.roomSummary()?.avatarUrl, + .resolveThumbnail( + room.roomSummary()?.avatarUrl, 250, 250, - ContentUrlResolver.ThumbnailMethod.SCALE), + ContentUrlResolver.ThumbnailMethod.SCALE + ), senderAvatarPath = session.contentUrlResolver() - .resolveThumbnail(event.senderInfo.avatarUrl, + .resolveThumbnail( + event.senderInfo.avatarUrl, 250, 250, - ContentUrlResolver.ThumbnailMethod.SCALE), + ContentUrlResolver.ThumbnailMethod.SCALE + ), matrixID = session.myUserId, soundName = null ) diff --git a/vector/src/main/java/im/vector/app/features/notifications/NotificationBroadcastReceiver.kt b/vector/src/main/java/im/vector/app/features/notifications/NotificationBroadcastReceiver.kt index 3d5bd7930c..261fcdd2ce 100644 --- a/vector/src/main/java/im/vector/app/features/notifications/NotificationBroadcastReceiver.kt +++ b/vector/src/main/java/im/vector/app/features/notifications/NotificationBroadcastReceiver.kt @@ -23,6 +23,7 @@ import androidx.core.app.RemoteInput import dagger.hilt.android.AndroidEntryPoint import im.vector.app.R import im.vector.app.core.di.ActiveSessionHolder +import im.vector.app.core.time.Clock import im.vector.app.features.analytics.AnalyticsTracker import im.vector.app.features.analytics.extensions.toAnalyticsJoinedRoom import im.vector.app.features.session.coroutineScope @@ -45,6 +46,7 @@ class NotificationBroadcastReceiver : BroadcastReceiver() { @Inject lateinit var notificationDrawerManager: NotificationDrawerManager @Inject lateinit var activeSessionHolder: ActiveSessionHolder @Inject lateinit var analyticsTracker: AnalyticsTracker + @Inject lateinit var clock: Clock override fun onReceive(context: Context?, intent: Intent?) { if (intent == null || context == null) return @@ -137,7 +139,7 @@ class NotificationBroadcastReceiver : BroadcastReceiver() { eventId = UUID.randomUUID().toString(), editedEventId = null, noisy = false, - timestamp = System.currentTimeMillis(), + timestamp = clock.epochMillis(), senderName = session.roomService().getRoomMember(session.myUserId, room.roomId)?.displayName ?: context?.getString(R.string.notification_sender_me), senderId = session.myUserId, @@ -188,7 +190,7 @@ class NotificationBroadcastReceiver : BroadcastReceiver() { val notifiableMessageEvent = NotifiableMessageEvent( event.eventId, false, - System.currentTimeMillis(), + clock.epochMillis(), session.myUser?.displayname ?: context?.getString(R.string.notification_sender_me), session.myUserId, diff --git a/vector/src/main/java/im/vector/app/features/notifications/NotificationUtils.kt b/vector/src/main/java/im/vector/app/features/notifications/NotificationUtils.kt index e44f42d5cd..08f6ccc2f3 100755 --- a/vector/src/main/java/im/vector/app/features/notifications/NotificationUtils.kt +++ b/vector/src/main/java/im/vector/app/features/notifications/NotificationUtils.kt @@ -51,6 +51,7 @@ import im.vector.app.core.extensions.createIgnoredUri import im.vector.app.core.platform.PendingIntentCompat import im.vector.app.core.resources.StringProvider import im.vector.app.core.services.CallService +import im.vector.app.core.time.Clock import im.vector.app.core.utils.startNotificationChannelSettingsIntent import im.vector.app.features.call.VectorCallActivity import im.vector.app.features.call.service.CallHeadsUpActionReceiver @@ -72,9 +73,12 @@ import kotlin.random.Random * Note: Cannot inject ColorProvider in the constructor, because it requires an Activity */ @Singleton -class NotificationUtils @Inject constructor(private val context: Context, - private val stringProvider: StringProvider, - private val vectorPreferences: VectorPreferences) { +class NotificationUtils @Inject constructor( + private val context: Context, + private val stringProvider: StringProvider, + private val vectorPreferences: VectorPreferences, + private val clock: Clock, +) { companion object { /* ========================================================================================== @@ -323,7 +327,7 @@ class NotificationUtils @Inject constructor(private val context: Context, } val contentPendingIntent = PendingIntent.getActivity( context, - System.currentTimeMillis().toInt(), + clock.epochMillis().toInt(), contentIntent, PendingIntentCompat.FLAG_IMMUTABLE ) @@ -337,7 +341,7 @@ class NotificationUtils @Inject constructor(private val context: Context, mode = VectorCallActivity.INCOMING_ACCEPT ) ) - .getPendingIntent(System.currentTimeMillis().toInt(), PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE) + .getPendingIntent(clock.epochMillis().toInt(), PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE) val rejectCallPendingIntent = buildRejectCallPendingIntent(call.callId) @@ -392,7 +396,7 @@ class NotificationUtils @Inject constructor(private val context: Context, } val contentPendingIntent = PendingIntent.getActivity( context, - System.currentTimeMillis().toInt(), + clock.epochMillis().toInt(), contentIntent, PendingIntentCompat.FLAG_IMMUTABLE ) @@ -453,7 +457,7 @@ class NotificationUtils @Inject constructor(private val context: Context, val contentPendingIntent = TaskStackBuilder.create(context) .addNextIntentWithParentStack(HomeActivity.newIntent(context)) .addNextIntent(VectorCallActivity.newIntent(context, call, null)) - .getPendingIntent(System.currentTimeMillis().toInt(), PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE) + .getPendingIntent(clock.epochMillis().toInt(), PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE) builder.setContentIntent(contentPendingIntent) @@ -467,7 +471,7 @@ class NotificationUtils @Inject constructor(private val context: Context, } return PendingIntent.getBroadcast( context, - System.currentTimeMillis().toInt(), + clock.epochMillis().toInt(), rejectCallActionReceiver, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE ) @@ -515,7 +519,7 @@ class NotificationUtils @Inject constructor(private val context: Context, val contentPendingIntent = TaskStackBuilder.create(context) .addNextIntentWithParentStack(HomeActivity.newIntent(context)) .addNextIntent(RoomDetailActivity.newIntent(context, TimelineArgs(callInformation.nativeRoomId))) - .getPendingIntent(System.currentTimeMillis().toInt(), PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE) + .getPendingIntent(clock.epochMillis().toInt(), PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE) builder.setContentIntent(contentPendingIntent) return builder.build() @@ -562,7 +566,7 @@ class NotificationUtils @Inject constructor(private val context: Context, } PendingIntent.getActivity( context, - System.currentTimeMillis().toInt(), + clock.epochMillis().toInt(), intent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE ).let { @@ -636,7 +640,7 @@ class NotificationUtils @Inject constructor(private val context: Context, markRoomReadIntent.putExtra(NotificationBroadcastReceiver.KEY_ROOM_ID, roomInfo.roomId) val markRoomReadPendingIntent = PendingIntent.getBroadcast( context, - System.currentTimeMillis().toInt(), + clock.epochMillis().toInt(), markRoomReadIntent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE ) @@ -677,7 +681,7 @@ class NotificationUtils @Inject constructor(private val context: Context, intent.action = DISMISS_ROOM_NOTIF_ACTION val pendingIntent = PendingIntent.getBroadcast( context.applicationContext, - System.currentTimeMillis().toInt(), + clock.epochMillis().toInt(), intent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE ) @@ -712,7 +716,7 @@ class NotificationUtils @Inject constructor(private val context: Context, rejectIntent.putExtra(NotificationBroadcastReceiver.KEY_ROOM_ID, roomId) val rejectIntentPendingIntent = PendingIntent.getBroadcast( context, - System.currentTimeMillis().toInt(), + clock.epochMillis().toInt(), rejectIntent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE ) @@ -730,7 +734,7 @@ class NotificationUtils @Inject constructor(private val context: Context, joinIntent.putExtra(NotificationBroadcastReceiver.KEY_ROOM_ID, roomId) val joinIntentPendingIntent = PendingIntent.getBroadcast( context, - System.currentTimeMillis().toInt(), + clock.epochMillis().toInt(), joinIntent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE ) @@ -811,7 +815,7 @@ class NotificationUtils @Inject constructor(private val context: Context, .addNextIntentWithParentStack(HomeActivity.newIntent(context)) .addNextIntent(roomIntentTap) .getPendingIntent( - System.currentTimeMillis().toInt(), + clock.epochMillis().toInt(), PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE ) } @@ -844,7 +848,7 @@ class NotificationUtils @Inject constructor(private val context: Context, intent.putExtra(NotificationBroadcastReceiver.KEY_ROOM_ID, roomId) return PendingIntent.getBroadcast( context, - System.currentTimeMillis().toInt(), + clock.epochMillis().toInt(), intent, // PendingIntents attached to actions with remote inputs must be mutable PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_MUTABLE diff --git a/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthChooseProfilePictureFragment.kt b/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthChooseProfilePictureFragment.kt index 81300932db..0ccff7dffc 100644 --- a/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthChooseProfilePictureFragment.kt +++ b/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthChooseProfilePictureFragment.kt @@ -29,6 +29,7 @@ import im.vector.app.core.di.ActiveSessionHolder import im.vector.app.core.dialogs.GalleryOrCameraDialogHelper import im.vector.app.core.extensions.singletonEntryPoint import im.vector.app.core.resources.ColorProvider +import im.vector.app.core.time.Clock import im.vector.app.databinding.FragmentFtueProfilePictureBinding import im.vector.app.features.home.AvatarRenderer import im.vector.app.features.onboarding.OnboardingAction @@ -39,10 +40,11 @@ import javax.inject.Inject class FtueAuthChooseProfilePictureFragment @Inject constructor( private val activeSessionHolder: ActiveSessionHolder, - colorProvider: ColorProvider + colorProvider: ColorProvider, + clock: Clock, ) : AbstractFtueAuthFragment(), GalleryOrCameraDialogHelper.Listener { - private val galleryOrCameraDialogHelper = GalleryOrCameraDialogHelper(this, colorProvider) + private val galleryOrCameraDialogHelper = GalleryOrCameraDialogHelper(this, colorProvider, clock) private val avatarRenderer: AvatarRenderer by lazy { requireContext().singletonEntryPoint().avatarRenderer() } override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentFtueProfilePictureBinding { diff --git a/vector/src/main/java/im/vector/app/features/popup/PopupAlertManager.kt b/vector/src/main/java/im/vector/app/features/popup/PopupAlertManager.kt index ae03b5345a..91292e42e5 100644 --- a/vector/src/main/java/im/vector/app/features/popup/PopupAlertManager.kt +++ b/vector/src/main/java/im/vector/app/features/popup/PopupAlertManager.kt @@ -24,6 +24,7 @@ import android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS import com.tapadoo.alerter.Alerter import im.vector.app.R import im.vector.app.core.platform.VectorBaseActivity +import im.vector.app.core.time.Clock import im.vector.app.core.utils.isAnimationDisabled import im.vector.app.features.analytics.ui.consent.AnalyticsOptInActivity import im.vector.app.features.pin.PinActivity @@ -41,7 +42,9 @@ import javax.inject.Singleton * will be back in the queue in first position. */ @Singleton -class PopupAlertManager @Inject constructor() { +class PopupAlertManager @Inject constructor( + private val clock: Clock, +) { companion object { const val INCOMING_CALL_PRIORITY = Int.MAX_VALUE @@ -116,7 +119,7 @@ class PopupAlertManager @Inject constructor() { return } if (currentAlerter != null) { - if (currentAlerter!!.expirationTimestamp != null && System.currentTimeMillis() > currentAlerter!!.expirationTimestamp!!) { + if (currentAlerter!!.expirationTimestamp != null && clock.epochMillis() > currentAlerter!!.expirationTimestamp!!) { // this alert has expired, remove it // perform dismiss try { @@ -162,7 +165,7 @@ class PopupAlertManager @Inject constructor() { currentAlerter = next next?.let { if (!shouldBeDisplayedIn(next, currentActivity)) return - val currentTime = System.currentTimeMillis() + val currentTime = clock.epochMillis() if (next.expirationTimestamp != null && currentTime > next.expirationTimestamp!!) { // skip try { diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomFragment.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomFragment.kt index 2871513c1f..0603342bb1 100644 --- a/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomFragment.kt +++ b/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomFragment.kt @@ -37,6 +37,7 @@ import im.vector.app.core.extensions.configureWith import im.vector.app.core.platform.OnBackPressed 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.databinding.FragmentCreateRoomBinding import im.vector.app.features.navigation.Navigator import im.vector.app.features.roomdirectory.RoomDirectorySharedAction @@ -62,7 +63,8 @@ data class CreateRoomArgs( class CreateRoomFragment @Inject constructor( private val createRoomController: CreateRoomController, private val createSpaceController: CreateSubSpaceController, - colorProvider: ColorProvider + colorProvider: ColorProvider, + clock: Clock, ) : VectorBaseFragment(), CreateRoomController.Listener, GalleryOrCameraDialogHelper.Listener, @@ -74,7 +76,7 @@ class CreateRoomFragment @Inject constructor( private lateinit var roomJoinRuleSharedActionViewModel: RoomJoinRuleSharedActionViewModel - private val galleryOrCameraDialogHelper = GalleryOrCameraDialogHelper(this, colorProvider) + private val galleryOrCameraDialogHelper = GalleryOrCameraDialogHelper(this, colorProvider, clock) override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentCreateRoomBinding { return FragmentCreateRoomBinding.inflate(inflater, container, false) @@ -166,7 +168,8 @@ class CreateRoomFragment @Inject constructor( } else { listOf(RoomJoinRules.INVITE, RoomJoinRules.PUBLIC) } - RoomJoinRuleBottomSheet.newInstance(state.roomJoinRules, + RoomJoinRuleBottomSheet.newInstance( + state.roomJoinRules, allowed.map { it.toOption(false) }, state.isSubSpace, state.parentSpaceSummary?.displayName diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsFragment.kt b/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsFragment.kt index eb69e36ba0..85151e1258 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsFragment.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsFragment.kt @@ -37,6 +37,7 @@ import im.vector.app.core.intent.getFilenameFromUri import im.vector.app.core.platform.OnBackPressed 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.toast import im.vector.app.databinding.FragmentRoomSettingGenericBinding import im.vector.app.features.analytics.plan.MobileScreen @@ -57,7 +58,8 @@ import javax.inject.Inject class RoomSettingsFragment @Inject constructor( private val controller: RoomSettingsController, colorProvider: ColorProvider, - private val avatarRenderer: AvatarRenderer + private val avatarRenderer: AvatarRenderer, + clock: Clock, ) : VectorBaseFragment(), RoomSettingsController.Callback, @@ -70,7 +72,7 @@ class RoomSettingsFragment @Inject constructor( private lateinit var roomJoinRuleSharedActionViewModel: RoomJoinRuleSharedActionViewModel private val roomProfileArgs: RoomProfileArgs by args() - private val galleryOrCameraDialogHelper = GalleryOrCameraDialogHelper(this, colorProvider) + private val galleryOrCameraDialogHelper = GalleryOrCameraDialogHelper(this, colorProvider, clock) override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentRoomSettingGenericBinding { return FragmentRoomSettingGenericBinding.inflate(inflater, container, false) @@ -198,7 +200,8 @@ class RoomSettingsFragment @Inject constructor( RoomSettingsAction.SetAvatarAction( RoomSettingsViewState.AvatarAction.UpdateAvatar( newAvatarUri = uri, - newAvatarFileName = getFilenameFromUri(requireContext(), uri) ?: UUID.randomUUID().toString()) + newAvatarFileName = getFilenameFromUri(requireContext(), uri) ?: UUID.randomUUID().toString() + ) ) ) } diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/uploads/RoomUploadsFragment.kt b/vector/src/main/java/im/vector/app/features/roomprofile/uploads/RoomUploadsFragment.kt index 6a115ad272..7e83046c36 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/uploads/RoomUploadsFragment.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/uploads/RoomUploadsFragment.kt @@ -30,6 +30,7 @@ import com.google.android.material.tabs.TabLayoutMediator import im.vector.app.R import im.vector.app.core.intent.getMimeTypeFromUri import im.vector.app.core.platform.VectorBaseFragment +import im.vector.app.core.time.Clock import im.vector.app.core.utils.saveMedia import im.vector.app.core.utils.shareMedia import im.vector.app.databinding.FragmentRoomUploadsBinding @@ -43,7 +44,8 @@ import javax.inject.Inject class RoomUploadsFragment @Inject constructor( private val avatarRenderer: AvatarRenderer, - private val notificationUtils: NotificationUtils + private val notificationUtils: NotificationUtils, + private val clock: Clock, ) : VectorBaseFragment() { private val roomProfileArgs: RoomProfileArgs by args() @@ -88,7 +90,8 @@ class RoomUploadsFragment @Inject constructor( file = it.file, title = it.title, mediaMimeType = getMimeTypeFromUri(requireContext(), it.file.toUri()), - notificationUtils = notificationUtils + notificationUtils = notificationUtils, + now = clock.epochMillis() ) }.onFailure { failure -> if (!isAdded) return@onFailure diff --git a/vector/src/main/java/im/vector/app/features/settings/VectorPreferences.kt b/vector/src/main/java/im/vector/app/features/settings/VectorPreferences.kt index 195072465b..ca42a07d50 100755 --- a/vector/src/main/java/im/vector/app/features/settings/VectorPreferences.kt +++ b/vector/src/main/java/im/vector/app/features/settings/VectorPreferences.kt @@ -26,6 +26,7 @@ import com.squareup.seismic.ShakeDetector import im.vector.app.BuildConfig import im.vector.app.R import im.vector.app.core.di.DefaultSharedPreferences +import im.vector.app.core.time.Clock import im.vector.app.features.disclaimer.SHARED_PREF_KEY import im.vector.app.features.homeserver.ServerUrlsRepository import im.vector.app.features.themes.ThemeUtils @@ -33,7 +34,10 @@ import org.matrix.android.sdk.api.extensions.tryOrNull import timber.log.Timber import javax.inject.Inject -class VectorPreferences @Inject constructor(private val context: Context) { +class VectorPreferences @Inject constructor( + private val context: Context, + private val clock: Clock, +) { companion object { const val SETTINGS_HELP_PREFERENCE_KEY = "SETTINGS_HELP_PREFERENCE_KEY" @@ -664,9 +668,9 @@ class VectorPreferences @Inject constructor(private val context: Context) { */ fun getMinMediasLastAccessTime(): Long { return when (getSelectedMediasSavingPeriod()) { - MEDIA_SAVING_3_DAYS -> System.currentTimeMillis() / 1000 - 3 * 24 * 60 * 60 - MEDIA_SAVING_1_WEEK -> System.currentTimeMillis() / 1000 - 7 * 24 * 60 * 60 - MEDIA_SAVING_1_MONTH -> System.currentTimeMillis() / 1000 - 30 * 24 * 60 * 60 + MEDIA_SAVING_3_DAYS -> clock.epochMillis() / 1000 - 3 * 24 * 60 * 60 + MEDIA_SAVING_1_WEEK -> clock.epochMillis() / 1000 - 7 * 24 * 60 * 60 + MEDIA_SAVING_1_MONTH -> clock.epochMillis() / 1000 - 30 * 24 * 60 * 60 MEDIA_SAVING_FOREVER -> 0 else -> 0 } @@ -872,8 +876,10 @@ class VectorPreferences @Inject constructor(private val context: Context) { * @return true if user should always appear offline */ fun userAlwaysAppearsOffline(): Boolean { - return defaultPrefs.getBoolean(SETTINGS_PRESENCE_USER_ALWAYS_APPEARS_OFFLINE, - getDefault(R.bool.settings_presence_user_always_appears_offline_default)) + return defaultPrefs.getBoolean( + SETTINGS_PRESENCE_USER_ALWAYS_APPEARS_OFFLINE, + getDefault(R.bool.settings_presence_user_always_appears_offline_default) + ) } /** @@ -1005,9 +1011,11 @@ class VectorPreferences @Inject constructor(private val context: Context) { } fun prefSpacesShowAllRoomInHome(): Boolean { - return defaultPrefs.getBoolean(SETTINGS_PREF_SPACE_SHOW_ALL_ROOM_IN_HOME, + return defaultPrefs.getBoolean( + SETTINGS_PREF_SPACE_SHOW_ALL_ROOM_IN_HOME, // migration of old property - !labsSpacesOnlyOrphansInHome()) + !labsSpacesOnlyOrphansInHome() + ) } /* diff --git a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsGeneralFragment.kt b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsGeneralFragment.kt index 678356b05b..99acea79df 100644 --- a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsGeneralFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsGeneralFragment.kt @@ -45,6 +45,7 @@ import im.vector.app.core.preference.UserAvatarPreference import im.vector.app.core.preference.VectorPreference import im.vector.app.core.preference.VectorSwitchPreference import im.vector.app.core.resources.ColorProvider +import im.vector.app.core.time.Clock import im.vector.app.core.utils.TextUtils import im.vector.app.core.utils.getSizeOfFiles import im.vector.app.core.utils.toast @@ -74,7 +75,8 @@ import java.util.UUID import javax.inject.Inject class VectorSettingsGeneralFragment @Inject constructor( - colorProvider: ColorProvider + colorProvider: ColorProvider, + clock: Clock, ) : VectorSettingsBaseFragment(), GalleryOrCameraDialogHelper.Listener { @@ -82,7 +84,7 @@ class VectorSettingsGeneralFragment @Inject constructor( override var titleRes = R.string.settings_general_title override val preferenceXmlRes = R.xml.vector_settings_general - private val galleryOrCameraDialogHelper = GalleryOrCameraDialogHelper(this, colorProvider) + private val galleryOrCameraDialogHelper = GalleryOrCameraDialogHelper(this, colorProvider, clock) private val mUserSettingsCategory by lazy { findPreference(VectorPreferences.SETTINGS_USER_SETTINGS_PREFERENCE_KEY)!! diff --git a/vector/src/main/java/im/vector/app/features/settings/devtools/KeyRequestsFragment.kt b/vector/src/main/java/im/vector/app/features/settings/devtools/KeyRequestsFragment.kt index cef68c01c1..4225cbdbec 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devtools/KeyRequestsFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devtools/KeyRequestsFragment.kt @@ -36,12 +36,15 @@ import im.vector.app.R import im.vector.app.core.extensions.registerStartForActivityResult import im.vector.app.core.extensions.safeOpenOutputStream import im.vector.app.core.platform.VectorBaseFragment +import im.vector.app.core.time.Clock import im.vector.app.core.utils.selectTxtFileToWrite import im.vector.app.databinding.FragmentDevtoolKeyrequestsBinding import org.matrix.android.sdk.api.extensions.tryOrNull import javax.inject.Inject -class KeyRequestsFragment @Inject constructor() : VectorBaseFragment() { +class KeyRequestsFragment @Inject constructor( + private val clock: Clock, +) : VectorBaseFragment() { override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentDevtoolKeyrequestsBinding { return FragmentDevtoolKeyrequestsBinding.inflate(inflater, container, false) @@ -126,7 +129,7 @@ class KeyRequestsFragment @Inject constructor() : VectorBaseFragment(), SpaceDetailEpoxyController.Listener, GalleryOrCameraDialogHelper.Listener, OnBackPressed { @@ -42,7 +44,7 @@ class CreateSpaceDetailsFragment @Inject constructor( override fun getBinding(inflater: LayoutInflater, container: ViewGroup?) = FragmentSpaceCreateGenericEpoxyFormBinding.inflate(layoutInflater, container, false) - private val galleryOrCameraDialogHelper = GalleryOrCameraDialogHelper(this, colorProvider) + private val galleryOrCameraDialogHelper = GalleryOrCameraDialogHelper(this, colorProvider, clock) override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) diff --git a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceSettingsFragment.kt b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceSettingsFragment.kt index 57b1c97efb..0264fdabfc 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceSettingsFragment.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceSettingsFragment.kt @@ -38,7 +38,7 @@ import im.vector.app.core.intent.getFilenameFromUri import im.vector.app.core.platform.OnBackPressed import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.core.resources.ColorProvider -import im.vector.app.core.resources.DrawableProvider +import im.vector.app.core.time.Clock import im.vector.app.core.utils.toast import im.vector.app.databinding.FragmentRoomSettingGenericBinding import im.vector.app.features.home.AvatarRenderer @@ -60,9 +60,9 @@ import javax.inject.Inject class SpaceSettingsFragment @Inject constructor( private val epoxyController: SpaceSettingsController, - private val colorProvider: ColorProvider, + colorProvider: ColorProvider, + clock: Clock, private val avatarRenderer: AvatarRenderer, - private val drawableProvider: DrawableProvider ) : VectorBaseFragment(), SpaceSettingsController.Callback, GalleryOrCameraDialogHelper.Listener, @@ -73,7 +73,7 @@ class SpaceSettingsFragment @Inject constructor( private lateinit var roomJoinRuleSharedActionViewModel: RoomJoinRuleSharedActionViewModel - private val galleryOrCameraDialogHelper = GalleryOrCameraDialogHelper(this, colorProvider) + private val galleryOrCameraDialogHelper = GalleryOrCameraDialogHelper(this, colorProvider, clock) override fun getBinding(inflater: LayoutInflater, container: ViewGroup?) = FragmentRoomSettingGenericBinding.inflate(inflater) @@ -236,7 +236,8 @@ class SpaceSettingsFragment @Inject constructor( RoomSettingsAction.SetAvatarAction( RoomSettingsViewState.AvatarAction.UpdateAvatar( newAvatarUri = uri, - newAvatarFileName = getFilenameFromUri(requireContext(), uri) ?: UUID.randomUUID().toString()) + newAvatarFileName = getFilenameFromUri(requireContext(), uri) ?: UUID.randomUUID().toString() + ) ) ) } diff --git a/vector/src/main/java/im/vector/app/features/userdirectory/UserListViewModel.kt b/vector/src/main/java/im/vector/app/features/userdirectory/UserListViewModel.kt index 291153ef2b..bb1f8db6ff 100644 --- a/vector/src/main/java/im/vector/app/features/userdirectory/UserListViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/userdirectory/UserListViewModel.kt @@ -47,6 +47,7 @@ import org.matrix.android.sdk.api.session.identity.IdentityServiceListener import org.matrix.android.sdk.api.session.identity.ThreePid import org.matrix.android.sdk.api.session.user.model.User import org.matrix.android.sdk.api.util.toMatrixItem +import kotlin.random.Random data class ThreePidUser( val email: String, @@ -142,7 +143,7 @@ class UserListViewModel @AssistedInject constructor( } private fun retryUserSearch(state: UserListViewState) { - identityServerUsersSearch.tryEmit(UserSearch(state.searchTerm, cacheBuster = System.currentTimeMillis())) + identityServerUsersSearch.tryEmit(UserSearch(state.searchTerm, cacheBuster = Random.nextLong())) } private fun handleSearchUsers(searchTerm: String) { diff --git a/vector/src/main/java/im/vector/app/features/voice/VoicePlayerHelper.kt b/vector/src/main/java/im/vector/app/features/voice/VoicePlayerHelper.kt index d2f7927d75..6c9b8d34c2 100644 --- a/vector/src/main/java/im/vector/app/features/voice/VoicePlayerHelper.kt +++ b/vector/src/main/java/im/vector/app/features/voice/VoicePlayerHelper.kt @@ -20,11 +20,13 @@ import android.content.Context import android.os.Build import com.arthenica.ffmpegkit.FFmpegKit import com.arthenica.ffmpegkit.ReturnCode +import im.vector.app.core.time.Clock import timber.log.Timber import java.io.File import javax.inject.Inject class VoicePlayerHelper @Inject constructor( + private val clock: Clock, context: Context ) { private val outputDirectory: File by lazy { @@ -46,9 +48,9 @@ class VoicePlayerHelper @Inject constructor( if (targetFile.exists()) { targetFile.delete() } - val start = System.currentTimeMillis() + val start = clock.epochMillis() val session = FFmpegKit.execute("-i \"${file.path}\" -c:a aac \"${targetFile.path}\"") - val duration = System.currentTimeMillis() - start + val duration = clock.epochMillis() - start Timber.d("Convert to mp4 in $duration ms. Size in bytes from ${file.length()} to ${targetFile.length()}") return when { ReturnCode.isSuccess(session.returnCode) -> { diff --git a/vector/src/main/java/im/vector/app/features/voice/VoiceRecorderL.kt b/vector/src/main/java/im/vector/app/features/voice/VoiceRecorderL.kt index 2d40f5f7a3..e5fd60dbc3 100644 --- a/vector/src/main/java/im/vector/app/features/voice/VoiceRecorderL.kt +++ b/vector/src/main/java/im/vector/app/features/voice/VoiceRecorderL.kt @@ -23,10 +23,14 @@ import com.arthenica.ffmpegkit.FFmpegKitConfig import com.arthenica.ffmpegkit.Level import com.arthenica.ffmpegkit.ReturnCode import im.vector.app.BuildConfig +import im.vector.app.core.time.Clock import timber.log.Timber import java.io.File -class VoiceRecorderL(context: Context) : AbstractVoiceRecorder(context, "mp4") { +class VoiceRecorderL( + context: Context, + private val clock: Clock, +) : AbstractVoiceRecorder(context, "mp4") { override fun setOutputFormat(mediaRecorder: MediaRecorder) { // Use AAC/MP4 format here mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4) @@ -43,9 +47,9 @@ class VoiceRecorderL(context: Context) : AbstractVoiceRecorder(context, "mp4") { if (targetFile.exists()) { targetFile.delete() } - val start = System.currentTimeMillis() + val start = clock.epochMillis() val session = FFmpegKit.execute("-i \"${recordedFile.path}\" -c:a libvorbis \"${targetFile.path}\"") - val duration = System.currentTimeMillis() - start + val duration = clock.epochMillis() - start Timber.d("Convert to ogg in $duration ms. Size in bytes from ${recordedFile.length()} to ${targetFile.length()}") return when { ReturnCode.isSuccess(session.returnCode) -> { diff --git a/vector/src/main/java/im/vector/app/features/voice/VoiceRecorderProvider.kt b/vector/src/main/java/im/vector/app/features/voice/VoiceRecorderProvider.kt index 004d520a6f..0cee8f4f6e 100644 --- a/vector/src/main/java/im/vector/app/features/voice/VoiceRecorderProvider.kt +++ b/vector/src/main/java/im/vector/app/features/voice/VoiceRecorderProvider.kt @@ -18,16 +18,18 @@ package im.vector.app.features.voice import android.content.Context import android.os.Build +import im.vector.app.core.time.Clock import javax.inject.Inject class VoiceRecorderProvider @Inject constructor( - private val context: Context + private val context: Context, + private val clock: Clock, ) { fun provideVoiceRecorder(): VoiceRecorder { return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { VoiceRecorderQ(context) } else { - VoiceRecorderL(context) + VoiceRecorderL(context, clock) } } } From 32bc93c87d7957fa7f41b67d2d47422ad52976bc Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 3 May 2022 12:02:30 +0200 Subject: [PATCH 05/11] Ensure the `Clock` interface is used. --- tools/check/forbidden_strings_in_code.txt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tools/check/forbidden_strings_in_code.txt b/tools/check/forbidden_strings_in_code.txt index 21ab0bab77..393e942b2a 100755 --- a/tools/check/forbidden_strings_in_code.txt +++ b/tools/check/forbidden_strings_in_code.txt @@ -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_ \ No newline at end of file +R\.string\.template_ + +### Use the Clock interface, or use `measureTimeMillis` +System\.currentTimeMillis\(\)===2 From cdcaf93fc704407a3a1461de11a443b659a9b4a1 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 3 May 2022 12:34:34 +0200 Subject: [PATCH 06/11] Fix F-Droid build --- .../vector/app/fdroid/BackgroundSyncStarter.kt | 13 +++++++++++-- .../receiver/AlarmSyncBroadcastReceiver.kt | 16 ++++++++++------ .../OnApplicationUpgradeOrRebootReceiver.kt | 3 ++- .../java/im/vector/app/push/fcm/FcmHelper.kt | 8 ++++++-- .../java/im/vector/app/push/fcm/FcmHelper.kt | 6 +++++- .../main/java/im/vector/app/VectorApplication.kt | 4 +++- .../im/vector/app/core/di/SingletonEntryPoint.kt | 3 +++ 7 files changed, 40 insertions(+), 13 deletions(-) diff --git a/vector/src/fdroid/java/im/vector/app/fdroid/BackgroundSyncStarter.kt b/vector/src/fdroid/java/im/vector/app/fdroid/BackgroundSyncStarter.kt index 0fe89e7fe5..e890180c4f 100644 --- a/vector/src/fdroid/java/im/vector/app/fdroid/BackgroundSyncStarter.kt +++ b/vector/src/fdroid/java/im/vector/app/fdroid/BackgroundSyncStarter.kt @@ -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 -> { diff --git a/vector/src/fdroid/java/im/vector/app/fdroid/receiver/AlarmSyncBroadcastReceiver.kt b/vector/src/fdroid/java/im/vector/app/fdroid/receiver/AlarmSyncBroadcastReceiver.kt index a847f8fc45..09bd56654d 100644 --- a/vector/src/fdroid/java/im/vector/app/fdroid/receiver/AlarmSyncBroadcastReceiver.kt +++ b/vector/src/fdroid/java/im/vector/app/fdroid/receiver/AlarmSyncBroadcastReceiver.kt @@ -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 { diff --git a/vector/src/fdroid/java/im/vector/app/fdroid/receiver/OnApplicationUpgradeOrRebootReceiver.kt b/vector/src/fdroid/java/im/vector/app/fdroid/receiver/OnApplicationUpgradeOrRebootReceiver.kt index c1bc90c4db..aacd7723f5 100644 --- a/vector/src/fdroid/java/im/vector/app/fdroid/receiver/OnApplicationUpgradeOrRebootReceiver.kt +++ b/vector/src/fdroid/java/im/vector/app/fdroid/receiver/OnApplicationUpgradeOrRebootReceiver.kt @@ -32,7 +32,8 @@ class OnApplicationUpgradeOrRebootReceiver : BroadcastReceiver() { BackgroundSyncStarter.start( context, singletonEntryPoint.vectorPreferences(), - singletonEntryPoint.activeSessionHolder() + singletonEntryPoint.activeSessionHolder(), + singletonEntryPoint.clock() ) } } diff --git a/vector/src/fdroid/java/im/vector/app/push/fcm/FcmHelper.kt b/vector/src/fdroid/java/im/vector/app/push/fcm/FcmHelper.kt index 7603e738d7..425fd1081a 100755 --- a/vector/src/fdroid/java/im/vector/app/push/fcm/FcmHelper.kt +++ b/vector/src/fdroid/java/im/vector/app/push/fcm/FcmHelper.kt @@ -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) } } diff --git a/vector/src/gplay/java/im/vector/app/push/fcm/FcmHelper.kt b/vector/src/gplay/java/im/vector/app/push/fcm/FcmHelper.kt index 52ad4be087..3d44f10f76 100755 --- a/vector/src/gplay/java/im/vector/app/push/fcm/FcmHelper.kt +++ b/vector/src/gplay/java/im/vector/app/push/fcm/FcmHelper.kt @@ -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 } } diff --git a/vector/src/main/java/im/vector/app/VectorApplication.kt b/vector/src/main/java/im/vector/app/VectorApplication.kt index e12eecfefc..8917513537 100644 --- a/vector/src/main/java/im/vector/app/VectorApplication.kt +++ b/vector/src/main/java/im/vector/app/VectorApplication.kt @@ -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) diff --git a/vector/src/main/java/im/vector/app/core/di/SingletonEntryPoint.kt b/vector/src/main/java/im/vector/app/core/di/SingletonEntryPoint.kt index 283437c679..dc88229a10 100644 --- a/vector/src/main/java/im/vector/app/core/di/SingletonEntryPoint.kt +++ b/vector/src/main/java/im/vector/app/core/di/SingletonEntryPoint.kt @@ -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 From 2f0e4e4f3ddb34b9f0af6e9ebd35f83415c9226f Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 3 May 2022 12:53:52 +0200 Subject: [PATCH 07/11] changelog --- changelog.d/5907.sdk | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/5907.sdk diff --git a/changelog.d/5907.sdk b/changelog.d/5907.sdk new file mode 100644 index 0000000000..623cc2a174 --- /dev/null +++ b/changelog.d/5907.sdk @@ -0,0 +1 @@ +Replace usage of `System.currentTimeMillis()` by a `Clock` interface From 8602cbba7a15f77ba124032665375a46d1180bb1 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 3 May 2022 17:43:00 +0200 Subject: [PATCH 08/11] Fix test --- .../usecase/DownloadMediaUseCaseTest.kt | 13 ++++++--- .../im/vector/app/test/fakes/FakeClock.kt | 27 +++++++++++++++++++ 2 files changed, 36 insertions(+), 4 deletions(-) create mode 100644 vector/src/test/java/im/vector/app/test/fakes/FakeClock.kt diff --git a/vector/src/test/java/im/vector/app/features/media/domain/usecase/DownloadMediaUseCaseTest.kt b/vector/src/test/java/im/vector/app/features/media/domain/usecase/DownloadMediaUseCaseTest.kt index bb05357cb2..d45e6e7ce1 100644 --- a/vector/src/test/java/im/vector/app/features/media/domain/usecase/DownloadMediaUseCaseTest.kt +++ b/vector/src/test/java/im/vector/app/features/media/domain/usecase/DownloadMediaUseCaseTest.kt @@ -23,6 +23,7 @@ import com.airbnb.mvrx.test.MvRxTestRule import im.vector.app.core.intent.getMimeTypeFromUri import im.vector.app.core.utils.saveMedia import im.vector.app.features.notifications.NotificationUtils +import im.vector.app.test.fakes.FakeClock import im.vector.app.test.fakes.FakeFile import im.vector.app.test.fakes.FakeSession import io.mockk.MockKAnnotations @@ -57,6 +58,8 @@ class DownloadMediaUseCaseTest { @MockK lateinit var notificationUtils: NotificationUtils + private val clock = FakeClock() + private val file = FakeFile() @OverrideMockKs @@ -85,7 +88,8 @@ class DownloadMediaUseCaseTest { every { getMimeTypeFromUri(appContext, uri) } returns mimeType file.givenName(name) file.givenUri(uri) - coEvery { saveMedia(any(), any(), any(), any(), any()) } just runs + clock.givenEpoch(123) + coEvery { saveMedia(any(), any(), any(), any(), any(), any()) } just runs // When val result = downloadMediaUseCase.execute(file.instance) @@ -100,7 +104,7 @@ class DownloadMediaUseCaseTest { getMimeTypeFromUri(appContext, uri) } coVerify { - saveMedia(appContext, file.instance, name, mimeType, notificationUtils) + saveMedia(appContext, file.instance, name, mimeType, notificationUtils, 123) } } @@ -113,8 +117,9 @@ class DownloadMediaUseCaseTest { val error = Throwable() file.givenName(name) file.givenUri(uri) + clock.givenEpoch(345) every { getMimeTypeFromUri(appContext, uri) } returns mimeType - coEvery { saveMedia(any(), any(), any(), any(), any()) } throws error + coEvery { saveMedia(any(), any(), any(), any(), any(), any()) } throws error // When val result = downloadMediaUseCase.execute(file.instance) @@ -129,7 +134,7 @@ class DownloadMediaUseCaseTest { getMimeTypeFromUri(appContext, uri) } coVerify { - saveMedia(appContext, file.instance, name, mimeType, notificationUtils) + saveMedia(appContext, file.instance, name, mimeType, notificationUtils, 345) } } } diff --git a/vector/src/test/java/im/vector/app/test/fakes/FakeClock.kt b/vector/src/test/java/im/vector/app/test/fakes/FakeClock.kt new file mode 100644 index 0000000000..1d531f147f --- /dev/null +++ b/vector/src/test/java/im/vector/app/test/fakes/FakeClock.kt @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2022 New Vector Ltd + * + * 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 im.vector.app.test.fakes + +import im.vector.app.core.time.Clock +import io.mockk.every +import io.mockk.mockk + +class FakeClock : Clock by mockk() { + fun givenEpoch(epoch: Long) { + every { epochMillis() } returns epoch + } +} From 2d98cbd9152ecd97ff1b08ef3fe2628815a6d588 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 4 May 2022 17:48:36 +0200 Subject: [PATCH 09/11] Rename `now` to more explicit `currentTimeMillis` --- .../crypto/model/IncomingRequestCancellation.kt | 5 +++-- .../crypto/model/IncomingRoomKeyRequest.kt | 5 +++-- .../crypto/model/IncomingSecretShareRequest.kt | 5 +++-- .../crypto/verification/VerificationService.kt | 6 +++--- .../internal/crypto/model/OlmSessionWrapper.kt | 4 ++-- .../database/helper/ThreadSummaryHelper.kt | 14 +++++++------- .../db/ContentScannerEntityQueries.kt | 4 ++-- .../threads/FetchThreadSummariesTask.kt | 2 +- .../sync/handler/room/ReadReceiptHandler.kt | 6 ++++-- .../sync/handler/room/RoomSyncHandler.kt | 2 +- .../app/core/services/VectorSyncService.kt | 8 ++++---- .../app/core/utils/ExternalApplicationsUtil.kt | 17 +++++++++-------- .../home/room/detail/TimelineFragment.kt | 2 +- .../domain/usecase/DownloadMediaUseCase.kt | 2 +- .../roomprofile/uploads/RoomUploadsFragment.kt | 2 +- 15 files changed, 45 insertions(+), 39 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/model/IncomingRequestCancellation.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/model/IncomingRequestCancellation.kt index 15f663d30d..ad11ef9a5e 100755 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/model/IncomingRequestCancellation.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/model/IncomingRequestCancellation.kt @@ -46,8 +46,9 @@ data class IncomingRequestCancellation( * Factory * * @param event the event + * @param currentTimeMillis the current time in milliseconds */ - fun fromEvent(event: Event, now: Long): IncomingRequestCancellation? { + fun fromEvent(event: Event, currentTimeMillis: Long): IncomingRequestCancellation? { return event.getClearContent() .toModel() ?.let { @@ -55,7 +56,7 @@ data class IncomingRequestCancellation( userId = event.senderId, deviceId = it.requestingDeviceId, requestId = it.requestId, - localCreationTimestamp = event.ageLocalTs ?: now + localCreationTimestamp = event.ageLocalTs ?: currentTimeMillis ) } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/model/IncomingRoomKeyRequest.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/model/IncomingRoomKeyRequest.kt index 7012dd1d15..0b2c32284b 100755 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/model/IncomingRoomKeyRequest.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/model/IncomingRoomKeyRequest.kt @@ -64,8 +64,9 @@ data class IncomingRoomKeyRequest( * Factory * * @param event the event + * @param currentTimeMillis the current time in milliseconds */ - fun fromEvent(event: Event, now: Long): IncomingRoomKeyRequest? { + fun fromEvent(event: Event, currentTimeMillis: Long): IncomingRoomKeyRequest? { return event.getClearContent() .toModel() ?.let { @@ -74,7 +75,7 @@ data class IncomingRoomKeyRequest( deviceId = it.requestingDeviceId, requestId = it.requestId, requestBody = it.body ?: RoomKeyRequestBody(), - localCreationTimestamp = event.ageLocalTs ?: now + localCreationTimestamp = event.ageLocalTs ?: currentTimeMillis ) } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/model/IncomingSecretShareRequest.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/model/IncomingSecretShareRequest.kt index 4c20bf5769..80f70c83f3 100755 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/model/IncomingSecretShareRequest.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/model/IncomingSecretShareRequest.kt @@ -64,8 +64,9 @@ data class IncomingSecretShareRequest( * Factory * * @param event the event + * @param currentTimeMillis the current time in milliseconds */ - fun fromEvent(event: Event, now: Long): IncomingSecretShareRequest? { + fun fromEvent(event: Event, currentTimeMillis: Long): IncomingSecretShareRequest? { return event.getClearContent() .toModel() ?.let { @@ -74,7 +75,7 @@ data class IncomingSecretShareRequest( deviceId = it.requestingDeviceId, requestId = it.requestId, secretName = it.secretName, - localCreationTimestamp = event.ageLocalTs ?: now + localCreationTimestamp = event.ageLocalTs ?: currentTimeMillis ) } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/verification/VerificationService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/verification/VerificationService.kt index 027cdbd70c..ec67e4b31d 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/verification/VerificationService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/verification/VerificationService.kt @@ -129,10 +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?, now: Long): Boolean { + fun isValidRequest(age: Long?, currentTimeMillis: Long): Boolean { if (age == null) return false - 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 } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/model/OlmSessionWrapper.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/model/OlmSessionWrapper.kt index 4636089364..d7ce553f39 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/model/OlmSessionWrapper.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/model/OlmSessionWrapper.kt @@ -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(now: Long) { - lastReceivedMessageTs = now + fun onMessageReceived(currentTimeMillis: Long) { + lastReceivedMessageTs = currentTimeMillis } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/helper/ThreadSummaryHelper.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/helper/ThreadSummaryHelper.kt index 76f0164ac6..d052a7dea4 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/helper/ThreadSummaryHelper.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/helper/ThreadSummaryHelper.kt @@ -138,7 +138,7 @@ internal fun ThreadSummaryEntity.Companion.createOrUpdate( roomEntity: RoomEntity, userId: String, cryptoService: CryptoService? = null, - now: Long, + currentTimeMillis: Long, ) { when (threadSummaryType) { ThreadSummaryUpdateType.REPLACE -> { @@ -154,14 +154,14 @@ internal fun ThreadSummaryEntity.Companion.createOrUpdate( Timber.i("###THREADS ThreadSummaryHelper REPLACE eventId:${it.rootThreadEventId} ") } - val rootThreadEventEntity = createEventEntity(realm, roomId, rootThreadEvent, now).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(realm, roomId, rootThreadEvent, roomMemberContentsByUser, now)?.also { + val latestThreadEventEntity = createLatestEventEntity(realm, roomId, rootThreadEvent, roomMemberContentsByUser, currentTimeMillis)?.also { try { decryptIfNeeded(cryptoService, it, roomId) } catch (e: InterruptedException) { @@ -269,8 +269,8 @@ private fun HashMap.addSenderState(realm: Realm, roo /** * Create an EventEntity for the root thread event or get an existing one */ -private fun createEventEntity(realm: Realm, roomId: String, event: Event, now: Long): EventEntity { - val ageLocalTs = event.unsignedData?.age?.let { now - 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) } @@ -283,13 +283,13 @@ private fun createLatestEventEntity( roomId: String, rootThreadEvent: Event, roomMemberContentsByUser: HashMap, - now: Long, + currentTimeMillis: Long, ): EventEntity? { return getLatestEvent(rootThreadEvent)?.let { it.senderId?.let { senderId -> roomMemberContentsByUser.addSenderState(realm, roomId, senderId) } - createEventEntity(realm, roomId, it, now) + createEventEntity(realm, roomId, it, currentTimeMillis) } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/contentscanner/db/ContentScannerEntityQueries.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/contentscanner/db/ContentScannerEntityQueries.kt index d41e4cb326..e4b64a1a0e 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/contentscanner/db/ContentScannerEntityQueries.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/contentscanner/db/ContentScannerEntityQueries.kt @@ -34,11 +34,11 @@ internal fun ContentScanResultEntity.Companion.get(realm: Realm, attachmentUrl: internal fun ContentScanResultEntity.Companion.getOrCreate(realm: Realm, attachmentUrl: String, contentScannerUrl: String?, - now: Long): ContentScanResultEntity { + currentTimeMillis: Long): ContentScanResultEntity { return ContentScanResultEntity.get(realm, attachmentUrl, contentScannerUrl) ?: realm.createObject().also { it.mediaUrl = attachmentUrl - it.scanDateTimestamp = now + it.scanDateTimestamp = currentTimeMillis it.scannerUrl = contentScannerUrl } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/threads/FetchThreadSummariesTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/threads/FetchThreadSummariesTask.kt index e82a3a5895..c5f9bd13fd 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/threads/FetchThreadSummariesTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/threads/FetchThreadSummariesTask.kt @@ -98,7 +98,7 @@ internal class DefaultFetchThreadSummariesTask @Inject constructor( roomEntity = roomEntity, userId = userId, cryptoService = cryptoService, - now = clock.epochMillis(), + currentTimeMillis = clock.epochMillis(), ) } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/handler/room/ReadReceiptHandler.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/handler/room/ReadReceiptHandler.kt index 5843e727a4..77bee18df9 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/handler/room/ReadReceiptHandler.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/handler/room/ReadReceiptHandler.kt @@ -44,12 +44,14 @@ internal class ReadReceiptHandler @Inject constructor( companion object { - fun createContent(userId: String, eventId: String, now: Long): 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 now.toDouble() + TIMESTAMP_KEY to currentTimeMillis.toDouble() ) ) ) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/handler/room/RoomSyncHandler.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/handler/room/RoomSyncHandler.kt index 0aff191ecc..5437a015fd 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/handler/room/RoomSyncHandler.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/handler/room/RoomSyncHandler.kt @@ -445,7 +445,7 @@ internal class RoomSyncHandler @Inject constructor( roomMemberContentsByUser = roomMemberContentsByUser, userId = userId, roomEntity = roomEntity, - now = clock.epochMillis(), + currentTimeMillis = clock.epochMillis(), ) } } ?: run { diff --git a/vector/src/main/java/im/vector/app/core/services/VectorSyncService.kt b/vector/src/main/java/im/vector/app/core/services/VectorSyncService.kt index 8051900bcb..131276bea6 100644 --- a/vector/src/main/java/im/vector/app/core/services/VectorSyncService.kt +++ b/vector/src/main/java/im/vector/app/core/services/VectorSyncService.kt @@ -106,7 +106,7 @@ class VectorSyncService : SyncService() { syncDelaySeconds = syncDelaySeconds, isPeriodic = true, isNetworkBack = false, - now = clock.epochMillis() + currentTimeMillis = clock.epochMillis() ) } @@ -162,7 +162,7 @@ class VectorSyncService : SyncService() { syncDelaySeconds = syncDelaySeconds, isPeriodic = isPeriodic, isNetworkBack = true, - now = clock.epochMillis() + currentTimeMillis = clock.epochMillis() ) // Indicate whether the work finished successfully with the Result return Result.success() @@ -195,7 +195,7 @@ private fun Context.rescheduleSyncService(sessionId: String, syncDelaySeconds: Int, isPeriodic: Boolean, isNetworkBack: Boolean, - now: Long) { + currentTimeMillis: Long) { Timber.d("## Sync: rescheduleSyncService") val intent = if (isPeriodic) { VectorSyncService.newPeriodicIntent( @@ -221,7 +221,7 @@ private fun Context.rescheduleSyncService(sessionId: String, } else { PendingIntent.getService(this, 0, intent, PendingIntentCompat.FLAG_IMMUTABLE) } - val firstMillis = now + syncDelaySeconds * 1000L + val firstMillis = currentTimeMillis + syncDelaySeconds * 1000L val alarmMgr = getSystemService()!! if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { alarmMgr.setAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, firstMillis, pendingIntent) diff --git a/vector/src/main/java/im/vector/app/core/utils/ExternalApplicationsUtil.kt b/vector/src/main/java/im/vector/app/core/utils/ExternalApplicationsUtil.kt index f34260c941..d961dcaa46 100644 --- a/vector/src/main/java/im/vector/app/core/utils/ExternalApplicationsUtil.kt +++ b/vector/src/main/java/im/vector/app/core/utils/ExternalApplicationsUtil.kt @@ -256,7 +256,7 @@ suspend fun saveMedia(context: Context, title: String, mediaMimeType: String?, notificationUtils: NotificationUtils, - now: Long) { + currentTimeMillis: Long) { withContext(Dispatchers.IO) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { val filename = appendTimeToFilename(title) @@ -265,8 +265,8 @@ suspend fun saveMedia(context: Context, 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, now) - put(MediaStore.Images.Media.DATE_TAKEN, now) + 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 @@ -297,7 +297,7 @@ suspend fun saveMedia(context: Context, } } } else { - saveMediaLegacy(context, mediaMimeType, title, file, now) + saveMediaLegacy(context, mediaMimeType, title, file, currentTimeMillis) } } } @@ -307,7 +307,7 @@ private fun saveMediaLegacy(context: Context, mediaMimeType: String?, title: String, file: File, - now: Long) { + currentTimeMillis: Long) { val state = Environment.getExternalStorageState() if (Environment.MEDIA_MOUNTED != state) { context.toast(context.getString(R.string.error_saving_media_file)) @@ -328,7 +328,7 @@ private fun saveMediaLegacy(context: Context, } else { title } - val savedFile = saveFileIntoLegacy(file, downloadDir, outputFilename, now) + val savedFile = saveFileIntoLegacy(file, downloadDir, outputFilename, currentTimeMillis) if (savedFile != null) { val downloadManager = context.getSystemService() downloadManager?.addCompletedDownload( @@ -418,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?, now: Long): File? { +fun saveFileIntoLegacy(sourceFile: File, dstDirPath: File, outputFilename: String?, currentTimeMillis: Long): File? { // defines another name for the external media var dstFileName: String @@ -433,7 +434,7 @@ fun saveFileIntoLegacy(sourceFile: File, dstDirPath: File, outputFilename: Strin if (dotPos > 0) { fileExt = sourceFile.name.substring(dotPos) } - dstFileName = "vector_$now$fileExt" + dstFileName = "vector_$currentTimeMillis$fileExt" } else { dstFileName = outputFilename } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt index 60a1ddcc62..099f3779ee 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt @@ -2187,7 +2187,7 @@ class TimelineFragment @Inject constructor( title = action.messageContent.body, mediaMimeType = action.messageContent.mimeType ?: getMimeTypeFromUri(requireContext(), it.toUri()), notificationUtils = notificationUtils, - now = clock.epochMillis() + currentTimeMillis = clock.epochMillis() ) } .onFailure { diff --git a/vector/src/main/java/im/vector/app/features/media/domain/usecase/DownloadMediaUseCase.kt b/vector/src/main/java/im/vector/app/features/media/domain/usecase/DownloadMediaUseCase.kt index 32b804e43b..6a24ac4fd7 100644 --- a/vector/src/main/java/im/vector/app/features/media/domain/usecase/DownloadMediaUseCase.kt +++ b/vector/src/main/java/im/vector/app/features/media/domain/usecase/DownloadMediaUseCase.kt @@ -43,7 +43,7 @@ class DownloadMediaUseCase @Inject constructor( title = input.name, mediaMimeType = getMimeTypeFromUri(appContext, input.toUri()), notificationUtils = notificationUtils, - now = clock.epochMillis() + currentTimeMillis = clock.epochMillis() ) } } diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/uploads/RoomUploadsFragment.kt b/vector/src/main/java/im/vector/app/features/roomprofile/uploads/RoomUploadsFragment.kt index 7e83046c36..721f99178f 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/uploads/RoomUploadsFragment.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/uploads/RoomUploadsFragment.kt @@ -91,7 +91,7 @@ class RoomUploadsFragment @Inject constructor( title = it.title, mediaMimeType = getMimeTypeFromUri(requireContext(), it.file.toUri()), notificationUtils = notificationUtils, - now = clock.epochMillis() + currentTimeMillis = clock.epochMillis() ) }.onFailure { failure -> if (!isAdded) return@onFailure From 3ecde755e05eece63d2d1318dfbd5191ba659436 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 4 May 2022 17:51:14 +0200 Subject: [PATCH 10/11] Rename val --- vector/src/androidTest/java/im/vector/app/EspressoExt.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vector/src/androidTest/java/im/vector/app/EspressoExt.kt b/vector/src/androidTest/java/im/vector/app/EspressoExt.kt index 899f268176..efb28cdff5 100644 --- a/vector/src/androidTest/java/im/vector/app/EspressoExt.kt +++ b/vector/src/androidTest/java/im/vector/app/EspressoExt.kt @@ -172,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 = Random.nextLong() + 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 { From 2fb5f423a5a0092f7138db7ae094758dfdc22106 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 4 May 2022 17:52:32 +0200 Subject: [PATCH 11/11] Rename val --- .../home/room/detail/composer/MessageComposerViewState.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerViewState.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerViewState.kt index 10263c1abc..016a39d919 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerViewState.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerViewState.kt @@ -36,7 +36,7 @@ sealed interface SendMode { val text: String, val fromSharing: Boolean, // This is necessary for forcing refresh on selectSubscribe - private val ts: Int = Random.nextInt() + private val random: Int = Random.nextInt() ) : SendMode data class Quote(val timelineEvent: TimelineEvent, val text: String) : SendMode