diff --git a/CHANGES.md b/CHANGES.md
index 125e9b3ac914145cddc73865193cf5e5559d7558..c64c102d129b913d0071a50668f61167854c0826 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,5 +1,10 @@
 Please also refer to the Changelog of Element Android: https://github.com/vector-im/element-android/blob/master/CHANGES.md
 
+Changes in Matrix-SDK 1.0.7 (2020-09-23)
+===================================================
+
+Imported from Element 1.0.7. (https://github.com/vector-im/element-android/releases/tag/v1.0.7)
+
 Changes in Matrix-SDK 1.0.6 (2020-09-08)
 ===================================================
 
diff --git a/matrix-sdk-android/build.gradle b/matrix-sdk-android/build.gradle
index e2ad3986e09e89dcd9715f47612682ef3886e210..222e8ea3c42846532296da088e16b68772197c1f 100644
--- a/matrix-sdk-android/build.gradle
+++ b/matrix-sdk-android/build.gradle
@@ -26,7 +26,7 @@ android {
         minSdkVersion 21
         targetSdkVersion 29
         versionCode 1
-        versionName "1.0.6"
+        versionName "1.0.7"
         // Multidex is useful for tests
         multiDexEnabled true
         testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/api/Matrix.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/api/Matrix.kt
index df26bb12273f973dc48dc6419e7fc9ce67021b97..751b2a708c7c4e41f1571cb57849dd270ef85fd5 100644
--- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/api/Matrix.kt
+++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/api/Matrix.kt
@@ -24,6 +24,7 @@ import com.zhuinden.monarchy.Monarchy
 import org.matrix.android.sdk.BuildConfig
 import org.matrix.android.sdk.api.auth.AuthenticationService
 import org.matrix.android.sdk.api.legacy.LegacySessionImporter
+import org.matrix.android.sdk.api.raw.RawService
 import org.matrix.android.sdk.common.DaggerTestMatrixComponent
 import org.matrix.android.sdk.internal.SessionManager
 import org.matrix.android.sdk.internal.network.UserAgentHolder
@@ -41,6 +42,7 @@ class Matrix private constructor(context: Context, matrixConfiguration: MatrixCo
 
     @Inject internal lateinit var legacySessionImporter: LegacySessionImporter
     @Inject internal lateinit var authenticationService: AuthenticationService
+    @Inject internal lateinit var rawService: RawService
     @Inject internal lateinit var userAgentHolder: UserAgentHolder
     @Inject internal lateinit var backgroundDetectionObserver: BackgroundDetectionObserver
     @Inject internal lateinit var olmManager: OlmManager
@@ -61,6 +63,8 @@ class Matrix private constructor(context: Context, matrixConfiguration: MatrixCo
         return authenticationService
     }
 
+    fun rawService() = rawService
+
     fun legacySessionImporter(): LegacySessionImporter {
         return legacySessionImporter
     }
diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/TestMatrixComponent.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/TestMatrixComponent.kt
index 50290e1d632498ca9878f3b43862ebeb0cd5eaf4..e2ab16cad31046593959c08ab10d9f90099c8252 100644
--- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/TestMatrixComponent.kt
+++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/TestMatrixComponent.kt
@@ -25,8 +25,16 @@ import org.matrix.android.sdk.internal.di.MatrixComponent
 import org.matrix.android.sdk.internal.di.MatrixModule
 import org.matrix.android.sdk.internal.di.MatrixScope
 import org.matrix.android.sdk.internal.di.NetworkModule
+import org.matrix.android.sdk.internal.raw.RawModule
 
-@Component(modules = [TestModule::class, MatrixModule::class, NetworkModule::class, AuthModule::class, TestNetworkModule::class])
+@Component(modules = [
+    TestModule::class,
+    MatrixModule::class,
+    NetworkModule::class,
+    AuthModule::class,
+    RawModule::class,
+    TestNetworkModule::class
+])
 @MatrixScope
 internal interface TestMatrixComponent : MatrixComponent {
 
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/Matrix.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/Matrix.kt
index aafefa204861176dbd2e27ac06a3751f3901bff2..e6f982682bc35664c1abcb2af0a1b9d7f2ea4961 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/Matrix.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/Matrix.kt
@@ -25,6 +25,7 @@ import com.zhuinden.monarchy.Monarchy
 import org.matrix.android.sdk.BuildConfig
 import org.matrix.android.sdk.api.auth.AuthenticationService
 import org.matrix.android.sdk.api.legacy.LegacySessionImporter
+import org.matrix.android.sdk.api.raw.RawService
 import org.matrix.android.sdk.internal.SessionManager
 import org.matrix.android.sdk.internal.di.DaggerMatrixComponent
 import org.matrix.android.sdk.internal.network.UserAgentHolder
@@ -42,6 +43,7 @@ class Matrix private constructor(context: Context, matrixConfiguration: MatrixCo
 
     @Inject internal lateinit var legacySessionImporter: LegacySessionImporter
     @Inject internal lateinit var authenticationService: AuthenticationService
+    @Inject internal lateinit var rawService: RawService
     @Inject internal lateinit var userAgentHolder: UserAgentHolder
     @Inject internal lateinit var backgroundDetectionObserver: BackgroundDetectionObserver
     @Inject internal lateinit var olmManager: OlmManager
@@ -62,6 +64,8 @@ class Matrix private constructor(context: Context, matrixConfiguration: MatrixCo
         return authenticationService
     }
 
+    fun rawService() = rawService
+
     fun legacySessionImporter(): LegacySessionImporter {
         return legacySessionImporter
     }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/data/WellKnown.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/data/WellKnown.kt
index b10cae6171a08d7228f2760bd18113af59980d1c..4f7bc75556c1e9988d57c33611745531a45b6f6f 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/data/WellKnown.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/data/WellKnown.kt
@@ -42,9 +42,6 @@ import org.matrix.android.sdk.api.util.JsonDict
  *              }
  *          ]
  *    }
- *     "im.vector.riot.jitsi": {
- *         "preferredDomain": "https://jitsi.riot.im/"
- *     }
  * }
  * </pre>
  */
@@ -57,24 +54,5 @@ data class WellKnown(
         val identityServer: WellKnownBaseConfig? = null,
 
         @Json(name = "m.integrations")
-        val integrations: JsonDict? = null,
-
-        @Json(name = "im.vector.riot.e2ee")
-        val e2eAdminSetting: E2EWellKnownConfig? = null,
-
-        @Json(name = "im.vector.riot.jitsi")
-        val jitsiServer: WellKnownPreferredConfig? = null
-
-)
-
-@JsonClass(generateAdapter = true)
-data class E2EWellKnownConfig(
-        @Json(name = "default")
-        val e2eDefault: Boolean = true
-)
-
-@JsonClass(generateAdapter = true)
-data class WellKnownPreferredConfig(
-        @Json(name = "preferredDomain")
-        val preferredDomain: String? = null
+        val integrations: JsonDict? = null
 )
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/failure/MatrixError.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/failure/MatrixError.kt
index ff68107ffcb585dac73fe5bd9a8c9269c1a6dbb7..77a5dc4f85bcca8ca914178e0566fcd01edeafe7 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/failure/MatrixError.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/failure/MatrixError.kt
@@ -132,6 +132,8 @@ data class MatrixError(
         const val M_CANNOT_LEAVE_SERVER_NOTICE_ROOM = "M_CANNOT_LEAVE_SERVER_NOTICE_ROOM"
         /** (Not documented yet) */
         const val M_WRONG_ROOM_KEYS_VERSION = "M_WRONG_ROOM_KEYS_VERSION"
+        /** (Not documented yet) */
+        const val M_WEAK_PASSWORD = "M_WEAK_PASSWORD"
 
         const val M_TERMS_NOT_SIGNED = "M_TERMS_NOT_SIGNED"
 
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/raw/RawCacheStrategy.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/raw/RawCacheStrategy.kt
new file mode 100644
index 0000000000000000000000000000000000000000..06657a98697d33eaabc9eabed99e44f4d13054da
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/raw/RawCacheStrategy.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2020 New Vector Ltd
+ * Copyright 2020 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.api.raw
+
+sealed class RawCacheStrategy {
+    // Data is always fetched from the server
+    object NoCache: RawCacheStrategy()
+
+    // Once data is retrieved, it is stored for the provided amount of time.
+    // In case of error, and if strict is set to false, the cache can be returned if available
+    data class TtlCache(val validityDurationInMillis: Long, val strict: Boolean): RawCacheStrategy()
+
+    // Once retrieved, the data is stored in cache and will be always get from the cache
+    object InfiniteCache: RawCacheStrategy()
+}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/raw/RawService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/raw/RawService.kt
new file mode 100644
index 0000000000000000000000000000000000000000..5c96d175cb30dd27c4dfb502786a306d86cd58d4
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/raw/RawService.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2020 New Vector Ltd
+ * Copyright 2020 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.api.raw
+
+import org.matrix.android.sdk.api.MatrixCallback
+import org.matrix.android.sdk.api.util.Cancelable
+
+/**
+ * Useful methods to fetch raw data from the server. The access token will not be used to fetched the data
+ */
+interface RawService {
+    /**
+     * Get a URL, either from cache or from the remote server, depending on the cache strategy
+     */
+    fun getUrl(url: String,
+               rawCacheStrategy: RawCacheStrategy,
+               matrixCallback: MatrixCallback<String>): Cancelable
+
+    /**
+     * Specific case for the well-known file. Cache validity is 8 hours
+     */
+    fun getWellknown(userId: String, matrixCallback: MatrixCallback<String>): Cancelable
+
+    /**
+     * Clear all the cache data
+     */
+    fun clearCache(matrixCallback: MatrixCallback<Unit>): Cancelable
+}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/Session.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/Session.kt
index cfddf73363cda80472e784ed55632794553819ff..4dfc24ddaeb6300c50a1e4056d3e675b3ce0dbf2 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/Session.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/Session.kt
@@ -110,7 +110,7 @@ interface Session :
      * This does not work in doze mode :/
      * If battery optimization is on it can work in app standby but that's all :/
      */
-    fun startAutomaticBackgroundSync(repeatDelay: Long = 30_000L)
+    fun startAutomaticBackgroundSync(timeOutInSeconds: Long, repeatDelayInSeconds: Long)
 
     fun stopAnyBackgroundSync()
 
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/homeserver/HomeServerCapabilities.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/homeserver/HomeServerCapabilities.kt
index de7ac45bf364943e338b043567719ceb21761e11..e12d99d6b8f980469bebc44d2e8fdf98e01aa6b4 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/homeserver/HomeServerCapabilities.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/homeserver/HomeServerCapabilities.kt
@@ -33,16 +33,7 @@ data class HomeServerCapabilities(
         /**
          * Default identity server url, provided in Wellknown
          */
-        val defaultIdentityServerUrl: String? = null,
-        /**
-         * Option to allow homeserver admins to set the default E2EE behaviour back to disabled for DMs / private rooms
-         * (as it was before) for various environments where this is desired.
-         */
-        val adminE2EByDefault: Boolean = true,
-        /**
-         * Preferred Jitsi domain, provided in Wellknown
-         */
-        val preferredJitsiDomain: String? = null
+        val defaultIdentityServerUrl: String? = null
 ) {
     companion object {
         const val MAX_UPLOAD_FILE_SIZE_UNKNOWN = -1L
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/summary/RoomSummaryConstants.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/summary/RoomSummaryConstants.kt
new file mode 100644
index 0000000000000000000000000000000000000000..2b0132817d3033d794b7ed00e85c6ee0d85adc58
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/summary/RoomSummaryConstants.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2020 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 org.matrix.android.sdk.api.session.room.summary
+
+import org.matrix.android.sdk.api.session.events.model.EventType
+
+object RoomSummaryConstants {
+
+    val PREVIEWABLE_TYPES = listOf(
+            // TODO filter message type (KEY_VERIFICATION_READY, etc.)
+            EventType.MESSAGE,
+            EventType.CALL_INVITE,
+            EventType.CALL_HANGUP,
+            EventType.CALL_ANSWER,
+            EventType.ENCRYPTED,
+            EventType.STICKER,
+            EventType.REACTION
+    )
+}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/timeline/TimelineEventFilters.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/timeline/TimelineEventFilters.kt
new file mode 100644
index 0000000000000000000000000000000000000000..eccc46b5d84261d1c47b282f8d071f9568fbad78
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/timeline/TimelineEventFilters.kt
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2020 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 org.matrix.android.sdk.api.session.room.timeline
+
+data class TimelineEventFilters(
+        /**
+         * A flag to filter edit events
+         */
+        val filterEdits: Boolean = false,
+        /**
+         * A flag to filter redacted events
+         */
+        val filterRedacted: Boolean = false,
+        /**
+         * A flag to filter useless events, such as membership events without any change
+         */
+        val filterUseless: Boolean = false,
+        /**
+         * A flag to filter by types. It should be used with [allowedTypes] field
+         */
+        val filterTypes: Boolean = false,
+        /**
+         * If [filterTypes] is true, the list of types allowed by the list.
+         */
+        val allowedTypes: List<String> = emptyList()
+)
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/timeline/TimelineSettings.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/timeline/TimelineSettings.kt
index 4f915cb907037b11e82ff3da9926dba6715cf414..ab98208eedeed7123b023b03b9e65a2c42d9181b 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/timeline/TimelineSettings.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/timeline/TimelineSettings.kt
@@ -26,25 +26,9 @@ data class TimelineSettings(
          */
         val initialSize: Int,
         /**
-         * A flag to filter edit events
+         * Filters for timeline event
          */
-        val filterEdits: Boolean = false,
-        /**
-         * A flag to filter redacted events
-         */
-        val filterRedacted: Boolean = false,
-        /**
-         * A flag to filter useless events, such as membership events without any change
-         */
-        val filterUseless: Boolean = false,
-        /**
-         * A flag to filter by types. It should be used with [allowedTypes] field
-         */
-        val filterTypes: Boolean = false,
-        /**
-         * If [filterTypes] is true, the list of types allowed by the list.
-         */
-        val allowedTypes: List<String> = emptyList(),
+        val filters: TimelineEventFilters = TimelineEventFilters(),
         /**
          * If true, will build read receipts for each event.
          */
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmInstanceWrapper.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmInstanceWrapper.kt
new file mode 100644
index 0000000000000000000000000000000000000000..e2ddbcbca8b4f7812fcc65aef3462eda69e28ae0
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmInstanceWrapper.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2020 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 org.matrix.android.sdk.internal.database
+
+import io.realm.Realm
+import java.io.Closeable
+
+internal class RealmInstanceWrapper(private val realm: Realm, private val closeRealmOnClose: Boolean) : Closeable {
+
+    override fun close() {
+        if (closeRealmOnClose) {
+            realm.close()
+        }
+    }
+
+    fun <R> withRealm(block: (Realm) -> R): R {
+        return use {
+            block(it.realm)
+        }
+    }
+}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionProvider.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionProvider.kt
new file mode 100644
index 0000000000000000000000000000000000000000..a7f934ffc0cb7685c1962ff429e7fa30041693f6
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionProvider.kt
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2020 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 org.matrix.android.sdk.internal.database
+
+import android.os.Looper
+import androidx.annotation.MainThread
+import com.zhuinden.monarchy.Monarchy
+import io.realm.Realm
+import org.matrix.android.sdk.internal.di.SessionDatabase
+import org.matrix.android.sdk.internal.session.SessionLifecycleObserver
+import org.matrix.android.sdk.internal.session.SessionScope
+import javax.inject.Inject
+import kotlin.concurrent.getOrSet
+
+/**
+ * This class keeps an instance of realm open in the main thread so you can grab it whenever you want to get a realm
+ * instance. This does check each time if you are on the main thread or not and returns the appropriate realm instance.
+ */
+@SessionScope
+internal class RealmSessionProvider @Inject constructor(@SessionDatabase private val monarchy: Monarchy)
+    : SessionLifecycleObserver {
+
+    private val realmThreadLocal = ThreadLocal<Realm>()
+
+    /**
+     * Allow you to execute a block with an opened realm. It automatically closes it if necessary (ie. when not in main thread)
+     */
+    fun <R> withRealm(block: (Realm) -> R): R {
+        return getRealmWrapper().withRealm(block)
+    }
+
+    @MainThread
+    override fun onStart() {
+        realmThreadLocal.getOrSet {
+            Realm.getInstance(monarchy.realmConfiguration)
+        }
+    }
+
+    @MainThread
+    override fun onStop() {
+        realmThreadLocal.get()?.close()
+        realmThreadLocal.remove()
+    }
+
+    private fun getRealmWrapper(): RealmInstanceWrapper {
+        val isOnMainThread = isOnMainThread()
+        val realm = if (isOnMainThread) {
+            realmThreadLocal.getOrSet {
+                Realm.getInstance(monarchy.realmConfiguration)
+            }
+        } else {
+            Realm.getInstance(monarchy.realmConfiguration)
+        }
+        return RealmInstanceWrapper(realm, closeRealmOnClose = !isOnMainThread)
+    }
+
+    private fun isOnMainThread() = Looper.myLooper() == Looper.getMainLooper()
+}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionStoreMigration.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionStoreMigration.kt
index ad05406aa0bd9dfa03cbab06e405128206dd12bd..26ce38e322b8d2571be26b4127991d21951a89ef 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionStoreMigration.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionStoreMigration.kt
@@ -28,7 +28,7 @@ import javax.inject.Inject
 class RealmSessionStoreMigration @Inject constructor() : RealmMigration {
 
     companion object {
-        const val SESSION_STORE_SCHEMA_VERSION = 4L
+        const val SESSION_STORE_SCHEMA_VERSION = 5L
     }
 
     override fun migrate(realm: DynamicRealm, oldVersion: Long, newVersion: Long) {
@@ -38,6 +38,7 @@ class RealmSessionStoreMigration @Inject constructor() : RealmMigration {
         if (oldVersion <= 1) migrateTo2(realm)
         if (oldVersion <= 2) migrateTo3(realm)
         if (oldVersion <= 3) migrateTo4(realm)
+        if (oldVersion <= 4) migrateTo5(realm)
     }
 
     private fun migrateTo1(realm: DynamicRealm) {
@@ -54,16 +55,16 @@ class RealmSessionStoreMigration @Inject constructor() : RealmMigration {
     private fun migrateTo2(realm: DynamicRealm) {
         Timber.d("Step 1 -> 2")
         realm.schema.get("HomeServerCapabilitiesEntity")
-                ?.addField(HomeServerCapabilitiesEntityFields.ADMIN_E2_E_BY_DEFAULT, Boolean::class.java)
+                ?.addField("adminE2EByDefault", Boolean::class.java)
                 ?.transform { obj ->
-                    obj.setBoolean(HomeServerCapabilitiesEntityFields.ADMIN_E2_E_BY_DEFAULT, true)
+                    obj.setBoolean("adminE2EByDefault", true)
                 }
     }
 
     private fun migrateTo3(realm: DynamicRealm) {
         Timber.d("Step 2 -> 3")
         realm.schema.get("HomeServerCapabilitiesEntity")
-                ?.addField(HomeServerCapabilitiesEntityFields.PREFERRED_JITSI_DOMAIN, String::class.java)
+                ?.addField("preferredJitsiDomain", String::class.java)
                 ?.transform { obj ->
                     // Schedule a refresh of the capabilities
                     obj.setLong(HomeServerCapabilitiesEntityFields.LAST_UPDATED_TIMESTAMP, 0)
@@ -82,4 +83,11 @@ class RealmSessionStoreMigration @Inject constructor() : RealmMigration {
                 .setRequired(PendingThreePidEntityFields.SID, true)
                 .addField(PendingThreePidEntityFields.SUBMIT_URL, String::class.java)
     }
+
+    private fun migrateTo5(realm: DynamicRealm) {
+        Timber.d("Step 4 -> 5")
+        realm.schema.get("HomeServerCapabilitiesEntity")
+                ?.removeField("adminE2EByDefault")
+                ?.removeField("preferredJitsiDomain")
+    }
 }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/mapper/HomeServerCapabilitiesMapper.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/mapper/HomeServerCapabilitiesMapper.kt
index e5de271d9328b7b9c87c39fdf5ad986445a451fa..4eb9b4b47f2ee2d6537dfde5a9190bfd084de967 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/mapper/HomeServerCapabilitiesMapper.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/mapper/HomeServerCapabilitiesMapper.kt
@@ -30,9 +30,7 @@ internal object HomeServerCapabilitiesMapper {
                 canChangePassword = entity.canChangePassword,
                 maxUploadFileSize = entity.maxUploadFileSize,
                 lastVersionIdentityServerSupported = entity.lastVersionIdentityServerSupported,
-                defaultIdentityServerUrl = entity.defaultIdentityServerUrl,
-                adminE2EByDefault = entity.adminE2EByDefault,
-                preferredJitsiDomain = entity.preferredJitsiDomain
+                defaultIdentityServerUrl = entity.defaultIdentityServerUrl
         )
     }
 }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/mapper/ReadReceiptsSummaryMapper.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/mapper/ReadReceiptsSummaryMapper.kt
index 188ca4937c47d758f8e8f709619bfd966d561bce..6b9c0e7a45277fe28d4d60366580d1cc4e6e521e 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/mapper/ReadReceiptsSummaryMapper.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/mapper/ReadReceiptsSummaryMapper.kt
@@ -18,26 +18,24 @@
 package org.matrix.android.sdk.internal.database.mapper
 
 import org.matrix.android.sdk.api.session.room.model.ReadReceipt
+import org.matrix.android.sdk.internal.database.RealmSessionProvider
 import org.matrix.android.sdk.internal.database.model.ReadReceiptsSummaryEntity
 import org.matrix.android.sdk.internal.database.model.UserEntity
 import org.matrix.android.sdk.internal.database.query.where
-import org.matrix.android.sdk.internal.di.SessionDatabase
-import io.realm.Realm
-import io.realm.RealmConfiguration
 import javax.inject.Inject
 
-internal class ReadReceiptsSummaryMapper @Inject constructor(@SessionDatabase private val realmConfiguration: RealmConfiguration) {
+internal class ReadReceiptsSummaryMapper @Inject constructor(private val realmSessionProvider: RealmSessionProvider) {
 
     fun map(readReceiptsSummaryEntity: ReadReceiptsSummaryEntity?): List<ReadReceipt> {
         if (readReceiptsSummaryEntity == null) {
             return emptyList()
         }
-        return Realm.getInstance(realmConfiguration).use { realm ->
+        return realmSessionProvider.withRealm { realm ->
             val readReceipts = readReceiptsSummaryEntity.readReceipts
             readReceipts
                     .mapNotNull {
                         val user = UserEntity.where(realm, it.userId).findFirst()
-                                   ?: return@mapNotNull null
+                                ?: return@mapNotNull null
                         ReadReceipt(user.asDomain(), it.originServerTs.toLong())
                     }
         }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/HomeServerCapabilitiesEntity.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/HomeServerCapabilitiesEntity.kt
index 7e3af69436e2355d79f200dd5bf4d16c76c9954b..a905dc9535f32a44b9ff861f2130698cc7a4e749 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/HomeServerCapabilitiesEntity.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/HomeServerCapabilitiesEntity.kt
@@ -17,17 +17,15 @@
 
 package org.matrix.android.sdk.internal.database.model
 
-import org.matrix.android.sdk.api.session.homeserver.HomeServerCapabilities
 import io.realm.RealmObject
+import org.matrix.android.sdk.api.session.homeserver.HomeServerCapabilities
 
 internal open class HomeServerCapabilitiesEntity(
         var canChangePassword: Boolean = true,
         var maxUploadFileSize: Long = HomeServerCapabilities.MAX_UPLOAD_FILE_SIZE_UNKNOWN,
         var lastVersionIdentityServerSupported: Boolean = false,
         var defaultIdentityServerUrl: String? = null,
-        var adminE2EByDefault: Boolean = true,
-        var lastUpdatedTimestamp: Long = 0L,
-        var preferredJitsiDomain: String? = null
+        var lastUpdatedTimestamp: Long = 0L
 ) : RealmObject() {
 
     companion object
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/RawCacheEntity.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/RawCacheEntity.kt
new file mode 100644
index 0000000000000000000000000000000000000000..3c0a280476d78042352a959fd0b98eefe1504c60
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/RawCacheEntity.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2019 New Vector Ltd
+ * Copyright 2020 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.database.model
+
+import io.realm.RealmObject
+import io.realm.annotations.PrimaryKey
+
+internal open class RawCacheEntity(
+        @PrimaryKey
+        var url: String = "",
+        var data: String = "",
+        var lastUpdatedTimestamp: Long = 0L
+) : RealmObject() {
+
+    companion object
+}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/RawCacheQueries.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/RawCacheQueries.kt
new file mode 100644
index 0000000000000000000000000000000000000000..93753ff24b525fdcfcb8afc177d93fa47a9a4cb8
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/RawCacheQueries.kt
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2019 New Vector Ltd
+ * Copyright 2020 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.database.query
+
+import io.realm.Realm
+import io.realm.kotlin.createObject
+import io.realm.kotlin.where
+import org.matrix.android.sdk.internal.database.model.RawCacheEntity
+import org.matrix.android.sdk.internal.database.model.RawCacheEntityFields
+
+/**
+ * Get the current RawCacheEntity, return null if it does not exist
+ */
+internal fun RawCacheEntity.Companion.get(realm: Realm, url: String): RawCacheEntity? {
+    return realm.where<RawCacheEntity>()
+            .equalTo(RawCacheEntityFields.URL, url)
+            .findFirst()
+}
+
+/**
+ * Get the current RawCacheEntity, create one if it does not exist
+ */
+internal fun RawCacheEntity.Companion.getOrCreate(realm: Realm, url: String): RawCacheEntity {
+    return get(realm, url) ?: realm.createObject(url)
+}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/TimelineEventEntityQueries.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/TimelineEventEntityQueries.kt
index 83075a192ca925cb40fefc6fa6231a87e889f9b2..d49b64c4328daec7e5fdc601618eb1ab0fce274e 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/TimelineEventEntityQueries.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/TimelineEventEntityQueries.kt
@@ -17,17 +17,18 @@
 
 package org.matrix.android.sdk.internal.database.query
 
-import org.matrix.android.sdk.api.session.room.send.SendState
-import org.matrix.android.sdk.internal.database.model.ChunkEntity
-import org.matrix.android.sdk.internal.database.model.RoomEntity
-import org.matrix.android.sdk.internal.database.model.TimelineEventEntity
-import org.matrix.android.sdk.internal.database.model.TimelineEventEntityFields
 import io.realm.Realm
 import io.realm.RealmList
 import io.realm.RealmQuery
 import io.realm.RealmResults
 import io.realm.Sort
 import io.realm.kotlin.where
+import org.matrix.android.sdk.api.session.room.send.SendState
+import org.matrix.android.sdk.api.session.room.timeline.TimelineEventFilters
+import org.matrix.android.sdk.internal.database.model.ChunkEntity
+import org.matrix.android.sdk.internal.database.model.RoomEntity
+import org.matrix.android.sdk.internal.database.model.TimelineEventEntity
+import org.matrix.android.sdk.internal.database.model.TimelineEventEntityFields
 
 internal fun TimelineEventEntity.Companion.where(realm: Realm, roomId: String, eventId: String): RealmQuery<TimelineEventEntity> {
     return realm.where<TimelineEventEntity>()
@@ -56,16 +57,10 @@ internal fun TimelineEventEntity.Companion.findWithSenderMembershipEvent(realm:
 internal fun TimelineEventEntity.Companion.latestEvent(realm: Realm,
                                                        roomId: String,
                                                        includesSending: Boolean,
-                                                       filterContentRelation: Boolean = false,
-                                                       filterTypes: List<String> = emptyList()): TimelineEventEntity? {
+                                                       filters: TimelineEventFilters = TimelineEventFilters()): TimelineEventEntity? {
     val roomEntity = RoomEntity.where(realm, roomId).findFirst() ?: return null
-    val sendingTimelineEvents = roomEntity.sendingTimelineEvents.where().filterTypes(filterTypes)
-    val liveEvents = ChunkEntity.findLastForwardChunkOfRoom(realm, roomId)?.timelineEvents?.where()?.filterTypes(filterTypes)
-    if (filterContentRelation) {
-        liveEvents
-                ?.not()?.like(TimelineEventEntityFields.ROOT.CONTENT, TimelineEventFilter.Content.EDIT)
-                ?.not()?.like(TimelineEventEntityFields.ROOT.CONTENT, TimelineEventFilter.Content.RESPONSE)
-    }
+    val sendingTimelineEvents = roomEntity.sendingTimelineEvents.where().filterEvents(filters)
+    val liveEvents = ChunkEntity.findLastForwardChunkOfRoom(realm, roomId)?.timelineEvents?.where()?.filterEvents(filters)
     val query = if (includesSending && sendingTimelineEvents.findAll().isNotEmpty()) {
         sendingTimelineEvents
     } else {
@@ -76,6 +71,24 @@ internal fun TimelineEventEntity.Companion.latestEvent(realm: Realm,
             ?.findFirst()
 }
 
+internal fun RealmQuery<TimelineEventEntity>.filterEvents(filters: TimelineEventFilters): RealmQuery<TimelineEventEntity> {
+    if (filters.filterTypes) {
+        `in`(TimelineEventEntityFields.ROOT.TYPE, filters.allowedTypes.toTypedArray())
+    }
+    if (filters.filterUseless) {
+        not()
+                .equalTo(TimelineEventEntityFields.ROOT.IS_USELESS, true)
+    }
+    if (filters.filterEdits) {
+        not().like(TimelineEventEntityFields.ROOT.CONTENT, TimelineEventFilter.Content.EDIT)
+        not().like(TimelineEventEntityFields.ROOT.CONTENT, TimelineEventFilter.Content.RESPONSE)
+    }
+    if (filters.filterRedacted) {
+        not().like(TimelineEventEntityFields.ROOT.UNSIGNED_DATA, TimelineEventFilter.Unsigned.REDACTED)
+    }
+    return this
+}
+
 internal fun RealmQuery<TimelineEventEntity>.filterTypes(filterTypes: List<String>): RealmQuery<TimelineEventEntity> {
     return if (filterTypes.isEmpty()) {
         this
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/DbQualifiers.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/DbQualifiers.kt
index 9442dc48653c2cb80a380601f1fd5d6df7c95b31..2380ea68b8d75cb08088d85e279b2e4a8b740528 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/DbQualifiers.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/DbQualifiers.kt
@@ -23,6 +23,10 @@ import javax.inject.Qualifier
 @Retention(AnnotationRetention.RUNTIME)
 internal annotation class AuthDatabase
 
+@Qualifier
+@Retention(AnnotationRetention.RUNTIME)
+internal annotation class GlobalDatabase
+
 @Qualifier
 @Retention(AnnotationRetention.RUNTIME)
 internal annotation class SessionDatabase
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/MatrixComponent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/MatrixComponent.kt
index 816a674d8184f75b11acf06feaae3adb5db6007c..e51d8f3ad32fec699211c2d550a8903fdf1802c9 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/MatrixComponent.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/MatrixComponent.kt
@@ -22,22 +22,30 @@ import android.content.res.Resources
 import com.squareup.moshi.Moshi
 import dagger.BindsInstance
 import dagger.Component
+import okhttp3.OkHttpClient
 import org.matrix.android.sdk.api.Matrix
 import org.matrix.android.sdk.api.MatrixConfiguration
 import org.matrix.android.sdk.api.auth.AuthenticationService
+import org.matrix.android.sdk.api.raw.RawService
 import org.matrix.android.sdk.internal.SessionManager
 import org.matrix.android.sdk.internal.auth.AuthModule
 import org.matrix.android.sdk.internal.auth.SessionParamsStore
+import org.matrix.android.sdk.internal.raw.RawModule
 import org.matrix.android.sdk.internal.session.MockHttpInterceptor
 import org.matrix.android.sdk.internal.session.TestInterceptor
 import org.matrix.android.sdk.internal.task.TaskExecutor
 import org.matrix.android.sdk.internal.util.BackgroundDetectionObserver
 import org.matrix.android.sdk.internal.util.MatrixCoroutineDispatchers
-import okhttp3.OkHttpClient
 import org.matrix.olm.OlmManager
 import java.io.File
 
-@Component(modules = [MatrixModule::class, NetworkModule::class, AuthModule::class, NoOpTestModule::class])
+@Component(modules = [
+    MatrixModule::class,
+    NetworkModule::class,
+    AuthModule::class,
+    RawModule::class,
+    NoOpTestModule::class
+])
 @MatrixScope
 internal interface MatrixComponent {
 
@@ -53,6 +61,8 @@ internal interface MatrixComponent {
 
     fun authenticationService(): AuthenticationService
 
+    fun rawService(): RawService
+
     fun context(): Context
 
     fun matrixConfiguration(): MatrixConfiguration
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/raw/DefaultCleanRawCacheTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/raw/DefaultCleanRawCacheTask.kt
new file mode 100644
index 0000000000000000000000000000000000000000..7ab664524420396973c6f051c57e6bb65b6a888c
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/raw/DefaultCleanRawCacheTask.kt
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2019 New Vector Ltd
+ * Copyright 2020 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.raw
+
+import com.zhuinden.monarchy.Monarchy
+import io.realm.kotlin.where
+import org.matrix.android.sdk.internal.database.model.RawCacheEntity
+import org.matrix.android.sdk.internal.di.GlobalDatabase
+import org.matrix.android.sdk.internal.task.Task
+import org.matrix.android.sdk.internal.util.awaitTransaction
+import javax.inject.Inject
+
+internal interface CleanRawCacheTask : Task<Unit, Unit>
+
+internal class DefaultCleanRawCacheTask @Inject constructor(
+        @GlobalDatabase private val monarchy: Monarchy
+) : CleanRawCacheTask {
+
+    override suspend fun execute(params: Unit) {
+        monarchy.awaitTransaction { realm ->
+            realm.where<RawCacheEntity>()
+                    .findAll()
+                    .deleteAllFromRealm()
+        }
+    }
+}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/raw/DefaultGetUrlTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/raw/DefaultGetUrlTask.kt
new file mode 100644
index 0000000000000000000000000000000000000000..1733abccd4a5c94377de53b3fbe84ac1e1f6bfd0
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/raw/DefaultGetUrlTask.kt
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2019 New Vector Ltd
+ * Copyright 2020 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.raw
+
+import com.zhuinden.monarchy.Monarchy
+import okhttp3.ResponseBody
+import org.matrix.android.sdk.api.raw.RawCacheStrategy
+import org.matrix.android.sdk.internal.database.model.RawCacheEntity
+import org.matrix.android.sdk.internal.database.query.get
+import org.matrix.android.sdk.internal.database.query.getOrCreate
+import org.matrix.android.sdk.internal.di.GlobalDatabase
+import org.matrix.android.sdk.internal.network.executeRequest
+import org.matrix.android.sdk.internal.task.Task
+import org.matrix.android.sdk.internal.util.awaitTransaction
+import java.util.Date
+import javax.inject.Inject
+
+internal interface GetUrlTask : Task<GetUrlTask.Params, String> {
+    data class Params(
+            val url: String,
+            val rawCacheStrategy: RawCacheStrategy
+    )
+}
+
+internal class DefaultGetUrlTask @Inject constructor(
+        private val rawAPI: RawAPI,
+        @GlobalDatabase private val monarchy: Monarchy
+) : GetUrlTask {
+
+    override suspend fun execute(params: GetUrlTask.Params): String {
+        return when (params.rawCacheStrategy) {
+            RawCacheStrategy.NoCache -> doRequest(params.url)
+            is RawCacheStrategy.TtlCache -> doRequestWithCache(
+                    params.url,
+                    params.rawCacheStrategy.validityDurationInMillis,
+                    params.rawCacheStrategy.strict
+            )
+            RawCacheStrategy.InfiniteCache -> doRequestWithCache(
+                    params.url,
+                    Long.MAX_VALUE,
+                    true
+            )
+        }
+    }
+
+    private suspend fun doRequest(url: String): String {
+        return executeRequest<ResponseBody>(null) {
+            apiCall = rawAPI.getUrl(url)
+        }
+                .string()
+    }
+
+    private suspend fun doRequestWithCache(url: String, validityDurationInMillis: Long, strict: Boolean): String {
+        // Get data from cache
+        var dataFromCache: String? = null
+        var isCacheValid = false
+        monarchy.doWithRealm { realm ->
+            val entity = RawCacheEntity.get(realm, url)
+            dataFromCache = entity?.data
+            isCacheValid = entity != null && Date().time < entity.lastUpdatedTimestamp + validityDurationInMillis
+        }
+
+        if (dataFromCache != null && isCacheValid) {
+            return dataFromCache as String
+        }
+
+        // No cache or outdated cache
+        val data = try {
+            doRequest(url)
+        } catch (throwable: Throwable) {
+            // In case of error, we can return value from cache even if outdated
+            return dataFromCache
+                    ?.takeIf { !strict }
+                    ?: throw throwable
+        }
+
+        // Store cache
+        monarchy.awaitTransaction { realm ->
+            val rawCacheEntity = RawCacheEntity.getOrCreate(realm, url)
+            rawCacheEntity.data = data
+            rawCacheEntity.lastUpdatedTimestamp = Date().time
+        }
+
+        return data
+    }
+}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/raw/DefaultRawService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/raw/DefaultRawService.kt
new file mode 100644
index 0000000000000000000000000000000000000000..792a0b3aa757eff486f2178a68996537d6ad56b1
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/raw/DefaultRawService.kt
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2020 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 org.matrix.android.sdk.internal.raw
+
+import org.matrix.android.sdk.api.MatrixCallback
+import org.matrix.android.sdk.api.raw.RawCacheStrategy
+import org.matrix.android.sdk.api.raw.RawService
+import org.matrix.android.sdk.api.util.Cancelable
+import org.matrix.android.sdk.internal.task.TaskExecutor
+import org.matrix.android.sdk.internal.task.configureWith
+import java.util.concurrent.TimeUnit
+import javax.inject.Inject
+
+internal class DefaultRawService @Inject constructor(
+        private val taskExecutor: TaskExecutor,
+        private val getUrlTask: GetUrlTask,
+        private val cleanRawCacheTask: CleanRawCacheTask
+) : RawService {
+    override fun getUrl(url: String,
+                        rawCacheStrategy: RawCacheStrategy,
+                        matrixCallback: MatrixCallback<String>): Cancelable {
+        return getUrlTask
+                .configureWith(GetUrlTask.Params(url, rawCacheStrategy)) {
+                    callback = matrixCallback
+                }
+                .executeBy(taskExecutor)
+    }
+
+    override fun getWellknown(userId: String,
+                              matrixCallback: MatrixCallback<String>): Cancelable {
+        val homeServerDomain = userId.substringAfter(":")
+        return getUrl(
+                "https://$homeServerDomain/.well-known/matrix/client",
+                RawCacheStrategy.TtlCache(TimeUnit.HOURS.toMillis(8), false),
+                matrixCallback
+        )
+    }
+
+    override fun clearCache(matrixCallback: MatrixCallback<Unit>): Cancelable {
+        return cleanRawCacheTask
+                .configureWith(Unit) {
+                    callback = matrixCallback
+                }
+                .executeBy(taskExecutor)
+    }
+}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/raw/GlobalRealmModule.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/raw/GlobalRealmModule.kt
new file mode 100644
index 0000000000000000000000000000000000000000..4df5edae884af5a48415912f8c7a702618ce9084
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/raw/GlobalRealmModule.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2019 New Vector Ltd
+ * Copyright 2020 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.raw
+
+import io.realm.annotations.RealmModule
+import org.matrix.android.sdk.internal.database.model.RawCacheEntity
+
+/**
+ * Realm module for global classes
+ */
+@RealmModule(library = true,
+        classes = [
+            RawCacheEntity::class
+        ])
+internal class GlobalRealmModule
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/raw/RawAPI.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/raw/RawAPI.kt
new file mode 100644
index 0000000000000000000000000000000000000000..f7aa738e90dda8c59b36a909b4398c4eda5257b8
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/raw/RawAPI.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2020 New Vector Ltd
+ * Copyright 2020 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.raw
+
+import okhttp3.ResponseBody
+import retrofit2.Call
+import retrofit2.http.GET
+import retrofit2.http.Url
+
+internal interface RawAPI {
+    @GET
+    fun getUrl(@Url url: String): Call<ResponseBody>
+}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/raw/RawModule.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/raw/RawModule.kt
new file mode 100644
index 0000000000000000000000000000000000000000..95de057f049c6fa5640680783d8d871f791d24ce
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/raw/RawModule.kt
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2020 New Vector Ltd
+ * Copyright 2020 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.raw
+
+import com.zhuinden.monarchy.Monarchy
+import dagger.Binds
+import dagger.Lazy
+import dagger.Module
+import dagger.Provides
+import io.realm.RealmConfiguration
+import okhttp3.OkHttpClient
+import org.matrix.android.sdk.api.raw.RawService
+import org.matrix.android.sdk.internal.database.RealmKeysUtils
+import org.matrix.android.sdk.internal.di.GlobalDatabase
+import org.matrix.android.sdk.internal.di.MatrixScope
+import org.matrix.android.sdk.internal.di.Unauthenticated
+import org.matrix.android.sdk.internal.network.RetrofitFactory
+
+@Module
+internal abstract class RawModule {
+
+    @Module
+    companion object {
+        private const val DB_ALIAS = "matrix-sdk-global"
+
+        @JvmStatic
+        @Provides
+        @GlobalDatabase
+        fun providesMonarchy(@GlobalDatabase realmConfiguration: RealmConfiguration): Monarchy {
+            return Monarchy.Builder()
+                    .setRealmConfiguration(realmConfiguration)
+                    .build()
+        }
+
+        @JvmStatic
+        @Provides
+        @GlobalDatabase
+        @MatrixScope
+        fun providesRealmConfiguration(realmKeysUtils: RealmKeysUtils): RealmConfiguration {
+            return RealmConfiguration.Builder()
+                    .apply {
+                        realmKeysUtils.configureEncryption(this, DB_ALIAS)
+                    }
+                    .name("matrix-sdk-global.realm")
+                    .modules(GlobalRealmModule())
+                    .build()
+        }
+
+        @Provides
+        @JvmStatic
+        fun providesRawAPI(@Unauthenticated okHttpClient: Lazy<OkHttpClient>,
+                           retrofitFactory: RetrofitFactory): RawAPI {
+            return retrofitFactory.create(okHttpClient, "https://example.org").create(RawAPI::class.java)
+        }
+    }
+
+    @Binds
+    abstract fun bindRawService(service: DefaultRawService): RawService
+
+    @Binds
+    abstract fun bindGetUrlTask(task: DefaultGetUrlTask): GetUrlTask
+
+    @Binds
+    abstract fun bindCleanRawCacheTask(task: DefaultCleanRawCacheTask): CleanRawCacheTask
+}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/DefaultFileService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/DefaultFileService.kt
index aa4114c8c2899221d81a71d374892249481a6cf6..a163cd48094fa3c894f393be91d832e2fb35c376 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/DefaultFileService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/DefaultFileService.kt
@@ -144,11 +144,13 @@ internal class DefaultFileService @Inject constructor(
 
                         if (elementToDecrypt != null) {
                             Timber.v("## FileService: decrypt file")
-                            val decryptSuccess = MXEncryptedAttachments.decryptAttachment(
-                                    source.inputStream(),
-                                    elementToDecrypt,
-                                    destFile.outputStream().buffered()
-                            )
+                            val decryptSuccess = destFile.outputStream().buffered().use {
+                                MXEncryptedAttachments.decryptAttachment(
+                                        source.inputStream(),
+                                        elementToDecrypt,
+                                        it
+                                )
+                            }
                             response.close()
                             if (!decryptSuccess) {
                                 return@flatMap Try.Failure(IllegalStateException("Decryption error"))
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/DefaultSession.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/DefaultSession.kt
index f8ba6259478c0a708adea3ad23dc107cd8d3a2fa..004c5afe8f87ba83bc4879f1738b2c673e4aef60 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/DefaultSession.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/DefaultSession.kt
@@ -166,8 +166,8 @@ internal class DefaultSession @Inject constructor(
         SyncWorker.requireBackgroundSync(workManagerProvider, sessionId)
     }
 
-    override fun startAutomaticBackgroundSync(repeatDelay: Long) {
-        SyncWorker.automaticallyBackgroundSync(workManagerProvider, sessionId, 0, repeatDelay)
+    override fun startAutomaticBackgroundSync(timeOutInSeconds: Long, repeatDelayInSeconds: Long) {
+        SyncWorker.automaticallyBackgroundSync(workManagerProvider, sessionId, timeOutInSeconds, repeatDelayInSeconds)
     }
 
     override fun stopAnyBackgroundSync() {
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionModule.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionModule.kt
index d404cecc51f214089d1434c98739266e5421a513..5397b8d9bd15ac2cfd06cf39369e6b39564b3674 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionModule.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionModule.kt
@@ -47,6 +47,7 @@ import org.matrix.android.sdk.internal.crypto.secrets.DefaultSharedSecretStorage
 import org.matrix.android.sdk.internal.crypto.verification.VerificationMessageProcessor
 import org.matrix.android.sdk.internal.database.DatabaseCleaner
 import org.matrix.android.sdk.internal.database.EventInsertLiveObserver
+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.DeviceId
@@ -325,23 +326,27 @@ internal abstract class SessionModule {
 
     @Binds
     @IntoSet
-    abstract fun bindIntegrationManager(observer: IntegrationManager): SessionLifecycleObserver
+    abstract fun bindIntegrationManager(manager: IntegrationManager): SessionLifecycleObserver
 
     @Binds
     @IntoSet
-    abstract fun bindWidgetUrlFormatter(observer: DefaultWidgetURLFormatter): SessionLifecycleObserver
+    abstract fun bindWidgetUrlFormatter(formatter: DefaultWidgetURLFormatter): SessionLifecycleObserver
 
     @Binds
     @IntoSet
-    abstract fun bindShieldTrustUpdated(observer: ShieldTrustUpdater): SessionLifecycleObserver
+    abstract fun bindShieldTrustUpdated(updater: ShieldTrustUpdater): SessionLifecycleObserver
 
     @Binds
     @IntoSet
-    abstract fun bindIdentityService(observer: DefaultIdentityService): SessionLifecycleObserver
+    abstract fun bindIdentityService(service: DefaultIdentityService): SessionLifecycleObserver
 
     @Binds
     @IntoSet
-    abstract fun bindDatabaseCleaner(observer: DatabaseCleaner): SessionLifecycleObserver
+    abstract fun bindDatabaseCleaner(cleaner: DatabaseCleaner): SessionLifecycleObserver
+
+    @Binds
+    @IntoSet
+    abstract fun bindRealmSessionProvider(provider: RealmSessionProvider): SessionLifecycleObserver
 
     @Binds
     abstract fun bindInitialSyncProgressService(service: DefaultInitialSyncProgressService): InitialSyncProgressService
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/DefaultGetHomeServerCapabilitiesTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/DefaultGetHomeServerCapabilitiesTask.kt
index 13ce84cf902577e3fabd08fb40268ab6cf82eddc..7e1ad600e32c2c3f3326af0fe5baa48110fce198 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/DefaultGetHomeServerCapabilitiesTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/DefaultGetHomeServerCapabilitiesTask.kt
@@ -18,6 +18,7 @@
 package org.matrix.android.sdk.internal.session.homeserver
 
 import com.zhuinden.monarchy.Monarchy
+import org.greenrobot.eventbus.EventBus
 import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig
 import org.matrix.android.sdk.api.auth.wellknown.WellknownResult
 import org.matrix.android.sdk.api.session.homeserver.HomeServerCapabilities
@@ -32,7 +33,6 @@ import org.matrix.android.sdk.internal.session.integrationmanager.IntegrationMan
 import org.matrix.android.sdk.internal.task.Task
 import org.matrix.android.sdk.internal.util.awaitTransaction
 import org.matrix.android.sdk.internal.wellknown.GetWellknownTask
-import org.greenrobot.eventbus.EventBus
 import timber.log.Timber
 import java.util.Date
 import javax.inject.Inject
@@ -109,16 +109,12 @@ internal class DefaultGetHomeServerCapabilitiesTask @Inject constructor(
 
             if (getWellknownResult != null && getWellknownResult is WellknownResult.Prompt) {
                 homeServerCapabilitiesEntity.defaultIdentityServerUrl = getWellknownResult.identityServerUrl
-                homeServerCapabilitiesEntity.adminE2EByDefault = getWellknownResult.wellKnown.e2eAdminSetting?.e2eDefault ?: true
-                homeServerCapabilitiesEntity.preferredJitsiDomain = getWellknownResult.wellKnown.jitsiServer?.preferredDomain
                 // We are also checking for integration manager configurations
                 val config = configExtractor.extract(getWellknownResult.wellKnown)
                 if (config != null) {
                     Timber.v("Extracted integration config : $config")
                     realm.insertOrUpdate(config)
                 }
-            } else {
-                homeServerCapabilitiesEntity.adminE2EByDefault = true
             }
             homeServerCapabilitiesEntity.lastUpdatedTimestamp = Date().time
         }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomGetter.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomGetter.kt
index 38dcad231167b82ab7c92317095059432461a3cf..985cf80e9727870221c257d4c6324e3b4120bb57 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomGetter.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomGetter.kt
@@ -17,17 +17,16 @@
 
 package org.matrix.android.sdk.internal.session.room
 
-import com.zhuinden.monarchy.Monarchy
+import io.realm.Realm
 import org.matrix.android.sdk.api.session.room.Room
 import org.matrix.android.sdk.api.session.room.model.Membership
+import org.matrix.android.sdk.internal.database.RealmSessionProvider
 import org.matrix.android.sdk.internal.database.model.RoomEntity
 import org.matrix.android.sdk.internal.database.model.RoomSummaryEntity
 import org.matrix.android.sdk.internal.database.model.RoomSummaryEntityFields
 import org.matrix.android.sdk.internal.database.query.where
-import org.matrix.android.sdk.internal.di.SessionDatabase
 import org.matrix.android.sdk.internal.session.SessionScope
 import org.matrix.android.sdk.internal.session.room.membership.RoomMemberHelper
-import io.realm.Realm
 import javax.inject.Inject
 
 internal interface RoomGetter {
@@ -38,18 +37,18 @@ internal interface RoomGetter {
 
 @SessionScope
 internal class DefaultRoomGetter @Inject constructor(
-        @SessionDatabase private val monarchy: Monarchy,
+        private val realmSessionProvider: RealmSessionProvider,
         private val roomFactory: RoomFactory
 ) : RoomGetter {
 
     override fun getRoom(roomId: String): Room? {
-        return Realm.getInstance(monarchy.realmConfiguration).use { realm ->
+        return realmSessionProvider.withRealm { realm ->
             createRoom(realm, roomId)
         }
     }
 
     override fun getDirectRoomWith(otherUserId: String): Room? {
-        return Realm.getInstance(monarchy.realmConfiguration).use { realm ->
+        return realmSessionProvider.withRealm { realm ->
             RoomSummaryEntity.where(realm)
                     .equalTo(RoomSummaryEntityFields.IS_DIRECT, true)
                     .equalTo(RoomSummaryEntityFields.MEMBERSHIP_STR, Membership.JOIN.name)
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/LocalEchoRepository.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/LocalEchoRepository.kt
index b3188883c052385ef6f250a30bf44657a91c0ca4..00c624a20dffb3edce15ba051ef2ea2143965188 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/LocalEchoRepository.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/LocalEchoRepository.kt
@@ -18,6 +18,7 @@
 package org.matrix.android.sdk.internal.session.room.send
 
 import com.zhuinden.monarchy.Monarchy
+import org.greenrobot.eventbus.EventBus
 import org.matrix.android.sdk.api.session.events.model.Content
 import org.matrix.android.sdk.api.session.events.model.Event
 import org.matrix.android.sdk.api.session.events.model.EventType
@@ -27,6 +28,7 @@ import org.matrix.android.sdk.api.session.room.model.message.MessageType
 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.crypto.MXEventDecryptionResult
+import org.matrix.android.sdk.internal.database.RealmSessionProvider
 import org.matrix.android.sdk.internal.database.helper.nextId
 import org.matrix.android.sdk.internal.database.mapper.ContentMapper
 import org.matrix.android.sdk.internal.database.mapper.TimelineEventMapper
@@ -43,12 +45,11 @@ import org.matrix.android.sdk.internal.session.room.membership.RoomMemberHelper
 import org.matrix.android.sdk.internal.session.room.summary.RoomSummaryUpdater
 import org.matrix.android.sdk.internal.session.room.timeline.DefaultTimeline
 import org.matrix.android.sdk.internal.util.awaitTransaction
-import io.realm.Realm
-import org.greenrobot.eventbus.EventBus
 import timber.log.Timber
 import javax.inject.Inject
 
 internal class LocalEchoRepository @Inject constructor(@SessionDatabase private val monarchy: Monarchy,
+                                                       private val realmSessionProvider: RealmSessionProvider,
                                                        private val roomSummaryUpdater: RoomSummaryUpdater,
                                                        private val eventBus: EventBus,
                                                        private val timelineEventMapper: TimelineEventMapper) {
@@ -59,7 +60,7 @@ internal class LocalEchoRepository @Inject constructor(@SessionDatabase private
         if (event.eventId == null) {
             throw IllegalStateException("You should have set an eventId for your event")
         }
-        val timelineEventEntity = Realm.getInstance(monarchy.realmConfiguration).use { realm ->
+        val timelineEventEntity = realmSessionProvider.withRealm { realm ->
             val eventEntity = event.toEntity(roomId, SendState.UNSENT, System.currentTimeMillis())
             val roomMemberHelper = RoomMemberHelper(realm, roomId)
             val myUser = roomMemberHelper.getLastRoomMember(senderId)
@@ -150,7 +151,7 @@ internal class LocalEchoRepository @Inject constructor(@SessionDatabase private
     }
 
     fun getAllEventsWithStates(roomId: String, states : List<SendState>): List<TimelineEvent> {
-        return Realm.getInstance(monarchy.realmConfiguration).use { realm ->
+        return realmSessionProvider.withRealm { realm ->
             TimelineEventEntity
                     .findAllInRoomWithSendStates(realm, roomId, states)
                     .sortedByDescending { it.displayIndex }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/state/StateEventDataSource.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/state/StateEventDataSource.kt
index e8dc2ddf4027486cde64cd2eb5ee05dc5b9dfa0e..65d30868d847a7580252202185d8f3267315b8e1 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/state/StateEventDataSource.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/state/StateEventDataSource.kt
@@ -20,24 +20,26 @@ package org.matrix.android.sdk.internal.session.room.state
 import androidx.lifecycle.LiveData
 import androidx.lifecycle.Transformations
 import com.zhuinden.monarchy.Monarchy
+import io.realm.Realm
+import io.realm.RealmQuery
+import io.realm.kotlin.where
 import org.matrix.android.sdk.api.query.QueryStringValue
 import org.matrix.android.sdk.api.session.events.model.Event
 import org.matrix.android.sdk.api.util.Optional
 import org.matrix.android.sdk.api.util.toOptional
+import org.matrix.android.sdk.internal.database.RealmSessionProvider
 import org.matrix.android.sdk.internal.database.mapper.asDomain
 import org.matrix.android.sdk.internal.database.model.CurrentStateEventEntity
 import org.matrix.android.sdk.internal.database.model.CurrentStateEventEntityFields
 import org.matrix.android.sdk.internal.di.SessionDatabase
 import org.matrix.android.sdk.internal.query.process
-import io.realm.Realm
-import io.realm.RealmQuery
-import io.realm.kotlin.where
 import javax.inject.Inject
 
-internal class StateEventDataSource @Inject constructor(@SessionDatabase private val monarchy: Monarchy) {
+internal class StateEventDataSource @Inject constructor(@SessionDatabase private val monarchy: Monarchy,
+                                                        private val realmSessionProvider: RealmSessionProvider) {
 
     fun getStateEvent(roomId: String, eventType: String, stateKey: QueryStringValue): Event? {
-        return Realm.getInstance(monarchy.realmConfiguration).use { realm ->
+        return realmSessionProvider.withRealm { realm ->
             buildStateEventQuery(realm, roomId, setOf(eventType), stateKey).findFirst()?.root?.asDomain()
         }
     }
@@ -53,7 +55,7 @@ internal class StateEventDataSource @Inject constructor(@SessionDatabase private
     }
 
     fun getStateEvents(roomId: String, eventTypes: Set<String>, stateKey: QueryStringValue): List<Event> {
-        return Realm.getInstance(monarchy.realmConfiguration).use { realm ->
+        return realmSessionProvider.withRealm { realm ->
             buildStateEventQuery(realm, roomId, eventTypes, stateKey)
                     .findAll()
                     .mapNotNull {
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/summary/RoomSummaryEventsHelper.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/summary/RoomSummaryEventsHelper.kt
new file mode 100644
index 0000000000000000000000000000000000000000..dd71bff436be02e76e6434f7375442ccb84424c5
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/summary/RoomSummaryEventsHelper.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2020 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 org.matrix.android.sdk.internal.session.room.summary
+
+import io.realm.Realm
+import org.matrix.android.sdk.api.session.room.summary.RoomSummaryConstants
+import org.matrix.android.sdk.api.session.room.timeline.TimelineEventFilters
+import org.matrix.android.sdk.internal.database.model.TimelineEventEntity
+import org.matrix.android.sdk.internal.database.query.latestEvent
+
+internal object RoomSummaryEventsHelper {
+
+    private val previewFilters = TimelineEventFilters(
+            filterTypes = true,
+            allowedTypes = RoomSummaryConstants.PREVIEWABLE_TYPES,
+            filterUseless = true,
+            filterRedacted = false,
+            filterEdits = true
+    )
+
+    fun getLatestPreviewableEvent(realm: Realm, roomId: String): TimelineEventEntity? {
+        return TimelineEventEntity.latestEvent(
+                realm = realm,
+                roomId = roomId,
+                includesSending = true,
+                filters = previewFilters
+        )
+    }
+}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/summary/RoomSummaryUpdater.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/summary/RoomSummaryUpdater.kt
index 99671c232a1ca4092ed89594647fbe4b19a38b86..0aac30654a5f8722dea26e6cbe563b528ba03a77 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/summary/RoomSummaryUpdater.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/summary/RoomSummaryUpdater.kt
@@ -18,6 +18,8 @@
 package org.matrix.android.sdk.internal.session.room.summary
 
 import dagger.Lazy
+import io.realm.Realm
+import org.greenrobot.eventbus.EventBus
 import org.matrix.android.sdk.api.crypto.RoomEncryptionTrustLevel
 import org.matrix.android.sdk.api.session.events.model.EventType
 import org.matrix.android.sdk.api.session.events.model.toModel
@@ -40,7 +42,6 @@ import org.matrix.android.sdk.internal.database.query.findAllInRoomWithSendState
 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.isEventRead
-import org.matrix.android.sdk.internal.database.query.latestEvent
 import org.matrix.android.sdk.internal.database.query.whereType
 import org.matrix.android.sdk.internal.di.UserId
 import org.matrix.android.sdk.internal.session.room.RoomAvatarResolver
@@ -49,8 +50,6 @@ import org.matrix.android.sdk.internal.session.room.membership.RoomMemberHelper
 import org.matrix.android.sdk.internal.session.room.timeline.TimelineEventDecryptor
 import org.matrix.android.sdk.internal.session.sync.model.RoomSyncSummary
 import org.matrix.android.sdk.internal.session.sync.model.RoomSyncUnreadNotifications
-import io.realm.Realm
-import org.greenrobot.eventbus.EventBus
 import timber.log.Timber
 import javax.inject.Inject
 
@@ -61,28 +60,6 @@ internal class RoomSummaryUpdater @Inject constructor(
         private val timelineEventDecryptor: Lazy<TimelineEventDecryptor>,
         private val eventBus: EventBus) {
 
-    companion object {
-        // TODO: maybe allow user of SDK to give that list
-        val PREVIEWABLE_TYPES = listOf(
-                // TODO filter message type (KEY_VERIFICATION_READY, etc.)
-                EventType.MESSAGE,
-                EventType.STATE_ROOM_NAME,
-                EventType.STATE_ROOM_TOPIC,
-                EventType.STATE_ROOM_AVATAR,
-                EventType.STATE_ROOM_MEMBER,
-                EventType.STATE_ROOM_HISTORY_VISIBILITY,
-                EventType.CALL_INVITE,
-                EventType.CALL_HANGUP,
-                EventType.CALL_ANSWER,
-                EventType.ENCRYPTED,
-                EventType.STATE_ROOM_ENCRYPTION,
-                EventType.STATE_ROOM_THIRD_PARTY_INVITE,
-                EventType.STICKER,
-                EventType.REACTION,
-                EventType.STATE_ROOM_CREATE
-        )
-    }
-
     fun update(realm: Realm,
                roomId: String,
                membership: Membership? = null,
@@ -110,9 +87,6 @@ internal class RoomSummaryUpdater @Inject constructor(
             roomSummaryEntity.membership = membership
         }
 
-        val latestPreviewableEvent = TimelineEventEntity.latestEvent(realm, roomId, includesSending = true,
-                filterTypes = PREVIEWABLE_TYPES, filterContentRelation = true)
-
         val lastNameEvent = CurrentStateEventEntity.getOrNull(realm, roomId, type = EventType.STATE_ROOM_NAME, stateKey = "")?.root
         val lastTopicEvent = CurrentStateEventEntity.getOrNull(realm, roomId, type = EventType.STATE_ROOM_TOPIC, stateKey = "")?.root
         val lastCanonicalAliasEvent = CurrentStateEventEntity.getOrNull(realm, roomId, type = EventType.STATE_ROOM_CANONICAL_ALIAS, stateKey = "")?.root
@@ -123,6 +97,8 @@ internal class RoomSummaryUpdater @Inject constructor(
                 .contains(EventEntityFields.CONTENT, "\"algorithm\":\"$MXCRYPTO_ALGORITHM_MEGOLM\"")
                 .findFirst()
 
+        val latestPreviewableEvent = RoomSummaryEventsHelper.getLatestPreviewableEvent(realm, roomId)
+
         roomSummaryEntity.hasUnreadMessages = roomSummaryEntity.notificationCount > 0
                 // avoid this call if we are sure there are unread events
                 || !isEventRead(realm.configuration, userId, roomId, latestPreviewableEvent?.eventId)
@@ -178,8 +154,7 @@ internal class RoomSummaryUpdater @Inject constructor(
     fun updateSendingInformation(realm: Realm, roomId: String) {
         val roomSummaryEntity = RoomSummaryEntity.getOrCreate(realm, roomId)
         roomSummaryEntity.updateHasFailedSending()
-        roomSummaryEntity.latestPreviewableEvent = TimelineEventEntity.latestEvent(realm, roomId, includesSending = true,
-                filterTypes = PREVIEWABLE_TYPES, filterContentRelation = true)
+        roomSummaryEntity.latestPreviewableEvent = RoomSummaryEventsHelper.getLatestPreviewableEvent(realm, roomId)
     }
 
     fun updateShieldTrust(realm: Realm,
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimeline.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimeline.kt
index 421cd1b0631924dca08fa8741e89a5f55e9828cc..2dead1d9cc7142fb2a2dec23ac85c261be607f4a 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimeline.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimeline.kt
@@ -39,13 +39,14 @@ import org.matrix.android.sdk.api.session.room.timeline.Timeline
 import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
 import org.matrix.android.sdk.api.session.room.timeline.TimelineSettings
 import org.matrix.android.sdk.api.util.CancelableBag
+import org.matrix.android.sdk.internal.database.RealmSessionProvider
 import org.matrix.android.sdk.internal.database.mapper.TimelineEventMapper
 import org.matrix.android.sdk.internal.database.model.ChunkEntity
 import org.matrix.android.sdk.internal.database.model.ChunkEntityFields
 import org.matrix.android.sdk.internal.database.model.RoomEntity
 import org.matrix.android.sdk.internal.database.model.TimelineEventEntity
 import org.matrix.android.sdk.internal.database.model.TimelineEventEntityFields
-import org.matrix.android.sdk.internal.database.query.TimelineEventFilter
+import org.matrix.android.sdk.internal.database.query.filterEvents
 import org.matrix.android.sdk.internal.database.query.findAllInRoomWithSendStates
 import org.matrix.android.sdk.internal.database.query.where
 import org.matrix.android.sdk.internal.database.query.whereRoomId
@@ -76,7 +77,8 @@ internal class DefaultTimeline(
         private val settings: TimelineSettings,
         private val hiddenReadReceipts: TimelineHiddenReadReceipts,
         private val eventBus: EventBus,
-        private val eventDecryptor: TimelineEventDecryptor
+        private val eventDecryptor: TimelineEventDecryptor,
+        private val realmSessionProvider: RealmSessionProvider
 ) : Timeline, TimelineHiddenReadReceipts.Delegate {
 
     data class OnNewTimelineEvents(val roomId: String, val eventIds: List<String>)
@@ -136,13 +138,13 @@ internal class DefaultTimeline(
     }
 
     override fun pendingEventCount(): Int {
-        return Realm.getInstance(realmConfiguration).use {
+        return realmSessionProvider.withRealm {
             RoomEntity.where(it, roomId).findFirst()?.sendingTimelineEvents?.count() ?: 0
         }
     }
 
     override fun failedToDeliverEventCount(): Int {
-        return Realm.getInstance(realmConfiguration).use {
+        return realmSessionProvider.withRealm {
             TimelineEventEntity.findAllInRoomWithSendStates(it, roomId, SendState.HAS_FAILED_STATES).count()
         }
     }
@@ -182,7 +184,7 @@ internal class DefaultTimeline(
     }
 
     private fun TimelineSettings.shouldHandleHiddenReadReceipts(): Boolean {
-        return buildReadReceipts && (filterEdits || filterTypes)
+        return buildReadReceipts && (filters.filterEdits || filters.filterTypes)
     }
 
     override fun dispose() {
@@ -239,7 +241,7 @@ internal class DefaultTimeline(
             return eventId
         }
         // Otherwise, we should check if the event is in the db, but is hidden because of filters
-        return Realm.getInstance(realmConfiguration).use { localRealm ->
+        return realmSessionProvider.withRealm { localRealm ->
             val nonFilteredEvents = buildEventQuery(localRealm)
                     .sort(TimelineEventEntityFields.DISPLAY_INDEX, Sort.DESCENDING)
                     .findAll()
@@ -757,29 +759,15 @@ internal class DefaultTimeline(
     }
 
     private fun RealmQuery<TimelineEventEntity>.filterEventsWithSettings(): RealmQuery<TimelineEventEntity> {
-        if (settings.filterTypes) {
-            `in`(TimelineEventEntityFields.ROOT.TYPE, settings.allowedTypes.toTypedArray())
-        }
-        if (settings.filterUseless) {
-            not()
-                    .equalTo(TimelineEventEntityFields.ROOT.IS_USELESS, true)
-        }
-        if (settings.filterEdits) {
-            not().like(TimelineEventEntityFields.ROOT.CONTENT, TimelineEventFilter.Content.EDIT)
-            not().like(TimelineEventEntityFields.ROOT.CONTENT, TimelineEventFilter.Content.RESPONSE)
-        }
-        if (settings.filterRedacted) {
-            not().like(TimelineEventEntityFields.ROOT.UNSIGNED_DATA, TimelineEventFilter.Unsigned.REDACTED)
-        }
-        return this
+        return filterEvents(settings.filters)
     }
 
     private fun List<TimelineEvent>.filterEventsWithSettings(): List<TimelineEvent> {
         return filter {
-            val filterType = !settings.filterTypes || settings.allowedTypes.contains(it.root.type)
+            val filterType = !settings.filters.filterTypes || settings.filters.allowedTypes.contains(it.root.type)
             if (!filterType) return@filter false
 
-            val filterEdits = if (settings.filterEdits && it.root.type == EventType.MESSAGE) {
+            val filterEdits = if (settings.filters.filterEdits && it.root.type == EventType.MESSAGE) {
                 val messageContent = it.root.content.toModel<MessageContent>()
                 messageContent?.relatesTo?.type != RelationType.REPLACE && messageContent?.relatesTo?.type != RelationType.RESPONSE
             } else {
@@ -787,7 +775,7 @@ internal class DefaultTimeline(
             }
             if (!filterEdits) return@filter false
 
-            val filterRedacted = !settings.filterRedacted || it.root.isRedacted()
+            val filterRedacted = !settings.filters.filterRedacted || it.root.isRedacted()
 
             filterRedacted
         }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimelineService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimelineService.kt
index db675f69f5bd64253eeccece8ee17f82f749cf46..c60a9444092ef42f9dfefd01acb111b7f0be630d 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimelineService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimelineService.kt
@@ -22,6 +22,9 @@ import androidx.lifecycle.Transformations
 import com.squareup.inject.assisted.Assisted
 import com.squareup.inject.assisted.AssistedInject
 import com.zhuinden.monarchy.Monarchy
+import io.realm.Sort
+import io.realm.kotlin.where
+import org.greenrobot.eventbus.EventBus
 import org.matrix.android.sdk.api.session.events.model.isImageMessage
 import org.matrix.android.sdk.api.session.events.model.isVideoMessage
 import org.matrix.android.sdk.api.session.room.timeline.Timeline
@@ -30,7 +33,7 @@ import org.matrix.android.sdk.api.session.room.timeline.TimelineService
 import org.matrix.android.sdk.api.session.room.timeline.TimelineSettings
 import org.matrix.android.sdk.api.util.Optional
 import org.matrix.android.sdk.api.util.toOptional
-import org.matrix.android.sdk.internal.crypto.store.db.doWithRealm
+import org.matrix.android.sdk.internal.database.RealmSessionProvider
 import org.matrix.android.sdk.internal.database.mapper.ReadReceiptsSummaryMapper
 import org.matrix.android.sdk.internal.database.mapper.TimelineEventMapper
 import org.matrix.android.sdk.internal.database.model.TimelineEventEntity
@@ -38,13 +41,10 @@ import org.matrix.android.sdk.internal.database.model.TimelineEventEntityFields
 import org.matrix.android.sdk.internal.database.query.where
 import org.matrix.android.sdk.internal.di.SessionDatabase
 import org.matrix.android.sdk.internal.task.TaskExecutor
-import org.matrix.android.sdk.internal.util.fetchCopyMap
-import io.realm.Sort
-import io.realm.kotlin.where
-import org.greenrobot.eventbus.EventBus
 
 internal class DefaultTimelineService @AssistedInject constructor(@Assisted private val roomId: String,
                                                                   @SessionDatabase private val monarchy: Monarchy,
+                                                                  private val realmSessionProvider: RealmSessionProvider,
                                                                   private val eventBus: EventBus,
                                                                   private val taskExecutor: TaskExecutor,
                                                                   private val contextOfEventTask: GetContextOfEventTask,
@@ -73,17 +73,17 @@ internal class DefaultTimelineService @AssistedInject constructor(@Assisted priv
                 hiddenReadReceipts = TimelineHiddenReadReceipts(readReceiptsSummaryMapper, roomId, settings),
                 eventBus = eventBus,
                 eventDecryptor = eventDecryptor,
-                fetchTokenAndPaginateTask = fetchTokenAndPaginateTask
+                fetchTokenAndPaginateTask = fetchTokenAndPaginateTask,
+                realmSessionProvider = realmSessionProvider
         )
     }
 
     override fun getTimeLineEvent(eventId: String): TimelineEvent? {
-        return monarchy
-                .fetchCopyMap({
-                    TimelineEventEntity.where(it, roomId = roomId, eventId = eventId).findFirst()
-                }, { entity, _ ->
-                    timelineEventMapper.map(entity)
-                })
+        return realmSessionProvider.withRealm { realm ->
+            TimelineEventEntity.where(realm, roomId = roomId, eventId = eventId).findFirst()?.let {
+                timelineEventMapper.map(it)
+            }
+        }
     }
 
     override fun getTimeLineEventLive(eventId: String): LiveData<Optional<TimelineEvent>> {
@@ -98,7 +98,7 @@ internal class DefaultTimelineService @AssistedInject constructor(@Assisted priv
 
     override fun getAttachmentMessages(): List<TimelineEvent> {
         // TODO pretty bad query.. maybe we should denormalize clear type in base?
-        return doWithRealm(monarchy.realmConfiguration) { realm ->
+        return realmSessionProvider.withRealm { realm ->
             realm.where<TimelineEventEntity>()
                     .equalTo(TimelineEventEntityFields.ROOM_ID, roomId)
                     .sort(TimelineEventEntityFields.DISPLAY_INDEX, Sort.ASCENDING)
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/TimelineHiddenReadReceipts.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/TimelineHiddenReadReceipts.kt
index 426daa4b57d15efdd0463984d476284714608035..f2c520a50f4227f2fddc55ef0324f8ebcb79e647 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/TimelineHiddenReadReceipts.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/TimelineHiddenReadReceipts.kt
@@ -18,6 +18,10 @@
 package org.matrix.android.sdk.internal.session.room.timeline
 
 import android.util.SparseArray
+import io.realm.OrderedRealmCollectionChangeListener
+import io.realm.Realm
+import io.realm.RealmQuery
+import io.realm.RealmResults
 import org.matrix.android.sdk.api.session.room.model.ReadReceipt
 import org.matrix.android.sdk.api.session.room.timeline.TimelineSettings
 import org.matrix.android.sdk.internal.database.mapper.ReadReceiptsSummaryMapper
@@ -27,10 +31,6 @@ import org.matrix.android.sdk.internal.database.model.TimelineEventEntity
 import org.matrix.android.sdk.internal.database.model.TimelineEventEntityFields
 import org.matrix.android.sdk.internal.database.query.TimelineEventFilter
 import org.matrix.android.sdk.internal.database.query.whereInRoom
-import io.realm.OrderedRealmCollectionChangeListener
-import io.realm.Realm
-import io.realm.RealmQuery
-import io.realm.RealmResults
 
 /**
  * This class is responsible for handling the read receipts for hidden events (check [TimelineSettings] to see filtering).
@@ -151,23 +151,24 @@ internal class TimelineHiddenReadReceipts constructor(private val readReceiptsSu
     private fun RealmQuery<ReadReceiptsSummaryEntity>.filterReceiptsWithSettings(): RealmQuery<ReadReceiptsSummaryEntity> {
         beginGroup()
         var needOr = false
-        if (settings.filterTypes) {
-            not().`in`("${ReadReceiptsSummaryEntityFields.TIMELINE_EVENT}.${TimelineEventEntityFields.ROOT.TYPE}", settings.allowedTypes.toTypedArray())
+        if (settings.filters.filterTypes) {
+            val allowedTypes = settings.filters.allowedTypes.toTypedArray()
+            not().`in`("${ReadReceiptsSummaryEntityFields.TIMELINE_EVENT}.${TimelineEventEntityFields.ROOT.TYPE}", allowedTypes)
             needOr = true
         }
-        if (settings.filterUseless) {
+        if (settings.filters.filterUseless) {
             if (needOr) or()
             equalTo("${ReadReceiptsSummaryEntityFields.TIMELINE_EVENT}.${TimelineEventEntityFields.ROOT.IS_USELESS}", true)
             needOr = true
         }
-        if (settings.filterEdits) {
+        if (settings.filters.filterEdits) {
             if (needOr) or()
             like("${ReadReceiptsSummaryEntityFields.TIMELINE_EVENT}.${TimelineEventEntityFields.ROOT.CONTENT}", TimelineEventFilter.Content.EDIT)
             or()
             like("${ReadReceiptsSummaryEntityFields.TIMELINE_EVENT}.${TimelineEventEntityFields.ROOT.CONTENT}", TimelineEventFilter.Content.RESPONSE)
             needOr = true
         }
-        if (settings.filterRedacted) {
+        if (settings.filters.filterRedacted) {
             if (needOr) or()
             like("${ReadReceiptsSummaryEntityFields.TIMELINE_EVENT}.${TimelineEventEntityFields.ROOT.UNSIGNED_DATA}", TimelineEventFilter.Unsigned.REDACTED)
         }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/TokenChunkEventPersistor.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/TokenChunkEventPersistor.kt
index da4eebe14222742bebfbb21005c36eaf664ea3c2..1fefdf9b5077b3314e0a92c6df0857d4f9dfdcb3 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/TokenChunkEventPersistor.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/TokenChunkEventPersistor.kt
@@ -18,6 +18,7 @@
 package org.matrix.android.sdk.internal.session.room.timeline
 
 import com.zhuinden.monarchy.Monarchy
+import io.realm.Realm
 import org.matrix.android.sdk.api.session.events.model.EventType
 import org.matrix.android.sdk.api.session.events.model.toModel
 import org.matrix.android.sdk.api.session.room.model.RoomMemberContent
@@ -32,19 +33,16 @@ import org.matrix.android.sdk.internal.database.model.ChunkEntity
 import org.matrix.android.sdk.internal.database.model.EventInsertType
 import org.matrix.android.sdk.internal.database.model.RoomEntity
 import org.matrix.android.sdk.internal.database.model.RoomSummaryEntity
-import org.matrix.android.sdk.internal.database.model.TimelineEventEntity
 import org.matrix.android.sdk.internal.database.query.copyToRealmOrIgnore
 import org.matrix.android.sdk.internal.database.query.create
 import org.matrix.android.sdk.internal.database.query.find
 import org.matrix.android.sdk.internal.database.query.findAllIncludingEvents
 import org.matrix.android.sdk.internal.database.query.findLastForwardChunkOfRoom
 import org.matrix.android.sdk.internal.database.query.getOrCreate
-import org.matrix.android.sdk.internal.database.query.latestEvent
 import org.matrix.android.sdk.internal.database.query.where
 import org.matrix.android.sdk.internal.di.SessionDatabase
-import org.matrix.android.sdk.internal.session.room.summary.RoomSummaryUpdater
+import org.matrix.android.sdk.internal.session.room.summary.RoomSummaryEventsHelper
 import org.matrix.android.sdk.internal.util.awaitTransaction
-import io.realm.Realm
 import timber.log.Timber
 import javax.inject.Inject
 
@@ -177,12 +175,7 @@ internal class TokenChunkEventPersistor @Inject constructor(@SessionDatabase pri
                 currentChunk.isLastForward = true
                 currentLastForwardChunk?.deleteOnCascade()
                 RoomSummaryEntity.where(realm, roomId).findFirst()?.apply {
-                    latestPreviewableEvent = TimelineEventEntity.latestEvent(
-                            realm,
-                            roomId,
-                            includesSending = true,
-                            filterTypes = RoomSummaryUpdater.PREVIEWABLE_TYPES
-                    )
+                    latestPreviewableEvent = RoomSummaryEventsHelper.getLatestPreviewableEvent(realm, roomId)
                 }
             }
         } else {
@@ -249,13 +242,7 @@ internal class TokenChunkEventPersistor @Inject constructor(@SessionDatabase pri
         val shouldUpdateSummary = roomSummaryEntity.latestPreviewableEvent == null
                 || (chunksToDelete.isNotEmpty() && currentChunk.isLastForward && direction == PaginationDirection.FORWARDS)
         if (shouldUpdateSummary) {
-            val latestPreviewableEvent = TimelineEventEntity.latestEvent(
-                    realm,
-                    roomId,
-                    includesSending = true,
-                    filterTypes = RoomSummaryUpdater.PREVIEWABLE_TYPES
-            )
-            roomSummaryEntity.latestPreviewableEvent = latestPreviewableEvent
+            roomSummaryEntity.latestPreviewableEvent = RoomSummaryEventsHelper.getLatestPreviewableEvent(realm, roomId)
         }
         if (currentChunk.isValid) {
             RoomEntity.where(realm, roomId).findFirst()?.addOrUpdate(currentChunk)
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/SyncTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/SyncTask.kt
index 02afd53908a7516d783dbe27febb42ddd60e0e55..9924d4476445b0c75d8125b648438403b292db47 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/SyncTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/SyncTask.kt
@@ -32,7 +32,7 @@ import javax.inject.Inject
 
 internal interface SyncTask : Task<SyncTask.Params, Unit> {
 
-    data class Params(var timeout: Long = 30_000L)
+    data class Params(var timeout: Long = 6_000L)
 }
 
 internal class DefaultSyncTask @Inject constructor(
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/job/SyncService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/job/SyncService.kt
index 20aa4093365870acfe6a57d61e0a5402661f69ba..485eca6f748a30304f75b5a612f981fd2e24e988 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/job/SyncService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/job/SyncService.kt
@@ -19,7 +19,14 @@ package org.matrix.android.sdk.internal.session.sync.job
 import android.app.Service
 import android.content.Intent
 import android.os.IBinder
+import android.os.PowerManager
+import androidx.core.content.getSystemService
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.SupervisorJob
+import kotlinx.coroutines.cancelChildren
+import kotlinx.coroutines.launch
 import org.matrix.android.sdk.api.Matrix
+import org.matrix.android.sdk.api.failure.Failure
 import org.matrix.android.sdk.api.failure.isTokenError
 import org.matrix.android.sdk.api.session.Session
 import org.matrix.android.sdk.api.session.sync.SyncState
@@ -28,10 +35,6 @@ import org.matrix.android.sdk.internal.session.sync.SyncTask
 import org.matrix.android.sdk.internal.task.TaskExecutor
 import org.matrix.android.sdk.internal.util.BackgroundDetectionObserver
 import org.matrix.android.sdk.internal.util.MatrixCoroutineDispatchers
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.SupervisorJob
-import kotlinx.coroutines.cancelChildren
-import kotlinx.coroutines.launch
 import timber.log.Timber
 import java.util.concurrent.atomic.AtomicBoolean
 
@@ -46,6 +49,11 @@ abstract class SyncService : Service() {
     private var sessionId: String? = null
     private var mIsSelfDestroyed: Boolean = false
 
+    private var syncTimeoutSeconds: Int = 6
+    private var syncDelaySeconds: Int = 60
+    private var periodic: Boolean = false
+    private var preventReschedule: Boolean = false
+
     private var isInitialSync: Boolean = false
     private lateinit var session: Session
     private lateinit var syncTask: SyncTask
@@ -59,27 +67,60 @@ abstract class SyncService : Service() {
     private val serviceScope = CoroutineScope(SupervisorJob())
 
     override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
-        Timber.i("onStartCommand $intent")
-        val isInit = initialize(intent)
-        if (isInit) {
-            onStart(isInitialSync)
-            doSyncIfNotAlreadyRunning()
-        } else {
-            // We should start and stop as we have to ensure to call Service.startForeground()
-            onStart(isInitialSync)
-            stopMe()
+        Timber.i("## Sync: onStartCommand [$this] $intent with action: ${intent?.action}")
+
+        // We should start we have to ensure we fulfill contract to show notification
+        // for foreground service (as per design for this service)
+        // TODO can we check if it's really in foreground
+        onStart(isInitialSync)
+        when (intent?.action) {
+            ACTION_STOP -> {
+                Timber.i("## Sync: stop command received")
+                // If it was periodic we ensure that it will not reschedule itself
+                preventReschedule = true
+                // we don't want to cancel initial syncs, let it finish
+                if (!isInitialSync) {
+                    stopMe()
+                }
+            }
+            else        -> {
+                val isInit = initialize(intent)
+                if (isInit) {
+                    periodic = intent?.getBooleanExtra(EXTRA_PERIODIC, false) ?: false
+                    val onNetworkBack = intent?.getBooleanExtra(EXTRA_NETWORK_BACK_RESTART, false) ?: false
+                    Timber.d("## Sync: command received, periodic: $periodic  networkBack: $onNetworkBack")
+                    if (onNetworkBack && !backgroundDetectionObserver.isInBackground) {
+                        // the restart after network occurs while the app is in foreground
+                        // so just stop. It will be restarted when entering background
+                        preventReschedule = true
+                        stopMe()
+                    } else {
+                        // default is syncing
+                        doSyncIfNotAlreadyRunning()
+                    }
+                } else {
+                    Timber.d("## Sync: Failed to initialize service")
+                    stopMe()
+                }
+            }
         }
-        // No intent just start the service, an alarm will should call with intent
-        return START_STICKY
+
+        // It's ok to be not sticky because we will explicitly start it again on the next alarm?
+        return START_NOT_STICKY
     }
 
     override fun onDestroy() {
-        Timber.i("## onDestroy() : $this")
+        Timber.i("## Sync: onDestroy() [$this] periodic:$periodic preventReschedule:$preventReschedule")
         if (!mIsSelfDestroyed) {
-            Timber.w("## Destroy by the system : $this")
+            Timber.d("## Sync: Destroy by the system : $this")
         }
-        serviceScope.coroutineContext.cancelChildren()
         isRunning.set(false)
+        // Cancelling the context will trigger the catch close the doSync try
+        serviceScope.coroutineContext.cancelChildren()
+        if (!preventReschedule && periodic && sessionId != null && backgroundDetectionObserver.isInBackground) {
+            Timber.d("## Sync: Reschedule service in $syncDelaySeconds sec")
+            onRescheduleAsked(sessionId ?: "", false, syncTimeoutSeconds, syncDelaySeconds)
+        }
         super.onDestroy()
     }
 
@@ -90,9 +131,15 @@ abstract class SyncService : Service() {
 
     private fun doSyncIfNotAlreadyRunning() {
         if (isRunning.get()) {
-            Timber.i("Received a start while was already syncing... ignore")
+            Timber.i("## Sync: Received a start while was already syncing... ignore")
         } else {
             isRunning.set(true)
+            // Acquire a lock to give enough time for the sync :/
+            getSystemService<PowerManager>()?.run {
+                newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "riotx:fdroidSynclock").apply {
+                    acquire((syncTimeoutSeconds * 1000L + 10_000L))
+                }
+            }
             serviceScope.launch(coroutineDispatchers.io) {
                 doSync()
             }
@@ -100,9 +147,10 @@ abstract class SyncService : Service() {
     }
 
     private suspend fun doSync() {
-        Timber.v("Execute sync request with timeout 0")
-        val params = SyncTask.Params(TIME_OUT)
+        Timber.v("## Sync: Execute sync request with timeout $syncTimeoutSeconds seconds")
+        val params = SyncTask.Params(syncTimeoutSeconds * 1000L)
         try {
+            // never do that in foreground, let the syncThread work
             syncTask.execute(params)
             // Start sync if we were doing an initial sync and the syncThread is not launched yet
             if (isInitialSync && session.getSyncState() == SyncState.Idle) {
@@ -111,28 +159,34 @@ abstract class SyncService : Service() {
             }
             stopMe()
         } catch (throwable: Throwable) {
-            Timber.e(throwable)
+            Timber.e(throwable, "## Sync: sync service did fail ${isRunning.get()}")
             if (throwable.isTokenError()) {
-                stopMe()
-            } else {
-                Timber.v("Should be rescheduled to avoid wasting resources")
-                sessionId?.also {
-                    onRescheduleAsked(it, isInitialSync, delay = 10_000L)
-                }
-                stopMe()
+                // no need to retry
+                preventReschedule = true
             }
+            if (throwable is Failure.NetworkConnection) {
+                // Network is off, no need to reschedule endless alarms :/
+                preventReschedule = true
+                // Instead start a work to restart background sync when network is back
+                onNetworkError(sessionId ?: "", isInitialSync, syncTimeoutSeconds, syncDelaySeconds)
+            }
+            // JobCancellation could be caught here when onDestroy cancels the coroutine context
+            if (isRunning.get()) stopMe()
         }
     }
 
     private fun initialize(intent: Intent?): Boolean {
         if (intent == null) {
+            Timber.d("## Sync: initialize intent is null")
             return false
         }
         val matrix = Matrix.getInstance(applicationContext)
         val safeSessionId = intent.getStringExtra(EXTRA_SESSION_ID) ?: return false
+        syncTimeoutSeconds = intent.getIntExtra(EXTRA_TIMEOUT_SECONDS, 6)
+        syncDelaySeconds = intent.getIntExtra(EXTRA_DELAY_SECONDS, 60)
         try {
             val sessionComponent = matrix.sessionManager.getSessionComponent(safeSessionId)
-                    ?: throw IllegalStateException("You should have a session to make it work")
+                    ?: throw IllegalStateException("## Sync: You should have a session to make it work")
             session = sessionComponent.session()
             sessionId = safeSessionId
             syncTask = sessionComponent.syncTask()
@@ -143,14 +197,16 @@ abstract class SyncService : Service() {
             backgroundDetectionObserver = matrix.backgroundDetectionObserver
             return true
         } catch (exception: Exception) {
-            Timber.e(exception, "An exception occurred during initialisation")
+            Timber.e(exception, "## Sync: An exception occurred during initialisation")
             return false
         }
     }
 
     abstract fun onStart(isInitialSync: Boolean)
 
-    abstract fun onRescheduleAsked(sessionId: String, isInitialSync: Boolean, delay: Long)
+    abstract fun onRescheduleAsked(sessionId: String, isInitialSync: Boolean, timeout: Int, delay: Int)
+
+    abstract fun onNetworkError(sessionId: String, isInitialSync: Boolean, timeout: Int, delay: Int)
 
     override fun onBind(intent: Intent?): IBinder? {
         return null
@@ -158,6 +214,11 @@ abstract class SyncService : Service() {
 
     companion object {
         const val EXTRA_SESSION_ID = "EXTRA_SESSION_ID"
-        private const val TIME_OUT = 0L
+        const val EXTRA_TIMEOUT_SECONDS = "EXTRA_TIMEOUT_SECONDS"
+        const val EXTRA_DELAY_SECONDS = "EXTRA_DELAY_SECONDS"
+        const val EXTRA_PERIODIC = "EXTRA_PERIODIC"
+        const val EXTRA_NETWORK_BACK_RESTART = "EXTRA_NETWORK_BACK_RESTART"
+
+        const val ACTION_STOP = "ACTION_STOP"
     }
 }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/job/SyncWorker.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/job/SyncWorker.kt
index e702de3573caffc07afa359a2811798793b382b4..3e0a29ba72e802e7c9ced67765a74f940868d9c4 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/job/SyncWorker.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/job/SyncWorker.kt
@@ -34,7 +34,8 @@ import timber.log.Timber
 import java.util.concurrent.TimeUnit
 import javax.inject.Inject
 
-private const val DEFAULT_LONG_POOL_TIMEOUT = 0L
+private const val DEFAULT_LONG_POOL_TIMEOUT = 6L
+private const val DEFAULT_DELAY_TIMEOUT = 30_000L
 
 /**
  * Possible previous worker: None
@@ -48,13 +49,15 @@ internal class SyncWorker(context: Context,
     internal data class Params(
             override val sessionId: String,
             val timeout: Long = DEFAULT_LONG_POOL_TIMEOUT,
-            val automaticallyRetry: Boolean = false,
+            val delay: Long = DEFAULT_DELAY_TIMEOUT,
+            val periodic: Boolean = false,
             override val lastFailureMessage: String? = null
     ) : SessionWorkerParams
 
     @Inject lateinit var syncTask: SyncTask
     @Inject lateinit var taskExecutor: TaskExecutor
     @Inject lateinit var networkConnectivityChecker: NetworkConnectivityChecker
+    @Inject lateinit var workManagerProvider: WorkManagerProvider
 
     override suspend fun doWork(): Result {
         Timber.i("Sync work starting")
@@ -67,11 +70,21 @@ internal class SyncWorker(context: Context,
         return runCatching {
             doSync(params.timeout)
         }.fold(
-                { Result.success() },
+                {
+                    Result.success().also {
+                        if (params.periodic) {
+                            // we want to schedule another one after delay
+                            automaticallyBackgroundSync(workManagerProvider, params.sessionId, params.timeout, params.delay)
+                        }
+                    }
+                },
                 { failure ->
-                    if (failure.isTokenError() || !params.automaticallyRetry) {
+                    if (failure.isTokenError()) {
                         Result.failure()
                     } else {
+                        // If the worker was stopped (when going back in foreground), a JobCancellation exception is sent
+                        // but in this case the result is ignored, as the work is considered stopped,
+                        // so don't worry of the retry here for this case
                         Result.retry()
                     }
                 }
@@ -79,7 +92,7 @@ internal class SyncWorker(context: Context,
     }
 
     private suspend fun doSync(timeout: Long) {
-        val taskParams = SyncTask.Params(timeout)
+        val taskParams = SyncTask.Params(timeout * 1000)
         syncTask.execute(taskParams)
     }
 
@@ -87,25 +100,27 @@ internal class SyncWorker(context: Context,
         private const val BG_SYNC_WORK_NAME = "BG_SYNCP"
 
         fun requireBackgroundSync(workManagerProvider: WorkManagerProvider, sessionId: String, serverTimeout: Long = 0) {
-            val data = WorkerParamsFactory.toData(Params(sessionId, serverTimeout, false))
+            val data = WorkerParamsFactory.toData(Params(sessionId, serverTimeout, 0L, false))
             val workRequest = workManagerProvider.matrixOneTimeWorkRequestBuilder<SyncWorker>()
                     .setConstraints(WorkManagerProvider.workConstraints)
                     .setBackoffCriteria(BackoffPolicy.LINEAR, 1_000, TimeUnit.MILLISECONDS)
                     .setInputData(data)
                     .build()
             workManagerProvider.workManager
-                    .enqueueUniqueWork(BG_SYNC_WORK_NAME, ExistingWorkPolicy.REPLACE, workRequest)
+                    .enqueueUniqueWork(BG_SYNC_WORK_NAME, ExistingWorkPolicy.APPEND_OR_REPLACE, workRequest)
         }
 
-        fun automaticallyBackgroundSync(workManagerProvider: WorkManagerProvider, sessionId: String, serverTimeout: Long = 0, delay: Long = 30_000) {
-            val data = WorkerParamsFactory.toData(Params(sessionId, serverTimeout, true))
+        fun automaticallyBackgroundSync(workManagerProvider: WorkManagerProvider, sessionId: String, serverTimeout: Long = 0, delayInSeconds: Long = 30) {
+            val data = WorkerParamsFactory.toData(Params(sessionId, serverTimeout, delayInSeconds, true))
             val workRequest = workManagerProvider.matrixOneTimeWorkRequestBuilder<SyncWorker>()
                     .setConstraints(WorkManagerProvider.workConstraints)
                     .setInputData(data)
-                    .setBackoffCriteria(BackoffPolicy.LINEAR, delay, TimeUnit.MILLISECONDS)
+                    .setBackoffCriteria(BackoffPolicy.LINEAR, 1_000, TimeUnit.MILLISECONDS)
+                    .setInitialDelay(delayInSeconds, TimeUnit.SECONDS)
                     .build()
+
             workManagerProvider.workManager
-                    .enqueueUniqueWork(BG_SYNC_WORK_NAME, ExistingWorkPolicy.REPLACE, workRequest)
+                    .enqueueUniqueWork(BG_SYNC_WORK_NAME, ExistingWorkPolicy.APPEND_OR_REPLACE, workRequest)
         }
 
         fun stopAnyBackgroundSync(workManagerProvider: WorkManagerProvider) {
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/user/UserDataSource.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/user/UserDataSource.kt
index dd3c6856c0af7d0c041d9476cc182e21b311e00c..f6cb86c0ed966f18aa370462b297c1ecb86667eb 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/user/UserDataSource.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/user/UserDataSource.kt
@@ -23,9 +23,11 @@ import androidx.paging.DataSource
 import androidx.paging.LivePagedListBuilder
 import androidx.paging.PagedList
 import com.zhuinden.monarchy.Monarchy
+import io.realm.Case
 import org.matrix.android.sdk.api.session.user.model.User
 import org.matrix.android.sdk.api.util.Optional
 import org.matrix.android.sdk.api.util.toOptional
+import org.matrix.android.sdk.internal.database.RealmSessionProvider
 import org.matrix.android.sdk.internal.database.mapper.asDomain
 import org.matrix.android.sdk.internal.database.model.IgnoredUserEntity
 import org.matrix.android.sdk.internal.database.model.IgnoredUserEntityFields
@@ -33,11 +35,10 @@ import org.matrix.android.sdk.internal.database.model.UserEntity
 import org.matrix.android.sdk.internal.database.model.UserEntityFields
 import org.matrix.android.sdk.internal.database.query.where
 import org.matrix.android.sdk.internal.di.SessionDatabase
-import org.matrix.android.sdk.internal.util.fetchCopied
-import io.realm.Case
 import javax.inject.Inject
 
-internal class UserDataSource @Inject constructor(@SessionDatabase private val monarchy: Monarchy) {
+internal class UserDataSource @Inject constructor(@SessionDatabase private val monarchy: Monarchy,
+                                                  private val realmSessionProvider: RealmSessionProvider) {
 
     private val realmDataSourceFactory: Monarchy.RealmDataSourceFactory<UserEntity> by lazy {
         monarchy.createDataSourceFactory { realm ->
@@ -58,10 +59,10 @@ internal class UserDataSource @Inject constructor(@SessionDatabase private val m
     }
 
     fun getUser(userId: String): User? {
-        val userEntity = monarchy.fetchCopied { UserEntity.where(it, userId).findFirst() }
-                ?: return null
-
-        return userEntity.asDomain()
+        return realmSessionProvider.withRealm {
+            val userEntity = UserEntity.where(it, userId).findFirst()
+            userEntity?.asDomain()
+        }
     }
 
     fun getUserLive(userId: String): LiveData<Optional<User>> {
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/user/accountdata/AccountDataDataSource.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/user/accountdata/AccountDataDataSource.kt
index d54bfdd63d856ebfd7949259b3f70dbf2032e3ad..a9261eddaba1945ce037a411143a5f72315e41f0 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/user/accountdata/AccountDataDataSource.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/user/accountdata/AccountDataDataSource.kt
@@ -20,18 +20,20 @@ package org.matrix.android.sdk.internal.session.user.accountdata
 import androidx.lifecycle.LiveData
 import androidx.lifecycle.Transformations
 import com.zhuinden.monarchy.Monarchy
+import io.realm.Realm
+import io.realm.RealmQuery
+import org.matrix.android.sdk.api.session.accountdata.UserAccountDataEvent
 import org.matrix.android.sdk.api.util.Optional
 import org.matrix.android.sdk.api.util.toOptional
+import org.matrix.android.sdk.internal.database.RealmSessionProvider
 import org.matrix.android.sdk.internal.database.mapper.AccountDataMapper
 import org.matrix.android.sdk.internal.database.model.UserAccountDataEntity
 import org.matrix.android.sdk.internal.database.model.UserAccountDataEntityFields
 import org.matrix.android.sdk.internal.di.SessionDatabase
-import org.matrix.android.sdk.api.session.accountdata.UserAccountDataEvent
-import io.realm.Realm
-import io.realm.RealmQuery
 import javax.inject.Inject
 
 internal class AccountDataDataSource @Inject constructor(@SessionDatabase private val monarchy: Monarchy,
+                                                         private val realmSessionProvider: RealmSessionProvider,
                                                          private val accountDataMapper: AccountDataMapper) {
 
     fun getAccountDataEvent(type: String): UserAccountDataEvent? {
@@ -45,10 +47,9 @@ internal class AccountDataDataSource @Inject constructor(@SessionDatabase privat
     }
 
     fun getAccountDataEvents(types: Set<String>): List<UserAccountDataEvent> {
-        return monarchy.fetchAllMappedSync(
-                { accountDataEventsQuery(it, types) },
-                accountDataMapper::map
-        )
+        return realmSessionProvider.withRealm {
+            accountDataEventsQuery(it, types).findAll().map(accountDataMapper::map)
+        }
     }
 
     fun getLiveAccountDataEvents(types: Set<String>): LiveData<List<UserAccountDataEvent>> {
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/widgets/helper/WidgetFactory.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/widgets/helper/WidgetFactory.kt
index 2cbc9b23dc63307c03e27957b23028dc558df134..992dbf16dfacdcdaa7e2f9e5c9ffb6cd605780f1 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/widgets/helper/WidgetFactory.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/widgets/helper/WidgetFactory.kt
@@ -23,17 +23,15 @@ import org.matrix.android.sdk.api.session.room.sender.SenderInfo
 import org.matrix.android.sdk.api.session.widgets.model.Widget
 import org.matrix.android.sdk.api.session.widgets.model.WidgetContent
 import org.matrix.android.sdk.api.session.widgets.model.WidgetType
-import org.matrix.android.sdk.internal.di.SessionDatabase
+import org.matrix.android.sdk.internal.database.RealmSessionProvider
 import org.matrix.android.sdk.internal.di.UserId
 import org.matrix.android.sdk.internal.session.room.membership.RoomMemberHelper
 import org.matrix.android.sdk.internal.session.user.UserDataSource
-import io.realm.Realm
-import io.realm.RealmConfiguration
 import java.net.URLEncoder
 import javax.inject.Inject
 
-internal class WidgetFactory @Inject constructor(@SessionDatabase private val realmConfiguration: RealmConfiguration,
-                                                 private val userDataSource: UserDataSource,
+internal class WidgetFactory @Inject constructor(private val userDataSource: UserDataSource,
+                                                 private val realmSessionProvider: RealmSessionProvider,
                                                  @UserId private val userId: String) {
 
     fun create(widgetEvent: Event): Widget? {
@@ -44,7 +42,7 @@ internal class WidgetFactory @Inject constructor(@SessionDatabase private val re
         val senderInfo = if (widgetEvent.senderId == null || widgetEvent.roomId == null) {
             null
         } else {
-            Realm.getInstance(realmConfiguration).use {
+            realmSessionProvider.withRealm {
                 val roomMemberHelper = RoomMemberHelper(it, widgetEvent.roomId)
                 val roomMemberSummaryEntity = roomMemberHelper.getLastRoomMember(widgetEvent.senderId)
                 SenderInfo(
diff --git a/matrix-sdk-android/src/main/res/values-ar/strings.xml b/matrix-sdk-android/src/main/res/values-ar/strings.xml
index e9aba1721afa64cf350740046a1b170d7e40980c..0fc7bd1b49597ab4b3710c582b409d20fa4764d2 100644
--- a/matrix-sdk-android/src/main/res/values-ar/strings.xml
+++ b/matrix-sdk-android/src/main/res/values-ar/strings.xml
@@ -6,21 +6,21 @@
     <string name="notice_room_invite_no_invitee">دعوة من ⁨%s⁩</string>
     <string name="notice_room_invite">دعى ⁨%1$s⁩ ⁨%2$s⁩</string>
     <string name="notice_room_invite_you">دعاك ⁨%1$s⁩</string>
-    <string name="notice_room_join">انضمّ ⁨%1$s⁩</string>
-    <string name="notice_room_leave">غادر ⁨%1$s⁩</string>
+    <string name="notice_room_join">انضمّ ⁨%1$s⁩ إلى الغرفة</string>
+    <string name="notice_room_leave">غادر ⁨%1$s⁩ الغرفة</string>
     <string name="notice_room_reject">رفض ⁨%1$s⁩ الدعوة</string>
     <string name="notice_room_kick">طرد ⁨%1$s⁩ ⁨%2$s⁩</string>
-    <string name="notice_room_unban">رفع ⁨%1$s⁩ الحظر عن ⁨%2$s⁩</string>
+    <string name="notice_room_unban">رفع ⁨%1$s⁩ المنع عن ⁨%2$s⁩</string>
     <string name="notice_room_ban">منع ⁨%1$s⁩ ⁨%2$s⁩</string>
     <string name="notice_avatar_url_changed">غيّر ⁨%1$s⁩ صورته</string>
     <string name="notice_display_name_set">ضبط ⁨%1$s⁩ اسم العرض على ⁨%2$s⁩</string>
-    <string name="notice_display_name_changed_from">غيّر ⁨%1$s⁩ اسم الحساب المعروض من %2$s⁩ إلى %3$s⁩</string>
-    <string name="notice_display_name_removed">أزال ⁨%1$s⁩ اسم الحساب المعروض (⁨%2$s⁩)</string>
+    <string name="notice_display_name_changed_from">غيّر ⁨%1$s⁩ اسم العرض من ⁨%2$s⁩ إلى ⁨%3$s⁩</string>
+    <string name="notice_display_name_removed">أزال ⁨%1$s⁩ اسم العرض (⁨كان ⁨%2$s⁩)</string>
     <string name="notice_room_topic_changed">غيّر ⁨%1$s⁩ الموضوع إلى: ⁨%2$s⁩</string>
     <string name="notice_room_name_changed">غيّر ⁨%1$s⁩ اسم الغرفة إلى: ⁨%2$s⁩</string>
     <string name="notice_answered_call">ردّ ⁨%s⁩ على المكالمة.</string>
     <string name="notice_ended_call">أنهى ⁨%s⁩ المكالمة.</string>
-    <string name="notice_made_future_room_visibility">جعل ⁨%1$s⁩ تأريخ الغرفة مستقبلًا ظاهرا على %2$s</string>
+    <string name="notice_made_future_room_visibility">جعل ⁨%1$s⁩ تأريخ الغرفة مستقبلًا ظاهرًا على ⁨%2$s⁩</string>
     <string name="notice_room_visibility_invited">كل أعضاء الغرفة من لحظة دعوتهم.</string>
     <string name="notice_room_visibility_joined">كل أعضاء الغرفة من لحظة انضمامهم.</string>
     <string name="notice_room_visibility_shared">كل أعضاء الغرفة.</string>
@@ -46,7 +46,7 @@
     <string name="network_error">خطأ في الشبكة</string>
     <string name="matrix_error">خطأ في «ماترِكس»</string>
 
-    <string name="room_error_join_failed_empty_room">ليس ممكنا الانضمام ثانيةً إلى غرفة فارغة.</string>
+    <string name="room_error_join_failed_empty_room">لا يمكنك حاليًا الانضمام ثانيةً إلى غرفة فارغة.</string>
 
     <string name="encrypted_message">رسالة معمّاة</string>
 
@@ -54,13 +54,13 @@
     <string name="medium_phone_number">رقم الهاتف</string>
 
     <string name="summary_message">‏‏⁨%1$s⁩: ‏⁨%2$s⁩</string>
-    <string name="notice_room_withdraw">انسحب ⁨%1$s⁩ من الدعوة ⁨%2$s⁩</string>
+    <string name="notice_room_withdraw">انسحب ⁨%1$s⁩ من دعوة ⁨%2$s⁩</string>
     <string name="notice_placed_video_call">أجرى ⁨%s⁩ مكالمة مرئية.</string>
     <string name="notice_placed_voice_call">أجرى ⁨%s⁩ مكالمة صوتية.</string>
-    <string name="notice_room_third_party_registered_invite">قبل ⁨%1$s⁩ دعوة ⁨%2$s⁩</string>
+    <string name="notice_room_third_party_registered_invite">قَبِل ⁨%1$s⁩ دعوة ⁨%2$s⁩</string>
 
     <string name="could_not_redact">تعذر التهذيب</string>
-    <string name="summary_user_sent_sticker">أرسل ⁨%1$s⁩ ملصقا.</string>
+    <string name="summary_user_sent_sticker">أرسل ⁨%1$s⁩ ملصقًا.</string>
 
     <string name="notice_avatar_changed_too">(تغيّرت الصورة أيضا)</string>
 
@@ -71,12 +71,77 @@
     <string name="room_displayname_room_invite">دعوة إلى غرفة</string>
 
     <plurals name="room_displayname_three_and_more_members">
-        <item quantity="zero">صفر</item>
-        <item quantity="one">واحد</item>
-        <item quantity="two">اثنان</item>
-        <item quantity="few">قليل</item>
-        <item quantity="many">كثير</item>
-        <item quantity="other">اخرى</item>
+        <item quantity="zero"></item>
+        <item quantity="one"></item>
+        <item quantity="two"></item>
+        <item quantity="few"></item>
+        <item quantity="many"></item>
+        <item quantity="other"></item>
     </plurals>
 
+    <string name="summary_you_sent_image">أرسلت صورة.</string>
+    <string name="summary_you_sent_sticker">أرسلت ملصقًا.</string>
+
+    <string name="notice_room_invite_no_invitee_by_you">دعوة منك أنت</string>
+    <string name="notice_room_created">أنشأ ⁨%1$s⁩ الغرفة</string>
+    <string name="notice_room_created_by_you">أنشأت الغرفة</string>
+    <string name="notice_room_invite_by_you">دعوت ⁨%1$s⁩</string>
+    <string name="notice_room_join_by_you">انضممت إلى الغرفة</string>
+    <string name="notice_room_leave_by_you">غادرت الغرفة</string>
+    <string name="notice_room_reject_by_you">رفضت الدعوة</string>
+    <string name="notice_room_kick_by_you">طردت ⁨%1$s⁩</string>
+    <string name="notice_room_unban_by_you">رفعت المنع عن ⁨%1$s⁩</string>
+    <string name="notice_room_ban_by_you">منعت ⁨%1$s⁩</string>
+    <string name="notice_room_withdraw_by_you">انسحبت من دعوة ⁨%1$s⁩</string>
+    <string name="notice_avatar_url_changed_by_you">غيّرت صورتك</string>
+    <string name="notice_display_name_set_by_you">ضبطت اسم العرض على ⁨%1$s⁩</string>
+    <string name="notice_display_name_changed_from_by_you">غيّرت اسم العرض من ⁨%1$s⁩ إلى ⁨%2$s⁩</string>
+    <string name="notice_display_name_removed_by_you">أزلت اسم العرض (كان ⁨%1$s⁩)</string>
+    <string name="notice_room_topic_changed_by_you">غيّرت الموضوع إلى: ⁨%1$s⁩</string>
+    <string name="notice_room_avatar_changed">غيّر ⁨%1$s⁩ صورة الغرفة</string>
+    <string name="notice_room_avatar_changed_by_you">غيّرت صورة الغرفة</string>
+    <string name="notice_room_name_changed_by_you">غيّرت اسم الغرفة إلى: ⁨%1$s⁩</string>
+    <string name="notice_placed_video_call_by_you">أجريت مكالمة مرئية.</string>
+    <string name="notice_placed_voice_call_by_you">أجريت مكالمة صوتية.</string>
+    <string name="notice_call_candidates">أرسل ⁨%s⁩ البيانات لإعداد المكالمة.</string>
+    <string name="notice_call_candidates_by_you">أرسلت البيانات لإعداد المكالمة.</string>
+    <string name="notice_answered_call_by_you">رددت على المكالمة.</string>
+    <string name="notice_ended_call_by_you">أنهيت المكالمة.</string>
+    <string name="notice_made_future_room_visibility_by_you">جعلت تأريخ الغرفة مستقبلًا ظاهرًا على ⁨%1$s⁩</string>
+    <string name="notice_end_to_end_by_you">فعّلت تعمية الطرفين (⁨%1$s⁩)</string>
+    <string name="notice_room_update">رقّى ⁨%s⁩ هذه الغرفة.</string>
+    <string name="notice_room_update_by_you">رقّيت هذه الغرفة.</string>
+
+    <string name="notice_requested_voip_conference_by_you">طلبت اجتماع VoIP</string>
+    <string name="notice_room_name_removed_by_you">أزلت اسم الغرفة</string>
+    <string name="notice_room_topic_removed_by_you">أزلت موضوع الغرفة</string>
+    <string name="notice_room_avatar_removed">أزال ⁨%1$s⁩ صورة الغرفة</string>
+    <string name="notice_room_avatar_removed_by_you">أزلت صورة الغرفة</string>
+    <string name="notice_event_redacted">أُزيلت الرسالة</string>
+    <string name="notice_event_redacted_by">أزال ⁨%1$s⁩ الرسالة</string>
+    <string name="notice_event_redacted_with_reason">أُزيلت الرسالة [السبب: ⁨%1$s⁩]</string>
+    <string name="notice_event_redacted_by_with_reason">أزال ⁨%1$s⁩ الرسالة [السبب: ⁨%2$s⁩]</string>
+    <string name="notice_room_third_party_invite_by_you">أرسلت دعوة إلى ⁨%1$s⁩ للانضمام إلى الغرفة</string>
+    <string name="notice_room_third_party_revoked_invite">سحب ⁨%1$s⁩ دعوة ⁨%2$s⁩ للانضمام إلى الغرفة</string>
+    <string name="notice_room_third_party_revoked_invite_by_you">سحبت دعوة ⁨%1$s⁩ للانضمام إلى الغرفة</string>
+    <string name="notice_room_third_party_registered_invite_by_you">قَبِلت دعوة ⁨%1$s⁩</string>
+
+    <string name="notice_widget_added">أضاف ⁨%1$s⁩ الودجة ⁨%2$s⁩</string>
+    <string name="notice_widget_added_by_you">أضفت الودجة ⁨%1$s⁩</string>
+    <string name="notice_widget_removed">أزال ⁨%1$s⁩ الودجة ⁨%2$s⁩</string>
+    <string name="notice_widget_removed_by_you">أزلت الودجة ⁨%1$s⁩</string>
+    <string name="notice_widget_modified">عدّل ⁨%1$s⁩ الودجة ⁨%2$s⁩</string>
+    <string name="notice_widget_modified_by_you">عدّلت الودجة ⁨%1$s⁩</string>
+
+    <string name="power_level_admin">مدير</string>
+    <string name="power_level_default">المبدئي</string>
+    <string name="power_level_custom">مخصّص (⁨%1$d⁩)</string>
+    <string name="power_level_custom_no_value">مخصّص</string>
+
+    <string name="notice_power_level_changed_by_you">غيّرت مستوى قوّة %1$s⁩.</string>
+    <string name="notice_power_level_changed">غيّر ⁨%1$s⁩ مستوى قوّة %2$s⁩.</string>
+    <string name="notice_power_level_diff">‏⁨%1$s⁩ من ⁨%2$s⁩ إلى ⁨%3$s⁩</string>
+
+    <string name="initial_sync_start_importing_account">المزامنة الأولية:
+\nيستورد الحساب…</string>
 </resources>
diff --git a/matrix-sdk-android/src/main/res/values-es/strings.xml b/matrix-sdk-android/src/main/res/values-es/strings.xml
index ae1f5633bf1e16b56dfbcdcc26288aaa5a86ba2a..1b1935602cd560cf89804cd86f0cdb67a630ceeb 100644
--- a/matrix-sdk-android/src/main/res/values-es/strings.xml
+++ b/matrix-sdk-android/src/main/res/values-es/strings.xml
@@ -147,4 +147,98 @@
 
     <string name="key_verification_request_fallback_message">%s solicita verificar su clave, pero su cliente no soporta la verificación de la clave en chat. Necesitará usar la verificación de claves clásica para poder verificar las claves.</string>
 
+    <string name="summary_you_sent_image">Enviaste una imagen.</string>
+    <string name="summary_you_sent_sticker">Enviaste un sticker.</string>
+
+    <string name="notice_room_invite_no_invitee_by_you">Tu invitación</string>
+    <string name="notice_room_created">%1$s creó la habitación</string>
+    <string name="notice_room_created_by_you">Tu creaste la habitación</string>
+    <string name="notice_room_invite_by_you">Invitaste a %1$s</string>
+    <string name="notice_room_join_by_you">Te uniste a la Sala</string>
+    <string name="notice_room_leave_by_you">Dejaste la Sala</string>
+    <string name="notice_room_reject_by_you">Rechazaste la invitación</string>
+    <string name="notice_room_kick_by_you">Tu pateaste a %1$s</string>
+    <string name="notice_room_unban_by_you">Tu desbanaste a %1$s</string>
+    <string name="notice_room_ban_by_you">Usted prohibió a %1$s</string>
+    <string name="notice_room_withdraw_by_you">Retiró la invitación de %1$s\'s</string>
+    <string name="notice_avatar_url_changed_by_you">Cambiaste tu avatar</string>
+    <string name="notice_display_name_set_by_you">Establece su nombre de visualización en %1$s</string>
+    <string name="notice_display_name_changed_from_by_you">Cambiaste tu nombre para mostrar de %1$s a %2$s</string>
+    <string name="notice_display_name_removed_by_you">Quitaste tu nombre para mostrar (era %1$s)</string>
+    <string name="notice_room_topic_changed_by_you">Cambiaste el tema a: %1$s</string>
+    <string name="notice_room_avatar_changed">%1$s cambió el avatar de la sala</string>
+    <string name="notice_room_avatar_changed_by_you">Cambiaste el avatar de la habitación</string>
+    <string name="notice_room_name_changed_by_you">Cambiaste el nombre de la habitación a: %1$s</string>
+    <string name="notice_placed_video_call_by_you">Hiciste una videollamada.</string>
+    <string name="notice_placed_voice_call_by_you">Hiciste una llamada de voz.</string>
+    <string name="notice_call_candidates">%s envió datos para configurar la llamada.</string>
+    <string name="notice_call_candidates_by_you">Enviaste datos para configurar la llamada.</string>
+    <string name="notice_answered_call_by_you">Respondiste la llamada.</string>
+    <string name="notice_ended_call_by_you">Terminaste la llamada.</string>
+    <string name="notice_made_future_room_visibility_by_you">Hiciste visible el futuro historial de la %1$s</string>
+    <string name="notice_end_to_end_by_you">Activó el cifrado de un extremo a otro (%1$s)</string>
+    <string name="notice_room_update_by_you">Has mejorado esta habitación.</string>
+
+    <string name="notice_requested_voip_conference_by_you">Solicitaste una conferencia de VoIP</string>
+    <string name="notice_room_name_removed_by_you">Quitaste el nombre de la sala</string>
+    <string name="notice_room_topic_removed_by_you">Quitaste el tema de la sala</string>
+    <string name="notice_room_avatar_removed">%1$s eliminó el avatar de la habitación</string>
+    <string name="notice_room_avatar_removed_by_you">Quitaste el avatar de la habitación</string>
+    <string name="notice_profile_change_redacted_by_you">Actualizaste tu perfil %1$s</string>
+    <string name="notice_room_third_party_invite_by_you">Enviaste una invitación a %1$s para unirse a la sala</string>
+    <string name="notice_room_third_party_revoked_invite_by_you">Revocaste la invitación para que %1$s se una a la sala</string>
+    <string name="notice_room_third_party_registered_invite_by_you">Aceptaste la invitación para %1$s</string>
+
+    <string name="notice_widget_added">%1$s agrego el widget %2$s</string>
+    <string name="notice_widget_added_by_you">Agregaste el widget %1$s</string>
+    <string name="notice_widget_removed">%1$s eliminó el widget %2$s</string>
+    <string name="notice_widget_removed_by_you">Quitaste el widget %1$s</string>
+    <string name="notice_widget_modified">%1$s modifico el widget %2$s</string>
+    <string name="notice_widget_modified_by_you">Modificaste el widget %1$s</string>
+
+    <string name="power_level_admin">Administrador</string>
+    <string name="power_level_moderator">Moderador</string>
+    <string name="power_level_default">Por defecto</string>
+    <string name="power_level_custom">Personalizado (%1$d)</string>
+    <string name="power_level_custom_no_value">Personalizado</string>
+
+    <string name="notice_power_level_changed_by_you">Cambiaste el nivel de potencia de %1$s.</string>
+    <string name="notice_power_level_changed">%1$s cambió el nivel de potencia de %2$s.</string>
+    <string name="notice_power_level_diff">%1$s de %2$s a %3$s</string>
+
+    <string name="notice_room_invite_no_invitee_with_reason_by_you">Tu invitación. Razón: %1$s</string>
+    <string name="notice_room_invite_with_reason_by_you">"nvitaste a %1$s.  Razón: %2$s"</string>
+    <string name="notice_room_join_with_reason_by_you">Te uniste a la habitación. Razón: %1$s</string>
+    <string name="notice_room_leave_with_reason_by_you">Dejaste la habitación. Razón: %1$s</string>
+    <string name="notice_room_reject_with_reason_by_you">Rechazaste la invitación. Razón: %1$s</string>
+    <string name="notice_room_kick_with_reason_by_you">Pateaste a %1$s. Motivo: %2$s</string>
+    <string name="notice_room_unban_with_reason_by_you">Has desactivado a %1$s. Motivo: %2$s</string>
+    <string name="notice_room_ban_with_reason_by_you">Prohibiste a %1$s. Motivo: %2$s</string>
+    <string name="notice_room_third_party_invite_with_reason_by_you">Enviaste una invitación a %1$s para unirse a la sala. Motivo: %2$s</string>
+    <string name="notice_room_third_party_revoked_invite_with_reason_by_you">Revocaste la invitación para que %1$s se una a la sala. Motivo: %2$s</string>
+    <string name="notice_room_third_party_registered_invite_with_reason_by_you">Aceptaste la invitación para %1$s. Motivo: %2$s</string>
+    <string name="notice_room_withdraw_with_reason_by_you">Retiró la invitación de %1$s\'s. Motivo: %2$s</string>
+
+    <plurals name="notice_room_aliases_added_by_you">
+        <item quantity="one">Agregaste %1$s como dirección para esta sala.</item>
+        <item quantity="other">Agregaste %1$s como direcciones para esta sala.</item>
+    </plurals>
+
+    <plurals name="notice_room_aliases_removed_by_you">
+        <item quantity="one">Quitaste %1$s como dirección para esta sala.</item>
+        <item quantity="other">Quitaste %2$s como direcciones para esta sala.</item>
+    </plurals>
+
+    <string name="notice_room_aliases_added_and_removed">"%1$s agregó %2$s  y eliminó %3$s como direcciones para esta sala."</string>
+    <string name="notice_room_aliases_added_and_removed_by_you">Agregaste %1$s y quitaste %2$s como direcciones para esta sala.</string>
+
+    <string name="notice_room_canonical_alias_set_by_you">Estableciste la dirección principal de esta sala en %1$s.</string>
+    <string name="notice_room_canonical_alias_unset_by_you">Quitaste la dirección principal de esta sala.</string>
+
+    <string name="notice_room_guest_access_can_join_by_you">Ha permitido que los invitados se unan a la sala.</string>
+    <string name="notice_room_guest_access_forbidden_by_you">Ha impedido que los invitados se unan a la sala.</string>
+
+    <string name="notice_end_to_end_ok_by_you">Activó el cifrado de extremo a extremo.</string>
+    <string name="notice_end_to_end_unknown_algorithm_by_you">Activó el cifrado de un extremo a otro (algoritmo %1$s no reconocido).</string>
+
 </resources>