Merge olm to Rust migration

This commit is contained in:
valere 2022-12-08 11:59:46 +01:00
commit d3ef5cc230
23 changed files with 453 additions and 67 deletions

1
.gitattributes vendored
View file

@ -1 +1,2 @@
**/snapshots/**/*.png filter=lfs diff=lfs merge=lfs -text
*.realm filter=lfs diff=lfs merge=lfs -text

View file

@ -125,25 +125,6 @@ android {
java.srcDirs += "src/sharedTest/java"
}
}
//
// flavorDimensions "crypto"
//
// productFlavors {
// kotlinCrypto {
// dimension "crypto"
// isDefault = true
// // versionName "${versionMajor}.${versionMinor}.${versionPatch}${getFdroidVersionSuffix()}"
// buildConfigField "String", "SHORT_FLAVOR_DESCRIPTION", "\"JC\""
// buildConfigField "String", "FLAVOR_DESCRIPTION", "\"KotlinCrypto\""
// }
// rustCrypto {
// dimension "crypto"
// // versionName "${versionMajor}.${versionMinor}.${versionPatch}${getFdroidVersionSuffix()}"
// buildConfigField "String", "SHORT_FLAVOR_DESCRIPTION", "\"RC\""
// buildConfigField "String", "FLAVOR_DESCRIPTION", "\"RustCrypto\""
// }
// }
// publishNonDefault true
}

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:59b4957aa2f9cdc17b14ec8546e144537fac9dee050c6eb173f56fa8602c2736
size 2097152

View file

