diff --git a/changelog.d/7217.wip b/changelog.d/7217.wip
new file mode 100644
index 0000000000..a8cc2a3ef3
--- /dev/null
+++ b/changelog.d/7217.wip
@@ -0,0 +1 @@
+Implements MSC3881: Parses `enabled` and `device_id` fields from updated Pusher API
diff --git a/library/ui-strings/src/main/res/values/strings.xml b/library/ui-strings/src/main/res/values/strings.xml
index 7387d24147..63a469a8da 100644
--- a/library/ui-strings/src/main/res/values/strings.xml
+++ b/library/ui-strings/src/main/res/values/strings.xml
@@ -1702,13 +1702,15 @@
     <string name="settings_push_rules_no_rules">No push rules defined</string>
     <string name="settings_push_gateway_no_pushers">No registered push gateways</string>
 
-    <string name="push_gateway_item_app_id">app_id:</string>
-    <string name="push_gateway_item_push_key">push_key:</string>
-    <string name="push_gateway_item_app_display_name">app_display_name:</string>
-    <string name="push_gateway_item_device_name">session_name:</string>
+    <string name="push_gateway_item_app_id">App ID:</string>
+    <string name="push_gateway_item_push_key">Push Key:</string>
+    <string name="push_gateway_item_app_display_name">App Display Name:</string>
+    <string name="push_gateway_item_device_name">Session Display Name:</string>
+    <string name="push_gateway_item_device_id">Session ID:</string>
     <string name="push_gateway_item_url">Url:</string>
     <string name="push_gateway_item_format">Format:</string>
     <string name="push_gateway_item_profile_tag">Profile tag:</string>
