diff --git a/dependencies.gradle b/dependencies.gradle
index 93a62a548ed30ba09328c73d4e43369e7997db11..5083dd455685008d9f7ccfd3cf09c25e4f607783 100644
--- a/dependencies.gradle
+++ b/dependencies.gradle
@@ -22,7 +22,7 @@ def markwon = "4.6.2"
 def moshi = "1.13.0"
 def lifecycle = "2.5.1"
 def flowBinding = "1.2.0"
-def flipper = "0.156.0"
+def flipper = "0.157.0"
 def epoxy = "4.6.2"
 def mavericks = "2.7.0"
 def glide = "4.13.2"
@@ -30,7 +30,7 @@ def bigImageViewer = "1.8.1"
 def jjwt = "0.11.5"
 def vanniktechEmoji = "0.15.0"
 
-def fragment = "1.5.1"
+def fragment = "1.5.2"
 
 // Testing
 def mockk = "1.12.3" // We need to use 1.12.3 to have mocking in androidTest until a new version is released: https://github.com/mockk/mockk/issues/819
diff --git a/dependencies_groups.gradle b/dependencies_groups.gradle
index d5972ed8461eff2b78f6deb8fbd20440b306bb6f..bcd737acc973c4cb904f3cadc215b79cddc1cb88 100644
--- a/dependencies_groups.gradle
+++ b/dependencies_groups.gradle
@@ -107,7 +107,9 @@ ext.groups = [
                         'com.pinterest.ktlint',
                         'com.posthog.android',
                         'com.squareup',
+                        'com.squareup.curtains',
                         'com.squareup.duktape',
+                        'com.squareup.leakcanary',
                         'com.squareup.moshi',
                         'com.squareup.okhttp3',
                         'com.squareup.okio',
diff --git a/matrix-sdk-android/build.gradle b/matrix-sdk-android/build.gradle
index e57154f6a8ad006db3e7934f236cba66e850b73f..bcd829ce74ac75155bffed0775df5e5cd523f98b 100644
--- a/matrix-sdk-android/build.gradle
+++ b/matrix-sdk-android/build.gradle
@@ -19,7 +19,7 @@ buildscript {
         }
     }
     dependencies {
-        classpath "io.realm:realm-gradle-plugin:10.11.0"
+        classpath "io.realm:realm-gradle-plugin:10.11.1"
     }
 }
 