@ -20,6 +20,7 @@ import org.amshove.kluent.shouldBeNull
import org.amshove.kluent.shouldBeTrue
import org.junit.Test
import org.matrix.android.sdk.api.util.fromBase64
import org.matrix.android.sdk.api.util.fromBase64Safe
@Suppress("SpellCheckingInspection")
class ExtensionsKtTest {

View file

@ -54,6 +54,8 @@ import java.util.concurrent.CountDownLatch
@Ignore
class SASTest : InstrumentedTest {
/*
@Test
fun test_aliceStartThenAliceCancel() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
// TODO
@ -614,4 +616,6 @@ class SASTest : InstrumentedTest {
bobPovTx?.state() == SasTransactionState.SasShortCodeReady
}
}
*/
}

View file

@ -0,0 +1,127 @@
/*
* Copyright 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.crypto.store.migration
import android.content.Context
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
import io.realm.Realm
import io.realm.kotlin.where
import org.amshove.kluent.internal.assertEquals
import org.junit.After
import org.junit.Assert.assertNotNull
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.matrix.android.sdk.InstrumentedTest
import org.matrix.android.sdk.internal.crypto.store.db.RealmCryptoStoreMigration
import org.matrix.android.sdk.internal.crypto.store.db.RealmCryptoStoreModule
import org.matrix.android.sdk.internal.crypto.store.db.model.CryptoMetadataEntity
import org.matrix.android.sdk.internal.crypto.store.db.model.OlmInboundGroupSessionEntity
import org.matrix.android.sdk.internal.crypto.store.db.model.OlmInboundGroupSessionEntityFields
import org.matrix.android.sdk.internal.database.TestRealmConfigurationFactory
import org.matrix.android.sdk.internal.session.MigrateEAtoEROperation
import org.matrix.android.sdk.internal.util.time.Clock
import org.matrix.olm.OlmAccount
import org.matrix.olm.OlmManager
import org.matrix.rustcomponents.sdk.crypto.OlmMachine
import java.io.File
@RunWith(AndroidJUnit4::class)
class ElementAndroidToElementRMigrationTest : InstrumentedTest {
@get:Rule val configurationFactory = TestRealmConfigurationFactory()
lateinit var context: Context
var realm: Realm? = null
@Before
fun setUp() {
// Ensure Olm is initialized
OlmManager()
context = InstrumentationRegistry.getInstrumentation().context
}
@After
fun tearDown() {
realm?.close()
}
@Test
fun given_a_valid_crypto_store_realm_file_then_migration_should_be_successful() {
val realmName = "crypto_store_migration_16.realm"
val migration = RealmCryptoStoreMigration(object : Clock {
override fun epochMillis() = 0L
})
val realmConfiguration = configurationFactory.createConfiguration(
realmName,
null,
RealmCryptoStoreModule(),
migration.schemaVersion,
migration
)
configurationFactory.copyRealmFromAssets(context, realmName, realmName)
realm = Realm.getInstance(realmConfiguration)
val metaData = realm!!.where<CryptoMetadataEntity>().findFirst()!!
val userId = metaData.userId!!
val deviceId = metaData.deviceId!!
val olmAccount = metaData.getOlmAccount()!!
val extractor = MigrateEAtoEROperation()
val targetFile = File(configurationFactory.root, "rust-sdk")
extractor.execute(realmConfiguration, targetFile)
val machine = OlmMachine(userId, deviceId, targetFile.path, null)
assertEquals(olmAccount.identityKeys()[OlmAccount.JSON_KEY_FINGER_PRINT_KEY], machine.identityKeys()["ed25519"])
assertNotNull(machine.getBackupKeys())
val crossSigningStatus = machine.crossSigningStatus()
assertTrue(crossSigningStatus.hasMaster)
assertTrue(crossSigningStatus.hasSelfSigning)
assertTrue(crossSigningStatus.hasUserSigning)
val inboundGroupSessionEntities = realm!!.where<OlmInboundGroupSessionEntity>().findAll()
assertEquals(inboundGroupSessionEntities.size, machine.roomKeyCounts().total.toInt())
val backedUpInboundGroupSessionEntities = realm!!
.where<OlmInboundGroupSessionEntity>()
.equalTo(OlmInboundGroupSessionEntityFields.BACKED_UP, true)
.findAll()
assertEquals(backedUpInboundGroupSessionEntities.size, machine.roomKeyCounts().backedUp.toInt())
}
// @Test
// fun given_an_empty_crypto_store_realm_file_then_migration_should_not_happen() {
// val realmConfiguration = realmConfigurationFactory.configurationForMigrationFrom15To16(populateCryptoStore = false)
// Realm.getInstance(realmConfiguration).use {
// assertTrue(it.isEmpty)
// }
// val machine = OlmMachine("@ganfra146:matrix.org", "UTDQCHKKNS", realmConfigurationFactory.root.path, null)
// assertNull(machine.getBackupKeys())
// val crossSigningStatus = machine.crossSigningStatus()
// assertFalse(crossSigningStatus.hasMaster)
// assertFalse(crossSigningStatus.hasSelfSigning)
// assertFalse(crossSigningStatus.hasUserSigning)
// }
}

View file

@ -15,11 +15,9 @@
*/
package org.matrix.android.sdk.internal.crypto.crosssigning
import android.util.Base64
import org.matrix.android.sdk.api.session.crypto.crosssigning.CryptoCrossSigningKey
import org.matrix.android.sdk.api.session.crypto.model.CryptoDeviceInfo
import org.matrix.android.sdk.internal.util.JsonCanonicalizer
import timber.log.Timber
internal fun CryptoDeviceInfo.canonicalSignable(): String {
return JsonCanonicalizer.getCanonicalJson(Map::class.java, signalableJSONDictionary())
@ -28,15 +26,3 @@ internal fun CryptoDeviceInfo.canonicalSignable(): String {
internal fun CryptoCrossSigningKey.canonicalSignable(): String {
return JsonCanonicalizer.getCanonicalJson(Map::class.java, signalableJSONDictionary())
}
/**
* Decode the base 64. Return null in case of bad format. Should be used when parsing received data from external source
*/
internal fun String.fromBase64Safe(): ByteArray? {
return try {
Base64.decode(this, Base64.DEFAULT)
} catch (throwable: Throwable) {
Timber.e(throwable, "Unable to decode base64 string")
null
}
}

View file

@ -0,0 +1,28 @@
/*
* Copyright (c) 2021 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.session
import io.realm.RealmConfiguration
import java.io.File
class MigrateEAtoEROperation {
fun execute(cryptoRealm: RealmConfiguration, sessionFilesDir: File): File {
// no op in kotlinCrypto
return sessionFilesDir
}
}

View file

@ -17,6 +17,7 @@
package org.matrix.android.sdk.api.util
import android.util.Base64
import timber.log.Timber
fun ByteArray.toBase64NoPadding(): String {
return Base64.encodeToString(this, Base64.NO_PADDING or Base64.NO_WRAP)
@ -25,3 +26,15 @@ fun ByteArray.toBase64NoPadding(): String {
fun String.fromBase64(): ByteArray {
return Base64.decode(this, Base64.DEFAULT)
}
/**
* Decode the base 64. Return null in case of bad format. Should be used when parsing received data from external source
*/
internal fun String.fromBase64Safe(): ByteArray? {
return try {
Base64.decode(this, Base64.DEFAULT)
} catch (throwable: Throwable) {
Timber.e(throwable, "Unable to decode base64 string")
null
}
}

View file

@ -146,37 +146,38 @@ internal class RealmCryptoStore @Inject constructor(
.setWriteAsyncExecutor(monarchyWriteAsyncExecutor)
.build()
init {
// Ensure CryptoMetadataEntity is inserted in DB
doRealmTransaction(realmConfiguration) { realm ->
var currentMetadata = realm.where<CryptoMetadataEntity>().findFirst()
// init {
// // Ensure CryptoMetadataEntity is inserted in DB
// doRealmTransaction(realmConfiguration) { realm ->
// var currentMetadata = realm.where<CryptoMetadataEntity>().findFirst()
//
// var deleteAll = false
//
// if (currentMetadata != null) {
// // Check credentials
// // The device id may not have been provided in credentials.
// // Check it only if provided, else trust the stored one.
// if (currentMetadata.userId != userId ||
// (deviceId != currentMetadata.deviceId)) {
// Timber.w("## open() : Credentials do not match, close this store and delete data")
// deleteAll = true
// currentMetadata = null
// }
// }
//
// if (currentMetadata == null) {
// if (deleteAll) {
// realm.deleteAll()
// }
//
// // Metadata not found, or database cleaned, create it
// realm.createObject(CryptoMetadataEntity::class.java, userId).apply {
// deviceId = this@RealmCryptoStore.deviceId
// }
// }
// }
// }
var deleteAll = false
if (currentMetadata != null) {
// Check credentials
// The device id may not have been provided in credentials.
// Check it only if provided, else trust the stored one.
if (currentMetadata.userId != userId ||
(deviceId != currentMetadata.deviceId)) {
Timber.w("## open() : Credentials do not match, close this store and delete data")
deleteAll = true
currentMetadata = null
}
}
if (currentMetadata == null) {
if (deleteAll) {
realm.deleteAll()
}
// Metadata not found, or database cleaned, create it
realm.createObject(CryptoMetadataEntity::class.java, userId).apply {
deviceId = this@RealmCryptoStore.deviceId
}
}
}
}
/* ==========================================================================================
* Other data
* ========================================================================================== */

View file

@ -33,3 +33,7 @@ internal annotation class CacheDirectory
@Qualifier
@Retention(AnnotationRetention.RUNTIME)
internal annotation class ExternalFilesDirectory
@Qualifier
@Retention(AnnotationRetention.RUNTIME)
internal annotation class SessionRustFilesDirectory

View file

@ -34,6 +34,7 @@ import org.matrix.android.sdk.internal.crypto.store.db.RealmCryptoStoreMigration
import org.matrix.android.sdk.internal.crypto.store.db.RealmCryptoStoreModule
import org.matrix.android.sdk.internal.database.RealmKeysUtils
import org.matrix.android.sdk.internal.legacy.riot.LoginStorage
import org.matrix.android.sdk.internal.util.time.Clock
import timber.log.Timber
import java.io.File
import javax.inject.Inject
@ -44,7 +45,7 @@ internal class DefaultLegacySessionImporter @Inject constructor(
private val context: Context,
private val sessionParamsStore: SessionParamsStore,
private val realmKeysUtils: RealmKeysUtils,
private val realmCryptoStoreMigration: RealmCryptoStoreMigration
private val clock: Clock,
) : LegacySessionImporter {
private val loginStorage = LoginStorage(context)
@ -167,6 +168,8 @@ internal class DefaultLegacySessionImporter @Inject constructor(
newLocation.deleteRecursively()
newLocation.mkdirs()
val realmCryptoStoreMigration = RealmCryptoStoreMigration(clock)
Timber.d("Migration: create legacy realm configuration")
val realmConfiguration = RealmConfiguration.Builder()

View file

@ -52,11 +52,13 @@ import org.matrix.android.sdk.internal.database.RealmSessionProvider
import org.matrix.android.sdk.internal.database.SessionRealmConfigurationFactory
import org.matrix.android.sdk.internal.di.Authenticated
import org.matrix.android.sdk.internal.di.CacheDirectory
import org.matrix.android.sdk.internal.di.CryptoDatabase
import org.matrix.android.sdk.internal.di.DeviceId
import org.matrix.android.sdk.internal.di.SessionDatabase
import org.matrix.android.sdk.internal.di.SessionDownloadsDirectory
import org.matrix.android.sdk.internal.di.SessionFilesDirectory
import org.matrix.android.sdk.internal.di.SessionId
import org.matrix.android.sdk.internal.di.SessionRustFilesDirectory
import org.matrix.android.sdk.internal.di.Unauthenticated
import org.matrix.android.sdk.internal.di.UnauthenticatedWithCertificate
import org.matrix.android.sdk.internal.di.UnauthenticatedWithCertificateWithProgress
@ -178,6 +180,18 @@ internal abstract class SessionModule {
return File(context.filesDir, sessionId)
}
@JvmStatic
@Provides
@SessionRustFilesDirectory
@SessionScope
fun providesRustCryptoFilesDir(
@SessionFilesDirectory parent: File,
@CryptoDatabase realmConfiguration: RealmConfiguration,
): File {
val target = File(parent, "rustFlavor")
return MigrateEAtoEROperation().execute(realmConfiguration, target)
}
@JvmStatic
@Provides
@SessionDownloadsDirectory

View file

@ -60,7 +60,7 @@ import org.matrix.android.sdk.internal.crypto.verification.VerificationsProvider
import org.matrix.android.sdk.internal.crypto.verification.qrcode.QrCodeVerification
import org.matrix.android.sdk.internal.di.DeviceId
import org.matrix.android.sdk.internal.di.MoshiProvider
import org.matrix.android.sdk.internal.di.SessionFilesDirectory
import org.matrix.android.sdk.internal.di.SessionRustFilesDirectory
import org.matrix.android.sdk.internal.di.UserId
import org.matrix.android.sdk.internal.network.parsing.CheckNumberType
import org.matrix.android.sdk.internal.session.SessionScope
@ -121,7 +121,7 @@ fun setRustLogger() {
internal class OlmMachine @Inject constructor(
@UserId userId: String,
@DeviceId deviceId: String,
@SessionFilesDirectory path: File,
@SessionRustFilesDirectory path: File,
private val requestSender: RequestSender,
private val coroutineDispatchers: MatrixCoroutineDispatchers,
baseMoshi: Moshi,

View file

@ -0,0 +1,19 @@
/*
* 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.crypto.store.db.migration.rust
object ExtractMigrationDataFailure : java.lang.RuntimeException("Can't proceed with migration, crypto store is empty or some necessary data is missing.")

View file

@ -0,0 +1,149 @@
/*
* 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.crypto.store.db.migration.rust
import io.realm.Realm
import io.realm.RealmConfiguration
import io.realm.kotlin.where
import org.matrix.android.sdk.internal.crypto.store.db.deserializeFromRealm
import org.matrix.android.sdk.internal.crypto.store.db.model.CryptoMetadataEntity
import org.matrix.android.sdk.internal.crypto.store.db.model.OlmInboundGroupSessionEntity
import org.matrix.android.sdk.internal.crypto.store.db.model.OlmSessionEntity
import org.matrix.android.sdk.internal.crypto.store.db.model.UserEntity
import org.matrix.olm.OlmSession
import org.matrix.olm.OlmUtility
import org.matrix.rustcomponents.sdk.crypto.CrossSigningKeyExport
import org.matrix.rustcomponents.sdk.crypto.MigrationData
import org.matrix.rustcomponents.sdk.crypto.PickledAccount
import org.matrix.rustcomponents.sdk.crypto.PickledInboundGroupSession
import org.matrix.rustcomponents.sdk.crypto.PickledSession
import timber.log.Timber
import java.nio.charset.Charset
private val charset = Charset.forName("UTF-8")
internal class ExtractMigrationDataUseCase {
fun extractData(realm: Realm): MigrationData {
return try {
extract(realm) ?: throw ExtractMigrationDataFailure
} catch (failure: Throwable) {
throw ExtractMigrationDataFailure
}
}
fun hasExistingData(realmConfiguration: RealmConfiguration): Boolean {
return Realm.getInstance(realmConfiguration).use { realm ->
!realm.isEmpty &&
// Check if there is a MetaData object
realm.where<CryptoMetadataEntity>().count() > 0
}
}
private fun extract(realm: Realm): MigrationData? {
val metadataEntity = realm.where<CryptoMetadataEntity>().findFirst() ?: return null.also {
Timber.w("Rust db migration: No existing metadataEntity")
}
val pickleKey = OlmUtility.getRandomKey()
val olmSessionEntities = realm.where<OlmSessionEntity>().findAll()
val pickledSessions = olmSessionEntities.map { it.toPickledSession(pickleKey) }
val inboundGroupSessionEntities = realm.where<OlmInboundGroupSessionEntity>().findAll()
val pickledInboundGroupSessions = inboundGroupSessionEntities.mapNotNull { it.toPickledInboundGroupSession(pickleKey) }
val masterKey = metadataEntity.xSignMasterPrivateKey
val userKey = metadataEntity.xSignUserPrivateKey
val selfSignedKey = metadataEntity.xSignSelfSignedPrivateKey
val userId = metadataEntity.userId ?: return null
val deviceId = metadataEntity.deviceId ?: return null
val backupVersion = metadataEntity.backupVersion
val backupRecoveryKey = metadataEntity.keyBackupRecoveryKey
val trackedUserEntities = realm.where<UserEntity>().findAll()
val trackedUserIds = trackedUserEntities.mapNotNull {
it.userId
}
val isOlmAccountShared = metadataEntity.deviceKeysSentToServer
val olmAccount = metadataEntity.getOlmAccount() ?: return null
val pickledOlmAccount = olmAccount.pickle(pickleKey, StringBuffer()).asString()
val pickledAccount = PickledAccount(
userId = userId,
deviceId = deviceId,
pickle = pickledOlmAccount,
shared = isOlmAccountShared,
uploadedSignedKeyCount = 50
)
return MigrationData(
account = pickledAccount,
sessions = pickledSessions,
inboundGroupSessions = pickledInboundGroupSessions,
pickleKey = pickleKey.map { it.toUByte() },
backupVersion = backupVersion,
backupRecoveryKey = backupRecoveryKey,
crossSigning = CrossSigningKeyExport(
masterKey = masterKey,
selfSigningKey = selfSignedKey,
userSigningKey = userKey
),
trackedUsers = trackedUserIds
)
}
private fun OlmInboundGroupSessionEntity.toPickledInboundGroupSession(pickleKey: ByteArray): PickledInboundGroupSession? {
val senderKey = this.senderKey ?: return null
val backedUp = this.backedUp
val olmInboundGroupSession = this.getOlmGroupSession() ?: return null.also {
Timber.w("Rust db migration: Failed to migrated group session $sessionId")
}
val data = this.getData() ?: return null.also {
Timber.w("Rust db migration: Failed to migrated group session $sessionId, no meta data")
}
val roomId = data.roomId ?: return null.also {
Timber.w("Rust db migration: Failed to migrated group session $sessionId, no roomId")
}
val pickledInboundGroupSession = olmInboundGroupSession.pickle(pickleKey, StringBuffer()).asString()
return PickledInboundGroupSession(
pickle = pickledInboundGroupSession,
senderKey = senderKey,
signingKey = data.keysClaimed.orEmpty(),
roomId = roomId,
forwardingChains = data.forwardingCurve25519KeyChain.orEmpty(),
imported = true,
backedUp = backedUp
)
}
private fun OlmSessionEntity.toPickledSession(pickleKey: ByteArray): PickledSession {
val deviceKey = this.deviceKey ?: ""
val lastReceivedMessageTs = this.lastReceivedMessageTs
val olmSessionStr = this.olmSessionData
val olmSession = deserializeFromRealm<OlmSession>(olmSessionStr)!!
val pickledOlmSession = olmSession.pickle(pickleKey, StringBuffer()).asString()
return PickledSession(
pickle = pickledOlmSession,
senderKey = deviceKey,
createdUsingFallbackKey = false,
creationTime = lastReceivedMessageTs.toString(),
lastUseTime = lastReceivedMessageTs.toString()
)
}
private fun ByteArray.asString() = String(this, charset)
}

View file

@ -0,0 +1,52 @@
/*
* 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.session
import io.realm.Realm
import io.realm.RealmConfiguration
import org.matrix.android.sdk.internal.crypto.store.db.migration.rust.ExtractMigrationDataUseCase
import org.matrix.rustcomponents.sdk.crypto.ProgressListener
import timber.log.Timber
import java.io.File
class MigrateEAtoEROperation {
fun execute(cryptoRealm: RealmConfiguration, sessionFilesDir: File): File {
// Temporary code for migration
if (!sessionFilesDir.exists()) {
sessionFilesDir.mkdir()
// perform a migration?
val extractMigrationData = ExtractMigrationDataUseCase()
try {
val progressListener = object : ProgressListener {
override fun onProgress(progress: Int, total: Int) {
Timber.v("OnProgress: $progress/$total")
}
}
Realm.getInstance(cryptoRealm).use { realm ->
val migrationData = extractMigrationData.extractData(realm)
org.matrix.rustcomponents.sdk.crypto.migrate(migrationData, sessionFilesDir.path, null, progressListener)
}
} catch (failure: Throwable) {
Timber.e(failure, "Failure while calling rust migration method")
throw failure
}
}
return sessionFilesDir
}
}

View file

@ -23,7 +23,7 @@ import javax.inject.Inject
class RoomSummaryRoomListDiffCallback @Inject constructor(
vectorPreferences: VectorPreferences
): DiffUtil.ItemCallback<RoomSummary>() {
) : DiffUtil.ItemCallback<RoomSummary>() {
override fun areItemsTheSame(oldItem: RoomSummary, newItem: RoomSummary): Boolean {
return oldItem.roomId == newItem.roomId