+    <string name="push_gateway_item_enabled">Enabled:</string>
 
     <string name="preference_voice_and_video">Voice &amp; Video</string>
     <string name="preference_root_help_about">Help &amp; About</string>
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/pushers/HttpPusher.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/pushers/HttpPusher.kt
index 1ae23e2b70..1258c5c02f 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/pushers/HttpPusher.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/pushers/HttpPusher.kt
@@ -58,6 +58,16 @@ data class HttpPusher(
          */
         val url: String,
 
+        /**
+         * Whether the pusher should actively create push notifications.
+         */
+        val enabled: Boolean,
+
+        /**
+         * The device ID of the session that registered the pusher.
+         */
+        val deviceId: String,
+
         /**
          * If true, the homeserver should add another pusher with the given pushkey and App ID in addition
          * to any others with different user IDs. Otherwise, the homeserver must remove any other pushers
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/pushers/Pusher.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/pushers/Pusher.kt
index b85ab32b21..92ac6c483b 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/pushers/Pusher.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/pushers/Pusher.kt
@@ -24,8 +24,9 @@ data class Pusher(
         val profileTag: String? = null,
         val lang: String?,
         val data: PusherData,
-
-        val state: PusherState
+        val enabled: Boolean,
+        val deviceId: String?,
+        val state: PusherState,
 ) {
     companion object {
 
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/mapper/PushersMapper.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/mapper/PushersMapper.kt
index 2dba2c228b..c3a37f5b95 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/mapper/PushersMapper.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/mapper/PushersMapper.kt
@@ -33,7 +33,9 @@ internal object PushersMapper {
                 profileTag = pushEntity.profileTag,
                 lang = pushEntity.lang,
                 data = PusherData(pushEntity.data?.url, pushEntity.data?.format),
-                state = pushEntity.state
+                enabled = pushEntity.enabled,
+                deviceId = pushEntity.deviceId,
+                state = pushEntity.state,
         )
     }
 
@@ -46,7 +48,9 @@ internal object PushersMapper {
                 deviceDisplayName = pusher.deviceDisplayName,
                 profileTag = pusher.profileTag,
                 lang = pusher.lang,
-                data = PusherDataEntity(pusher.data?.url, pusher.data?.format)
+                data = PusherDataEntity(pusher.data?.url, pusher.data?.format),
+                enabled = pusher.enabled,
+                deviceId = pusher.deviceId,
         )
     }
 }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/PusherEntity.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/PusherEntity.kt
index af8e4f2d37..c08f695168 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/PusherEntity.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/PusherEntity.kt
@@ -18,15 +18,6 @@ package org.matrix.android.sdk.internal.database.model
 import io.realm.RealmObject
 import org.matrix.android.sdk.api.session.pushers.PusherState
 
-// TODO
-//        at java.lang.Thread.run(Thread.java:764)
-//     Caused by: java.lang.IllegalArgumentException: 'value' is not a valid managed object.
-//        at io.realm.ProxyState.checkValidObject(ProxyState.java:213)
-//        at io.realm.im_vector_matrix_android_internal_database_model_PusherEntityRealmProxy
-//            .realmSet$data(im_vector_matrix_android_internal_database_model_PusherEntityRealmProxy.java:413)
-//        at org.matrix.android.sdk.internal.database.model.PusherEntity.setData(PusherEntity.kt:16)
-//        at org.matrix.android.sdk.internal.session.pushers.AddHttpPusherWorker$doWork$$inlined$fold$lambda$2.execute(AddHttpPusherWorker.kt:70)
-//        at io.realm.Realm.executeTransaction(Realm.java:1493)
 internal open class PusherEntity(
         var pushKey: String = "",
         var kind: String? = null,
@@ -35,7 +26,9 @@ internal open class PusherEntity(
         var deviceDisplayName: String? = null,
         var profileTag: String? = null,
         var lang: String? = null,
-        var data: PusherDataEntity? = null
+        var data: PusherDataEntity? = null,
+        var enabled: Boolean = true,
+        var deviceId: String? = null,
 ) : RealmObject() {
     private var stateStr: String = PusherState.UNREGISTERED.name
 
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/AddPusherTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/AddPusherTask.kt
index 7d81e19265..3e145dc668 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/AddPusherTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/AddPusherTask.kt
@@ -38,6 +38,7 @@ internal class DefaultAddPusherTask @Inject constructor(
         private val requestExecutor: RequestExecutor,
         private val globalErrorReceiver: GlobalErrorReceiver
 ) : AddPusherTask {
+
     override suspend fun execute(params: AddPusherTask.Params) {
         val pusher = params.pusher
         try {
@@ -71,6 +72,8 @@ internal class DefaultAddPusherTask @Inject constructor(
                 echo.profileTag = pusher.profileTag
                 echo.data?.format = pusher.data?.format
                 echo.data?.url = pusher.data?.url
+                echo.enabled = pusher.enabled
+                echo.deviceId = pusher.deviceId
                 echo.state = PusherState.REGISTERED
             }
         }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/DefaultPushersService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/DefaultPushersService.kt
index e912d9ccf8..082b5b63eb 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/DefaultPushersService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/DefaultPushersService.kt
@@ -78,7 +78,9 @@ internal class DefaultPushersService @Inject constructor(
             appDisplayName = appDisplayName,
             deviceDisplayName = deviceDisplayName,
             data = JsonPusherData(url, EVENT_ID_ONLY.takeIf { withEventIdOnly }),
-            append = append
+            append = append,
+            enabled = enabled,
+            deviceId = deviceId,
     )
 
     override suspend fun addEmailPusher(
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/JsonPusher.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/JsonPusher.kt
index 71a1ea8c66..c1cf3eb276 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/JsonPusher.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/JsonPusher.kt
@@ -33,6 +33,8 @@ import java.security.InvalidParameterException
  *              "device_display_name": "Alice's Phone",
  *              "profile_tag": "xyz",
  *              "lang": "en-US",
+ *              "enabled": true,
+ *              "device_id": "abc123",
  *              "data": {
  *              "url": "https://example.com/_matrix/push/v1/notify"
  *          }
@@ -112,7 +114,19 @@ internal data class JsonPusher(
          * The default is false.
          */
         @Json(name = "append")
-        val append: Boolean? = false
+        val append: Boolean? = false,
+
+        /**
+         * Whether the pusher should actively create push notifications.
+         */
+        @Json(name = "org.matrix.msc3881.enabled")
+        val enabled: Boolean = true,
+
+        /**
+         * The device_id of the session that registered the pusher.
+         */
+        @Json(name = "org.matrix.msc3881.device_id")
+        val deviceId: String? = null,
 ) {
     init {
         // Do some parameter checks. It's ok to throw Exception, to inform developer of the problem
diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/database/mapper/PushersMapperTest.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/database/mapper/PushersMapperTest.kt
new file mode 100644
index 0000000000..08ed20a766
--- /dev/null
+++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/database/mapper/PushersMapperTest.kt
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2022 The Matrix.org Foundation C.I.C.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.matrix.android.sdk.internal.database.mapper
+
+import org.amshove.kluent.shouldBeEqualTo
+import org.junit.Test
+import org.matrix.android.sdk.test.fixtures.JsonPusherFixture.aJsonPusher
+import org.matrix.android.sdk.test.fixtures.PusherEntityFixture.aPusherEntity
+
+class PushersMapperTest {
+
+    @Test
+    fun `when mapping PusherEntity, then it is mapped into Pusher successfully`() {
+        val pusherEntity = aPusherEntity()
+
+        val mappedPusher = PushersMapper.map(pusherEntity)
+
+        mappedPusher.pushKey shouldBeEqualTo pusherEntity.pushKey
+        mappedPusher.kind shouldBeEqualTo pusherEntity.kind.orEmpty()
+        mappedPusher.appId shouldBeEqualTo pusherEntity.appId
+        mappedPusher.appDisplayName shouldBeEqualTo pusherEntity.appDisplayName
+        mappedPusher.deviceDisplayName shouldBeEqualTo pusherEntity.deviceDisplayName
+        mappedPusher.profileTag shouldBeEqualTo pusherEntity.profileTag
+        mappedPusher.lang shouldBeEqualTo pusherEntity.lang
+        mappedPusher.data.url shouldBeEqualTo pusherEntity.data?.url
+        mappedPusher.data.format shouldBeEqualTo pusherEntity.data?.format
+        mappedPusher.enabled shouldBeEqualTo pusherEntity.enabled
+        mappedPusher.deviceId shouldBeEqualTo pusherEntity.deviceId
+        mappedPusher.state shouldBeEqualTo pusherEntity.state
+    }
+
+    @Test
+    fun `when mapping JsonPusher, then it is mapped into Pusher successfully`() {
+        val jsonPusher = aJsonPusher()
+
+        val mappedPusherEntity = PushersMapper.map(jsonPusher)
+
+        mappedPusherEntity.pushKey shouldBeEqualTo jsonPusher.pushKey
+        mappedPusherEntity.kind shouldBeEqualTo jsonPusher.kind
+        mappedPusherEntity.appId shouldBeEqualTo jsonPusher.appId
+        mappedPusherEntity.appDisplayName shouldBeEqualTo jsonPusher.appDisplayName
+        mappedPusherEntity.deviceDisplayName shouldBeEqualTo jsonPusher.deviceDisplayName
+        mappedPusherEntity.profileTag shouldBeEqualTo jsonPusher.profileTag
+        mappedPusherEntity.lang shouldBeEqualTo jsonPusher.lang
+        mappedPusherEntity.data?.url shouldBeEqualTo jsonPusher.data?.url
+        mappedPusherEntity.data?.format shouldBeEqualTo jsonPusher.data?.format
+        mappedPusherEntity.enabled shouldBeEqualTo jsonPusher.enabled
+        mappedPusherEntity.deviceId shouldBeEqualTo jsonPusher.deviceId
+    }
+}
diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/pushers/DefaultAddPusherTaskTest.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/pushers/DefaultAddPusherTaskTest.kt
index dac33069f3..a971973f56 100644
--- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/pushers/DefaultAddPusherTaskTest.kt
+++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/pushers/DefaultAddPusherTaskTest.kt
@@ -71,7 +71,7 @@ class DefaultAddPusherTaskTest {
     }
 
     @Test
-    fun `given a persisted pusher when adding Pusher then updates api and mutates persisted result with Registered state`() {
+    fun `given a persisted pusher, when adding Pusher, then updates api and mutates persisted result with Registered state`() {
         val realmResult = PusherEntity(appDisplayName = null)
         monarchy.givenWhereReturns(result = realmResult)
                 .givenEqualTo(PusherEntityFields.PUSH_KEY, A_JSON_PUSHER.pushKey)
@@ -85,7 +85,7 @@ class DefaultAddPusherTaskTest {
     }
 
     @Test
-    fun `given a persisted push entity and SetPush API fails when adding Pusher then mutates persisted result with Failed registration state and rethrows`() {
+    fun `given a persisted push entity and SetPush API fails, when adding Pusher, then mutates persisted result with Failed registration state and rethrows`() {
         val realmResult = PusherEntity()
         monarchy.givenWhereReturns(result = realmResult)
                 .givenEqualTo(PusherEntityFields.PUSH_KEY, A_JSON_PUSHER.pushKey)
@@ -99,7 +99,7 @@ class DefaultAddPusherTaskTest {
     }
 
     @Test
-    fun `given no persisted push entity and SetPush API fails when adding Pusher then rethrows error`() {
+    fun `given no persisted push entity and SetPush API fails, when adding Pusher, then rethrows error`() {
         monarchy.givenWhereReturns<PusherEntity>(result = null)
                 .givenEqualTo(PusherEntityFields.PUSH_KEY, A_JSON_PUSHER.pushKey)
         pushersAPI.givenSetPusherErrors(SocketException())
diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fixtures/JsonPusherFixture.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fixtures/JsonPusherFixture.kt
new file mode 100644
index 0000000000..8e679ff91c
--- /dev/null
+++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fixtures/JsonPusherFixture.kt
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2022 The Matrix.org Foundation C.I.C.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.matrix.android.sdk.test.fixtures
+
+import org.matrix.android.sdk.internal.session.pushers.JsonPusher
+import org.matrix.android.sdk.internal.session.pushers.JsonPusherData
+
+internal object JsonPusherFixture {
+
+    fun aJsonPusher(
+            pushKey: String = "",
+            kind: String? = null,
+            appId: String = "",
+            appDisplayName: String? = null,
+            deviceDisplayName: String? = null,
+            profileTag: String? = null,
+            lang: String? = null,
+            data: JsonPusherData? = null,
+            append: Boolean? = false,
+            enabled: Boolean = true,
+            deviceId: String? = null,
+    ) = JsonPusher(
+            pushKey,
+            kind,
+            appId,
+            appDisplayName,
+            deviceDisplayName,
+            profileTag,
+            lang,
+            data,
+            append,
+            enabled,
+            deviceId,
+    )
+}
diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fixtures/PusherEntityFixture.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fixtures/PusherEntityFixture.kt
new file mode 100644
index 0000000000..8d048d4c9a
--- /dev/null
+++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fixtures/PusherEntityFixture.kt
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2022 The Matrix.org Foundation C.I.C.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.matrix.android.sdk.test.fixtures
+
+import org.matrix.android.sdk.internal.database.model.PusherDataEntity
+import org.matrix.android.sdk.internal.database.model.PusherEntity
+
+internal object PusherEntityFixture {
+
+    fun aPusherEntity(
+            pushKey: String = "",
+            kind: String? = null,
+            appId: String = "",
+            appDisplayName: String? = null,
+            deviceDisplayName: String? = null,
+            profileTag: String? = null,
+            lang: String? = null,
+            data: PusherDataEntity? = null,
+            enabled: Boolean = true,
+            deviceId: String? = null,
+    ) = PusherEntity(
+            pushKey,
+            kind,
+            appId,
+            appDisplayName,
+            deviceDisplayName,
+            profileTag,
+            lang,
+            data,
+            enabled,
+            deviceId,
+    )
+}
diff --git a/vector-app/src/fdroid/java/im/vector/app/di/FlavorModule.kt b/vector-app/src/fdroid/java/im/vector/app/di/FlavorModule.kt
index 5355aa3cd9..63b4c2a3cd 100644
--- a/vector-app/src/fdroid/java/im/vector/app/di/FlavorModule.kt
+++ b/vector-app/src/fdroid/java/im/vector/app/di/FlavorModule.kt
@@ -23,6 +23,10 @@ import dagger.Provides
 import dagger.hilt.InstallIn
 import dagger.hilt.components.SingletonComponent
 import im.vector.app.core.pushers.FcmHelper
+import im.vector.app.core.resources.AppNameProvider
+import im.vector.app.core.resources.DefaultAppNameProvider
+import im.vector.app.core.resources.DefaultLocaleProvider
+import im.vector.app.core.resources.LocaleProvider
 import im.vector.app.core.services.GuardServiceStarter
 import im.vector.app.fdroid.service.FDroidGuardServiceStarter
 import im.vector.app.features.home.NightlyProxy
@@ -59,4 +63,10 @@ abstract class FlavorModule {
 
     @Binds
     abstract fun bindsFcmHelper(fcmHelper: FdroidFcmHelper): FcmHelper
+
+    @Binds
+    abstract fun bindsLocaleProvider(localeProvider: DefaultLocaleProvider): LocaleProvider
+
+    @Binds
+    abstract fun bindsAppNameProvider(appNameProvider: DefaultAppNameProvider): AppNameProvider
 }
diff --git a/vector-app/src/gplay/java/im/vector/app/di/FlavorModule.kt b/vector-app/src/gplay/java/im/vector/app/di/FlavorModule.kt
index 2fe72313ea..d70a55afb8 100644
--- a/vector-app/src/gplay/java/im/vector/app/di/FlavorModule.kt
+++ b/vector-app/src/gplay/java/im/vector/app/di/FlavorModule.kt
@@ -23,6 +23,10 @@ import dagger.hilt.InstallIn
 import dagger.hilt.components.SingletonComponent
 import im.vector.app.GoogleFlavorLegals
 import im.vector.app.core.pushers.FcmHelper
+import im.vector.app.core.resources.AppNameProvider
+import im.vector.app.core.resources.DefaultAppNameProvider
+import im.vector.app.core.resources.DefaultLocaleProvider
+import im.vector.app.core.resources.LocaleProvider
 import im.vector.app.core.services.GuardServiceStarter
 import im.vector.app.features.home.NightlyProxy
 import im.vector.app.features.settings.legals.FlavorLegals
@@ -46,6 +50,12 @@ abstract class FlavorModule {
     @Binds
     abstract fun bindsFcmHelper(fcmHelper: GoogleFcmHelper): FcmHelper
 
+    @Binds
+    abstract fun bindsLocaleProvider(localeProvider: DefaultLocaleProvider): LocaleProvider
+
+    @Binds
+    abstract fun bindsAppNameProvider(appNameProvider: DefaultAppNameProvider): AppNameProvider
+
     @Binds
     abstract fun bindsFlavorLegals(legals: GoogleFlavorLegals): FlavorLegals
 }
diff --git a/vector-app/src/main/java/im/vector/app/core/di/SingletonModule.kt b/vector-app/src/main/java/im/vector/app/core/di/SingletonModule.kt
index 384c584e0c..dbfda024d8 100644
--- a/vector-app/src/main/java/im/vector/app/core/di/SingletonModule.kt
+++ b/vector-app/src/main/java/im/vector/app/core/di/SingletonModule.kt
@@ -34,6 +34,8 @@ import im.vector.app.SpaceStateHandler
 import im.vector.app.SpaceStateHandlerImpl
 import im.vector.app.config.Config
 import im.vector.app.core.debug.FlipperProxy
+import im.vector.app.core.device.DefaultGetDeviceInfoUseCase
+import im.vector.app.core.device.GetDeviceInfoUseCase
 import im.vector.app.core.dispatchers.CoroutineDispatchers
 import im.vector.app.core.error.DefaultErrorFormatter
 import im.vector.app.core.error.ErrorFormatter
@@ -112,6 +114,9 @@ abstract class VectorBindModule {
 
     @Binds
     abstract fun bindSpaceStateHandler(spaceStateHandlerImpl: SpaceStateHandlerImpl): SpaceStateHandler
+
+    @Binds
+    abstract fun bindGetDeviceInfoUseCase(getDeviceInfoUseCase: DefaultGetDeviceInfoUseCase): GetDeviceInfoUseCase
 }
 
 @InstallIn(SingletonComponent::class)
diff --git a/vector/src/main/java/im/vector/app/core/device/GetDeviceInfoUseCase.kt b/vector/src/main/java/im/vector/app/core/device/GetDeviceInfoUseCase.kt
new file mode 100644
index 0000000000..344173aab7
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/core/device/GetDeviceInfoUseCase.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2022 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.app.core.device
+
+import im.vector.app.core.di.ActiveSessionHolder
+import org.matrix.android.sdk.api.session.crypto.model.CryptoDeviceInfo
+import javax.inject.Inject
+
+interface GetDeviceInfoUseCase {
+
+    fun execute(): CryptoDeviceInfo
+}
+
+class DefaultGetDeviceInfoUseCase @Inject constructor(
+        private val activeSessionHolder: ActiveSessionHolder
+) : GetDeviceInfoUseCase {
+
+    override fun execute(): CryptoDeviceInfo {
+        return activeSessionHolder.getActiveSession().cryptoService().getMyDevice()
+    }
+}
diff --git a/vector/src/main/java/im/vector/app/core/pushers/PushersManager.kt b/vector/src/main/java/im/vector/app/core/pushers/PushersManager.kt
index c77f454ab0..44cccbd3f5 100644
--- a/vector/src/main/java/im/vector/app/core/pushers/PushersManager.kt
+++ b/vector/src/main/java/im/vector/app/core/pushers/PushersManager.kt
@@ -17,6 +17,7 @@
 package im.vector.app.core.pushers
 
 import im.vector.app.R
+import im.vector.app.core.device.GetDeviceInfoUseCase
 import im.vector.app.core.di.ActiveSessionHolder
 import im.vector.app.core.resources.AppNameProvider
 import im.vector.app.core.resources.LocaleProvider
@@ -26,7 +27,7 @@ import java.util.UUID
 import javax.inject.Inject
 import kotlin.math.abs
 
-private const val DEFAULT_PUSHER_FILE_TAG = "mobile"
+internal const val DEFAULT_PUSHER_FILE_TAG = "mobile"
 
 class PushersManager @Inject constructor(
         private val unifiedPushHelper: UnifiedPushHelper,
@@ -34,6 +35,7 @@ class PushersManager @Inject constructor(
         private val localeProvider: LocaleProvider,
         private val stringProvider: StringProvider,
         private val appNameProvider: AppNameProvider,
+        private val getDeviceInfoUseCase: GetDeviceInfoUseCase,
 ) {
     suspend fun testPush() {
         val currentSession = activeSessionHolder.getActiveSession()
@@ -63,15 +65,17 @@ class PushersManager @Inject constructor(
             pushKey: String,
             gateway: String
     ) = HttpPusher(
-            pushKey,
-            stringProvider.getString(R.string.pusher_app_id),
+            pushkey = pushKey,
+            appId = stringProvider.getString(R.string.pusher_app_id),
             profileTag = DEFAULT_PUSHER_FILE_TAG + "_" + abs(activeSessionHolder.getActiveSession().myUserId.hashCode()),
-            localeProvider.current().language,
-            appNameProvider.getAppName(),
-            activeSessionHolder.getActiveSession().sessionParams.deviceId ?: "MOBILE",
-            gateway,
+            lang = localeProvider.current().language,
+            appDisplayName = appNameProvider.getAppName(),
+            deviceDisplayName = getDeviceInfoUseCase.execute().displayName().orEmpty(),
+            url = gateway,
+            enabled = true,
+            deviceId = activeSessionHolder.getActiveSession().sessionParams.deviceId ?: "MOBILE",
             append = false,
-            withEventIdOnly = true
+            withEventIdOnly = true,
     )
 
     suspend fun registerEmailForPush(email: String) {
diff --git a/vector/src/main/java/im/vector/app/core/resources/AppNameProvider.kt b/vector/src/main/java/im/vector/app/core/resources/AppNameProvider.kt
index 3b6a8b595c..a25862f3a8 100644
--- a/vector/src/main/java/im/vector/app/core/resources/AppNameProvider.kt
+++ b/vector/src/main/java/im/vector/app/core/resources/AppNameProvider.kt
@@ -21,9 +21,14 @@ import im.vector.app.core.utils.getApplicationLabel
 import timber.log.Timber
 import javax.inject.Inject
 
-class AppNameProvider @Inject constructor(private val context: Context) {
+interface AppNameProvider {
 
-    fun getAppName(): String {
+    fun getAppName(): String
+}
+
+class DefaultAppNameProvider @Inject constructor(private val context: Context) : AppNameProvider {
+
+    override fun getAppName(): String {
         return try {
             val appPackageName = context.applicationContext.packageName
             var appName = context.getApplicationLabel(appPackageName)
diff --git a/vector/src/main/java/im/vector/app/core/resources/LocaleProvider.kt b/vector/src/main/java/im/vector/app/core/resources/LocaleProvider.kt
index d91a09e6df..3bdbc64eb4 100644
--- a/vector/src/main/java/im/vector/app/core/resources/LocaleProvider.kt
+++ b/vector/src/main/java/im/vector/app/core/resources/LocaleProvider.kt
@@ -23,9 +23,14 @@ import androidx.core.os.ConfigurationCompat
 import java.util.Locale
 import javax.inject.Inject
 
-class LocaleProvider @Inject constructor(private val resources: Resources) {
+interface LocaleProvider {
 
-    fun current(): Locale {
+    fun current(): Locale
+}
+
+class DefaultLocaleProvider @Inject constructor(private val resources: Resources) : LocaleProvider {
+
+    override fun current(): Locale {
         return ConfigurationCompat.getLocales(resources.configuration).get(0) ?: Locale.getDefault()
     }
 }
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/view/MessageBubbleContentLayout.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/view/MessageBubbleContentLayout.kt
index f11b1c6951..6d8fd87a52 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/view/MessageBubbleContentLayout.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/view/MessageBubbleContentLayout.kt
@@ -27,7 +27,7 @@ import androidx.core.view.marginEnd
 import androidx.core.view.marginStart
 import androidx.core.view.marginTop
 import im.vector.app.R
-import im.vector.app.core.resources.LocaleProvider
+import im.vector.app.core.resources.DefaultLocaleProvider
 import im.vector.app.core.resources.getLayoutDirectionFromCurrentLocale
 
 class MessageBubbleContentLayout @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) :
@@ -53,7 +53,7 @@ class MessageBubbleContentLayout @JvmOverloads constructor(context: Context, att
             textViewStub.setOnInflateListener(null)
             messageTextView = inflated.findViewById(R.id.messageTextView)
         }
-        localeLayoutDirection = LocaleProvider(resources).getLayoutDirectionFromCurrentLocale()
+        localeLayoutDirection = DefaultLocaleProvider(resources).getLayoutDirectionFromCurrentLocale()
     }
 
     override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/view/MessageBubbleView.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/view/MessageBubbleView.kt
index c9665a9125..6ac787b719 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/view/MessageBubbleView.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/view/MessageBubbleView.kt
@@ -33,7 +33,7 @@ import androidx.core.view.isVisible
 import androidx.core.view.updateLayoutParams
 import com.google.android.material.shape.MaterialShapeDrawable
 import im.vector.app.R
-import im.vector.app.core.resources.LocaleProvider
+import im.vector.app.core.resources.DefaultLocaleProvider
 import im.vector.app.core.resources.getLayoutDirectionFromCurrentLocale
 import im.vector.app.core.utils.DimensionConverter
 import im.vector.app.databinding.ViewMessageBubbleBinding
@@ -67,7 +67,7 @@ class MessageBubbleView @JvmOverloads constructor(
     override fun onFinishInflate() {
         super.onFinishInflate()
         views = ViewMessageBubbleBinding.bind(this)
-        val currentLayoutDirection = LocaleProvider(resources).getLayoutDirectionFromCurrentLocale()
+        val currentLayoutDirection = DefaultLocaleProvider(resources).getLayoutDirectionFromCurrentLocale()
         val layoutDirectionToSet = if (isIncoming) {
             currentLayoutDirection
         } else {
diff --git a/vector/src/main/java/im/vector/app/features/settings/push/PushGatewayItem.kt b/vector/src/main/java/im/vector/app/features/settings/push/PushGatewayItem.kt
index 5ef6e02330..4fa1e422a1 100644
--- a/vector/src/main/java/im/vector/app/features/settings/push/PushGatewayItem.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/push/PushGatewayItem.kt
@@ -50,6 +50,8 @@ abstract class PushGatewayItem : VectorEpoxyModel<PushGatewayItem.Holder>(R.layo
         holder.format.setTextOrHide(pusher.data.format, hideWhenBlank = true, holder.formatTitle)
         holder.profileTag.setTextOrHide(pusher.profileTag, hideWhenBlank = true, holder.profileTagTitle)
         holder.deviceName.text = pusher.deviceDisplayName
+        holder.deviceId.text = pusher.deviceId ?: "null"
+        holder.enabled.text = pusher.enabled.toString()
         holder.removeButton.setOnClickListener {
             interactions.onRemovePushTapped(pusher)
         }
@@ -59,10 +61,12 @@ abstract class PushGatewayItem : VectorEpoxyModel<PushGatewayItem.Holder>(R.layo
         val kind by bind<TextView>(R.id.pushGatewayKind)
         val pushKey by bind<TextView>(R.id.pushGatewayKeyValue)
         val deviceName by bind<TextView>(R.id.pushGatewayDeviceNameValue)
+        val deviceId by bind<TextView>(R.id.pushGatewayDeviceIdValue)
         val formatTitle by bind<View>(R.id.pushGatewayFormat)
         val format by bind<TextView>(R.id.pushGatewayFormatValue)
         val profileTagTitle by bind<TextView>(R.id.pushGatewayProfileTag)
         val profileTag by bind<TextView>(R.id.pushGatewayProfileTagValue)
+        val enabled by bind<TextView>(R.id.pushGatewayEnabledValue)
         val urlTitle by bind<View>(R.id.pushGatewayURL)
         val url by bind<TextView>(R.id.pushGatewayURLValue)
         val appName by bind<TextView>(R.id.pushGatewayAppNameValue)
diff --git a/vector/src/main/res/layout/item_pushgateway.xml b/vector/src/main/res/layout/item_pushgateway.xml
index afc40e5b7a..67a3a7d390 100644
--- a/vector/src/main/res/layout/item_pushgateway.xml
+++ b/vector/src/main/res/layout/item_pushgateway.xml
@@ -83,6 +83,23 @@
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:layout_marginBottom="16dp"
+        tools:text="Pixel 6" />
+
+    <TextView
+        android:id="@+id/pushGatewayDeviceId"
+        style="@style/Widget.Vector.TextView.Body"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginBottom="4dp"
+        android:text="@string/push_gateway_item_device_id"
+        android:textStyle="bold" />
+
+    <TextView
+        android:id="@+id/pushGatewayDeviceIdValue"
+        style="@style/Widget.Vector.TextView.Body"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginBottom="16dp"
         tools:text="EBMDOLFJD" />
 
     <TextView
@@ -135,6 +152,23 @@
         android:layout_height="wrap_content"
         android:layout_marginBottom="16dp" />
 
+    <TextView
+        android:id="@+id/pushGatewayEnabled"
+        style="@style/Widget.Vector.TextView.Body"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginBottom="4dp"
+        android:text="@string/push_gateway_item_enabled"
+        android:textStyle="bold" />
+
+    <TextView
+        android:id="@+id/pushGatewayEnabledValue"
+        style="@style/Widget.Vector.TextView.Body"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginBottom="16dp"
+        tools:text="true" />
+
     <Button
         android:id="@+id/pushGatewayDeleteButton"
         android:layout_width="wrap_content"
diff --git a/vector/src/test/java/im/vector/app/core/device/DefaultGetDeviceInfoUseCaseTest.kt b/vector/src/test/java/im/vector/app/core/device/DefaultGetDeviceInfoUseCaseTest.kt
new file mode 100644
index 0000000000..0673fbadb5
--- /dev/null
+++ b/vector/src/test/java/im/vector/app/core/device/DefaultGetDeviceInfoUseCaseTest.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2022 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.app.core.device
+
+import im.vector.app.test.fakes.FakeActiveSessionHolder
+import im.vector.app.test.fakes.FakeCryptoService
+import im.vector.app.test.fakes.FakeSession
+import org.amshove.kluent.shouldBeEqualTo
+import org.junit.Test
+
+class DefaultGetDeviceInfoUseCaseTest {
+
+    private val cryptoService = FakeCryptoService()
+    private val session = FakeSession(fakeCryptoService = cryptoService)
+    private val activeSessionHolder = FakeActiveSessionHolder(session)
+
+    private val getDeviceInfoUseCase = DefaultGetDeviceInfoUseCase(activeSessionHolder.instance)
+
+    @Test
+    fun `when execute, then get crypto device info`() {
+        val result = getDeviceInfoUseCase.execute()
+
+        result shouldBeEqualTo cryptoService.cryptoDeviceInfo
+    }
+}
diff --git a/vector/src/test/java/im/vector/app/core/pushers/PushersManagerTest.kt b/vector/src/test/java/im/vector/app/core/pushers/PushersManagerTest.kt
new file mode 100644
index 0000000000..50c9024e86
--- /dev/null
+++ b/vector/src/test/java/im/vector/app/core/pushers/PushersManagerTest.kt
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2022 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.app.core.pushers
+
+import im.vector.app.R
+import im.vector.app.test.fakes.FakeActiveSessionHolder
+import im.vector.app.test.fakes.FakeAppNameProvider
+import im.vector.app.test.fakes.FakeGetDeviceInfoUseCase
+import im.vector.app.test.fakes.FakeLocaleProvider
+import im.vector.app.test.fakes.FakePushersService
+import im.vector.app.test.fakes.FakeSession
+import im.vector.app.test.fakes.FakeStringProvider
+import im.vector.app.test.fixtures.CryptoDeviceInfoFixture.aCryptoDeviceInfo
+import io.mockk.mockk
+import org.amshove.kluent.shouldBeEqualTo
+import org.junit.Test
+import org.matrix.android.sdk.api.session.crypto.model.UnsignedDeviceInfo
+import org.matrix.android.sdk.api.session.pushers.HttpPusher
+import java.util.Locale
+import kotlin.math.abs
+
+class PushersManagerTest {
+
+    private val pushersService = FakePushersService()
+    private val session = FakeSession(fakePushersService = pushersService)
+    private val activeSessionHolder = FakeActiveSessionHolder(session)
+    private val stringProvider = FakeStringProvider()
+    private val localeProvider = FakeLocaleProvider()
+    private val appNameProvider = FakeAppNameProvider()
+    private val getDeviceInfoUseCase = FakeGetDeviceInfoUseCase()
+
+    private val pushersManager = PushersManager(
+            mockk(),
+            activeSessionHolder.instance,
+            localeProvider,
+            stringProvider.instance,
+            appNameProvider,
+            getDeviceInfoUseCase,
+    )
+
+    @Test
+    fun `when enqueueRegisterPusher, then HttpPusher created and enqueued`() {
+        val pushKey = "abc"
+        val gateway = "123"
+        val pusherAppId = "app-id"
+        val appName = "element"
+        val deviceDisplayName = "iPhone Lollipop"
+        stringProvider.given(R.string.pusher_app_id, pusherAppId)
+        localeProvider.givenCurrent(Locale.UK)
+        appNameProvider.givenAppName(appName)
+        getDeviceInfoUseCase.givenDeviceInfo(aCryptoDeviceInfo(unsigned = UnsignedDeviceInfo(deviceDisplayName)))
+        val expectedHttpPusher = HttpPusher(
+                pushkey = pushKey,
+                appId = pusherAppId,
+                profileTag = DEFAULT_PUSHER_FILE_TAG + "_" + abs(session.myUserId.hashCode()),
+                lang = Locale.UK.language,
+                appDisplayName = appName,
+                deviceDisplayName = deviceDisplayName,
+                url = gateway,
+                enabled = true,
+                deviceId = session.sessionParams.deviceId!!,
+                append = false,
+                withEventIdOnly = true,
+        )
+
+        pushersManager.enqueueRegisterPusher(pushKey, gateway)
+
+        val httpPusher = pushersService.verifyEnqueueAddHttpPusher()
+        httpPusher shouldBeEqualTo expectedHttpPusher
+    }
+}
diff --git a/vector/src/test/java/im/vector/app/test/fakes/FakeAppNameProvider.kt b/vector/src/test/java/im/vector/app/test/fakes/FakeAppNameProvider.kt
new file mode 100644
index 0000000000..2e60a78853
--- /dev/null
+++ b/vector/src/test/java/im/vector/app/test/fakes/FakeAppNameProvider.kt
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2022 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.app.test.fakes
+
+import im.vector.app.core.resources.AppNameProvider
+import io.mockk.every
+import io.mockk.mockk
+
+class FakeAppNameProvider : AppNameProvider by mockk() {
+
+    fun givenAppName(appName: String) {
+        every { getAppName() } returns appName
+    }
+}
diff --git a/vector/src/test/java/im/vector/app/test/fakes/FakeCryptoService.kt b/vector/src/test/java/im/vector/app/test/fakes/FakeCryptoService.kt
index 3b1b4df7d1..e96a58faa0 100644
--- a/vector/src/test/java/im/vector/app/test/fakes/FakeCryptoService.kt
+++ b/vector/src/test/java/im/vector/app/test/fakes/FakeCryptoService.kt
@@ -17,6 +17,7 @@
 package im.vector.app.test.fakes
 
 import androidx.lifecycle.MutableLiveData
+import im.vector.app.test.fixtures.CryptoDeviceInfoFixture.aCryptoDeviceInfo
 import io.mockk.every
 import io.mockk.mockk
 import io.mockk.slot
@@ -35,6 +36,7 @@ class FakeCryptoService(
     var cryptoDeviceInfos = mutableMapOf<String, CryptoDeviceInfo>()
     var cryptoDeviceInfoWithIdLiveData: MutableLiveData<Optional<CryptoDeviceInfo>> = MutableLiveData()
     var myDevicesInfoWithIdLiveData: MutableLiveData<Optional<DeviceInfo>> = MutableLiveData()
+    var cryptoDeviceInfo = aCryptoDeviceInfo()
 
     override fun crossSigningService() = fakeCrossSigningService
 
@@ -81,4 +83,6 @@ class FakeCryptoService(
             thirdArg<MatrixCallback<Unit>>().onFailure(error)
         }
     }
+
+    override fun getMyDevice() = cryptoDeviceInfo
 }
diff --git a/vector/src/test/java/im/vector/app/test/fakes/FakeGetDeviceInfoUseCase.kt b/vector/src/test/java/im/vector/app/test/fakes/FakeGetDeviceInfoUseCase.kt
new file mode 100644
index 0000000000..c284263d28
--- /dev/null
+++ b/vector/src/test/java/im/vector/app/test/fakes/FakeGetDeviceInfoUseCase.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2022 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.app.test.fakes
+
+import im.vector.app.core.device.GetDeviceInfoUseCase
+import io.mockk.every
+import io.mockk.mockk
+import org.matrix.android.sdk.api.session.crypto.model.CryptoDeviceInfo
+
+class FakeGetDeviceInfoUseCase : GetDeviceInfoUseCase by mockk() {
+
+    fun givenDeviceInfo(cryptoDeviceInfo: CryptoDeviceInfo) {
+        every { execute() } returns cryptoDeviceInfo
+    }
+}
diff --git a/vector/src/test/java/im/vector/app/test/fakes/FakeLocaleProvider.kt b/vector/src/test/java/im/vector/app/test/fakes/FakeLocaleProvider.kt
new file mode 100644
index 0000000000..4d4e2b5bcb
--- /dev/null
+++ b/vector/src/test/java/im/vector/app/test/fakes/FakeLocaleProvider.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2022 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.app.test.fakes
+
+import im.vector.app.core.resources.LocaleProvider
+import io.mockk.every
+import io.mockk.mockk
+import java.util.Locale
+
+class FakeLocaleProvider : LocaleProvider by mockk() {
+
+    fun givenCurrent(locale: Locale) {
+        every { current() } returns locale
+    }
+}
diff --git a/vector/src/test/java/im/vector/app/test/fakes/FakePushersService.kt b/vector/src/test/java/im/vector/app/test/fakes/FakePushersService.kt
new file mode 100644
index 0000000000..9e11b86871
--- /dev/null
+++ b/vector/src/test/java/im/vector/app/test/fakes/FakePushersService.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2022 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.app.test.fakes
+
+import io.mockk.mockk
+import io.mockk.slot
+import io.mockk.verify
+import org.matrix.android.sdk.api.session.pushers.HttpPusher
+import org.matrix.android.sdk.api.session.pushers.PushersService
+
+class FakePushersService : PushersService by mockk(relaxed = true) {
+
+    fun verifyEnqueueAddHttpPusher(): HttpPusher {
+        val httpPusherSlot = slot<HttpPusher>()
+        verify { enqueueAddHttpPusher(capture(httpPusherSlot)) }
+        return httpPusherSlot.captured
+    }
+}
diff --git a/vector/src/test/java/im/vector/app/test/fakes/FakeSession.kt b/vector/src/test/java/im/vector/app/test/fakes/FakeSession.kt
index 4171307922..bf437c123b 100644
--- a/vector/src/test/java/im/vector/app/test/fakes/FakeSession.kt
+++ b/vector/src/test/java/im/vector/app/test/fakes/FakeSession.kt
@@ -41,6 +41,7 @@ class FakeSession(
         val fakeHomeServerCapabilitiesService: FakeHomeServerCapabilitiesService = FakeHomeServerCapabilitiesService(),
         val fakeSharedSecretStorageService: FakeSharedSecretStorageService = FakeSharedSecretStorageService(),
         val fakeRoomService: FakeRoomService = FakeRoomService(),
+        val fakePushersService: FakePushersService = FakePushersService(),
         private val fakeEventService: FakeEventService = FakeEventService(),
 ) : Session by mockk(relaxed = true) {
 
@@ -58,6 +59,7 @@ class FakeSession(
     override fun sharedSecretStorageService() = fakeSharedSecretStorageService
     override fun roomService() = fakeRoomService
     override fun eventService() = fakeEventService
+    override fun pushersService() = fakePushersService
 
     fun givenVectorStore(vectorSessionStore: VectorSessionStore) {
         coEvery {
diff --git a/vector/src/test/java/im/vector/app/test/fixtures/CryptoDeviceInfoFixture.kt b/vector/src/test/java/im/vector/app/test/fixtures/CryptoDeviceInfoFixture.kt
new file mode 100644
index 0000000000..834a428b4a
--- /dev/null
+++ b/vector/src/test/java/im/vector/app/test/fixtures/CryptoDeviceInfoFixture.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2022 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.app.test.fixtures
+
+import org.matrix.android.sdk.api.session.crypto.crosssigning.DeviceTrustLevel
+import org.matrix.android.sdk.api.session.crypto.model.CryptoDeviceInfo
+import org.matrix.android.sdk.api.session.crypto.model.UnsignedDeviceInfo
+
+object CryptoDeviceInfoFixture {
+
+    fun aCryptoDeviceInfo(
+            deviceId: String = "",
+            userId: String = "",
+            algorithms: List<String>? = null,
+            keys: Map<String, String>? = null,
+            signatures: Map<String, Map<String, String>>? = null,
+            unsigned: UnsignedDeviceInfo? = null,
+            trustLevel: DeviceTrustLevel? = null,
+            isBlocked: Boolean = false,
+            firstTimeSeenLocalTs: Long? = null,
+    ) = CryptoDeviceInfo(
+            deviceId,
+            userId,
+            algorithms,
+            keys,
+            signatures,
+            unsigned,
+            trustLevel,
+            isBlocked,
+            firstTimeSeenLocalTs,
+    )
+}