@@ -202,7 +202,7 @@ dependencies {
     implementation libs.apache.commonsImaging
 
     // Phone number https://github.com/google/libphonenumber
-    implementation 'com.googlecode.libphonenumber:libphonenumber:8.12.53'
+    implementation 'com.googlecode.libphonenumber:libphonenumber:8.12.54'
 
     testImplementation libs.tests.junit
     // Note: version sticks to 1.9.2 due to https://github.com/mockk/mockk/issues/281
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/debug/DebugService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/debug/DebugService.kt
index d0cee08831d90160ab84c5713b7a886d68b31356..7f5e4f2ee7c3b3e859c97764d6a17e598cb3cd5f 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/debug/DebugService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/debug/DebugService.kt
@@ -28,7 +28,7 @@ interface DebugService {
     fun getAllRealmConfigurations(): List<RealmConfiguration>
 
     /**
-     * Prints out info on DB size to logcat.
+     * Get info on DB size.
      */
-    fun logDbUsageInfo()
+    fun getDbUsageInfo(): String
 }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/failure/Extensions.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/failure/Extensions.kt
index 68b931b33c21f6a6652b72628eea1e36044aedb8..5b41ddaaec9f085ba3c413917b958f56770030fb 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/failure/Extensions.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/failure/Extensions.kt
@@ -89,10 +89,14 @@ fun Throwable.isInvalidUIAAuth() = this is Failure.ServerError &&
 fun Throwable.isHomeserverUnavailable() = this is Failure.NetworkConnection &&
         this.ioException is UnknownHostException
 
+fun Throwable.isHomeserverConnectionError() = this is Failure.NetworkConnection
+
 fun Throwable.isMissingEmailVerification() = this is Failure.ServerError &&
         error.code == MatrixError.M_UNAUTHORIZED &&
         error.message == "Unable to get validated threepid"
 
+fun Throwable.isUnrecognisedCertificate() = this is Failure.UnrecognizedCertificateFailure
+
 /**
  * Try to convert to a RegistrationFlowResponse. Return null in the cases it's not possible
  */
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 63c1c25130de509574070607bada8df944f3f667..13993149f4eaffa4c3d34ab23b90b19c0996b039 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
@@ -323,9 +323,9 @@ interface Session {
     fun getUiaSsoFallbackUrl(authenticationSessionId: String): String
 
     /**
-     * Debug API, will print out info on DB size to logcat.
+     * Debug API, will return info about the DB.
      */
-    fun logDbUsageInfo()
+    fun getDbUsageInfo(): String
 
     /**
      * Debug API, return the list of all RealmConfiguration used by this session.
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/RoomGuestAccessContent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/RoomGuestAccessContent.kt
index 7dd853d75d785dda18530866b016252b7f127639..b229a354584e8a884a2aa0bd046d3ab7efc77af2 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/RoomGuestAccessContent.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/RoomGuestAccessContent.kt
@@ -29,14 +29,12 @@ data class RoomGuestAccessContent(
         // Required. Whether guests can join the room. One of: ["can_join", "forbidden"]
         @Json(name = "guest_access") val guestAccessStr: String? = null
 ) {
-    val guestAccess: GuestAccess? = when (guestAccessStr) {
-        "can_join" -> GuestAccess.CanJoin
-        "forbidden" -> GuestAccess.Forbidden
-        else -> {
-            Timber.w("Invalid value for GuestAccess: `$guestAccessStr`")
-            null
-        }
-    }
+    val guestAccess: GuestAccess? = GuestAccess.values()
+            .find { it.value == guestAccessStr }
+            ?: run {
+                Timber.w("Invalid value for GuestAccess: `$guestAccessStr`")
+                null
+            }
 }
 
 @JsonClass(generateAdapter = false)
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/RoomHistoryVisibility.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/RoomHistoryVisibility.kt
index 2b0ea1d8fbcae85b219881de691a14186da248be..eef986ef963d5c0d4334b3a06fd01e27b9ee3f26 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/RoomHistoryVisibility.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/RoomHistoryVisibility.kt
@@ -23,30 +23,30 @@ import com.squareup.moshi.JsonClass
  * Ref: https://matrix.org/docs/spec/client_server/latest#room-history-visibility
  */
 @JsonClass(generateAdapter = false)
-enum class RoomHistoryVisibility {
+enum class RoomHistoryVisibility(val value: String) {
     /**
      * All events while this is the m.room.history_visibility value may be shared by any
      * participating homeserver with anyone, regardless of whether they have ever joined the room.
      */
-    @Json(name = "world_readable") WORLD_READABLE,
+    @Json(name = "world_readable") WORLD_READABLE("world_readable"),
 
     /**
      * Previous events are always accessible to newly joined members. All events in the
      * room are accessible, even those sent when the member was not a part of the room.
      */
-    @Json(name = "shared") SHARED,
+    @Json(name = "shared") SHARED("shared"),
 
     /**
      * Events are accessible to newly joined members from the point they were invited onwards.
      * Events stop being accessible when the member's state changes to something other than invite or join.
      */
-    @Json(name = "invited") INVITED,
+    @Json(name = "invited") INVITED("invited"),
 
     /**
      * Events are accessible to newly joined members from the point they joined the room onwards.
      * Events stop being accessible when the member's state changes to something other than join.
      */
-    @Json(name = "joined") JOINED
+    @Json(name = "joined") JOINED("joined")
 }
 
 /**
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/RoomHistoryVisibilityContent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/RoomHistoryVisibilityContent.kt
index 39b4722c0c5e8953b799d269b590f1778022388d..696cd8f6132f8caf4d577f1d6635b9efeb671633 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/RoomHistoryVisibilityContent.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/RoomHistoryVisibilityContent.kt
@@ -24,14 +24,10 @@ import timber.log.Timber
 data class RoomHistoryVisibilityContent(
         @Json(name = "history_visibility") val historyVisibilityStr: String? = null
 ) {
-    val historyVisibility: RoomHistoryVisibility? = when (historyVisibilityStr) {
-        "world_readable" -> RoomHistoryVisibility.WORLD_READABLE
-        "shared" -> RoomHistoryVisibility.SHARED
-        "invited" -> RoomHistoryVisibility.INVITED
-        "joined" -> RoomHistoryVisibility.JOINED
-        else -> {
-            Timber.w("Invalid value for RoomHistoryVisibility: `$historyVisibilityStr`")
-            null
-        }
-    }
+    val historyVisibility: RoomHistoryVisibility? = RoomHistoryVisibility.values()
+            .find { it.value == historyVisibilityStr }
+            ?: run {
+                Timber.w("Invalid value for RoomHistoryVisibility: `$historyVisibilityStr`")
+                null
+            }
 }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/create/RoomFeaturePreset.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/create/RoomFeaturePreset.kt
index 6487ad947fa18356c5c6b9d3893ec080a4468170..3ed3e2d3a69bd58f5463d5baa0799e97fc824da5 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/create/RoomFeaturePreset.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/create/RoomFeaturePreset.kt
@@ -16,7 +16,6 @@
 
 package org.matrix.android.sdk.api.session.room.model.create
 
-import org.matrix.android.sdk.api.session.events.model.Event
 import org.matrix.android.sdk.api.session.events.model.EventType
 import org.matrix.android.sdk.api.session.events.model.toContent
 import org.matrix.android.sdk.api.session.homeserver.HomeServerCapabilities
@@ -30,7 +29,7 @@ interface RoomFeaturePreset {
 
     fun updateRoomParams(params: CreateRoomParams)
 
-    fun setupInitialStates(): List<Event>?
+    fun setupInitialStates(): List<CreateRoomStateEvent>?
 }
 
 class RestrictedRoomPreset(val homeServerCapabilities: HomeServerCapabilities, val restrictedList: List<RoomJoinRulesAllowEntry>) : RoomFeaturePreset {
@@ -41,9 +40,9 @@ class RestrictedRoomPreset(val homeServerCapabilities: HomeServerCapabilities, v
         params.roomVersion = homeServerCapabilities.versionOverrideForFeature(HomeServerCapabilities.ROOM_CAP_RESTRICTED)
     }
 
-    override fun setupInitialStates(): List<Event>? {
+    override fun setupInitialStates(): List<CreateRoomStateEvent> {
         return listOf(
-                Event(
+                CreateRoomStateEvent(
                         type = EventType.STATE_ROOM_JOIN_RULES,
                         stateKey = "",
                         content = RoomJoinRulesContent(
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageBeaconLocationDataContent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageBeaconLocationDataContent.kt
index e261ab52066495786445e215d852e2d85e3fc7a6..aa2777d0a671621bf33e538063745ddaf33aaeef 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageBeaconLocationDataContent.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageBeaconLocationDataContent.kt
@@ -22,7 +22,7 @@ import org.matrix.android.sdk.api.session.events.model.Content
 import org.matrix.android.sdk.api.session.room.model.relation.RelationDefaultContent
 
 /**
- * Content of the state event of type
+ * Content of the event of type
  * [EventType.BEACON_LOCATION_DATA][org.matrix.android.sdk.api.session.events.model.EventType.BEACON_LOCATION_DATA]
  *
  * It contains location data related to a live location share.
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/sync/SyncService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/sync/SyncService.kt
index 71f7ab8494b6be34bd67c0e4629e6667b58a1675..6640b8a9af6d0eedaa897002f1e8733820918b2c 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/sync/SyncService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/sync/SyncService.kt
@@ -53,6 +53,11 @@ interface SyncService {
      */
     fun getSyncState(): SyncState
 
+    /**
+     * This method returns true if the sync thread is alive, i.e. started.
+     */
+    fun isSyncThreadAlive(): Boolean
+
     /**
      * This method allows to listen the sync state.
      * @return a [LiveData] of [SyncState].
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/VerificationTransportToDevice.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/VerificationTransportToDevice.kt
index 861a7a3a77a3a6300571b3dd06505ad7fc320ecf..23a75d2bb3a46be13a90146345d45ba87ef74612 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/VerificationTransportToDevice.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/VerificationTransportToDevice.kt
@@ -76,12 +76,12 @@ internal class VerificationTransportToDevice(
                 .configureWith(SendToDeviceTask.Params(MessageType.MSGTYPE_VERIFICATION_REQUEST, contentMap)) {
                     this.callback = object : MatrixCallback<Unit> {
                         override fun onSuccess(data: Unit) {
-                            Timber.v("## verification [$tx.transactionId] send toDevice request success")
+                            Timber.v("## verification [${tx?.transactionId}] send toDevice request success")
                             callback.invoke(localId, validKeyReq)
                         }
 
                         override fun onFailure(failure: Throwable) {
-                            Timber.e("## verification [$tx.transactionId] failed to send toDevice request")
+                            Timber.e("## verification [${tx?.transactionId}] failed to send toDevice request")
                         }
                     }
                 }
@@ -103,12 +103,12 @@ internal class VerificationTransportToDevice(
                 .configureWith(SendToDeviceTask.Params(EventType.KEY_VERIFICATION_READY, contentMap)) {
                     this.callback = object : MatrixCallback<Unit> {
                         override fun onSuccess(data: Unit) {
-                            Timber.v("## verification [$tx.transactionId] send toDevice request success")
+                            Timber.v("## verification [${tx?.transactionId}] send toDevice request success")
                             callback?.invoke()
                         }
 
                         override fun onFailure(failure: Throwable) {
-                            Timber.e("## verification [$tx.transactionId] failed to send toDevice request")
+                            Timber.e("## verification [${tx?.transactionId}] failed to send toDevice request")
                         }
                     }
                 }
@@ -136,7 +136,7 @@ internal class VerificationTransportToDevice(
                 .configureWith(SendToDeviceTask.Params(type, contentMap)) {
                     this.callback = object : MatrixCallback<Unit> {
                         override fun onSuccess(data: Unit) {
-                            Timber.v("## SAS verification [$tx.transactionId] toDevice type '$type' success.")
+                            Timber.v("## SAS verification [${tx.transactionId}] toDevice type '$type' success.")
                             if (onDone != null) {
                                 onDone()
                             } else {
@@ -149,7 +149,7 @@ internal class VerificationTransportToDevice(
                         }
 
                         override fun onFailure(failure: Throwable) {
-                            Timber.e("## SAS verification [$tx.transactionId] failed to send toDevice in state : $tx.state")
+                            Timber.e("## SAS verification [${tx.transactionId}] failed to send toDevice in state : ${tx.state}")
                             tx.cancel(onErrorReason)
                         }
                     }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmCompactOnLaunch.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmCompactOnLaunch.kt
new file mode 100644
index 0000000000000000000000000000000000000000..1efb2541a75e9b0648088a3295a05804d45cd23f
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmCompactOnLaunch.kt
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2022 The Matrix.org Foundation C.I.C.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.matrix.android.sdk.internal.database
+
+import io.realm.DefaultCompactOnLaunchCallback
+
+class RealmCompactOnLaunch : DefaultCompactOnLaunchCallback() {
+    /**
+     * Forces all RealmCompactOnLaunch instances to be equal.
+     * Avoids Realm throwing when multiple instances of this class are used.
+     */
+    override fun equals(other: Any?) = other is RealmCompactOnLaunch
+    override fun hashCode() = 0x1000
+}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmLiveEntityObserver.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmLiveEntityObserver.kt
index f2f88e216bcc98e7f67d9b564e1c913e1cc4e193..020b42b3b84e9853ec9aefffb91a0366cd3d3f86 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmLiveEntityObserver.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmLiveEntityObserver.kt
@@ -38,7 +38,7 @@ internal abstract class RealmLiveEntityObserver<T : RealmObject>(protected val r
         LiveEntityObserver, RealmChangeListener<RealmResults<T>> {
 
     private companion object {
-        val BACKGROUND_HANDLER = createBackgroundHandler("LIVE_ENTITY_BACKGROUND")
+        val BACKGROUND_HANDLER = createBackgroundHandler("Matrix-LIVE_ENTITY_BACKGROUND")
     }
 
     protected val observerScope = CoroutineScope(SupervisorJob() + BACKGROUND_HANDLER.asCoroutineDispatcher())
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/SessionRealmConfigurationFactory.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/SessionRealmConfigurationFactory.kt
index 949dd5daa1de60d76e2cd0f087c411a2d9b1f632..16a55c22ace7ec870c7c7d9f6aee13ae87332702 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/SessionRealmConfigurationFactory.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/SessionRealmConfigurationFactory.kt
@@ -64,7 +64,7 @@ internal class SessionRealmConfigurationFactory @Inject constructor(
         }
 
         val realmConfiguration = RealmConfiguration.Builder()
-                .compactOnLaunch()
+                .compactOnLaunch(RealmCompactOnLaunch())
                 .directory(directory)
                 .name(REALM_NAME)
                 .apply {
@@ -93,7 +93,7 @@ internal class SessionRealmConfigurationFactory @Inject constructor(
             return
         }
 
-        listOf(REALM_NAME, "$REALM_NAME.lock", "$REALM_NAME.note", "$REALM_NAME.management").forEach { file ->
+        listOf(REALM_NAME, "${REALM_NAME}.lock", "${REALM_NAME}.note", "${REALM_NAME}.management").forEach { file ->
             try {
                 File(directory, file).deleteRecursively()
             } catch (e: Exception) {
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/tools/RealmDebugTools.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/tools/RealmDebugTools.kt
index dc20549eb3e7da3aed0e8623596ea6a956045614..2e9c3303d4a0977e21f171069cc1f8d4ac960d31 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/tools/RealmDebugTools.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/tools/RealmDebugTools.kt
@@ -19,16 +19,15 @@ package org.matrix.android.sdk.internal.database.tools
 import io.realm.Realm
 import io.realm.RealmConfiguration
 import org.matrix.android.sdk.BuildConfig
-import timber.log.Timber
 
 internal class RealmDebugTools(
         private val realmConfiguration: RealmConfiguration
 ) {
     /**
-     * Log info about the DB.
+     * Get info about the DB.
      */
-    fun logInfo(baseName: String) {
-        buildString {
+    fun getInfo(baseName: String): String {
+        return buildString {
             append("\n$baseName Realm located at : ${realmConfiguration.realmDirectory}/${realmConfiguration.realmFileName}")
 
             if (BuildConfig.LOG_PRIVATE_DATA) {
@@ -54,7 +53,6 @@ internal class RealmDebugTools(
                 separator()
             }
         }
-                .let { Timber.i(it) }
     }
 
     private fun StringBuilder.separator() = append("\n==============================================")
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/debug/DefaultDebugService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/debug/DefaultDebugService.kt
index 3f2e6fafc880bd9081968afd64bfe7fbbf3000f0..46479c3db65e6838ad0a5345583aadff5388d9a3 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/debug/DefaultDebugService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/debug/DefaultDebugService.kt
@@ -36,9 +36,9 @@ internal class DefaultDebugService @Inject constructor(
                 realmConfigurationGlobal
     }
 
-    override fun logDbUsageInfo() {
-        RealmDebugTools(realmConfigurationAuth).logInfo("Auth")
-        RealmDebugTools(realmConfigurationGlobal).logInfo("Global")
-        sessionManager.getLastSession()?.logDbUsageInfo()
+    override fun getDbUsageInfo() = buildString {
+        append(RealmDebugTools(realmConfigurationAuth).getInfo("Auth"))
+        append(RealmDebugTools(realmConfigurationGlobal).getInfo("Global"))
+        append(sessionManager.getLastSession()?.getDbUsageInfo())
     }
 }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/MatrixModule.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/MatrixModule.kt
index 49713a1d7f3502f83782f3193e81c8b9c10c7f62..f2f8a5dc044dbb839da79fd658bebaff527241a3 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/MatrixModule.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/MatrixModule.kt
@@ -40,7 +40,7 @@ internal object MatrixModule {
                 io = Dispatchers.IO,
                 computation = Dispatchers.Default,
                 main = Dispatchers.Main,
-                crypto = createBackgroundHandler("Crypto_Thread").asCoroutineDispatcher(),
+                crypto = createBackgroundHandler("Matrix-Crypto_Thread").asCoroutineDispatcher(),
                 dmVerif = Executors.newSingleThreadExecutor().asCoroutineDispatcher()
         )
     }
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 57db187bdc8047a9dc7eddf36dedcaf4f6cd2cf1..679c5085efc5068a9c6db20dc20371690877c8f5 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
@@ -263,11 +263,11 @@ internal class DefaultSession @Inject constructor(
         }
     }
 
-    override fun logDbUsageInfo() {
-        RealmDebugTools(realmConfiguration).logInfo("Session")
-        RealmDebugTools(realmConfigurationCrypto).logInfo("Crypto")
-        RealmDebugTools(realmConfigurationIdentity).logInfo("Identity")
-        RealmDebugTools(realmConfigurationContentScanner).logInfo("ContentScanner")
+    override fun getDbUsageInfo() = buildString {
+        append(RealmDebugTools(realmConfiguration).getInfo("Session"))
+        append(RealmDebugTools(realmConfigurationCrypto).getInfo("Crypto"))
+        append(RealmDebugTools(realmConfigurationIdentity).getInfo("Identity"))
+        append(RealmDebugTools(realmConfigurationContentScanner).getInfo("ContentScanner"))
     }
 
     override fun getRealmConfigurations(): List<RealmConfiguration> {
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/create/CreateRoomBodyBuilder.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/create/CreateRoomBodyBuilder.kt
index faf68d538fd9d8fd52f650a9e040564dfcad826a..4105c77cc85ef5b92476ca60fcf4f55a895c2e54 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/create/CreateRoomBodyBuilder.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/create/CreateRoomBodyBuilder.kt
@@ -20,8 +20,13 @@ import org.matrix.android.sdk.api.crypto.MXCRYPTO_ALGORITHM_MEGOLM
 import org.matrix.android.sdk.api.extensions.tryOrNull
 import org.matrix.android.sdk.api.session.events.model.Event
 import org.matrix.android.sdk.api.session.events.model.EventType
+import org.matrix.android.sdk.api.session.events.model.content.EncryptionEventContent
+import org.matrix.android.sdk.api.session.events.model.toContent
 import org.matrix.android.sdk.api.session.identity.IdentityServiceError
 import org.matrix.android.sdk.api.session.identity.toMedium
+import org.matrix.android.sdk.api.session.room.model.RoomAvatarContent
+import org.matrix.android.sdk.api.session.room.model.RoomGuestAccessContent
+import org.matrix.android.sdk.api.session.room.model.RoomHistoryVisibilityContent
 import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams
 import org.matrix.android.sdk.api.util.MimeTypes
 import org.matrix.android.sdk.internal.crypto.DeviceListManager
@@ -78,7 +83,7 @@ internal class CreateRoomBodyBuilder @Inject constructor(
                         buildAvatarEvent(params),
                         buildGuestAccess(params)
                 ) +
-                        params.featurePreset?.setupInitialStates().orEmpty() +
+                        buildFeaturePresetInitialStates(params) +
                         buildCustomInitialStates(params)
                 )
                 .takeIf { it.isNotEmpty() }
@@ -99,6 +104,16 @@ internal class CreateRoomBodyBuilder @Inject constructor(
         )
     }
 
+    private fun buildFeaturePresetInitialStates(params: CreateRoomParams): List<Event> {
+        return params.featurePreset?.setupInitialStates().orEmpty().map {
+            Event(
+                    type = it.type,
+                    stateKey = it.stateKey,
+                    content = it.content
+            )
+        }
+    }
+
     private fun buildCustomInitialStates(params: CreateRoomParams): List<Event> {
         return params.initialStates.map {
             Event(
@@ -123,7 +138,7 @@ internal class CreateRoomBodyBuilder @Inject constructor(
             Event(
                     type = EventType.STATE_ROOM_AVATAR,
                     stateKey = "",
-                    content = mapOf("url" to response.contentUri)
+                    content = RoomAvatarContent(response.contentUri).toContent()
             )
         }
     }
@@ -134,7 +149,7 @@ internal class CreateRoomBodyBuilder @Inject constructor(
                     Event(
                             type = EventType.STATE_ROOM_HISTORY_VISIBILITY,
                             stateKey = "",
-                            content = mapOf("history_visibility" to it)
+                            content = RoomHistoryVisibilityContent(it.value).toContent()
                     )
                 }
     }
@@ -145,7 +160,7 @@ internal class CreateRoomBodyBuilder @Inject constructor(
                     Event(
                             type = EventType.STATE_ROOM_GUEST_ACCESS,
                             stateKey = "",
-                            content = mapOf("guest_access" to it.value)
+                            content = RoomGuestAccessContent(it.value).toContent()
                     )
                 }
     }
@@ -167,7 +182,7 @@ internal class CreateRoomBodyBuilder @Inject constructor(
                     Event(
                             type = EventType.STATE_ROOM_ENCRYPTION,
                             stateKey = "",
-                            content = mapOf("algorithm" to it)
+                            content = EncryptionEventContent(it).toContent()
                     )
                 }
     }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/MultipleEventSendingDispatcherWorker.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/MultipleEventSendingDispatcherWorker.kt
index 2afca6e554a76d92be906640db22dc5787e9b7f8..801ff0ec7985c56576ab95ae54d46133d7101704 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/MultipleEventSendingDispatcherWorker.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/MultipleEventSendingDispatcherWorker.kt
@@ -53,7 +53,7 @@ internal class MultipleEventSendingDispatcherWorker(context: Context, params: Wo
     @Inject lateinit var timelineSendEventWorkCommon: TimelineSendEventWorkCommon
     @Inject lateinit var localEchoRepository: LocalEchoRepository
 
-    override fun doOnError(params: Params): Result {
+    override fun doOnError(params: Params, failureMessage: String): Result {
         params.localEchoIds.forEach { localEchoIds ->
             localEchoRepository.updateSendState(
                     eventId = localEchoIds.eventId,
@@ -63,7 +63,7 @@ internal class MultipleEventSendingDispatcherWorker(context: Context, params: Wo
             )
         }
 
-        return super.doOnError(params)
+        return super.doOnError(params, failureMessage)
     }
 
     override fun injectWith(injector: SessionComponent) {
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/EventSenderProcessorThread.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/EventSenderProcessorThread.kt
index 51107c96557bd6737287e1d67f5b6fd1b8c6a02b..55363a725103e5cb93183c96cec1dc20aab8ec16 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/EventSenderProcessorThread.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/EventSenderProcessorThread.kt
@@ -55,7 +55,7 @@ internal class EventSenderProcessorThread @Inject constructor(
         private val queuedTaskFactory: QueuedTaskFactory,
         private val taskExecutor: TaskExecutor,
         private val memento: QueueMemento
-) : Thread("SENDER_THREAD_SID_${sessionParams.credentials.sessionId()}"), EventSenderProcessor {
+) : Thread("Matrix-SENDER_THREAD_SID_${sessionParams.credentials.sessionId()}"), EventSenderProcessor {
 
     private fun markAsManaged(task: QueuedTask) {
         memento.track(task)
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 4eaac67e5ae3662ddfe4632e18cef9446e533deb..c380ccf14f86e91e2afdde90640563c426ff9356 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
@@ -76,7 +76,7 @@ internal class DefaultTimeline(
 ) : Timeline {
 
     companion object {
-        val BACKGROUND_HANDLER = createBackgroundHandler("DefaultTimeline_Thread")
+        val BACKGROUND_HANDLER = createBackgroundHandler("Matrix-DefaultTimeline_Thread")
     }
 
     override val timelineID = UUID.randomUUID().toString()
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/DefaultSyncService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/DefaultSyncService.kt
index 691dd7b20d31add110d5f2a251108ee7e44ab989..76c3c38abf637cccb816caf98f85eba8a2c514f2 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/DefaultSyncService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/DefaultSyncService.kt
@@ -73,6 +73,8 @@ internal class DefaultSyncService @Inject constructor(
 
     override fun getSyncState() = getSyncThread().currentState()
 
+    override fun isSyncThreadAlive() = getSyncThread().isAlive
+
     override fun getSyncRequestStateFlow() = syncRequestStateTracker.syncRequestState
 
     override fun hasAlreadySynced(): Boolean {
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/job/SyncThread.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/job/SyncThread.kt
index 24a60a80da6ae50059dd3d619a2922b6db3c67fc..b47b2156552ad344ce24d2c14f34b6d883f92d0b 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/job/SyncThread.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/job/SyncThread.kt
@@ -62,7 +62,7 @@ internal class SyncThread @Inject constructor(
         private val backgroundDetectionObserver: BackgroundDetectionObserver,
         private val activeCallHandler: ActiveCallHandler,
         private val lightweightSettingsStorage: DefaultLightweightSettingsStorage
-) : Thread("SyncThread"), NetworkConnectivityChecker.Listener, BackgroundDetectionObserver.Listener {
+) : Thread("Matrix-SyncThread"), NetworkConnectivityChecker.Listener, BackgroundDetectionObserver.Listener {
 
     private var state: SyncState = SyncState.Idle
     private var liveState = MutableLiveData(state)
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 0cc7944d58c02eba1f173932d0254eb34cca9697..a04bc74628063f268d176991af94ec95649c0511 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
@@ -30,6 +30,7 @@ import org.matrix.android.sdk.internal.session.sync.SyncTask
 import org.matrix.android.sdk.internal.worker.SessionSafeCoroutineWorker
 import org.matrix.android.sdk.internal.worker.SessionWorkerParams
 import org.matrix.android.sdk.internal.worker.WorkerParamsFactory
+import org.matrix.android.sdk.internal.worker.startChain
 import timber.log.Timber
 import java.util.concurrent.TimeUnit
 import javax.inject.Inject
@@ -136,6 +137,7 @@ internal class SyncWorker(context: Context, workerParameters: WorkerParameters,
                     .setConstraints(WorkManagerProvider.workConstraints)
                     .setBackoffCriteria(BackoffPolicy.LINEAR, WorkManagerProvider.BACKOFF_DELAY_MILLIS, TimeUnit.MILLISECONDS)
                     .setInputData(data)
+                    .startChain(true)
                     .build()
             workManagerProvider.workManager
                     .enqueueUniqueWork(BG_SYNC_WORK_NAME, ExistingWorkPolicy.APPEND_OR_REPLACE, workRequest)
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/BackgroundDetectionObserver.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/BackgroundDetectionObserver.kt
index 901d0eca8fb98b1028f5e93210097480507ff75c..dea5f131b9bf2cf294b9512c123f8ec6a33d9e0e 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/BackgroundDetectionObserver.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/BackgroundDetectionObserver.kt
@@ -49,13 +49,13 @@ internal class DefaultBackgroundDetectionObserver : BackgroundDetectionObserver
     }
 
     override fun onStart(owner: LifecycleOwner) {
-        Timber.v("App returning to foreground…")
+        Timber.d("App returning to foreground…")
         isInBackground = false
         listeners.forEach { it.onMoveToForeground() }
     }
 
     override fun onStop(owner: LifecycleOwner) {
-        Timber.v("App going to background…")
+        Timber.d("App going to background…")
         isInBackground = true
         listeners.forEach { it.onMoveToBackground() }
     }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/worker/SessionSafeCoroutineWorker.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/worker/SessionSafeCoroutineWorker.kt
index 030f51428ba830fe9d93507d1be67bf9503de9ea..b98b61c9f0d66890a41df4de2d8c3ef1b6f3a8be 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/worker/SessionSafeCoroutineWorker.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/worker/SessionSafeCoroutineWorker.kt
@@ -55,14 +55,16 @@ internal abstract class SessionSafeCoroutineWorker<PARAM : SessionWorkerParams>(
 
             // Make sure to inject before handling error as you may need some dependencies to process them.
             injectWith(sessionComponent)
-            if (params.lastFailureMessage != null) {
-                // Forward error to the next workers
-                doOnError(params)
-            } else {
-                doSafeWork(params)
+
+            when (val lastFailureMessage = params.lastFailureMessage) {
+                null -> doSafeWork(params)
+                else -> {
+                    // Forward error to the next workers
+                    doOnError(params, lastFailureMessage)
+                }
             }
         } catch (throwable: Throwable) {
-            buildErrorResult(params, throwable.localizedMessage ?: "error")
+            buildErrorResult(params, "${throwable::class.java.name}: ${throwable.localizedMessage ?: "N/A error message"}")
         }
     }
 
@@ -89,10 +91,10 @@ internal abstract class SessionSafeCoroutineWorker<PARAM : SessionWorkerParams>(
      * This is called when the input parameters are correct, but contain an error from the previous worker.
      */
     @CallSuper
-    open fun doOnError(params: PARAM): Result {
+    open fun doOnError(params: PARAM, failureMessage: String): Result {
         // Forward the error
         return Result.success(inputData)
-                .also { Timber.e("Work cancelled due to input error from parent") }
+                .also { Timber.e("Work cancelled due to input error from parent: $failureMessage") }
     }
 
     companion object {