diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/SessionRealmConfigurationFactory.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/SessionRealmConfigurationFactory.kt new file mode 100644 index 0000000000..d5a60e9d2b --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/SessionRealmConfigurationFactory.kt @@ -0,0 +1,91 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.database + +import android.content.Context +import im.vector.matrix.android.internal.database.model.SessionRealmModule +import im.vector.matrix.android.internal.di.UserCacheDirectory +import im.vector.matrix.android.internal.di.UserMd5 +import im.vector.matrix.android.internal.session.SessionModule +import io.realm.Realm +import io.realm.RealmConfiguration +import timber.log.Timber +import java.io.File +import javax.inject.Inject + +private const val REALM_SHOULD_CLEAR_FLAG_ = "REALM_SHOULD_CLEAR_FLAG_" +private const val REALM_NAME = "disk_store.realm" + +/** + * This class is handling creation of RealmConfiguration for a session. + * It will handle corrupted realm by clearing the db file. It allows to just clear cache without losing your crypto keys. + * It's clearly not perfect but there is no way to catch the native crash. + */ +internal class SessionRealmConfigurationFactory @Inject constructor(private val realmKeysUtils: RealmKeysUtils, + @UserCacheDirectory val directory: File, + @UserMd5 val userMd5: String, + context: Context) { + + private val sharedPreferences = context.getSharedPreferences("im.vector.matrix.android.realm", Context.MODE_PRIVATE) + + + fun create(): RealmConfiguration { + val shouldClearRealm = sharedPreferences.getBoolean("$REALM_SHOULD_CLEAR_FLAG_$userMd5", false) + if (shouldClearRealm) { + Timber.v("************************************************************") + Timber.v("The realm file session was corrupted and couldn't be loaded.") + Timber.v("The file has been deleted to recover.") + Timber.v("************************************************************") + deleteRealmFiles() + } + sharedPreferences + .edit() + .putBoolean("$REALM_SHOULD_CLEAR_FLAG_$userMd5", true) + .apply() + + val realmConfiguration = RealmConfiguration.Builder() + .directory(directory) + .name(REALM_NAME) + .apply { + realmKeysUtils.configureEncryption(this, "${SessionModule.DB_ALIAS_PREFIX}$userMd5") + } + .modules(SessionRealmModule()) + .deleteRealmIfMigrationNeeded() + .build() + + // Try creating a realm instance and if it succeeds we can clear the flag + Realm.getInstance(realmConfiguration).use { + Timber.v("Successfully create realm instance") + sharedPreferences + .edit() + .putBoolean("$REALM_SHOULD_CLEAR_FLAG_$userMd5", false) + .apply() + } + return realmConfiguration + } + + // Delete all the realm files of the session + private fun deleteRealmFiles() { + listOf(REALM_NAME, "$REALM_NAME.lock", "$REALM_NAME.note", "$REALM_NAME.management").forEach { file -> + try { + File(directory, file).deleteRecursively() + } catch (e: Exception) { + Timber.e(e, "Unable to move files") + } + } + } +} \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/SessionModule.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/SessionModule.kt index 7b655dd939..4637b1428c 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/SessionModule.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/SessionModule.kt @@ -30,8 +30,7 @@ import im.vector.matrix.android.api.session.Session import im.vector.matrix.android.api.session.homeserver.HomeServerCapabilitiesService import im.vector.matrix.android.api.session.securestorage.SecureStorageService import im.vector.matrix.android.internal.database.LiveEntityObserver -import im.vector.matrix.android.internal.database.RealmKeysUtils -import im.vector.matrix.android.internal.database.model.SessionRealmModule +import im.vector.matrix.android.internal.database.SessionRealmConfigurationFactory import im.vector.matrix.android.internal.di.* import im.vector.matrix.android.internal.network.AccessTokenInterceptor import im.vector.matrix.android.internal.network.RetrofitFactory @@ -54,6 +53,7 @@ internal abstract class SessionModule { @Module companion object { + internal const val DB_ALIAS_PREFIX = "session_db_" @JvmStatic @@ -94,18 +94,8 @@ internal abstract class SessionModule { @Provides @SessionDatabase @SessionScope - fun providesRealmConfiguration(realmKeysUtils: RealmKeysUtils, - @UserCacheDirectory directory: File, - @UserMd5 userMd5: String): RealmConfiguration { - return RealmConfiguration.Builder() - .directory(directory) - .name("disk_store.realm") - .apply { - realmKeysUtils.configureEncryption(this, "$DB_ALIAS_PREFIX$userMd5") - } - .modules(SessionRealmModule()) - .deleteRealmIfMigrationNeeded() - .build() + fun providesRealmConfiguration(realmConfigurationFactory: SessionRealmConfigurationFactory): RealmConfiguration { + return realmConfigurationFactory.create() } @JvmStatic