mirror of
https://github.com/vector-im/element-android.git
synced 2024-11-15 01:35:07 +08:00
Merge pull request #5907 from vector-im/feature/bma/currentTimeMillis
Use Clock interface
This commit is contained in:
commit
330d802079
1
changelog.d/5907.sdk
Normal file
1
changelog.d/5907.sdk
Normal file
@ -0,0 +1 @@
|
||||
Replace usage of `System.currentTimeMillis()` by a `Clock` interface
|
@ -57,7 +57,8 @@ import java.util.concurrent.TimeUnit
|
||||
class CommonTestHelper(context: Context) {
|
||||
|
||||
internal val matrix: TestMatrix
|
||||
val coroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.Main)
|
||||
private val coroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.Main)
|
||||
private var accountNumber = 0
|
||||
|
||||
fun getTestInterceptor(session: Session): MockOkHttpInterceptor? = TestModule.interceptorForSession(session.sessionId) as? MockOkHttpInterceptor
|
||||
|
||||
@ -167,7 +168,8 @@ class CommonTestHelper(context: Context) {
|
||||
if (rootThreadEventId != null) {
|
||||
room.relationService().replyInThread(
|
||||
rootThreadEventId = rootThreadEventId,
|
||||
replyInThreadText = formattedMessage)
|
||||
replyInThreadText = formattedMessage
|
||||
)
|
||||
} else {
|
||||
room.sendService().sendTextMessage(formattedMessage)
|
||||
}
|
||||
@ -237,7 +239,7 @@ class CommonTestHelper(context: Context) {
|
||||
password: String,
|
||||
testParams: SessionTestParams): Session {
|
||||
val session = createAccountAndSync(
|
||||
userNamePrefix + "_" + System.currentTimeMillis() + UUID.randomUUID(),
|
||||
userNamePrefix + "_" + accountNumber++ + "_" + UUID.randomUUID(),
|
||||
password,
|
||||
testParams
|
||||
)
|
||||
|
@ -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
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -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(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -35,6 +35,7 @@ import org.matrix.android.sdk.internal.database.mapper.toEntity
|
||||
import org.matrix.android.sdk.internal.database.model.ChunkEntity
|
||||
import org.matrix.android.sdk.internal.database.model.SessionRealmModule
|
||||
import org.matrix.android.sdk.internal.session.room.timeline.PaginationDirection
|
||||
import org.matrix.android.sdk.internal.util.time.DefaultClock
|
||||
import org.matrix.android.sdk.session.room.timeline.RoomDataHelper.createFakeListOfEvents
|
||||
import org.matrix.android.sdk.session.room.timeline.RoomDataHelper.createFakeMessageEvent
|
||||
|
||||
@ -42,6 +43,7 @@ import org.matrix.android.sdk.session.room.timeline.RoomDataHelper.createFakeMes
|
||||
internal class ChunkEntityTest : InstrumentedTest {
|
||||
|
||||
private lateinit var monarchy: Monarchy
|
||||
private val clock = DefaultClock()
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
@ -59,7 +61,7 @@ internal class ChunkEntityTest : InstrumentedTest {
|
||||
monarchy.runTransactionSync { realm ->
|
||||
val chunk: ChunkEntity = realm.createObject()
|
||||
|
||||
val fakeEvent = createFakeMessageEvent().toEntity(ROOM_ID, SendState.SYNCED, System.currentTimeMillis()).let {
|
||||
val fakeEvent = createFakeMessageEvent().toEntity(ROOM_ID, SendState.SYNCED, clock.epochMillis()).let {
|
||||
realm.copyToRealm(it)
|
||||
}
|
||||
chunk.addTimelineEvent(
|
||||
@ -75,7 +77,7 @@ internal class ChunkEntityTest : InstrumentedTest {
|
||||
fun add_shouldNotAdd_whenAlreadyIncluded() {
|
||||
monarchy.runTransactionSync { realm ->
|
||||
val chunk: ChunkEntity = realm.createObject()
|
||||
val fakeEvent = createFakeMessageEvent().toEntity(ROOM_ID, SendState.SYNCED, System.currentTimeMillis()).let {
|
||||
val fakeEvent = createFakeMessageEvent().toEntity(ROOM_ID, SendState.SYNCED, clock.epochMillis()).let {
|
||||
realm.copyToRealm(it)
|
||||
}
|
||||
chunk.addTimelineEvent(
|
||||
@ -153,7 +155,7 @@ internal class ChunkEntityTest : InstrumentedTest {
|
||||
events: List<Event>,
|
||||
direction: PaginationDirection) {
|
||||
events.forEach { event ->
|
||||
val fakeEvent = event.toEntity(roomId, SendState.SYNCED, System.currentTimeMillis()).let {
|
||||
val fakeEvent = event.toEntity(roomId, SendState.SYNCED, clock.epochMillis()).let {
|
||||
realm.copyToRealm(it)
|
||||
}
|
||||
addTimelineEvent(
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -46,8 +46,9 @@ data class IncomingRequestCancellation(
|
||||
* Factory
|
||||
*
|
||||
* @param event the event
|
||||
* @param currentTimeMillis the current time in milliseconds
|
||||
*/
|
||||
fun fromEvent(event: Event): IncomingRequestCancellation? {
|
||||
fun fromEvent(event: Event, currentTimeMillis: Long): IncomingRequestCancellation? {
|
||||
return event.getClearContent()
|
||||
.toModel<ShareRequestCancellation>()
|
||||
?.let {
|
||||
@ -55,7 +56,7 @@ data class IncomingRequestCancellation(
|
||||
userId = event.senderId,
|
||||
deviceId = it.requestingDeviceId,
|
||||
requestId = it.requestId,
|
||||
localCreationTimestamp = event.ageLocalTs ?: System.currentTimeMillis()
|
||||
localCreationTimestamp = event.ageLocalTs ?: currentTimeMillis
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -64,8 +64,9 @@ data class IncomingRoomKeyRequest(
|
||||
* Factory
|
||||
*
|
||||
* @param event the event
|
||||
* @param currentTimeMillis the current time in milliseconds
|
||||
*/
|
||||
fun fromEvent(event: Event): IncomingRoomKeyRequest? {
|
||||
fun fromEvent(event: Event, currentTimeMillis: Long): IncomingRoomKeyRequest? {
|
||||
return event.getClearContent()
|
||||
.toModel<RoomKeyShareRequest>()
|
||||
?.let {
|
||||
@ -74,7 +75,7 @@ data class IncomingRoomKeyRequest(
|
||||
deviceId = it.requestingDeviceId,
|
||||
requestId = it.requestId,
|
||||
requestBody = it.body ?: RoomKeyRequestBody(),
|
||||
localCreationTimestamp = event.ageLocalTs ?: System.currentTimeMillis()
|
||||
localCreationTimestamp = event.ageLocalTs ?: currentTimeMillis
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -64,8 +64,9 @@ data class IncomingSecretShareRequest(
|
||||
* Factory
|
||||
*
|
||||
* @param event the event
|
||||
* @param currentTimeMillis the current time in milliseconds
|
||||
*/
|
||||
fun fromEvent(event: Event): IncomingSecretShareRequest? {
|
||||
fun fromEvent(event: Event, currentTimeMillis: Long): IncomingSecretShareRequest? {
|
||||
return event.getClearContent()
|
||||
.toModel<SecretShareRequest>()
|
||||
?.let {
|
||||
@ -74,7 +75,7 @@ data class IncomingSecretShareRequest(
|
||||
deviceId = it.requestingDeviceId,
|
||||
requestId = it.requestId,
|
||||
secretName = it.secretName,
|
||||
localCreationTimestamp = event.ageLocalTs ?: System.currentTimeMillis()
|
||||
localCreationTimestamp = event.ageLocalTs ?: currentTimeMillis
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -129,11 +129,10 @@ interface VerificationService {
|
||||
private const val TEN_MINUTES_IN_MILLIS = 10 * 60 * 1000
|
||||
private const val FIVE_MINUTES_IN_MILLIS = 5 * 60 * 1000
|
||||
|
||||
fun isValidRequest(age: Long?): Boolean {
|
||||
fun isValidRequest(age: Long?, currentTimeMillis: Long): Boolean {
|
||||
if (age == null) return false
|
||||
val now = System.currentTimeMillis()
|
||||
val tooInThePast = now - TEN_MINUTES_IN_MILLIS
|
||||
val tooInTheFuture = now + FIVE_MINUTES_IN_MILLIS
|
||||
val tooInThePast = currentTimeMillis - TEN_MINUTES_IN_MILLIS
|
||||
val tooInTheFuture = currentTimeMillis + FIVE_MINUTES_IN_MILLIS
|
||||
return age in tooInThePast..tooInTheFuture
|
||||
}
|
||||
}
|
||||
|
@ -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 ->
|
||||
|
@ -103,6 +103,7 @@ import org.matrix.android.sdk.internal.task.TaskThread
|
||||
import org.matrix.android.sdk.internal.task.configureWith
|
||||
import org.matrix.android.sdk.internal.task.launchToCallback
|
||||
import org.matrix.android.sdk.internal.util.JsonCanonicalizer
|
||||
import org.matrix.android.sdk.internal.util.time.Clock
|
||||
import org.matrix.olm.OlmManager
|
||||
import timber.log.Timber
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
@ -130,6 +131,7 @@ internal class DefaultCryptoService @Inject constructor(
|
||||
private val userId: String,
|
||||
@DeviceId
|
||||
private val deviceId: String?,
|
||||
private val clock: Clock,
|
||||
private val myDeviceInfoHolder: Lazy<MyDeviceInfoHolder>,
|
||||
// the crypto store
|
||||
private val cryptoStore: IMXCryptoStore,
|
||||
@ -701,11 +703,11 @@ internal class DefaultCryptoService @Inject constructor(
|
||||
}
|
||||
val safeAlgorithm = alg
|
||||
if (safeAlgorithm != null) {
|
||||
val t0 = System.currentTimeMillis()
|
||||
val t0 = clock.epochMillis()
|
||||
Timber.tag(loggerTag.value).v("encryptEventContent() starts")
|
||||
runCatching {
|
||||
val content = safeAlgorithm.encryptEventContent(eventContent, eventType, userIds)
|
||||
Timber.tag(loggerTag.value).v("## CRYPTO | encryptEventContent() : succeeds after ${System.currentTimeMillis() - t0} ms")
|
||||
Timber.tag(loggerTag.value).v("## CRYPTO | encryptEventContent() : succeeds after ${clock.epochMillis() - t0} ms")
|
||||
MXEncryptEventContentResult(content, EventType.ENCRYPTED)
|
||||
}.foldToCallback(callback)
|
||||
} else {
|
||||
@ -1022,9 +1024,9 @@ internal class DefaultCryptoService @Inject constructor(
|
||||
return withContext(coroutineDispatchers.crypto) {
|
||||
Timber.tag(loggerTag.value).v("importRoomKeys starts")
|
||||
|
||||
val t0 = System.currentTimeMillis()
|
||||
val t0 = clock.epochMillis()
|
||||
val roomKeys = MXMegolmExportEncryption.decryptMegolmKeyFile(roomKeysAsArray, password)
|
||||
val t1 = System.currentTimeMillis()
|
||||
val t1 = clock.epochMillis()
|
||||
|
||||
Timber.tag(loggerTag.value).v("importRoomKeys : decryptMegolmKeyFile done in ${t1 - t0} ms")
|
||||
|
||||
@ -1032,7 +1034,7 @@ internal class DefaultCryptoService @Inject constructor(
|
||||
.adapter<List<MegolmSessionData>>(Types.newParameterizedType(List::class.java, MegolmSessionData::class.java))
|
||||
.fromJson(roomKeys)
|
||||
|
||||
val t2 = System.currentTimeMillis()
|
||||
val t2 = clock.epochMillis()
|
||||
|
||||
Timber.tag(loggerTag.value).v("importRoomKeys : JSON parsing ${t2 - t1} ms")
|
||||
|
||||
@ -1224,7 +1226,7 @@ internal class DefaultCryptoService @Inject constructor(
|
||||
// val deviceKey = deviceInfo.identityKey()
|
||||
//
|
||||
// val lastForcedDate = lastNewSessionForcedDates.getObject(senderId, deviceKey) ?: 0
|
||||
// val now = System.currentTimeMillis()
|
||||
// val now = clock.epochMillis()
|
||||
// if (now - lastForcedDate < CRYPTO_MIN_FORCE_SESSION_PERIOD_MILLIS) {
|
||||
// Timber.d("## CRYPTO | markOlmSessionForUnwedging: New session already forced with device at $lastForcedDate. Not forcing another")
|
||||
// return
|
||||
|
@ -31,19 +31,23 @@ import org.matrix.android.sdk.internal.session.SessionScope
|
||||
import org.matrix.android.sdk.internal.session.sync.SyncTokenStore
|
||||
import org.matrix.android.sdk.internal.task.TaskExecutor
|
||||
import org.matrix.android.sdk.internal.util.logLimit
|
||||
import org.matrix.android.sdk.internal.util.time.Clock
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
// Legacy name: MXDeviceList
|
||||
@SessionScope
|
||||
internal class DeviceListManager @Inject constructor(private val cryptoStore: IMXCryptoStore,
|
||||
private val olmDevice: MXOlmDevice,
|
||||
private val syncTokenStore: SyncTokenStore,
|
||||
private val credentials: Credentials,
|
||||
private val downloadKeysForUsersTask: DownloadKeysForUsersTask,
|
||||
private val cryptoSessionInfoProvider: CryptoSessionInfoProvider,
|
||||
coroutineDispatchers: MatrixCoroutineDispatchers,
|
||||
private val taskExecutor: TaskExecutor) {
|
||||
internal class DeviceListManager @Inject constructor(
|
||||
private val cryptoStore: IMXCryptoStore,
|
||||
private val olmDevice: MXOlmDevice,
|
||||
private val syncTokenStore: SyncTokenStore,
|
||||
private val credentials: Credentials,
|
||||
private val downloadKeysForUsersTask: DownloadKeysForUsersTask,
|
||||
private val cryptoSessionInfoProvider: CryptoSessionInfoProvider,
|
||||
coroutineDispatchers: MatrixCoroutineDispatchers,
|
||||
private val taskExecutor: TaskExecutor,
|
||||
private val clock: Clock,
|
||||
) {
|
||||
|
||||
interface UserDevicesUpdateListener {
|
||||
fun onUsersDeviceUpdate(userIds: List<String>)
|
||||
@ -310,9 +314,9 @@ internal class DeviceListManager @Inject constructor(private val cryptoStore: IM
|
||||
stored
|
||||
} else {
|
||||
Timber.v("## CRYPTO | downloadKeys() : starts")
|
||||
val t0 = System.currentTimeMillis()
|
||||
val t0 = clock.epochMillis()
|
||||
val result = doKeyDownloadForUsers(downloadUsers)
|
||||
Timber.v("## CRYPTO | downloadKeys() : doKeyDownloadForUsers succeeds after ${System.currentTimeMillis() - t0} ms")
|
||||
Timber.v("## CRYPTO | downloadKeys() : doKeyDownloadForUsers succeeds after ${clock.epochMillis() - t0} ms")
|
||||
result.also {
|
||||
it.addEntriesFromMap(stored)
|
||||
}
|
||||
@ -475,8 +479,10 @@ internal class DeviceListManager @Inject constructor(private val cryptoStore: IM
|
||||
}
|
||||
|
||||
if (!isVerified) {
|
||||
Timber.e("## CRYPTO | validateDeviceKeys() : Unable to verify signature on device " + userId + ":" +
|
||||
deviceKeys.deviceId + " with error " + errorMessage)
|
||||
Timber.e(
|
||||
"## CRYPTO | validateDeviceKeys() : Unable to verify signature on device " + userId + ":" +
|
||||
deviceKeys.deviceId + " with error " + errorMessage
|
||||
)
|
||||
return false
|
||||
}
|
||||
|
||||
@ -486,9 +492,11 @@ internal class DeviceListManager @Inject constructor(private val cryptoStore: IM
|
||||
// best off sticking with the original keys.
|
||||
//
|
||||
// Should we warn the user about it somehow?
|
||||
Timber.e("## CRYPTO | validateDeviceKeys() : WARNING:Ed25519 key for device " + userId + ":" +
|
||||
deviceKeys.deviceId + " has changed : " +
|
||||
previouslyStoredDeviceKeys.fingerprint() + " -> " + signKey)
|
||||
Timber.e(
|
||||
"## CRYPTO | validateDeviceKeys() : WARNING:Ed25519 key for device " + userId + ":" +
|
||||
deviceKeys.deviceId + " has changed : " +
|
||||
previouslyStoredDeviceKeys.fingerprint() + " -> " + signKey
|
||||
)
|
||||
|
||||
Timber.e("## CRYPTO | validateDeviceKeys() : $previouslyStoredDeviceKeys -> $deviceKeys")
|
||||
Timber.e("## CRYPTO | validateDeviceKeys() : ${previouslyStoredDeviceKeys.keys} -> ${deviceKeys.keys}")
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
@ -44,6 +44,7 @@ import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore
|
||||
import org.matrix.android.sdk.internal.crypto.tasks.createUniqueTxnId
|
||||
import org.matrix.android.sdk.internal.di.SessionId
|
||||
import org.matrix.android.sdk.internal.session.SessionScope
|
||||
import org.matrix.android.sdk.internal.util.time.Clock
|
||||
import org.matrix.android.sdk.internal.worker.WorkerParamsFactory
|
||||
import timber.log.Timber
|
||||
import java.util.concurrent.Executors
|
||||
@ -59,7 +60,9 @@ internal class IncomingGossipingRequestManager @Inject constructor(
|
||||
private val roomEncryptorsStore: RoomEncryptorsStore,
|
||||
private val roomDecryptorProvider: RoomDecryptorProvider,
|
||||
private val coroutineDispatchers: MatrixCoroutineDispatchers,
|
||||
private val cryptoCoroutineScope: CoroutineScope) {
|
||||
private val cryptoCoroutineScope: CoroutineScope,
|
||||
private val clock: Clock,
|
||||
) {
|
||||
|
||||
private val executor = Executors.newSingleThreadExecutor()
|
||||
|
||||
@ -89,7 +92,7 @@ internal class IncomingGossipingRequestManager @Inject constructor(
|
||||
fun onVerificationCompleteForDevice(deviceId: String) {
|
||||
// For now we just keep an in memory cache
|
||||
synchronized(recentlyVerifiedDevices) {
|
||||
recentlyVerifiedDevices[deviceId] = System.currentTimeMillis()
|
||||
recentlyVerifiedDevices[deviceId] = clock.epochMillis()
|
||||
}
|
||||
}
|
||||
|
||||
@ -100,7 +103,7 @@ internal class IncomingGossipingRequestManager @Inject constructor(
|
||||
}
|
||||
if (verifTimestamp == null) return false
|
||||
|
||||
val age = System.currentTimeMillis() - verifTimestamp
|
||||
val age = clock.epochMillis() - verifTimestamp
|
||||
|
||||
return age < FIVE_MINUTES_IN_MILLIS
|
||||
}
|
||||
@ -114,11 +117,11 @@ internal class IncomingGossipingRequestManager @Inject constructor(
|
||||
fun onGossipingRequestEvent(event: Event) {
|
||||
val roomKeyShare = event.getClearContent().toModel<GossipingDefaultContent>()
|
||||
Timber.i("## CRYPTO | GOSSIP onGossipingRequestEvent received type ${event.type} from user:${event.senderId}, content:$roomKeyShare")
|
||||
// val ageLocalTs = event.unsignedData?.age?.let { System.currentTimeMillis() - it }
|
||||
// val ageLocalTs = event.unsignedData?.age?.let { clock.epochMillis() - it }
|
||||
when (roomKeyShare?.action) {
|
||||
GossipingToDeviceObject.ACTION_SHARE_REQUEST -> {
|
||||
if (event.getClearType() == EventType.REQUEST_SECRET) {
|
||||
IncomingSecretShareRequest.fromEvent(event)?.let {
|
||||
IncomingSecretShareRequest.fromEvent(event, clock.epochMillis())?.let {
|
||||
if (event.senderId == credentials.userId && it.deviceId == credentials.deviceId) {
|
||||
// ignore, it was sent by me as *
|
||||
Timber.v("## GOSSIP onGossipingRequestEvent type ${event.type} ignore remote echo")
|
||||
@ -129,7 +132,7 @@ internal class IncomingGossipingRequestManager @Inject constructor(
|
||||
}
|
||||
}
|
||||
} else if (event.getClearType() == EventType.ROOM_KEY_REQUEST) {
|
||||
IncomingRoomKeyRequest.fromEvent(event)?.let {
|
||||
IncomingRoomKeyRequest.fromEvent(event, clock.epochMillis())?.let {
|
||||
if (event.senderId == credentials.userId && it.deviceId == credentials.deviceId) {
|
||||
// ignore, it was sent by me as *
|
||||
Timber.v("## GOSSIP onGossipingRequestEvent type ${event.type} ignore remote echo")
|
||||
@ -141,7 +144,7 @@ internal class IncomingGossipingRequestManager @Inject constructor(
|
||||
}
|
||||
}
|
||||
GossipingToDeviceObject.ACTION_SHARE_CANCELLATION -> {
|
||||
IncomingRequestCancellation.fromEvent(event)?.let {
|
||||
IncomingRequestCancellation.fromEvent(event, clock.epochMillis())?.let {
|
||||
receivedRequestCancellations.add(it)
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
)
|
||||
}
|
||||
|
@ -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)) {
|
||||
|
@ -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 ->
|
||||
|
@ -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 {
|
||||
|
@ -26,13 +26,17 @@ import org.matrix.android.sdk.internal.crypto.OutgoingGossipingRequestManager
|
||||
import org.matrix.android.sdk.internal.crypto.RoomDecryptorProvider
|
||||
import org.matrix.android.sdk.internal.crypto.algorithms.megolm.MXMegolmDecryption
|
||||
import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore
|
||||
import org.matrix.android.sdk.internal.util.time.Clock
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
internal class MegolmSessionDataImporter @Inject constructor(private val olmDevice: MXOlmDevice,
|
||||
private val roomDecryptorProvider: RoomDecryptorProvider,
|
||||
private val outgoingGossipingRequestManager: OutgoingGossipingRequestManager,
|
||||
private val cryptoStore: IMXCryptoStore) {
|
||||
internal class MegolmSessionDataImporter @Inject constructor(
|
||||
private val olmDevice: MXOlmDevice,
|
||||
private val roomDecryptorProvider: RoomDecryptorProvider,
|
||||
private val outgoingGossipingRequestManager: OutgoingGossipingRequestManager,
|
||||
private val cryptoStore: IMXCryptoStore,
|
||||
private val clock: Clock,
|
||||
) {
|
||||
|
||||
/**
|
||||
* Import a list of megolm session keys.
|
||||
@ -47,7 +51,7 @@ internal class MegolmSessionDataImporter @Inject constructor(private val olmDevi
|
||||
fun handle(megolmSessionsData: List<MegolmSessionData>,
|
||||
fromBackup: Boolean,
|
||||
progressListener: ProgressListener?): ImportRoomKeysResult {
|
||||
val t0 = System.currentTimeMillis()
|
||||
val t0 = clock.epochMillis()
|
||||
|
||||
val totalNumbersOfKeys = megolmSessionsData.size
|
||||
var lastProgress = 0
|
||||
@ -103,7 +107,7 @@ internal class MegolmSessionDataImporter @Inject constructor(private val olmDevi
|
||||
cryptoStore.markBackupDoneForInboundGroupSessions(olmInboundGroupSessionWrappers)
|
||||
}
|
||||
|
||||
val t1 = System.currentTimeMillis()
|
||||
val t1 = clock.epochMillis()
|
||||
|
||||
Timber.v("## importMegolmSessionsData : sessions import " + (t1 - t0) + " ms (" + megolmSessionsData.size + " sessions)")
|
||||
|
||||
|
@ -46,6 +46,7 @@ import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore
|
||||
import org.matrix.android.sdk.internal.crypto.tasks.SendToDeviceTask
|
||||
import org.matrix.android.sdk.internal.util.JsonCanonicalizer
|
||||
import org.matrix.android.sdk.internal.util.convertToUTF8
|
||||
import org.matrix.android.sdk.internal.util.time.Clock
|
||||
import timber.log.Timber
|
||||
|
||||
private val loggerTag = LoggerTag("MXMegolmEncryption", LoggerTag.CRYPTO)
|
||||
@ -64,7 +65,8 @@ internal class MXMegolmEncryption(
|
||||
private val messageEncrypter: MessageEncrypter,
|
||||
private val warnOnUnknownDevicesRepository: WarnOnUnknownDeviceRepository,
|
||||
private val coroutineDispatchers: MatrixCoroutineDispatchers,
|
||||
private val cryptoCoroutineScope: CoroutineScope
|
||||
private val cryptoCoroutineScope: CoroutineScope,
|
||||
private val clock: Clock,
|
||||
) : IMXEncrypting, IMXGroupEncryption {
|
||||
|
||||
// OutboundSessionInfo. Null if we haven't yet started setting one up. Note
|
||||
@ -86,11 +88,11 @@ internal class MXMegolmEncryption(
|
||||
override suspend fun encryptEventContent(eventContent: Content,
|
||||
eventType: String,
|
||||
userIds: List<String>): Content {
|
||||
val ts = System.currentTimeMillis()
|
||||
val ts = clock.epochMillis()
|
||||
Timber.tag(loggerTag.value).v("encryptEventContent : getDevicesInRoom")
|
||||
val devices = getDevicesInRoom(userIds)
|
||||
Timber.tag(loggerTag.value).d("encrypt event in room=$roomId - devices count in room ${devices.allowedDevices.toDebugCount()}")
|
||||
Timber.tag(loggerTag.value).v("encryptEventContent ${System.currentTimeMillis() - ts}: getDevicesInRoom ${devices.allowedDevices.toDebugString()}")
|
||||
Timber.tag(loggerTag.value).v("encryptEventContent ${clock.epochMillis() - ts}: getDevicesInRoom ${devices.allowedDevices.toDebugString()}")
|
||||
val outboundSession = ensureOutboundSession(devices.allowedDevices)
|
||||
|
||||
return encryptContent(outboundSession, eventType, eventContent)
|
||||
@ -99,7 +101,7 @@ internal class MXMegolmEncryption(
|
||||
// annoyingly we have to serialize again the saved outbound session to store message index :/
|
||||
// if not we would see duplicate message index errors
|
||||
olmDevice.storeOutboundGroupSessionForRoom(roomId, outboundSession.sessionId)
|
||||
Timber.tag(loggerTag.value).d("encrypt event in room=$roomId Finished in ${System.currentTimeMillis() - ts} millis")
|
||||
Timber.tag(loggerTag.value).d("encrypt event in room=$roomId Finished in ${clock.epochMillis() - ts} millis")
|
||||
}
|
||||
}
|
||||
|
||||
@ -125,14 +127,14 @@ internal class MXMegolmEncryption(
|
||||
}
|
||||
|
||||
override suspend fun preshareKey(userIds: List<String>) {
|
||||
val ts = System.currentTimeMillis()
|
||||
val ts = clock.epochMillis()
|
||||
Timber.tag(loggerTag.value).d("preshareKey started in $roomId ...")
|
||||
val devices = getDevicesInRoom(userIds)
|
||||
val outboundSession = ensureOutboundSession(devices.allowedDevices)
|
||||
|
||||
notifyWithheldForSession(devices.withHeldDevices, outboundSession)
|
||||
|
||||
Timber.tag(loggerTag.value).d("preshareKey in $roomId done in ${System.currentTimeMillis() - ts} millis")
|
||||
Timber.tag(loggerTag.value).d("preshareKey in $roomId done in ${clock.epochMillis() - ts} millis")
|
||||
}
|
||||
|
||||
/**
|
||||
@ -148,12 +150,14 @@ internal class MXMegolmEncryption(
|
||||
"ed25519" to olmDevice.deviceEd25519Key!!
|
||||
)
|
||||
|
||||
olmDevice.addInboundGroupSession(sessionId!!, olmDevice.getSessionKey(sessionId)!!, roomId, olmDevice.deviceCurve25519Key!!,
|
||||
emptyList(), keysClaimedMap, false)
|
||||
olmDevice.addInboundGroupSession(
|
||||
sessionId!!, olmDevice.getSessionKey(sessionId)!!, roomId, olmDevice.deviceCurve25519Key!!,
|
||||
emptyList(), keysClaimedMap, false
|
||||
)
|
||||
|
||||
defaultKeysBackupService.maybeBackupKeys()
|
||||
|
||||
return MXOutboundSessionInfo(sessionId, SharedWithHelper(roomId, sessionId, cryptoStore))
|
||||
return MXOutboundSessionInfo(sessionId, SharedWithHelper(roomId, sessionId, cryptoStore), clock)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -243,12 +247,12 @@ internal class MXMegolmEncryption(
|
||||
payload["type"] = EventType.ROOM_KEY
|
||||
payload["content"] = submap
|
||||
|
||||
var t0 = System.currentTimeMillis()
|
||||
var t0 = clock.epochMillis()
|
||||
Timber.tag(loggerTag.value).v("shareUserDevicesKey() : starts")
|
||||
|
||||
val results = ensureOlmSessionsForDevicesAction.handle(devicesByUser)
|
||||
Timber.tag(loggerTag.value).v(
|
||||
"""shareUserDevicesKey(): ensureOlmSessionsForDevices succeeds after ${System.currentTimeMillis() - t0} ms"""
|
||||
"""shareUserDevicesKey(): ensureOlmSessionsForDevices succeeds after ${clock.epochMillis() - t0} ms"""
|
||||
.trimMargin()
|
||||
)
|
||||
val contentMap = MXUsersDevicesMap<Any>()
|
||||
@ -301,7 +305,7 @@ internal class MXMegolmEncryption(
|
||||
cryptoStore.saveGossipingEvents(gossipingEventBuffer)
|
||||
|
||||
if (haveTargets) {
|
||||
t0 = System.currentTimeMillis()
|
||||
t0 = clock.epochMillis()
|
||||
Timber.tag(loggerTag.value).i("shareUserDevicesKey() ${session.sessionId} : has target")
|
||||
Timber.tag(loggerTag.value).d("sending to device room key for ${session.sessionId} to ${contentMap.toDebugString()}")
|
||||
val sendToDeviceParams = SendToDeviceTask.Params(EventType.ENCRYPTED, contentMap)
|
||||
@ -309,7 +313,7 @@ internal class MXMegolmEncryption(
|
||||
withContext(coroutineDispatchers.io) {
|
||||
sendToDeviceTask.execute(sendToDeviceParams)
|
||||
}
|
||||
Timber.tag(loggerTag.value).i("shareUserDevicesKey() : sendToDevice succeeds after ${System.currentTimeMillis() - t0} ms")
|
||||
Timber.tag(loggerTag.value).i("shareUserDevicesKey() : sendToDevice succeeds after ${clock.epochMillis() - t0} ms")
|
||||
} catch (failure: Throwable) {
|
||||
// What to do here...
|
||||
Timber.tag(loggerTag.value).e("shareUserDevicesKey() : Failed to share <${session.sessionId}>")
|
||||
@ -334,7 +338,8 @@ internal class MXMegolmEncryption(
|
||||
senderKey: String?,
|
||||
code: WithHeldCode) {
|
||||
Timber.tag(loggerTag.value).d("notifyKeyWithHeld() :sending withheld for session:$sessionId and code $code to" +
|
||||
" ${targets.joinToString { "${it.userId}|${it.deviceId}" }}")
|
||||
" ${targets.joinToString { "${it.userId}|${it.deviceId}" }}"
|
||||
)
|
||||
val withHeldContent = RoomKeyWithHeldContent(
|
||||
roomId = roomId,
|
||||
senderKey = senderKey,
|
||||
|
@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -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")
|
||||
|
@ -23,6 +23,7 @@ import org.matrix.android.sdk.api.session.crypto.model.EncryptedFileKey
|
||||
import org.matrix.android.sdk.internal.util.base64ToBase64Url
|
||||
import org.matrix.android.sdk.internal.util.base64ToUnpaddedBase64
|
||||
import org.matrix.android.sdk.internal.util.base64UrlToBase64
|
||||
import org.matrix.android.sdk.internal.util.time.Clock
|
||||
import timber.log.Timber
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.io.File
|
||||
@ -42,8 +43,9 @@ internal object MXEncryptedAttachments {
|
||||
|
||||
fun encrypt(clearStream: InputStream,
|
||||
outputFile: File,
|
||||
clock: Clock,
|
||||
progress: ((current: Int, total: Int) -> Unit)): EncryptedFileInfo {
|
||||
val t0 = System.currentTimeMillis()
|
||||
val t0 = clock.epochMillis()
|
||||
val secureRandom = SecureRandom()
|
||||
val initVectorBytes = ByteArray(16) { 0.toByte() }
|
||||
|
||||
@ -100,7 +102,7 @@ internal object MXEncryptedAttachments {
|
||||
hashes = mapOf("sha256" to base64ToUnpaddedBase64(Base64.encodeToString(messageDigest.digest(), Base64.DEFAULT))),
|
||||
v = "v2"
|
||||
)
|
||||
.also { Timber.v("Encrypt in ${System.currentTimeMillis() - t0}ms") }
|
||||
.also { Timber.v("Encrypt in ${clock.epochMillis() - t0}ms") }
|
||||
}
|
||||
|
||||
// fun cipherInputStream(attachmentStream: InputStream, mimetype: String?): Pair<DigestInputStream, EncryptedFileInfo> {
|
||||
@ -159,8 +161,8 @@ internal object MXEncryptedAttachments {
|
||||
* @param attachmentStream the attachment stream. Will be closed after this method call.
|
||||
* @return the encryption file info
|
||||
*/
|
||||
fun encryptAttachment(attachmentStream: InputStream): EncryptionResult {
|
||||
val t0 = System.currentTimeMillis()
|
||||
fun encryptAttachment(attachmentStream: InputStream, clock: Clock): EncryptionResult {
|
||||
val t0 = clock.epochMillis()
|
||||
val secureRandom = SecureRandom()
|
||||
|
||||
// generate a random iv key
|
||||
@ -221,7 +223,7 @@ internal object MXEncryptedAttachments {
|
||||
),
|
||||
encryptedByteArray = byteArrayOutputStream.toByteArray()
|
||||
)
|
||||
.also { Timber.v("Encrypt in ${System.currentTimeMillis() - t0}ms") }
|
||||
.also { Timber.v("Encrypt in ${clock.epochMillis() - t0}ms") }
|
||||
}
|
||||
|
||||
/**
|
||||
@ -234,14 +236,16 @@ internal object MXEncryptedAttachments {
|
||||
*/
|
||||
fun decryptAttachment(attachmentStream: InputStream?,
|
||||
elementToDecrypt: ElementToDecrypt?,
|
||||
outputStream: OutputStream): Boolean {
|
||||
outputStream: OutputStream,
|
||||
clock: Clock
|
||||
): Boolean {
|
||||
// sanity checks
|
||||
if (null == attachmentStream || elementToDecrypt == null) {
|
||||
Timber.e("## decryptAttachment() : null stream")
|
||||
return false
|
||||
}
|
||||
|
||||
val t0 = System.currentTimeMillis()
|
||||
val t0 = clock.epochMillis()
|
||||
|
||||
try {
|
||||
val key = Base64.decode(base64UrlToBase64(elementToDecrypt.k), Base64.DEFAULT)
|
||||
@ -279,7 +283,8 @@ internal object MXEncryptedAttachments {
|
||||
return false
|
||||
}
|
||||
|
||||
return true.also { Timber.v("Decrypt in ${System.currentTimeMillis() - t0}ms") }
|
||||
Timber.v("Decrypt in ${clock.epochMillis() - t0} ms")
|
||||
return true
|
||||
} catch (oom: OutOfMemoryError) {
|
||||
Timber.e(oom, "## decryptAttachment() failed: OOM")
|
||||
} catch (e: Exception) {
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -34,7 +34,7 @@ internal data class OlmSessionWrapper(
|
||||
/**
|
||||
* Notify that a message has been received on this olm session so that it updates `lastReceivedMessageTs`
|
||||
*/
|
||||
fun onMessageReceived() {
|
||||
lastReceivedMessageTs = System.currentTimeMillis()
|
||||
fun onMessageReceived(currentTimeMillis: Long) {
|
||||
lastReceivedMessageTs = currentTimeMillis
|
||||
}
|
||||
}
|
||||
|
@ -97,6 +97,7 @@ import org.matrix.android.sdk.internal.di.MoshiProvider
|
||||
import org.matrix.android.sdk.internal.di.UserId
|
||||
import org.matrix.android.sdk.internal.extensions.clearWith
|
||||
import org.matrix.android.sdk.internal.session.SessionScope
|
||||
import org.matrix.android.sdk.internal.util.time.Clock
|
||||
import org.matrix.olm.OlmAccount
|
||||
import org.matrix.olm.OlmException
|
||||
import org.matrix.olm.OlmOutboundGroupSession
|
||||
@ -110,7 +111,8 @@ internal class RealmCryptoStore @Inject constructor(
|
||||
@CryptoDatabase private val realmConfiguration: RealmConfiguration,
|
||||
private val crossSigningKeysMapper: CrossSigningKeysMapper,
|
||||
@UserId private val userId: String,
|
||||
@DeviceId private val deviceId: String?
|
||||
@DeviceId private val deviceId: String?,
|
||||
private val clock: Clock,
|
||||
) : IMXCryptoStore {
|
||||
|
||||
/* ==========================================================================================
|
||||
@ -307,7 +309,7 @@ internal class RealmCryptoStore @Inject constructor(
|
||||
// Add the device
|
||||
Timber.d("Add device ${cryptoDeviceInfo.deviceId} of user $userId")
|
||||
val newEntity = CryptoMapper.mapToEntity(cryptoDeviceInfo)
|
||||
newEntity.firstTimeSeenLocalTs = System.currentTimeMillis()
|
||||
newEntity.firstTimeSeenLocalTs = clock.epochMillis()
|
||||
userEntity.devices.add(newEntity)
|
||||
} else {
|
||||
// Update the device
|
||||
@ -792,7 +794,7 @@ internal class RealmCryptoStore @Inject constructor(
|
||||
|
||||
if (outboundGroupSession != null) {
|
||||
val info = realm.createObject(OutboundGroupSessionInfoEntity::class.java).apply {
|
||||
creationTime = System.currentTimeMillis()
|
||||
creationTime = clock.epochMillis()
|
||||
putOutboundGroupSession(outboundGroupSession)
|
||||
}
|
||||
entity.outboundSessionInfo = info
|
||||
@ -882,7 +884,8 @@ internal class RealmCryptoStore @Inject constructor(
|
||||
try {
|
||||
val key = OlmInboundGroupSessionEntity.createPrimaryKey(
|
||||
olmInboundGroupSessionWrapper.olmInboundGroupSession?.sessionIdentifier(),
|
||||
olmInboundGroupSessionWrapper.senderKey)
|
||||
olmInboundGroupSessionWrapper.senderKey
|
||||
)
|
||||
|
||||
it.where<OlmInboundGroupSessionEntity>()
|
||||
.equalTo(OlmInboundGroupSessionEntityFields.PRIMARY_KEY, key)
|
||||
@ -1057,13 +1060,16 @@ internal class RealmCryptoStore @Inject constructor(
|
||||
localCreationTimestamp = 0
|
||||
)
|
||||
}
|
||||
return monarchy.findAllPagedWithChanges(realmDataSourceFactory,
|
||||
LivePagedListBuilder(dataSourceFactory,
|
||||
return monarchy.findAllPagedWithChanges(
|
||||
realmDataSourceFactory,
|
||||
LivePagedListBuilder(
|
||||
dataSourceFactory,
|
||||
PagedList.Config.Builder()
|
||||
.setPageSize(20)
|
||||
.setEnablePlaceholders(false)
|
||||
.setPrefetchDistance(1)
|
||||
.build())
|
||||
.build()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@ -1072,13 +1078,16 @@ internal class RealmCryptoStore @Inject constructor(
|
||||
realm.where<GossipingEventEntity>().sort(GossipingEventEntityFields.AGE_LOCAL_TS, Sort.DESCENDING)
|
||||
}
|
||||
val dataSourceFactory = realmDataSourceFactory.map { it.toModel() }
|
||||
val trail = monarchy.findAllPagedWithChanges(realmDataSourceFactory,
|
||||
LivePagedListBuilder(dataSourceFactory,
|
||||
val trail = monarchy.findAllPagedWithChanges(
|
||||
realmDataSourceFactory,
|
||||
LivePagedListBuilder(
|
||||
dataSourceFactory,
|
||||
PagedList.Config.Builder()
|
||||
.setPageSize(20)
|
||||
.setEnablePlaceholders(false)
|
||||
.setPrefetchDistance(1)
|
||||
.build())
|
||||
.build()
|
||||
)
|
||||
)
|
||||
return trail
|
||||
}
|
||||
@ -1153,7 +1162,7 @@ internal class RealmCryptoStore @Inject constructor(
|
||||
|
||||
override fun saveGossipingEvents(events: List<Event>) {
|
||||
monarchy.writeAsync { realm ->
|
||||
val now = System.currentTimeMillis()
|
||||
val now = clock.epochMillis()
|
||||
events.forEach { event ->
|
||||
val ageLocalTs = event.unsignedData?.age?.let { now - it } ?: now
|
||||
val entity = GossipingEventEntity(
|
||||
@ -1326,7 +1335,7 @@ internal class RealmCryptoStore @Inject constructor(
|
||||
.findAll()
|
||||
.mapNotNull { entity ->
|
||||
when (entity.type) {
|
||||
GossipRequestType.KEY -> {
|
||||
GossipRequestType.KEY -> {
|
||||
IncomingRoomKeyRequest(
|
||||
userId = entity.otherUserId,
|
||||
deviceId = entity.otherDeviceId,
|
||||
@ -1359,7 +1368,7 @@ internal class RealmCryptoStore @Inject constructor(
|
||||
it.otherUserId = request.userId
|
||||
it.requestId = request.requestId ?: ""
|
||||
it.requestState = GossipingRequestState.PENDING
|
||||
it.localCreationTimestamp = ageLocalTS ?: System.currentTimeMillis()
|
||||
it.localCreationTimestamp = ageLocalTS ?: clock.epochMillis()
|
||||
if (request is IncomingSecretShareRequest) {
|
||||
it.type = GossipRequestType.SECRET
|
||||
it.requestedInfoStr = request.secretName
|
||||
@ -1380,7 +1389,7 @@ internal class RealmCryptoStore @Inject constructor(
|
||||
it.otherUserId = request.userId
|
||||
it.requestId = request.requestId ?: ""
|
||||
it.requestState = GossipingRequestState.PENDING
|
||||
it.localCreationTimestamp = request.localCreationTimestamp ?: System.currentTimeMillis()
|
||||
it.localCreationTimestamp = request.localCreationTimestamp ?: clock.epochMillis()
|
||||
if (request is IncomingSecretShareRequest) {
|
||||
it.type = GossipRequestType.SECRET
|
||||
it.requestedInfoStr = request.secretName
|
||||
@ -1536,13 +1545,16 @@ internal class RealmCryptoStore @Inject constructor(
|
||||
it.toOutgoingGossipingRequest() as? OutgoingRoomKeyRequest
|
||||
?: OutgoingRoomKeyRequest(requestBody = null, requestId = "?", recipients = emptyMap(), state = OutgoingGossipingRequestState.CANCELLED)
|
||||
}
|
||||
val trail = monarchy.findAllPagedWithChanges(realmDataSourceFactory,
|
||||
LivePagedListBuilder(dataSourceFactory,
|
||||
val trail = monarchy.findAllPagedWithChanges(
|
||||
realmDataSourceFactory,
|
||||
LivePagedListBuilder(
|
||||
dataSourceFactory,
|
||||
PagedList.Config.Builder()
|
||||
.setPageSize(20)
|
||||
.setEnablePlaceholders(false)
|
||||
.setPrefetchDistance(1)
|
||||
.build())
|
||||
.build()
|
||||
)
|
||||
)
|
||||
return trail
|
||||
}
|
||||
@ -1707,7 +1719,7 @@ internal class RealmCryptoStore @Inject constructor(
|
||||
* So we need to tidy up a bit
|
||||
*/
|
||||
override fun tidyUpDataBase() {
|
||||
val prevWeekTs = System.currentTimeMillis() - 7 * 24 * 60 * 60 * 1_000
|
||||
val prevWeekTs = clock.epochMillis() - 7 * 24 * 60 * 60 * 1_000
|
||||
doRealmTransaction(realmConfiguration) { realm ->
|
||||
|
||||
// Only keep one week history
|
||||
|
@ -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()
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
// )
|
||||
//
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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")
|
||||
}
|
||||
|
||||
|
@ -47,6 +47,7 @@ import org.matrix.android.sdk.internal.crypto.model.rest.VERIFICATION_METHOD_REC
|
||||
import org.matrix.android.sdk.internal.crypto.model.rest.VERIFICATION_METHOD_SAS
|
||||
import org.matrix.android.sdk.internal.di.WorkManagerProvider
|
||||
import org.matrix.android.sdk.internal.session.room.send.LocalEchoEventFactory
|
||||
import org.matrix.android.sdk.internal.util.time.Clock
|
||||
import org.matrix.android.sdk.internal.worker.SessionSafeCoroutineWorker
|
||||
import org.matrix.android.sdk.internal.worker.WorkerParamsFactory
|
||||
import timber.log.Timber
|
||||
@ -61,7 +62,8 @@ internal class VerificationTransportRoomMessage(
|
||||
private val roomId: String,
|
||||
private val localEchoEventFactory: LocalEchoEventFactory,
|
||||
private val tx: DefaultVerificationTransaction?,
|
||||
private val coroutineScope: CoroutineScope
|
||||
private val coroutineScope: CoroutineScope,
|
||||
private val clock: Clock,
|
||||
) : VerificationTransport {
|
||||
|
||||
override fun <T> sendToOther(type: String,
|
||||
@ -77,10 +79,12 @@ internal class VerificationTransportRoomMessage(
|
||||
content = verificationInfo.toEventContent()!!
|
||||
)
|
||||
|
||||
val workerParams = WorkerParamsFactory.toData(SendVerificationMessageWorker.Params(
|
||||
sessionId = sessionId,
|
||||
eventId = event.eventId ?: ""
|
||||
))
|
||||
val workerParams = WorkerParamsFactory.toData(
|
||||
SendVerificationMessageWorker.Params(
|
||||
sessionId = sessionId,
|
||||
eventId = event.eventId ?: ""
|
||||
)
|
||||
)
|
||||
val enqueueInfo = enqueueSendWork(workerParams)
|
||||
|
||||
// I cannot just listen to the given work request, because when used in a uniqueWork,
|
||||
@ -155,7 +159,7 @@ internal class VerificationTransportRoomMessage(
|
||||
transactionId = "",
|
||||
fromDevice = userDeviceId ?: "",
|
||||
methods = supportedMethods,
|
||||
timestamp = System.currentTimeMillis()
|
||||
timestamp = clock.epochMillis()
|
||||
)
|
||||
|
||||
val info = MessageVerificationRequestContent(
|
||||
@ -175,10 +179,12 @@ internal class VerificationTransportRoomMessage(
|
||||
content
|
||||
)
|
||||
|
||||
val workerParams = WorkerParamsFactory.toData(SendVerificationMessageWorker.Params(
|
||||
sessionId = sessionId,
|
||||
eventId = event.eventId ?: ""
|
||||
))
|
||||
val workerParams = WorkerParamsFactory.toData(
|
||||
SendVerificationMessageWorker.Params(
|
||||
sessionId = sessionId,
|
||||
eventId = event.eventId ?: ""
|
||||
)
|
||||
)
|
||||
|
||||
val workRequest = workManagerProvider.matrixOneTimeWorkRequestBuilder<SendVerificationMessageWorker>()
|
||||
.setConstraints(WorkManagerProvider.workConstraints)
|
||||
@ -230,10 +236,12 @@ internal class VerificationTransportRoomMessage(
|
||||
roomId = roomId,
|
||||
content = MessageVerificationCancelContent.create(transactionId, code).toContent()
|
||||
)
|
||||
val workerParams = WorkerParamsFactory.toData(SendVerificationMessageWorker.Params(
|
||||
sessionId = sessionId,
|
||||
eventId = event.eventId ?: ""
|
||||
))
|
||||
val workerParams = WorkerParamsFactory.toData(
|
||||
SendVerificationMessageWorker.Params(
|
||||
sessionId = sessionId,
|
||||
eventId = event.eventId ?: ""
|
||||
)
|
||||
)
|
||||
enqueueSendWork(workerParams)
|
||||
}
|
||||
|
||||
@ -250,10 +258,12 @@ internal class VerificationTransportRoomMessage(
|
||||
)
|
||||
).toContent()
|
||||
)
|
||||
val workerParams = WorkerParamsFactory.toData(SendVerificationMessageWorker.Params(
|
||||
sessionId = sessionId,
|
||||
eventId = event.eventId ?: ""
|
||||
))
|
||||
val workerParams = WorkerParamsFactory.toData(
|
||||
SendVerificationMessageWorker.Params(
|
||||
sessionId = sessionId,
|
||||
eventId = event.eventId ?: ""
|
||||
)
|
||||
)
|
||||
val enqueueInfo = enqueueSendWork(workerParams)
|
||||
|
||||
val workLiveData = workManagerProvider.workManager
|
||||
@ -361,7 +371,7 @@ internal class VerificationTransportRoomMessage(
|
||||
private fun createEventAndLocalEcho(localId: String = LocalEcho.createLocalEchoId(), type: String, roomId: String, content: Content): Event {
|
||||
return Event(
|
||||
roomId = roomId,
|
||||
originServerTs = System.currentTimeMillis(),
|
||||
originServerTs = clock.epochMillis(),
|
||||
senderId = userId,
|
||||
eventId = localId,
|
||||
type = type,
|
||||
|
@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -35,13 +35,16 @@ import org.matrix.android.sdk.internal.crypto.model.rest.VERIFICATION_METHOD_SAS
|
||||
import org.matrix.android.sdk.internal.crypto.tasks.SendToDeviceTask
|
||||
import org.matrix.android.sdk.internal.task.TaskExecutor
|
||||
import org.matrix.android.sdk.internal.task.configureWith
|
||||
import org.matrix.android.sdk.internal.util.time.Clock
|
||||
import timber.log.Timber
|
||||
|
||||
// TODO var could be val
|
||||
internal class VerificationTransportToDevice(
|
||||
private var tx: DefaultVerificationTransaction?,
|
||||
private var sendToDeviceTask: SendToDeviceTask,
|
||||
private val myDeviceId: String?,
|
||||
private var taskExecutor: TaskExecutor
|
||||
private var taskExecutor: TaskExecutor,
|
||||
private val clock: Clock,
|
||||
) : VerificationTransport {
|
||||
|
||||
override fun sendVerificationRequest(supportedMethods: List<String>,
|
||||
@ -56,7 +59,7 @@ internal class VerificationTransportToDevice(
|
||||
transactionId = localId,
|
||||
fromDevice = myDeviceId ?: "",
|
||||
methods = supportedMethods,
|
||||
timestamp = System.currentTimeMillis()
|
||||
timestamp = clock.epochMillis()
|
||||
)
|
||||
val keyReq = KeyVerificationRequest(
|
||||
fromDevice = validKeyReq.fromDevice,
|
||||
@ -201,7 +204,8 @@ internal class VerificationTransportToDevice(
|
||||
hash,
|
||||
commitment,
|
||||
messageAuthenticationCode,
|
||||
shortAuthenticationStrings)
|
||||
shortAuthenticationStrings
|
||||
)
|
||||
|
||||
override fun createKey(tid: String, pubKey: String): VerificationInfoKey = KeyVerificationKey.create(tid, pubKey)
|
||||
|
||||
@ -221,7 +225,8 @@ internal class VerificationTransportToDevice(
|
||||
hashes,
|
||||
messageAuthenticationCodes,
|
||||
shortAuthenticationStrings,
|
||||
null)
|
||||
null
|
||||
)
|
||||
}
|
||||
|
||||
override fun createStartForQrCode(fromDevice: String,
|
||||
@ -235,7 +240,8 @@ internal class VerificationTransportToDevice(
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
sharedSecret)
|
||||
sharedSecret
|
||||
)
|
||||
}
|
||||
|
||||
override fun createReady(tid: String, fromDevice: String, methods: List<String>): VerificationInfoReady {
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ import kotlinx.coroutines.isActive
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import timber.log.Timber
|
||||
import kotlin.system.measureTimeMillis
|
||||
|
||||
internal fun <T> CoroutineScope.asyncTransaction(monarchy: Monarchy, transaction: suspend (realm: Realm) -> T) {
|
||||
asyncTransaction(monarchy.realmConfiguration, transaction)
|
||||
@ -41,13 +42,13 @@ internal suspend fun <T> awaitTransaction(config: RealmConfiguration, transactio
|
||||
bgRealm.beginTransaction()
|
||||
val result: T
|
||||
try {
|
||||
val start = System.currentTimeMillis()
|
||||
result = transaction(bgRealm)
|
||||
if (isActive) {
|
||||
bgRealm.commitTransaction()
|
||||
val end = System.currentTimeMillis()
|
||||
val time = end - start
|
||||
Timber.v("Execute transaction in $time millis")
|
||||
measureTimeMillis {
|
||||
result = transaction(bgRealm)
|
||||
if (isActive) {
|
||||
bgRealm.commitTransaction()
|
||||
}
|
||||
}.also {
|
||||
Timber.v("Execute transaction in $it millis")
|
||||
}
|
||||
} finally {
|
||||
if (bgRealm.isInTransaction) {
|
||||
|
@ -137,7 +137,8 @@ internal fun ThreadSummaryEntity.Companion.createOrUpdate(
|
||||
roomMemberContentsByUser: HashMap<String, RoomMemberContent?>,
|
||||
roomEntity: RoomEntity,
|
||||
userId: String,
|
||||
cryptoService: CryptoService? = null
|
||||
cryptoService: CryptoService? = null,
|
||||
currentTimeMillis: Long,
|
||||
) {
|
||||
when (threadSummaryType) {
|
||||
ThreadSummaryUpdateType.REPLACE -> {
|
||||
@ -153,14 +154,14 @@ internal fun ThreadSummaryEntity.Companion.createOrUpdate(
|
||||
Timber.i("###THREADS ThreadSummaryHelper REPLACE eventId:${it.rootThreadEventId} ")
|
||||
}
|
||||
|
||||
val rootThreadEventEntity = createEventEntity(roomId, rootThreadEvent, realm).also {
|
||||
val rootThreadEventEntity = createEventEntity(realm, roomId, rootThreadEvent, currentTimeMillis).also {
|
||||
try {
|
||||
decryptIfNeeded(cryptoService, it, roomId)
|
||||
} catch (e: InterruptedException) {
|
||||
Timber.i("Decryption got interrupted")
|
||||
}
|
||||
}
|
||||
val latestThreadEventEntity = createLatestEventEntity(roomId, rootThreadEvent, roomMemberContentsByUser, realm)?.also {
|
||||
val latestThreadEventEntity = createLatestEventEntity(realm, roomId, rootThreadEvent, roomMemberContentsByUser, currentTimeMillis)?.also {
|
||||
try {
|
||||
decryptIfNeeded(cryptoService, it, roomId)
|
||||
} catch (e: InterruptedException) {
|
||||
@ -268,8 +269,8 @@ private fun HashMap<String, RoomMemberContent?>.addSenderState(realm: Realm, roo
|
||||
/**
|
||||
* Create an EventEntity for the root thread event or get an existing one
|
||||
*/
|
||||
private fun createEventEntity(roomId: String, event: Event, realm: Realm): EventEntity {
|
||||
val ageLocalTs = event.unsignedData?.age?.let { System.currentTimeMillis() - it }
|
||||
private fun createEventEntity(realm: Realm, roomId: String, event: Event, currentTimeMillis: Long): EventEntity {
|
||||
val ageLocalTs = event.unsignedData?.age?.let { currentTimeMillis - it }
|
||||
return event.toEntity(roomId, SendState.SYNCED, ageLocalTs).copyToRealmOrIgnore(realm, EventInsertType.PAGINATION)
|
||||
}
|
||||
|
||||
@ -278,15 +279,17 @@ private fun createEventEntity(roomId: String, event: Event, realm: Realm): Event
|
||||
* state
|
||||
*/
|
||||
private fun createLatestEventEntity(
|
||||
realm: Realm,
|
||||
roomId: String,
|
||||
rootThreadEvent: Event,
|
||||
roomMemberContentsByUser: HashMap<String, RoomMemberContent?>,
|
||||
realm: Realm): EventEntity? {
|
||||
currentTimeMillis: Long,
|
||||
): EventEntity? {
|
||||
return getLatestEvent(rootThreadEvent)?.let {
|
||||
it.senderId?.let { senderId ->
|
||||
roomMemberContentsByUser.addSenderState(realm, roomId, senderId)
|
||||
}
|
||||
createEventEntity(roomId, it, realm)
|
||||
createEventEntity(realm, roomId, it, currentTimeMillis)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -34,6 +34,7 @@ import org.matrix.android.sdk.api.session.room.model.call.CallSelectAnswerConten
|
||||
import org.matrix.android.sdk.api.session.room.model.call.CallSignalingContent
|
||||
import org.matrix.android.sdk.internal.di.UserId
|
||||
import org.matrix.android.sdk.internal.session.SessionScope
|
||||
import org.matrix.android.sdk.internal.util.time.Clock
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
@ -41,9 +42,12 @@ private val loggerTag = LoggerTag("CallSignalingHandler", LoggerTag.VOIP)
|
||||
private const val MAX_AGE_TO_RING = 40_000
|
||||
|
||||
@SessionScope
|
||||
internal class CallSignalingHandler @Inject constructor(private val activeCallHandler: ActiveCallHandler,
|
||||
private val mxCallFactory: MxCallFactory,
|
||||
@UserId private val userId: String) {
|
||||
internal class CallSignalingHandler @Inject constructor(
|
||||
private val activeCallHandler: ActiveCallHandler,
|
||||
private val mxCallFactory: MxCallFactory,
|
||||
@UserId private val userId: String,
|
||||
private val clock: Clock,
|
||||
) {
|
||||
|
||||
private val invitedCallIds = mutableSetOf<String>()
|
||||
private val callListeners = mutableSetOf<CallListener>()
|
||||
@ -184,7 +188,7 @@ internal class CallSignalingHandler @Inject constructor(private val activeCallHa
|
||||
if (event.roomId == null || event.senderId == null) {
|
||||
return
|
||||
}
|
||||
val now = System.currentTimeMillis()
|
||||
val now = clock.epochMillis()
|
||||
val age = now - (event.ageLocalTs ?: now)
|
||||
if (age > MAX_AGE_TO_RING) {
|
||||
Timber.tag(loggerTag.value).w("Call invite is too old to ring.")
|
||||
|
@ -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
|
||||
|
@ -46,6 +46,7 @@ import org.matrix.android.sdk.internal.session.call.DefaultCallSignalingService
|
||||
import org.matrix.android.sdk.internal.session.profile.GetProfileInfoTask
|
||||
import org.matrix.android.sdk.internal.session.room.send.LocalEchoEventFactory
|
||||
import org.matrix.android.sdk.internal.session.room.send.queue.EventSenderProcessor
|
||||
import org.matrix.android.sdk.internal.util.time.Clock
|
||||
import timber.log.Timber
|
||||
import java.math.BigDecimal
|
||||
|
||||
@ -61,7 +62,8 @@ internal class MxCallImpl(
|
||||
private val localEchoEventFactory: LocalEchoEventFactory,
|
||||
private val eventSenderProcessor: EventSenderProcessor,
|
||||
private val matrixConfiguration: MatrixConfiguration,
|
||||
private val getProfileInfoTask: GetProfileInfoTask
|
||||
private val getProfileInfoTask: GetProfileInfoTask,
|
||||
private val clock: Clock,
|
||||
) : MxCall {
|
||||
|
||||
override var opponentPartyId: Optional<String>? = null
|
||||
@ -250,7 +252,7 @@ internal class MxCallImpl(
|
||||
private fun createEventAndLocalEcho(localId: String = LocalEcho.createLocalEchoId(), type: String, roomId: String, content: Content): Event {
|
||||
return Event(
|
||||
roomId = roomId,
|
||||
originServerTs = System.currentTimeMillis(),
|
||||
originServerTs = clock.epochMillis(),
|
||||
senderId = userId,
|
||||
eventId = localId,
|
||||
type = type,
|
||||
|
@ -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,
|
||||
|
@ -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()
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -31,11 +31,14 @@ internal fun ContentScanResultEntity.Companion.get(realm: Realm, attachmentUrl:
|
||||
.findFirst()
|
||||
}
|
||||
|
||||
internal fun ContentScanResultEntity.Companion.getOrCreate(realm: Realm, attachmentUrl: String, contentScannerUrl: String?): ContentScanResultEntity {
|
||||
internal fun ContentScanResultEntity.Companion.getOrCreate(realm: Realm,
|
||||
attachmentUrl: String,
|
||||
contentScannerUrl: String?,
|
||||
currentTimeMillis: Long): ContentScanResultEntity {
|
||||
return ContentScanResultEntity.get(realm, attachmentUrl, contentScannerUrl)
|
||||
?: realm.createObject<ContentScanResultEntity>().also {
|
||||
it.mediaUrl = attachmentUrl
|
||||
it.scanDateTimestamp = System.currentTimeMillis()
|
||||
it.scanDateTimestamp = currentTimeMillis
|
||||
it.scannerUrl = contentScannerUrl
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -69,6 +69,7 @@ import org.matrix.android.sdk.internal.di.UserId
|
||||
import org.matrix.android.sdk.internal.session.EventInsertLiveProcessor
|
||||
import org.matrix.android.sdk.internal.session.room.aggregation.livelocation.LiveLocationAggregationProcessor
|
||||
import org.matrix.android.sdk.internal.session.room.state.StateEventDataSource
|
||||
import org.matrix.android.sdk.internal.util.time.Clock
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
@ -77,7 +78,8 @@ internal class EventRelationsAggregationProcessor @Inject constructor(
|
||||
private val stateEventDataSource: StateEventDataSource,
|
||||
@SessionId private val sessionId: String,
|
||||
private val sessionManager: SessionManager,
|
||||
private val liveLocationAggregationProcessor: LiveLocationAggregationProcessor
|
||||
private val liveLocationAggregationProcessor: LiveLocationAggregationProcessor,
|
||||
private val clock: Clock,
|
||||
) : EventInsertLiveProcessor {
|
||||
|
||||
private val allowedTypes = listOf(
|
||||
@ -338,7 +340,7 @@ internal class EventRelationsAggregationProcessor @Inject constructor(
|
||||
Timber.v("###REPLACE Receiving remote echo of edit (edit already done)")
|
||||
existingSummary.editions.firstOrNull { it.eventId == txId }?.let {
|
||||
it.eventId = event.eventId
|
||||
it.timestamp = event.originServerTs ?: System.currentTimeMillis()
|
||||
it.timestamp = event.originServerTs ?: clock.epochMillis()
|
||||
it.isLocalEcho = false
|
||||
}
|
||||
} else {
|
||||
@ -349,10 +351,10 @@ internal class EventRelationsAggregationProcessor @Inject constructor(
|
||||
eventId = event.eventId,
|
||||
content = ContentMapper.map(newContent),
|
||||
timestamp = if (isLocalEcho) {
|
||||
System.currentTimeMillis()
|
||||
clock.epochMillis()
|
||||
} else {
|
||||
// Do not take local echo originServerTs here, could mess up ordering (keep old ts)
|
||||
event.originServerTs ?: System.currentTimeMillis()
|
||||
event.originServerTs ?: clock.epochMillis()
|
||||
},
|
||||
isLocalEcho = isLocalEcho
|
||||
)
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
@ -33,6 +33,7 @@ import org.matrix.android.sdk.internal.session.room.timeline.PaginationDirection
|
||||
import org.matrix.android.sdk.internal.session.room.timeline.PaginationResponse
|
||||
import org.matrix.android.sdk.internal.task.Task
|
||||
import org.matrix.android.sdk.internal.util.awaitTransaction
|
||||
import org.matrix.android.sdk.internal.util.time.Clock
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
@ -55,12 +56,14 @@ internal class DefaultFetchThreadSummariesTask @Inject constructor(
|
||||
@SessionDatabase private val monarchy: Monarchy,
|
||||
private val cryptoService: DefaultCryptoService,
|
||||
@UserId private val userId: String,
|
||||
private val clock: Clock,
|
||||
) : FetchThreadSummariesTask {
|
||||
|
||||
override suspend fun execute(params: FetchThreadSummariesTask.Params): Result {
|
||||
val filter = FilterFactory.createThreadsFilter(
|
||||
numberOfEvents = params.limit,
|
||||
userId = if (params.isUserParticipating) userId else null).toJSONString()
|
||||
userId = if (params.isUserParticipating) userId else null
|
||||
).toJSONString()
|
||||
|
||||
val response = executeRequest(
|
||||
globalErrorReceiver,
|
||||
@ -94,7 +97,9 @@ internal class DefaultFetchThreadSummariesTask @Inject constructor(
|
||||
roomMemberContentsByUser = roomMemberContentsByUser,
|
||||
roomEntity = roomEntity,
|
||||
userId = userId,
|
||||
cryptoService = cryptoService)
|
||||
cryptoService = cryptoService,
|
||||
currentTimeMillis = clock.epochMillis(),
|
||||
)
|
||||
}
|
||||
}
|
||||
return Result.SUCCESS
|
||||
|
@ -23,7 +23,6 @@ import org.matrix.android.sdk.api.session.events.model.Event
|
||||
import org.matrix.android.sdk.api.session.events.model.EventType
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomMemberContent
|
||||
import org.matrix.android.sdk.api.session.room.send.SendState
|
||||
import org.matrix.android.sdk.internal.crypto.CryptoSessionInfoProvider
|
||||
import org.matrix.android.sdk.internal.crypto.DefaultCryptoService
|
||||
import org.matrix.android.sdk.internal.database.helper.addTimelineEvent
|
||||
import org.matrix.android.sdk.internal.database.mapper.asDomain
|
||||
@ -42,7 +41,6 @@ import org.matrix.android.sdk.internal.database.query.getOrCreate
|
||||
import org.matrix.android.sdk.internal.database.query.getOrNull
|
||||
import org.matrix.android.sdk.internal.database.query.where
|
||||
import org.matrix.android.sdk.internal.di.SessionDatabase
|
||||
import org.matrix.android.sdk.internal.di.UserId
|
||||
import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
|
||||
import org.matrix.android.sdk.internal.network.executeRequest
|
||||
import org.matrix.android.sdk.internal.session.events.getFixedRoomMemberContent
|
||||
@ -51,6 +49,7 @@ import org.matrix.android.sdk.internal.session.room.relation.RelationsResponse
|
||||
import org.matrix.android.sdk.internal.session.room.timeline.PaginationDirection
|
||||
import org.matrix.android.sdk.internal.task.Task
|
||||
import org.matrix.android.sdk.internal.util.awaitTransaction
|
||||
import org.matrix.android.sdk.internal.util.time.Clock
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
@ -85,10 +84,9 @@ internal interface FetchThreadTimelineTask : Task<FetchThreadTimelineTask.Params
|
||||
internal class DefaultFetchThreadTimelineTask @Inject constructor(
|
||||
private val roomAPI: RoomAPI,
|
||||
private val globalErrorReceiver: GlobalErrorReceiver,
|
||||
private val cryptoSessionInfoProvider: CryptoSessionInfoProvider,
|
||||
@SessionDatabase private val monarchy: Monarchy,
|
||||
@UserId private val userId: String,
|
||||
private val cryptoService: DefaultCryptoService
|
||||
private val cryptoService: DefaultCryptoService,
|
||||
private val clock: Clock,
|
||||
) : FetchThreadTimelineTask {
|
||||
|
||||
enum class Result {
|
||||
@ -156,7 +154,8 @@ internal class DefaultFetchThreadTimelineTask @Inject constructor(
|
||||
eventEntity = eventEntity,
|
||||
direction = PaginationDirection.FORWARDS,
|
||||
ownedByThreadChunk = true,
|
||||
roomMemberContentsByUser = roomMemberContentsByUser)
|
||||
roomMemberContentsByUser = roomMemberContentsByUser
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -178,7 +177,8 @@ internal class DefaultFetchThreadTimelineTask @Inject constructor(
|
||||
eventEntity = eventEntity,
|
||||
direction = PaginationDirection.FORWARDS,
|
||||
ownedByThreadChunk = true,
|
||||
roomMemberContentsByUser = roomMemberContentsByUser)
|
||||
roomMemberContentsByUser = roomMemberContentsByUser
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -207,7 +207,7 @@ internal class DefaultFetchThreadTimelineTask @Inject constructor(
|
||||
* Create an EventEntity to be added in the TimelineEventEntity
|
||||
*/
|
||||
private fun createEventEntity(roomId: String, event: Event, realm: Realm): EventEntity {
|
||||
val ageLocalTs = event.unsignedData?.age?.let { System.currentTimeMillis() - it }
|
||||
val ageLocalTs = event.unsignedData?.age?.let { clock.epochMillis() - it }
|
||||
return event.toEntity(roomId, SendState.SYNCED, ageLocalTs).copyToRealmOrIgnore(realm, EventInsertType.PAGINATION)
|
||||
}
|
||||
|
||||
|
@ -70,6 +70,7 @@ import org.matrix.android.sdk.internal.di.UserId
|
||||
import org.matrix.android.sdk.internal.session.content.ThumbnailExtractor
|
||||
import org.matrix.android.sdk.internal.session.permalinks.PermalinkFactory
|
||||
import org.matrix.android.sdk.internal.session.room.send.pills.TextPillsUtils
|
||||
import org.matrix.android.sdk.internal.util.time.Clock
|
||||
import java.util.UUID
|
||||
import javax.inject.Inject
|
||||
|
||||
@ -90,7 +91,8 @@ internal class LocalEchoEventFactory @Inject constructor(
|
||||
private val thumbnailExtractor: ThumbnailExtractor,
|
||||
private val waveformSanitizer: WaveFormSanitizer,
|
||||
private val localEchoRepository: LocalEchoRepository,
|
||||
private val permalinkFactory: PermalinkFactory
|
||||
private val permalinkFactory: PermalinkFactory,
|
||||
private val clock: Clock,
|
||||
) {
|
||||
fun createTextEvent(roomId: String, msgType: String, text: CharSequence, autoMarkdown: Boolean): Event {
|
||||
if (msgType == MessageType.MSGTYPE_TEXT || msgType == MessageType.MSGTYPE_EMOTE) {
|
||||
@ -242,7 +244,7 @@ internal class LocalEchoEventFactory @Inject constructor(
|
||||
body = geoUri,
|
||||
unstableLocationInfo = LocationInfo(geoUri = geoUri, description = geoUri),
|
||||
unstableLocationAsset = LocationAsset(type = assetType),
|
||||
unstableTimestampMillis = System.currentTimeMillis(),
|
||||
unstableTimestampMillis = clock.epochMillis(),
|
||||
unstableText = geoUri
|
||||
)
|
||||
return createMessageEvent(roomId, content)
|
||||
@ -261,7 +263,7 @@ internal class LocalEchoEventFactory @Inject constructor(
|
||||
eventId = beaconInfoEventId
|
||||
),
|
||||
unstableLocationInfo = LocationInfo(geoUri = geoUri, description = geoUri),
|
||||
unstableTimestampMillis = System.currentTimeMillis(),
|
||||
unstableTimestampMillis = clock.epochMillis(),
|
||||
)
|
||||
val localId = LocalEcho.createLocalEchoId()
|
||||
return Event(
|
||||
@ -547,7 +549,7 @@ internal class LocalEchoEventFactory @Inject constructor(
|
||||
}
|
||||
|
||||
private fun dummyOriginServerTs(): Long {
|
||||
return System.currentTimeMillis()
|
||||
return clock.epochMillis()
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -43,16 +43,20 @@ import org.matrix.android.sdk.internal.session.room.summary.RoomSummaryUpdater
|
||||
import org.matrix.android.sdk.internal.session.room.timeline.TimelineInput
|
||||
import org.matrix.android.sdk.internal.task.TaskExecutor
|
||||
import org.matrix.android.sdk.internal.util.awaitTransaction
|
||||
import org.matrix.android.sdk.internal.util.time.Clock
|
||||
import timber.log.Timber
|
||||
import java.util.UUID
|
||||
import javax.inject.Inject
|
||||
|
||||
internal class LocalEchoRepository @Inject constructor(@SessionDatabase private val monarchy: Monarchy,
|
||||
private val taskExecutor: TaskExecutor,
|
||||
private val realmSessionProvider: RealmSessionProvider,
|
||||
private val roomSummaryUpdater: RoomSummaryUpdater,
|
||||
private val timelineInput: TimelineInput,
|
||||
private val timelineEventMapper: TimelineEventMapper) {
|
||||
internal class LocalEchoRepository @Inject constructor(
|
||||
@SessionDatabase private val monarchy: Monarchy,
|
||||
private val taskExecutor: TaskExecutor,
|
||||
private val realmSessionProvider: RealmSessionProvider,
|
||||
private val roomSummaryUpdater: RoomSummaryUpdater,
|
||||
private val timelineInput: TimelineInput,
|
||||
private val timelineEventMapper: TimelineEventMapper,
|
||||
private val clock: Clock,
|
||||
) {
|
||||
|
||||
fun createLocalEcho(event: Event) {
|
||||
val roomId = event.roomId ?: throw IllegalStateException("You should have set a roomId for your event")
|
||||
@ -61,7 +65,7 @@ internal class LocalEchoRepository @Inject constructor(@SessionDatabase private
|
||||
event.type ?: throw IllegalStateException("You should have set a type for your event")
|
||||
|
||||
val timelineEventEntity = realmSessionProvider.withRealm { realm ->
|
||||
val eventEntity = event.toEntity(roomId, SendState.UNSENT, System.currentTimeMillis())
|
||||
val eventEntity = event.toEntity(roomId, SendState.UNSENT, clock.epochMillis())
|
||||
val roomMemberHelper = RoomMemberHelper(realm, roomId)
|
||||
val myUser = roomMemberHelper.getLastRoomMember(senderId)
|
||||
val localId = UUID.randomUUID().mostSignificantBits
|
||||
@ -88,7 +92,7 @@ internal class LocalEchoRepository @Inject constructor(@SessionDatabase private
|
||||
}
|
||||
|
||||
fun updateSendState(eventId: String, roomId: String?, sendState: SendState, sendStateDetails: String? = null) {
|
||||
Timber.v("## SendEvent: [${System.currentTimeMillis()}] Update local state of $eventId to ${sendState.name}")
|
||||
Timber.v("## SendEvent: [${clock.epochMillis()}] Update local state of $eventId to ${sendState.name}")
|
||||
timelineInput.onLocalEchoUpdated(roomId = roomId ?: "", eventId = eventId, sendState = sendState)
|
||||
updateEchoAsync(eventId) { realm, sendingEventEntity ->
|
||||
if (sendState == SendState.SENT && sendingEventEntity.sendState == SendState.SYNCED) {
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
@ -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()
|
||||
|
@ -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() {
|
||||
|
@ -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,
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -24,6 +24,7 @@ import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
|
||||
import org.matrix.android.sdk.internal.network.executeRequest
|
||||
import org.matrix.android.sdk.internal.session.room.RoomAPI
|
||||
import org.matrix.android.sdk.internal.task.Task
|
||||
import org.matrix.android.sdk.internal.util.time.Clock
|
||||
import javax.inject.Inject
|
||||
|
||||
internal interface GetEventTask : Task<GetEventTask.Params, Event> {
|
||||
@ -36,7 +37,8 @@ internal interface GetEventTask : Task<GetEventTask.Params, Event> {
|
||||
internal class DefaultGetEventTask @Inject constructor(
|
||||
private val roomAPI: RoomAPI,
|
||||
private val globalErrorReceiver: GlobalErrorReceiver,
|
||||
private val eventDecryptor: EventDecryptor
|
||||
private val eventDecryptor: EventDecryptor,
|
||||
private val clock: Clock,
|
||||
) : GetEventTask {
|
||||
|
||||
override suspend fun execute(params: GetEventTask.Params): Event {
|
||||
@ -59,7 +61,7 @@ internal class DefaultGetEventTask @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
event.ageLocalTs = event.unsignedData?.age?.let { System.currentTimeMillis() - it }
|
||||
event.ageLocalTs = event.unsignedData?.age?.let { clock.epochMillis() - it }
|
||||
|
||||
return event
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -44,6 +44,7 @@ import org.matrix.android.sdk.internal.di.SessionDatabase
|
||||
import org.matrix.android.sdk.internal.di.UserId
|
||||
import org.matrix.android.sdk.internal.session.StreamEventsManager
|
||||
import org.matrix.android.sdk.internal.util.awaitTransaction
|
||||
import org.matrix.android.sdk.internal.util.time.Clock
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
@ -54,7 +55,9 @@ internal class TokenChunkEventPersistor @Inject constructor(
|
||||
@SessionDatabase private val monarchy: Monarchy,
|
||||
@UserId private val userId: String,
|
||||
private val lightweightSettingsStorage: LightweightSettingsStorage,
|
||||
private val liveEventManager: Lazy<StreamEventsManager>) {
|
||||
private val liveEventManager: Lazy<StreamEventsManager>,
|
||||
private val clock: Clock,
|
||||
) {
|
||||
|
||||
enum class Result {
|
||||
SHOULD_FETCH_MORE,
|
||||
@ -166,7 +169,7 @@ internal class TokenChunkEventPersistor @Inject constructor(
|
||||
val eventList = receivedChunk.events
|
||||
val stateEvents = receivedChunk.stateEvents
|
||||
|
||||
val now = System.currentTimeMillis()
|
||||
val now = clock.epochMillis()
|
||||
|
||||
stateEvents?.forEach { stateEvent ->
|
||||
val ageLocalTs = stateEvent.unsignedData?.age?.let { now - it }
|
||||
|
@ -24,10 +24,14 @@ import org.matrix.android.sdk.api.session.room.model.ReactionAggregatedSummary
|
||||
import org.matrix.android.sdk.api.session.room.model.relation.ReactionContent
|
||||
import org.matrix.android.sdk.api.session.room.send.SendState
|
||||
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
|
||||
import org.matrix.android.sdk.internal.util.time.Clock
|
||||
import timber.log.Timber
|
||||
import java.util.Collections
|
||||
|
||||
internal class UIEchoManager(private val listener: Listener) {
|
||||
internal class UIEchoManager(
|
||||
private val listener: Listener,
|
||||
private val clock: Clock,
|
||||
) {
|
||||
|
||||
interface Listener {
|
||||
fun rebuildEvent(eventId: String, builder: (TimelineEvent) -> TimelineEvent?): Boolean
|
||||
@ -109,7 +113,7 @@ internal class UIEchoManager(private val listener: Listener) {
|
||||
key = uiEchoReaction.reaction,
|
||||
count = 1,
|
||||
addedByMe = true,
|
||||
firstTimestamp = System.currentTimeMillis(),
|
||||
firstTimestamp = clock.epochMillis(),
|
||||
sourceEvents = emptyList(),
|
||||
localEchoEvents = listOf(uiEchoReaction.localEchoId)
|
||||
).let { updateReactions.add(it) }
|
||||
@ -143,7 +147,7 @@ internal class UIEchoManager(private val listener: Listener) {
|
||||
fun updateSentStateWithUiEcho(timelineEvent: TimelineEvent): TimelineEvent {
|
||||
if (timelineEvent.root.sendState.isSent()) return timelineEvent
|
||||
val inMemoryState = inMemorySendingStates[timelineEvent.eventId] ?: return timelineEvent
|
||||
// Timber.v("## ${System.currentTimeMillis()} Send event refresh echo with live state $inMemoryState from state ${element.root.sendState}")
|
||||
// Timber.v("## ${clock.epochMillis()} Send event refresh echo with live state $inMemoryState from state ${element.root.sendState}")
|
||||
return timelineEvent.copy(
|
||||
root = timelineEvent.root.copyAll()
|
||||
.also { it.sendState = inMemoryState }
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -44,12 +44,14 @@ internal class ReadReceiptHandler @Inject constructor(
|
||||
|
||||
companion object {
|
||||
|
||||
fun createContent(userId: String, eventId: String): ReadReceiptContent {
|
||||
fun createContent(userId: String,
|
||||
eventId: String,
|
||||
currentTimeMillis: Long): ReadReceiptContent {
|
||||
return mapOf(
|
||||
eventId to mapOf(
|
||||
READ_KEY to mapOf(
|
||||
userId to mapOf(
|
||||
TIMESTAMP_KEY to System.currentTimeMillis().toDouble()
|
||||
TIMESTAMP_KEY to currentTimeMillis.toDouble()
|
||||
)
|
||||
)
|
||||
)
|
||||
|
@ -79,22 +79,26 @@ import org.matrix.android.sdk.internal.session.room.typing.TypingEventContent
|
||||
import org.matrix.android.sdk.internal.session.sync.SyncResponsePostTreatmentAggregator
|
||||
import org.matrix.android.sdk.internal.session.sync.parsing.RoomSyncAccountDataHandler
|
||||
import org.matrix.android.sdk.internal.util.computeBestChunkSize
|
||||
import org.matrix.android.sdk.internal.util.time.Clock
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
internal class RoomSyncHandler @Inject constructor(private val readReceiptHandler: ReadReceiptHandler,
|
||||
private val roomSummaryUpdater: RoomSummaryUpdater,
|
||||
private val roomAccountDataHandler: RoomSyncAccountDataHandler,
|
||||
private val cryptoService: DefaultCryptoService,
|
||||
private val roomMemberEventHandler: RoomMemberEventHandler,
|
||||
private val roomTypingUsersHandler: RoomTypingUsersHandler,
|
||||
private val threadsAwarenessHandler: ThreadsAwarenessHandler,
|
||||
private val roomChangeMembershipStateDataSource: RoomChangeMembershipStateDataSource,
|
||||
@UserId private val userId: String,
|
||||
private val homeServerCapabilitiesService: HomeServerCapabilitiesService,
|
||||
private val lightweightSettingsStorage: LightweightSettingsStorage,
|
||||
private val timelineInput: TimelineInput,
|
||||
private val liveEventService: Lazy<StreamEventsManager>) {
|
||||
internal class RoomSyncHandler @Inject constructor(
|
||||
private val readReceiptHandler: ReadReceiptHandler,
|
||||
private val roomSummaryUpdater: RoomSummaryUpdater,
|
||||
private val roomAccountDataHandler: RoomSyncAccountDataHandler,
|
||||
private val cryptoService: DefaultCryptoService,
|
||||
private val roomMemberEventHandler: RoomMemberEventHandler,
|
||||
private val roomTypingUsersHandler: RoomTypingUsersHandler,
|
||||
private val threadsAwarenessHandler: ThreadsAwarenessHandler,
|
||||
private val roomChangeMembershipStateDataSource: RoomChangeMembershipStateDataSource,
|
||||
@UserId private val userId: String,
|
||||
private val homeServerCapabilitiesService: HomeServerCapabilitiesService,
|
||||
private val lightweightSettingsStorage: LightweightSettingsStorage,
|
||||
private val timelineInput: TimelineInput,
|
||||
private val liveEventService: Lazy<StreamEventsManager>,
|
||||
private val clock: Clock,
|
||||
) {
|
||||
|
||||
sealed class HandlingStrategy {
|
||||
data class JOINED(val data: Map<String, RoomSync>) : HandlingStrategy()
|
||||
@ -130,7 +134,7 @@ internal class RoomSyncHandler @Inject constructor(private val readReceiptHandle
|
||||
} else {
|
||||
EventInsertType.INCREMENTAL_SYNC
|
||||
}
|
||||
val syncLocalTimeStampMillis = System.currentTimeMillis()
|
||||
val syncLocalTimeStampMillis = clock.epochMillis()
|
||||
val rooms = when (handlingStrategy) {
|
||||
is HandlingStrategy.JOINED -> {
|
||||
if (isInitialSync && initialSyncStrategy is InitialSyncStrategy.Optimized) {
|
||||
@ -440,7 +444,8 @@ internal class RoomSyncHandler @Inject constructor(private val readReceiptHandle
|
||||
threadEventEntity = eventEntity,
|
||||
roomMemberContentsByUser = roomMemberContentsByUser,
|
||||
userId = userId,
|
||||
roomEntity = roomEntity
|
||||
roomEntity = roomEntity,
|
||||
currentTimeMillis = clock.epochMillis(),
|
||||
)
|
||||
}
|
||||
} ?: run {
|
||||
|
@ -18,6 +18,7 @@ package org.matrix.android.sdk.internal.util
|
||||
|
||||
import org.matrix.android.sdk.BuildConfig
|
||||
import org.matrix.android.sdk.api.logger.LoggerTag
|
||||
import org.matrix.android.sdk.internal.util.time.Clock
|
||||
import timber.log.Timber
|
||||
|
||||
internal fun <T> Collection<T>.logLimit(maxQuantity: Int = 5): String {
|
||||
@ -34,13 +35,14 @@ internal fun <T> Collection<T>.logLimit(maxQuantity: Int = 5): String {
|
||||
|
||||
internal suspend fun <T> logDuration(message: String,
|
||||
loggerTag: LoggerTag,
|
||||
clock: Clock,
|
||||
block: suspend () -> T): T {
|
||||
Timber.tag(loggerTag.value).d("$message -- BEGIN")
|
||||
val start = System.currentTimeMillis()
|
||||
val start = clock.epochMillis()
|
||||
val result = logRamUsage(message) {
|
||||
block()
|
||||
}
|
||||
val duration = System.currentTimeMillis() - start
|
||||
val duration = clock.epochMillis() - start
|
||||
Timber.tag(loggerTag.value).d("$message -- END duration: $duration ms")
|
||||
|
||||
return result
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
@ -173,4 +173,7 @@ PreferenceManager\.getDefaultSharedPreferences==2
|
||||
# findViewById
|
||||
|
||||
### Do not use `template_` string. Please remove the prefix `template_` to use the generated resource instead.
|
||||
R\.string\.template_
|
||||
R\.string\.template_
|
||||
|
||||
### Use the Clock interface, or use `measureTimeMillis`
|
||||
System\.currentTimeMillis\(\)===2
|
||||
|
@ -43,6 +43,7 @@ import com.google.android.material.bottomsheet.BottomSheetBehavior
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
||||
import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment
|
||||
import im.vector.app.core.time.DefaultClock
|
||||
import im.vector.app.espresso.tools.waitUntilViewVisible
|
||||
import org.hamcrest.Matcher
|
||||
import org.hamcrest.Matchers
|
||||
@ -52,6 +53,7 @@ import org.matrix.android.sdk.api.session.crypto.crosssigning.PrivateKeysInfo
|
||||
import org.matrix.android.sdk.api.session.sync.SyncState
|
||||
import org.matrix.android.sdk.api.util.Optional
|
||||
import java.util.concurrent.TimeoutException
|
||||
import kotlin.random.Random
|
||||
|
||||
object EspressoHelper {
|
||||
fun getCurrentActivity(): Activity? {
|
||||
@ -89,6 +91,8 @@ fun getString(@StringRes id: Int): String {
|
||||
|
||||
fun waitForView(viewMatcher: Matcher<View>, timeout: Long = 10_000, waitForDisplayed: Boolean = true): ViewAction {
|
||||
return object : ViewAction {
|
||||
private val clock = DefaultClock()
|
||||
|
||||
override fun getConstraints(): Matcher<View> {
|
||||
return Matchers.any(View::class.java)
|
||||
}
|
||||
@ -102,14 +106,14 @@ fun waitForView(viewMatcher: Matcher<View>, timeout: Long = 10_000, waitForDispl
|
||||
override fun perform(uiController: UiController, view: View) {
|
||||
println("*** waitForView 1 $view")
|
||||
uiController.loopMainThreadUntilIdle()
|
||||
val startTime = System.currentTimeMillis()
|
||||
val startTime = clock.epochMillis()
|
||||
val endTime = startTime + timeout
|
||||
val visibleMatcher = isDisplayed()
|
||||
|
||||
uiController.loopMainThreadForAtLeast(100)
|
||||
|
||||
do {
|
||||
println("*** waitForView loop $view end:$endTime current:${System.currentTimeMillis()}")
|
||||
println("*** waitForView loop $view end:$endTime current:${clock.epochMillis()}")
|
||||
val viewVisible = TreeIterables.breadthFirstViewTraversal(view)
|
||||
.any { viewMatcher.matches(it) && visibleMatcher.matches(it) }
|
||||
|
||||
@ -118,7 +122,7 @@ fun waitForView(viewMatcher: Matcher<View>, timeout: Long = 10_000, waitForDispl
|
||||
println("*** waitForView loop loopMainThreadForAtLeast...")
|
||||
uiController.loopMainThreadForAtLeast(50)
|
||||
println("*** waitForView loop ...loopMainThreadForAtLeast")
|
||||
} while (System.currentTimeMillis() < endTime)
|
||||
} while (clock.epochMillis() < endTime)
|
||||
|
||||
println("*** waitForView timeout $view")
|
||||
// Timeout happens.
|
||||
@ -168,9 +172,9 @@ fun activityIdlingResource(activityClass: Class<*>): IdlingResource {
|
||||
val res = object : IdlingResource, ActivityLifecycleCallback {
|
||||
private var callback: IdlingResource.ResourceCallback? = null
|
||||
private var resumedActivity: Activity? = null
|
||||
private val uniqTS = System.currentTimeMillis()
|
||||
private val uniqueSuffix = Random.nextLong()
|
||||
|
||||
override fun getName() = "activityIdlingResource_${activityClass.name}_$uniqTS"
|
||||
override fun getName() = "activityIdlingResource_${activityClass.name}_$uniqueSuffix"
|
||||
|
||||
override fun isIdleNow(): Boolean {
|
||||
val activity = resumedActivity ?: ActivityLifecycleMonitorRegistry.getInstance().getActivitiesInStage(Stage.RESUMED).firstOrNull {
|
||||
|
@ -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"
|
||||
|
||||
|
@ -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()
|
||||
}
|
||||
|
@ -53,6 +53,7 @@ import org.matrix.android.sdk.api.session.crypto.verification.VerificationTransa
|
||||
import org.matrix.android.sdk.api.session.crypto.verification.VerificationTxState
|
||||
import kotlin.coroutines.Continuation
|
||||
import kotlin.coroutines.resume
|
||||
import kotlin.random.Random
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
@LargeTest
|
||||
@ -66,7 +67,7 @@ class VerifySessionInteractiveTest : VerificationTestBase() {
|
||||
@Before
|
||||
fun createSessionWithCrossSigning() {
|
||||
val matrix = getMatrixInstance()
|
||||
val userName = "foobar_${System.currentTimeMillis()}"
|
||||
val userName = "foobar_${Random.nextLong()}"
|
||||
existingSession = createAccountAndSync(matrix, userName, password, true)
|
||||
doSync<Unit> {
|
||||
existingSession!!.cryptoService().crossSigningService()
|
||||
|
@ -53,6 +53,7 @@ import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse
|
||||
import org.matrix.android.sdk.api.session.Session
|
||||
import kotlin.coroutines.Continuation
|
||||
import kotlin.coroutines.resume
|
||||
import kotlin.random.Random
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
@LargeTest
|
||||
@ -68,7 +69,7 @@ class VerifySessionPassphraseTest : VerificationTestBase() {
|
||||
fun createSessionWithCrossSigningAnd4S() {
|
||||
val context = InstrumentationRegistry.getInstrumentation().targetContext
|
||||
val matrix = getMatrixInstance()
|
||||
val userName = "foobar_${System.currentTimeMillis()}"
|
||||
val userName = "foobar_${Random.nextLong()}"
|
||||
existingSession = createAccountAndSync(matrix, userName, password, true)
|
||||
doSync<Unit> {
|
||||
existingSession!!.cryptoService().crossSigningService()
|
||||
|
@ -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(
|
||||
|
@ -29,6 +29,7 @@ import im.vector.app.R
|
||||
import im.vector.app.core.di.ActiveSessionHolder
|
||||
import im.vector.app.core.extensions.registerStartForActivityResult
|
||||
import im.vector.app.core.platform.VectorBaseActivity
|
||||
import im.vector.app.core.time.Clock
|
||||
import im.vector.app.core.utils.PERMISSIONS_FOR_TAKING_PHOTO
|
||||
import im.vector.app.core.utils.checkPermissions
|
||||
import im.vector.app.core.utils.registerForPermissionsResult
|
||||
@ -59,6 +60,8 @@ class DebugMenuActivity : VectorBaseActivity<ActivityDebugMenuBinding>() {
|
||||
|
||||
@Inject
|
||||
lateinit var activeSessionHolder: ActiveSessionHolder
|
||||
@Inject
|
||||
lateinit var clock: Clock
|
||||
|
||||
private lateinit var buffer: ByteArray
|
||||
|
||||
@ -165,7 +168,7 @@ class DebugMenuActivity : VectorBaseActivity<ActivityDebugMenuBinding>() {
|
||||
}
|
||||
|
||||
val builder = NotificationCompat.Builder(this, "CHAN")
|
||||
.setWhen(System.currentTimeMillis())
|
||||
.setWhen(clock.epochMillis())
|
||||
.setContentTitle("Title")
|
||||
.setContentText("Content")
|
||||
// No effect because it's a group summary notif
|
||||
@ -180,16 +183,16 @@ class DebugMenuActivity : VectorBaseActivity<ActivityDebugMenuBinding>() {
|
||||
.setName("User name")
|
||||
.build()
|
||||
)
|
||||
.addMessage("Message 1 - 1", System.currentTimeMillis(), Person.Builder().setName("user 1-1").build())
|
||||
.addMessage("Message 1 - 2", System.currentTimeMillis(), Person.Builder().setName("user 1-2").build())
|
||||
.addMessage("Message 1 - 1", clock.epochMillis(), Person.Builder().setName("user 1-1").build())
|
||||
.addMessage("Message 1 - 2", clock.epochMillis(), Person.Builder().setName("user 1-2").build())
|
||||
|
||||
val messagingStyle2 = NotificationCompat.MessagingStyle(
|
||||
Person.Builder()
|
||||
.setName("User name 2")
|
||||
.build()
|
||||
)
|
||||
.addMessage("Message 2 - 1", System.currentTimeMillis(), Person.Builder().setName("user 1-1").build())
|
||||
.addMessage("Message 2 - 2", System.currentTimeMillis(), Person.Builder().setName("user 1-2").build())
|
||||
.addMessage("Message 2 - 1", clock.epochMillis(), Person.Builder().setName("user 1-1").build())
|
||||
.addMessage("Message 2 - 2", clock.epochMillis(), Person.Builder().setName("user 1-2").build())
|
||||
|
||||
notificationManager.notify(10, builder.build())
|
||||
|
||||
@ -197,7 +200,7 @@ class DebugMenuActivity : VectorBaseActivity<ActivityDebugMenuBinding>() {
|
||||
11,
|
||||
NotificationCompat.Builder(this, "CHAN")
|
||||
.setChannelId("CHAN")
|
||||
.setWhen(System.currentTimeMillis())
|
||||
.setWhen(clock.epochMillis())
|
||||
.setContentTitle("Title 1")
|
||||
.setContentText("Content 1")
|
||||
// For shortcut on long press on launcher icon
|
||||
@ -211,7 +214,7 @@ class DebugMenuActivity : VectorBaseActivity<ActivityDebugMenuBinding>() {
|
||||
notificationManager.notify(
|
||||
12,
|
||||
NotificationCompat.Builder(this, "CHAN2")
|
||||
.setWhen(System.currentTimeMillis())
|
||||
.setWhen(clock.epochMillis())
|
||||
.setContentTitle("Title 2")
|
||||
.setContentText("Content 2")
|
||||
.setStyle(messagingStyle2)
|
||||
|
@ -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 -> {
|
||||
|
@ -27,6 +27,7 @@ import androidx.core.content.getSystemService
|
||||
import im.vector.app.core.extensions.singletonEntryPoint
|
||||
import im.vector.app.core.platform.PendingIntentCompat
|
||||
import im.vector.app.core.services.VectorSyncService
|
||||
import im.vector.app.core.time.Clock
|
||||
import org.matrix.android.sdk.api.session.sync.job.SyncService
|
||||
import timber.log.Timber
|
||||
|
||||
@ -34,10 +35,13 @@ class AlarmSyncBroadcastReceiver : BroadcastReceiver() {
|
||||
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
Timber.d("## Sync: AlarmSyncBroadcastReceiver received intent")
|
||||
val vectorPreferences = context.singletonEntryPoint()
|
||||
.takeIf { it.activeSessionHolder().getSafeActiveSession() != null }
|
||||
?.vectorPreferences()
|
||||
?: return Unit.also { Timber.v("No active session, so don't launch sync service.") }
|
||||
val singletonEntryPoint = context.singletonEntryPoint()
|
||||
if (singletonEntryPoint.activeSessionHolder().getSafeActiveSession() == null) {
|
||||
Timber.v("No active session, so don't launch sync service.")
|
||||
return
|
||||
}
|
||||
val vectorPreferences = singletonEntryPoint.vectorPreferences()
|
||||
val clock = singletonEntryPoint.clock()
|
||||
|
||||
val sessionId = intent.getStringExtra(SyncService.EXTRA_SESSION_ID) ?: return
|
||||
VectorSyncService.newPeriodicIntent(
|
||||
@ -52,7 +56,7 @@ class AlarmSyncBroadcastReceiver : BroadcastReceiver() {
|
||||
ContextCompat.startForegroundService(context, it)
|
||||
} catch (ex: Throwable) {
|
||||
Timber.i("## Sync: Failed to start service, Alarm scheduled to restart service")
|
||||
scheduleAlarm(context, sessionId, vectorPreferences.backgroundSyncDelay())
|
||||
scheduleAlarm(context, sessionId, vectorPreferences.backgroundSyncDelay(), clock)
|
||||
Timber.e(ex)
|
||||
}
|
||||
}
|
||||
@ -61,7 +65,7 @@ class AlarmSyncBroadcastReceiver : BroadcastReceiver() {
|
||||
companion object {
|
||||
private const val REQUEST_CODE = 0
|
||||
|
||||
fun scheduleAlarm(context: Context, sessionId: String, delayInSeconds: Int) {
|
||||
fun scheduleAlarm(context: Context, sessionId: String, delayInSeconds: Int, clock: Clock) {
|
||||
// Reschedule
|
||||
Timber.v("## Sync: Scheduling alarm for background sync in $delayInSeconds seconds")
|
||||
val intent = Intent(context, AlarmSyncBroadcastReceiver::class.java).apply {
|
||||
@ -74,7 +78,7 @@ class AlarmSyncBroadcastReceiver : BroadcastReceiver() {
|
||||
intent,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE
|
||||
)
|
||||
val firstMillis = System.currentTimeMillis() + delayInSeconds * 1000L
|
||||
val firstMillis = clock.epochMillis() + delayInSeconds * 1000L
|
||||
val alarmMgr = context.getSystemService<AlarmManager>()!!
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
alarmMgr.setAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, firstMillis, pIntent)
|
||||
|
@ -32,7 +32,8 @@ class OnApplicationUpgradeOrRebootReceiver : BroadcastReceiver() {
|
||||
BackgroundSyncStarter.start(
|
||||
context,
|
||||
singletonEntryPoint.vectorPreferences(),
|
||||
singletonEntryPoint.activeSessionHolder()
|
||||
singletonEntryPoint.activeSessionHolder(),
|
||||
singletonEntryPoint.clock()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -33,6 +33,8 @@ import androidx.work.WorkerParameters
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import im.vector.app.R
|
||||
import im.vector.app.core.platform.PendingIntentCompat
|
||||
import im.vector.app.core.time.Clock
|
||||
import im.vector.app.core.time.DefaultClock
|
||||
import im.vector.app.features.notifications.NotificationUtils
|
||||
import im.vector.app.features.settings.BackgroundSyncMode
|
||||
import org.matrix.android.sdk.api.Matrix
|
||||
@ -77,6 +79,7 @@ class VectorSyncService : SyncService() {
|
||||
|
||||
@Inject lateinit var notificationUtils: NotificationUtils
|
||||
@Inject lateinit var matrix: Matrix
|
||||
@Inject lateinit var clock: Clock
|
||||
|
||||
override fun provideMatrix() = matrix
|
||||
|
||||
@ -102,7 +105,8 @@ class VectorSyncService : SyncService() {
|
||||
syncTimeoutSeconds = syncTimeoutSeconds,
|
||||
syncDelaySeconds = syncDelaySeconds,
|
||||
isPeriodic = true,
|
||||
isNetworkBack = false
|
||||
isNetworkBack = false,
|
||||
currentTimeMillis = clock.epochMillis()
|
||||
)
|
||||
}
|
||||
|
||||
@ -114,9 +118,10 @@ class VectorSyncService : SyncService() {
|
||||
val rescheduleSyncWorkRequest: WorkRequest =
|
||||
OneTimeWorkRequestBuilder<RestartWhenNetworkOn>()
|
||||
.setInputData(RestartWhenNetworkOn.createInputData(sessionId, syncTimeoutSeconds, syncDelaySeconds, isPeriodic))
|
||||
.setConstraints(Constraints.Builder()
|
||||
.setRequiredNetworkType(NetworkType.CONNECTED)
|
||||
.build()
|
||||
.setConstraints(
|
||||
Constraints.Builder()
|
||||
.setRequiredNetworkType(NetworkType.CONNECTED)
|
||||
.build()
|
||||
)
|
||||
.build()
|
||||
|
||||
@ -137,20 +142,27 @@ class VectorSyncService : SyncService() {
|
||||
}
|
||||
|
||||
// I do not move or rename this class, since I'm not sure about the side effect regarding the WorkManager
|
||||
class RestartWhenNetworkOn(appContext: Context, workerParams: WorkerParameters) :
|
||||
Worker(appContext, workerParams) {
|
||||
class RestartWhenNetworkOn(
|
||||
appContext: Context,
|
||||
workerParams: WorkerParameters
|
||||
) : Worker(appContext, workerParams) {
|
||||
|
||||
override fun doWork(): Result {
|
||||
Timber.d("## Sync: RestartWhenNetworkOn.doWork()")
|
||||
val sessionId = inputData.getString(KEY_SESSION_ID) ?: return Result.failure()
|
||||
val syncTimeoutSeconds = inputData.getInt(KEY_SYNC_TIMEOUT_SECONDS, BackgroundSyncMode.DEFAULT_SYNC_TIMEOUT_SECONDS)
|
||||
val syncDelaySeconds = inputData.getInt(KEY_SYNC_DELAY_SECONDS, BackgroundSyncMode.DEFAULT_SYNC_DELAY_SECONDS)
|
||||
val isPeriodic = inputData.getBoolean(KEY_IS_PERIODIC, false)
|
||||
|
||||
// Not sure how to inject a Clock here
|
||||
val clock = DefaultClock()
|
||||
applicationContext.rescheduleSyncService(
|
||||
sessionId = sessionId,
|
||||
syncTimeoutSeconds = syncTimeoutSeconds,
|
||||
syncDelaySeconds = syncDelaySeconds,
|
||||
isPeriodic = isPeriodic,
|
||||
isNetworkBack = true
|
||||
isNetworkBack = true,
|
||||
currentTimeMillis = clock.epochMillis()
|
||||
)
|
||||
// Indicate whether the work finished successfully with the Result
|
||||
return Result.success()
|
||||
@ -182,7 +194,8 @@ private fun Context.rescheduleSyncService(sessionId: String,
|
||||
syncTimeoutSeconds: Int,
|
||||
syncDelaySeconds: Int,
|
||||
isPeriodic: Boolean,
|
||||
isNetworkBack: Boolean) {
|
||||
isNetworkBack: Boolean,
|
||||
currentTimeMillis: Long) {
|
||||
Timber.d("## Sync: rescheduleSyncService")
|
||||
val intent = if (isPeriodic) {
|
||||
VectorSyncService.newPeriodicIntent(
|
||||
@ -208,7 +221,7 @@ private fun Context.rescheduleSyncService(sessionId: String,
|
||||
} else {
|
||||
PendingIntent.getService(this, 0, intent, PendingIntentCompat.FLAG_IMMUTABLE)
|
||||
}
|
||||
val firstMillis = System.currentTimeMillis() + syncDelaySeconds * 1000L
|
||||
val firstMillis = currentTimeMillis + syncDelaySeconds * 1000L
|
||||
val alarmMgr = getSystemService<AlarmManager>()!!
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
alarmMgr.setAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, firstMillis, pendingIntent)
|
||||
|
@ -147,8 +147,11 @@ fun openFileSelection(activity: Activity,
|
||||
* Send an email to address with optional subject and message
|
||||
*/
|
||||
fun sendMailTo(address: String, subject: String? = null, message: String? = null, activity: Activity) {
|
||||
val intent = Intent(Intent.ACTION_SENDTO, Uri.fromParts(
|
||||
"mailto", address, null))
|
||||
val intent = Intent(
|
||||
Intent.ACTION_SENDTO, Uri.fromParts(
|
||||
"mailto", address, null
|
||||
)
|
||||
)
|
||||
intent.putExtra(Intent.EXTRA_SUBJECT, subject)
|
||||
intent.putExtra(Intent.EXTRA_TEXT, message)
|
||||
|
||||
@ -248,7 +251,12 @@ private fun appendTimeToFilename(name: String): String {
|
||||
return """${filename}_$dateExtension.$fileExtension"""
|
||||
}
|
||||
|
||||
suspend fun saveMedia(context: Context, file: File, title: String, mediaMimeType: String?, notificationUtils: NotificationUtils) {
|
||||
suspend fun saveMedia(context: Context,
|
||||
file: File,
|
||||
title: String,
|
||||
mediaMimeType: String?,
|
||||
notificationUtils: NotificationUtils,
|
||||
currentTimeMillis: Long) {
|
||||
withContext(Dispatchers.IO) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
val filename = appendTimeToFilename(title)
|
||||
@ -257,8 +265,8 @@ suspend fun saveMedia(context: Context, file: File, title: String, mediaMimeType
|
||||
put(MediaStore.Images.Media.TITLE, filename)
|
||||
put(MediaStore.Images.Media.DISPLAY_NAME, filename)
|
||||
put(MediaStore.Images.Media.MIME_TYPE, mediaMimeType)
|
||||
put(MediaStore.Images.Media.DATE_ADDED, System.currentTimeMillis())
|
||||
put(MediaStore.Images.Media.DATE_TAKEN, System.currentTimeMillis())
|
||||
put(MediaStore.Images.Media.DATE_ADDED, currentTimeMillis)
|
||||
put(MediaStore.Images.Media.DATE_TAKEN, currentTimeMillis)
|
||||
}
|
||||
val externalContentUri = when {
|
||||
mediaMimeType?.isMimeTypeImage() == true -> MediaStore.Images.Media.EXTERNAL_CONTENT_URI
|
||||
@ -289,7 +297,7 @@ suspend fun saveMedia(context: Context, file: File, title: String, mediaMimeType
|
||||
}
|
||||
}
|
||||
} else {
|
||||
saveMediaLegacy(context, mediaMimeType, title, file)
|
||||
saveMediaLegacy(context, mediaMimeType, title, file, currentTimeMillis)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -298,7 +306,8 @@ suspend fun saveMedia(context: Context, file: File, title: String, mediaMimeType
|
||||
private fun saveMediaLegacy(context: Context,
|
||||
mediaMimeType: String?,
|
||||
title: String,
|
||||
file: File) {
|
||||
file: File,
|
||||
currentTimeMillis: Long) {
|
||||
val state = Environment.getExternalStorageState()
|
||||
if (Environment.MEDIA_MOUNTED != state) {
|
||||
context.toast(context.getString(R.string.error_saving_media_file))
|
||||
@ -319,7 +328,7 @@ private fun saveMediaLegacy(context: Context,
|
||||
} else {
|
||||
title
|
||||
}
|
||||
val savedFile = saveFileIntoLegacy(file, downloadDir, outputFilename)
|
||||
val savedFile = saveFileIntoLegacy(file, downloadDir, outputFilename, currentTimeMillis)
|
||||
if (savedFile != null) {
|
||||
val downloadManager = context.getSystemService<DownloadManager>()
|
||||
downloadManager?.addCompletedDownload(
|
||||
@ -329,7 +338,8 @@ private fun saveMediaLegacy(context: Context,
|
||||
mediaMimeType ?: MimeTypes.OctetStream,
|
||||
savedFile.absolutePath,
|
||||
savedFile.length(),
|
||||
true)
|
||||
true
|
||||
)
|
||||
addToGallery(savedFile, mediaMimeType, context)
|
||||
}
|
||||
} catch (error: Throwable) {
|
||||
@ -408,10 +418,11 @@ fun selectTxtFileToWrite(
|
||||
* @param sourceFile the file source path
|
||||
* @param dstDirPath the dst path
|
||||
* @param outputFilename optional the output filename
|
||||
* @param currentTimeMillis the current time in milliseconds
|
||||
* @return the created file
|
||||
*/
|
||||
@Suppress("DEPRECATION")
|
||||
fun saveFileIntoLegacy(sourceFile: File, dstDirPath: File, outputFilename: String?): File? {
|
||||
fun saveFileIntoLegacy(sourceFile: File, dstDirPath: File, outputFilename: String?, currentTimeMillis: Long): File? {
|
||||
// defines another name for the external media
|
||||
var dstFileName: String
|
||||
|
||||
@ -423,7 +434,7 @@ fun saveFileIntoLegacy(sourceFile: File, dstDirPath: File, outputFilename: Strin
|
||||
if (dotPos > 0) {
|
||||
fileExt = sourceFile.name.substring(dotPos)
|
||||
}
|
||||
dstFileName = "vector_" + System.currentTimeMillis() + fileExt
|
||||
dstFileName = "vector_$currentTimeMillis$fileExt"
|
||||
} else {
|
||||
dstFileName = outputFilename
|
||||
}
|
||||
|
@ -45,6 +45,7 @@ import im.vector.app.core.extensions.insertBeforeLast
|
||||
import im.vector.app.core.extensions.registerStartForActivityResult
|
||||
import im.vector.app.core.platform.VectorBaseFragment
|
||||
import im.vector.app.core.resources.ColorProvider
|
||||
import im.vector.app.core.time.Clock
|
||||
import im.vector.app.core.utils.OnSnapPositionChangeListener
|
||||
import im.vector.app.core.utils.SnapOnScrollListener
|
||||
import im.vector.app.core.utils.attachSnapHelperWithListener
|
||||
@ -64,7 +65,8 @@ data class AttachmentsPreviewArgs(
|
||||
class AttachmentsPreviewFragment @Inject constructor(
|
||||
private val attachmentMiniaturePreviewController: AttachmentMiniaturePreviewController,
|
||||
private val attachmentBigPreviewController: AttachmentBigPreviewController,
|
||||
private val colorProvider: ColorProvider
|
||||
private val colorProvider: ColorProvider,
|
||||
private val clock: Clock,
|
||||
) : VectorBaseFragment<FragmentAttachmentsPreviewBinding>(), AttachmentMiniaturePreviewController.Callback {
|
||||
|
||||
private val fragmentArgs: AttachmentsPreviewArgs by args()
|
||||
@ -192,7 +194,7 @@ class AttachmentsPreviewFragment @Inject constructor(
|
||||
|
||||
private fun handleEditAction() = withState(viewModel) {
|
||||
val currentAttachment = it.attachments.getOrNull(it.currentAttachmentIndex) ?: return@withState
|
||||
val destinationFile = File(requireContext().cacheDir, currentAttachment.name.insertBeforeLast("_edited_image_${System.currentTimeMillis()}"))
|
||||
val destinationFile = File(requireContext().cacheDir, currentAttachment.name.insertBeforeLast("_edited_image_${clock.epochMillis()}"))
|
||||
val uri = currentAttachment.queryUri
|
||||
createUCropWithDefaultSettings(colorProvider, uri, destinationFile.toUri(), currentAttachment.name)
|
||||
.getIntent(requireContext())
|
||||
|
@ -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
|
||||
|
@ -21,6 +21,7 @@ import android.os.Binder
|
||||
import android.os.IBinder
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import im.vector.app.core.services.VectorService
|
||||
import im.vector.app.core.time.Clock
|
||||
import im.vector.app.features.notifications.NotificationUtils
|
||||
import javax.inject.Inject
|
||||
|
||||
@ -28,6 +29,7 @@ import javax.inject.Inject
|
||||
class ScreenCaptureService : VectorService() {
|
||||
|
||||
@Inject lateinit var notificationUtils: NotificationUtils
|
||||
@Inject lateinit var clock: Clock
|
||||
private val binder = LocalBinder()
|
||||
|
||||
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
||||
@ -37,7 +39,7 @@ class ScreenCaptureService : VectorService() {
|
||||
}
|
||||
|
||||
private fun showStickyNotification() {
|
||||
val notificationId = System.currentTimeMillis().toInt()
|
||||
val notificationId = clock.epochMillis().toInt()
|
||||
val notification = notificationUtils.buildScreenSharingNotification()
|
||||
startForeground(notificationId, notification)
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user