diff --git a/matrix-sdk-android/build.gradle b/matrix-sdk-android/build.gradle
index f980279a8d..97a1d977cc 100644
--- a/matrix-sdk-android/build.gradle
+++ b/matrix-sdk-android/build.gradle
@@ -97,6 +97,7 @@ dependencies {
     def coroutines_version = "1.3.2"
     def markwon_version = '3.1.0'
     def daggerVersion = '2.25.4'
+    def work_version = '2.3.2'
 
     implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
     implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version"
@@ -126,7 +127,7 @@ dependencies {
     kapt 'dk.ilios:realmfieldnameshelper:1.1.1'
 
     // Work
-    implementation "androidx.work:work-runtime-ktx:2.3.3"
+    implementation "androidx.work:work-runtime-ktx:$work_version"
 
     // FP
     implementation "io.arrow-kt:arrow-core:$arrow_version"
diff --git a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/gossiping/KeyShareTests.kt b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/gossiping/KeyShareTests.kt
new file mode 100644
index 0000000000..fca406ac31
--- /dev/null
+++ b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/gossiping/KeyShareTests.kt
@@ -0,0 +1,210 @@
+/*
+ * 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 im.vector.matrix.android.internal.crypto.gossiping
+
+import android.util.Log
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import im.vector.matrix.android.InstrumentedTest
+import im.vector.matrix.android.api.session.events.model.toModel
+import im.vector.matrix.android.api.session.room.model.RoomDirectoryVisibility
+import im.vector.matrix.android.api.session.room.model.create.CreateRoomParams
+import im.vector.matrix.android.common.CommonTestHelper
+import im.vector.matrix.android.common.SessionTestParams
+import im.vector.matrix.android.common.TestConstants
+import im.vector.matrix.android.internal.crypto.GossipingRequestState
+import im.vector.matrix.android.internal.crypto.OutgoingGossipingRequestState
+import im.vector.matrix.android.internal.crypto.crosssigning.DeviceTrustLevel
+import im.vector.matrix.android.internal.crypto.model.event.EncryptedEventContent
+import junit.framework.TestCase.assertNotNull
+import junit.framework.TestCase.assertTrue
+import junit.framework.TestCase.fail
+import kotlinx.coroutines.GlobalScope
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.launch
+import org.junit.Assert
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import java.util.concurrent.CountDownLatch
+
+@RunWith(AndroidJUnit4::class)
+@FixMethodOrder(MethodSorters.JVM)
+class KeyShareTests : InstrumentedTest {
+
+    private val mTestHelper = CommonTestHelper(context())
+
+//    @Before
+//    fun setup() {
+//        mockkStatic(Log::class)
+//        every { Log.v(any(), any()) } returns 0
+//        every { Log.d(any(), any()) } returns 0
+//        every { Log.i(any(), any()) } returns 0
+//        every { Log.e(any(), any()) } returns 0
+////        every { Log.println(any(), any(), any()) } returns 0
+////        every { Log.wtf(any(), any(), any()) } returns 0
+//    }
+
+    @Test
+    fun test_DoNotSelfShareIfNotTrusted() {
+        val aliceSession = mTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true))
+
+        // Create an encrypted room and add a message
+        val roomId = mTestHelper.doSync<String> {
+            aliceSession.createRoom(
+                    CreateRoomParams(RoomDirectoryVisibility.PRIVATE).enableEncryptionWithAlgorithm(true),
+                    it
+            )
+        }
+        val room = aliceSession.getRoom(roomId)
+        assertNotNull(room)
+        Thread.sleep(4_000)
+        assertTrue(room?.isEncrypted() == true)
+        val sentEventId = mTestHelper.sendTextMessage(room!!, "My Message", 1).first().eventId
+
+        // Open a new sessionx
+
+        val aliceSession2 = mTestHelper.logIntoAccount(aliceSession.myUserId, SessionTestParams(true))
+
+        val roomSecondSessionPOV = aliceSession2.getRoom(roomId)
+
+        val receivedEvent = roomSecondSessionPOV?.getTimeLineEvent(sentEventId)
+        assertNotNull(receivedEvent)
+        assert(receivedEvent!!.isEncrypted())
+
+        try {
+            aliceSession2.cryptoService().decryptEvent(receivedEvent.root, "foo")
+            fail("should fail")
+        } catch (failure: Throwable) {
+        }
+
+        val outgoingRequestBefore = aliceSession2.cryptoService().getOutgoingRoomKeyRequest()
+        // Try to request
+        aliceSession2.cryptoService().requestRoomKeyForEvent(receivedEvent.root)
+
+        val waitLatch = CountDownLatch(1)
+        val eventMegolmSessionId = receivedEvent.root.content.toModel<EncryptedEventContent>()?.sessionId
+
+        var outGoingRequestId: String? = null
+
+        retryPeriodicallyWithLatch(waitLatch) {
+            aliceSession2.cryptoService().getOutgoingRoomKeyRequest()
+                    .filter { req ->
+                        // filter out request that was known before
+                        !outgoingRequestBefore.any { req.requestId == it.requestId }
+                    }
+                    .let {
+                        val outgoing = it.firstOrNull { it.sessionId == eventMegolmSessionId }
+                        outGoingRequestId = outgoing?.requestId
+                        outgoing != null
+                    }
+        }
+        mTestHelper.await(waitLatch)
+
+        Log.v("TEST", "=======> Outgoing requet Id is $outGoingRequestId")
+
+        val outgoingRequestAfter = aliceSession2.cryptoService().getOutgoingRoomKeyRequest()
+
+        //We should have a new request
+        Assert.assertTrue(outgoingRequestAfter.size > outgoingRequestBefore.size)
+        Assert.assertNotNull(outgoingRequestAfter.first { it.sessionId == eventMegolmSessionId })
+
+        // The first session should see an incoming request
+        // the request should be refused, because the device is not trusted
+        waitWithLatch { latch ->
+            retryPeriodicallyWithLatch(latch) {
+                // DEBUG LOGS
+                aliceSession.cryptoService().getIncomingRoomKeyRequest().let {
+                    Log.v("TEST", "Incoming request Session 1 (looking for $outGoingRequestId)")
+                    Log.v("TEST", "=========================")
+                    it.forEach { keyRequest ->
+                        Log.v("TEST", "[ts${keyRequest.localCreationTimestamp}] requestId ${keyRequest.requestId}, for sessionId ${keyRequest.requestBody?.sessionId} is ${keyRequest.state}")
+                    }
+                    Log.v("TEST", "=========================")
+                }
+
+                val incoming = aliceSession.cryptoService().getIncomingRoomKeyRequest().firstOrNull { it.requestId == outGoingRequestId }
+                incoming?.state == GossipingRequestState.REJECTED
+            }
+        }
+
+        try {
+            aliceSession2.cryptoService().decryptEvent(receivedEvent.root, "foo")
+            fail("should fail")
+        } catch (failure: Throwable) {
+        }
+
+        // Mark the device as trusted
+        aliceSession.cryptoService().setDeviceVerification(DeviceTrustLevel(crossSigningVerified = false, locallyVerified = true), aliceSession.myUserId,
+                aliceSession2.sessionParams.credentials.deviceId ?: "")
+
+        // Re request
+        aliceSession2.cryptoService().reRequestRoomKeyForEvent(receivedEvent.root)
+
+        waitWithLatch { latch ->
+            retryPeriodicallyWithLatch(latch) {
+                aliceSession.cryptoService().getIncomingRoomKeyRequest().let {
+                    Log.v("TEST", "Incoming request Session 1")
+                    Log.v("TEST", "=========================")
+                    it.forEach {
+                        Log.v("TEST", "requestId ${it.requestId}, for sessionId ${it.requestBody?.sessionId} is ${it.state}")
+                    }
+                    Log.v("TEST", "=========================")
+
+
+                    it.any { it.requestBody?.sessionId == eventMegolmSessionId && it.state == GossipingRequestState.ACCEPTED }
+                }
+            }
+        }
+
+        Thread.sleep(6_000)
+        waitWithLatch { latch ->
+            retryPeriodicallyWithLatch(latch) {
+                aliceSession2.cryptoService().getOutgoingRoomKeyRequest().let {
+                    it.any { it.requestBody?.sessionId == eventMegolmSessionId && it.state == OutgoingGossipingRequestState.CANCELLED }
+                }
+            }
+        }
+
+        try {
+            aliceSession2.cryptoService().decryptEvent(receivedEvent.root, "foo")
+        } catch (failure: Throwable) {
+            fail("should have been able to decrypt")
+        }
+
+        mTestHelper.signOutAndClose(aliceSession)
+        mTestHelper.signOutAndClose(aliceSession2)
+    }
+
+    fun retryPeriodicallyWithLatch(latch: CountDownLatch, condition: (() -> Boolean)) {
+        GlobalScope.launch {
+            while (true) {
+                delay(1000)
+                if (condition()) {
+                    latch.countDown()
+                    return@launch
+                }
+            }
+        }
+    }
+
+    fun waitWithLatch(block: (CountDownLatch) -> Unit) {
+        val latch = CountDownLatch(1)
+        block(latch)
+        mTestHelper.await(latch)
+    }
+}
diff --git a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/keysbackup/KeysBackupTest.kt b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/keysbackup/KeysBackupTest.kt
index e599eda7f9..d584482774 100644
--- a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/keysbackup/KeysBackupTest.kt
+++ b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/keysbackup/KeysBackupTest.kt
@@ -16,6 +16,7 @@
 
 package im.vector.matrix.android.internal.crypto.keysbackup
 
+import android.util.Log
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import im.vector.matrix.android.InstrumentedTest
 import im.vector.matrix.android.api.listeners.ProgressListener
@@ -34,6 +35,7 @@ import im.vector.matrix.android.common.assertDictEquals
 import im.vector.matrix.android.common.assertListEquals
 import im.vector.matrix.android.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM_BACKUP
 import im.vector.matrix.android.internal.crypto.MegolmSessionData
+import im.vector.matrix.android.internal.crypto.ShareRequestState
 import im.vector.matrix.android.internal.crypto.crosssigning.DeviceTrustLevel
 import im.vector.matrix.android.internal.crypto.keysbackup.model.KeysBackupVersionTrust
 import im.vector.matrix.android.internal.crypto.keysbackup.model.MegolmBackupCreationInfo
@@ -41,12 +43,15 @@ import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.KeysVersio
 import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.KeysVersionResult
 import im.vector.matrix.android.internal.crypto.model.ImportRoomKeysResult
 import im.vector.matrix.android.internal.crypto.model.OlmInboundGroupSessionWrapper
+import io.mockk.every
+import io.mockk.mockkStatic
 import org.junit.Assert.assertEquals
 import org.junit.Assert.assertFalse
 import org.junit.Assert.assertNotNull
 import org.junit.Assert.assertNull
 import org.junit.Assert.assertTrue
 import org.junit.Assert.fail
+import org.junit.BeforeClass
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -325,46 +330,46 @@ class KeysBackupTest : InstrumentedTest {
      * - Restore must be successful
      * - *** There must be no more pending key share requests
      */
-    @Test
-    fun restoreKeysBackupAndKeyShareRequestTest() {
-        fail("Check with Valere for this test. I think we do not send key share request")
-
-        val testData = createKeysBackupScenarioWithPassword(null)
-
-        // - Check the SDK sent key share requests
-        val cryptoStore2 = (testData.aliceSession2.cryptoService().keysBackupService() as DefaultKeysBackupService).store
-        val unsentRequest = cryptoStore2
-                .getOutgoingRoomKeyRequestByState(setOf(ShareRequestState.UNSENT))
-        val sentRequest = cryptoStore2
-                .getOutgoingRoomKeyRequestByState(setOf(ShareRequestState.SENT))
-
-        // Request is either sent or unsent
-        assertTrue(unsentRequest != null || sentRequest != null)
-
-        // - Restore the e2e backup from the homeserver
-        val importRoomKeysResult = mTestHelper.doSync<ImportRoomKeysResult> {
-            testData.aliceSession2.cryptoService().keysBackupService().restoreKeysWithRecoveryKey(testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion!!,
-                    testData.prepareKeysBackupDataResult.megolmBackupCreationInfo.recoveryKey,
-                    null,
-                    null,
-                    null,
-                    it
-            )
-        }
-
-        checkRestoreSuccess(testData, importRoomKeysResult.totalNumberOfKeys, importRoomKeysResult.successfullyNumberOfImportedKeys)
-
-        // - There must be no more pending key share requests
-        val unsentRequestAfterRestoration = cryptoStore2
-                .getOutgoingRoomKeyRequestByState(setOf(ShareRequestState.UNSENT))
-        val sentRequestAfterRestoration = cryptoStore2
-                .getOutgoingRoomKeyRequestByState(setOf(ShareRequestState.SENT))
-
-        // Request is either sent or unsent
-        assertTrue(unsentRequestAfterRestoration == null && sentRequestAfterRestoration == null)
-
-        testData.cleanUp(mTestHelper)
-    }
+//    @Test
+//    fun restoreKeysBackupAndKeyShareRequestTest() {
+//        fail("Check with Valere for this test. I think we do not send key share request")
+//
+//        val testData = createKeysBackupScenarioWithPassword(null)
+//
+//        // - Check the SDK sent key share requests
+//        val cryptoStore2 = (testData.aliceSession2.cryptoService().keysBackupService() as DefaultKeysBackupService).store
+//        val unsentRequest = cryptoStore2
+//                .getOutgoingRoomKeyRequestByState(setOf(ShareRequestState.UNSENT))
+//        val sentRequest = cryptoStore2
+//                .getOutgoingRoomKeyRequestByState(setOf(ShareRequestState.SENT))
+//
+//        // Request is either sent or unsent
+//        assertTrue(unsentRequest != null || sentRequest != null)
+//
+//        // - Restore the e2e backup from the homeserver
+//        val importRoomKeysResult = mTestHelper.doSync<ImportRoomKeysResult> {
+//            testData.aliceSession2.cryptoService().keysBackupService().restoreKeysWithRecoveryKey(testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion!!,
+//                    testData.prepareKeysBackupDataResult.megolmBackupCreationInfo.recoveryKey,
+//                    null,
+//                    null,
+//                    null,
+//                    it
+//            )
+//        }
+//
+//        checkRestoreSuccess(testData, importRoomKeysResult.totalNumberOfKeys, importRoomKeysResult.successfullyNumberOfImportedKeys)
+//
+//        // - There must be no more pending key share requests
+//        val unsentRequestAfterRestoration = cryptoStore2
+//                .getOutgoingRoomKeyRequestByState(setOf(ShareRequestState.UNSENT))
+//        val sentRequestAfterRestoration = cryptoStore2
+//                .getOutgoingRoomKeyRequestByState(setOf(ShareRequestState.SENT))
+//
+//        // Request is either sent or unsent
+//        assertTrue(unsentRequestAfterRestoration == null && sentRequestAfterRestoration == null)
+//
+//        testData.cleanUp(mTestHelper)
+//    }
 
     /**
      * - Do an e2e backup to the homeserver with a recovery key
diff --git a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/verification/qrcode/QrCodeTest.kt b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/verification/qrcode/QrCodeTest.kt
index d19fad4b59..ff1780865c 100644
--- a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/verification/qrcode/QrCodeTest.kt
+++ b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/verification/qrcode/QrCodeTest.kt
@@ -18,6 +18,7 @@ package im.vector.matrix.android.internal.crypto.verification.qrcode
 
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import im.vector.matrix.android.InstrumentedTest
+import im.vector.matrix.android.internal.crypto.crosssigning.fromBase64NoPadding
 import org.amshove.kluent.shouldBeNull
 import org.amshove.kluent.shouldEqual
 import org.amshove.kluent.shouldEqualTo
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/crypto/MXCryptoConfig.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/crypto/MXCryptoConfig.kt
index dc08023d99..a8d576bae9 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/crypto/MXCryptoConfig.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/crypto/MXCryptoConfig.kt
@@ -23,5 +23,14 @@ data class MXCryptoConfig(
         // Tell whether the encryption of the event content is enabled for the invited members.
         // SDK clients can disable this by settings it to false.
         // Note that the encryption for the invited members will be blocked if the history visibility is "joined".
-        var enableEncryptionForInvitedMembers: Boolean = true
+        var enableEncryptionForInvitedMembers: Boolean = true,
+
+        /**
+         * If set to true, the SDK will automatically ignore room key request (gossiping)
+         * coming from your other untrusted sessions (or blocked).
+         * If set to false, the request will be forwarded to the application layer; in this
+         * case the application can decide to prompt the user.
+         */
+        var discardRoomKeyRequestsFromUntrustedDevices : Boolean = true
+
 )
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/extensions/Try.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/extensions/Try.kt
new file mode 100644
index 0000000000..3afcac08c1
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/extensions/Try.kt
@@ -0,0 +1,25 @@
+/*
+ * 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 im.vector.matrix.android.api.extensions
+
+inline fun <A> tryThis(operation: () -> A): A? {
+    return try {
+        operation()
+    } catch (any: Throwable) {
+        null
+    }
+}
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/CryptoService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/CryptoService.kt
index d6823d49cb..ab8417b542 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/CryptoService.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/CryptoService.kt
@@ -26,6 +26,7 @@ import im.vector.matrix.android.api.session.crypto.keyshare.GossipingRequestList
 import im.vector.matrix.android.api.session.crypto.verification.VerificationService
 import im.vector.matrix.android.api.session.events.model.Content
 import im.vector.matrix.android.api.session.events.model.Event
+import im.vector.matrix.android.internal.crypto.IncomingRoomKeyRequest
 import im.vector.matrix.android.internal.crypto.MXEventDecryptionResult
 import im.vector.matrix.android.internal.crypto.NewSessionListener
 import im.vector.matrix.android.internal.crypto.OutgoingRoomKeyRequest
@@ -87,6 +88,8 @@ interface CryptoService {
 
     fun getDeviceInfo(userId: String, deviceId: String?): CryptoDeviceInfo?
 
+    fun requestRoomKeyForEvent(event: Event)
+
     fun reRequestRoomKeyForEvent(event: Event)
 
     fun cancelRoomKeyRequest(requestBody: RoomKeyRequestBody)
@@ -132,4 +135,6 @@ interface CryptoService {
     fun removeSessionListener(listener: NewSessionListener)
 
     fun getOutgoingRoomKeyRequest(): List<OutgoingRoomKeyRequest>
+    fun getIncomingRoomKeyRequest(): List<IncomingRoomKeyRequest>
+    fun getGossipingEventsTrail(): List<Event>
 }
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/crosssigning/CrossSigningService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/crosssigning/CrossSigningService.kt
index ff4745ef46..181508402d 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/crosssigning/CrossSigningService.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/crosssigning/CrossSigningService.kt
@@ -68,4 +68,7 @@ interface CrossSigningService {
     fun checkDeviceTrust(otherUserId: String,
                          otherDeviceId: String,
                          locallyTrusted: Boolean?): DeviceTrustResult
+
+    fun onSecretSSKGossip(sskPrivateKey: String): Boolean
+    fun onSecretUSKGossip(uskPrivateKey: String): Boolean
 }
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/securestorage/SharedSecretStorageService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/securestorage/SharedSecretStorageService.kt
index 596d8d3e5d..ad0c6f10fa 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/securestorage/SharedSecretStorageService.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/securestorage/SharedSecretStorageService.kt
@@ -111,6 +111,9 @@ interface SharedSecretStorageService {
 
     fun checkShouldBeAbleToAccessSecrets(secretNames: List<String>, keyId: String?) : IntegrityResult
 
+
+    fun requestSecret(name: String, myOtherDeviceId: String)
+
     data class KeyRef(
             val keyId: String?,
             val keySpec: SsssKeySpec?
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/CancelGossipRequestWorker.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/CancelGossipRequestWorker.kt
new file mode 100644
index 0000000000..a87dae3690
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/CancelGossipRequestWorker.kt
@@ -0,0 +1,119 @@
+/*
+ * 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 im.vector.matrix.android.internal.crypto
+
+import android.content.Context
+import androidx.work.CoroutineWorker
+import androidx.work.Data
+import androidx.work.WorkerParameters
+import com.squareup.moshi.JsonClass
+import im.vector.matrix.android.api.auth.data.Credentials
+import im.vector.matrix.android.api.failure.shouldBeRetried
+import im.vector.matrix.android.api.session.events.model.Event
+import im.vector.matrix.android.api.session.events.model.EventType
+import im.vector.matrix.android.api.session.events.model.LocalEcho
+import im.vector.matrix.android.api.session.events.model.toContent
+import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap
+import im.vector.matrix.android.internal.crypto.model.rest.ShareRequestCancellation
+import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore
+import im.vector.matrix.android.internal.crypto.tasks.SendToDeviceTask
+import im.vector.matrix.android.internal.worker.WorkerParamsFactory
+import im.vector.matrix.android.internal.worker.getSessionComponent
+import org.greenrobot.eventbus.EventBus
+import timber.log.Timber
+import javax.inject.Inject
+
+internal class CancelGossipRequestWorker(context: Context,
+                                         params: WorkerParameters)
+    : CoroutineWorker(context, params) {
+
+    @JsonClass(generateAdapter = true)
+    internal data class Params(
+            val sessionId: String,
+            val requestId: String,
+            val recipients: Map<String, List<String>>
+    ) {
+        companion object {
+            fun fromRequest(sessionId: String, request: OutgoingGossipingRequest): Params {
+                return Params(
+                        sessionId = sessionId,
+                        requestId = request.requestId,
+                        recipients = request.recipients
+                )
+            }
+        }
+    }
+
+    @Inject lateinit var sendToDeviceTask: SendToDeviceTask
+    @Inject lateinit var cryptoStore: IMXCryptoStore
+    @Inject lateinit var eventBus: EventBus
+    @Inject lateinit var credentials: Credentials
+
+    override suspend fun doWork(): Result {
+
+        val errorOutputData = Data.Builder().putBoolean("failed", true).build()
+        val params = WorkerParamsFactory.fromData<Params>(inputData)
+                ?: return Result.success(errorOutputData)
+
+        val sessionComponent = getSessionComponent(params.sessionId)
+                ?: return Result.success(errorOutputData).also {
+                    // TODO, can this happen? should I update local echo?
+                    Timber.e("Unknown Session, cannot send message, sessionId: ${params.sessionId}")
+                }
+        sessionComponent.inject(this)
+
+        val localId = LocalEcho.createLocalEchoId()
+        val contentMap = MXUsersDevicesMap<Any>()
+        val toDeviceContent = ShareRequestCancellation(
+                requestingDeviceId = credentials.deviceId,
+                requestId = params.requestId
+        )
+        cryptoStore.saveGossipingEvent(Event(
+                type = EventType.ROOM_KEY_REQUEST,
+                content = toDeviceContent.toContent(),
+                senderId = credentials.userId
+        ).also {
+            it.ageLocalTs = System.currentTimeMillis()
+        })
+
+        params.recipients.forEach { userToDeviceMap ->
+            userToDeviceMap.value.forEach { deviceId ->
+                contentMap.setObject(userToDeviceMap.key, deviceId, toDeviceContent)
+            }
+        }
+
+        try {
+            cryptoStore.updateOutgoingGossipingRequestState(params.requestId, OutgoingGossipingRequestState.CANCELLING)
+            sendToDeviceTask.execute(
+                    SendToDeviceTask.Params(
+                            eventType = EventType.ROOM_KEY_REQUEST,
+                            contentMap = contentMap,
+                            transactionId = localId
+                    )
+            )
+            cryptoStore.updateOutgoingGossipingRequestState(params.requestId, OutgoingGossipingRequestState.CANCELLED)
+            return Result.success()
+        } catch (exception: Throwable) {
+            return if (exception.shouldBeRetried()) {
+                Result.retry()
+            } else {
+                cryptoStore.updateOutgoingGossipingRequestState(params.requestId, OutgoingGossipingRequestState.FAILED_TO_CANCEL)
+                Result.success(errorOutputData)
+            }
+        }
+    }
+}
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/DefaultCryptoService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/DefaultCryptoService.kt
index 2ea0a71c50..7b7490f233 100755
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/DefaultCryptoService.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/DefaultCryptoService.kt
@@ -33,6 +33,8 @@ import im.vector.matrix.android.api.failure.Failure
 import im.vector.matrix.android.api.listeners.ProgressListener
 import im.vector.matrix.android.api.session.crypto.CryptoService
 import im.vector.matrix.android.api.session.crypto.MXCryptoError
+import im.vector.matrix.android.api.session.crypto.crosssigning.SELF_SIGNING_KEY_SSSS_NAME
+import im.vector.matrix.android.api.session.crypto.crosssigning.USER_SIGNING_KEY_SSSS_NAME
 import im.vector.matrix.android.api.session.crypto.keyshare.GossipingRequestListener
 import im.vector.matrix.android.api.session.events.model.Content
 import im.vector.matrix.android.api.session.events.model.Event
@@ -47,6 +49,7 @@ import im.vector.matrix.android.internal.crypto.actions.SetDeviceVerificationAct
 import im.vector.matrix.android.internal.crypto.algorithms.IMXEncrypting
 import im.vector.matrix.android.internal.crypto.algorithms.megolm.MXMegolmEncryptionFactory
 import im.vector.matrix.android.internal.crypto.algorithms.olm.MXOlmEncryptionFactory
+import im.vector.matrix.android.internal.crypto.algorithms.olm.OlmDecryptionResult
 import im.vector.matrix.android.internal.crypto.crosssigning.DefaultCrossSigningService
 import im.vector.matrix.android.internal.crypto.crosssigning.DeviceTrustLevel
 import im.vector.matrix.android.internal.crypto.keysbackup.DefaultKeysBackupService
@@ -55,7 +58,9 @@ import im.vector.matrix.android.internal.crypto.model.ImportRoomKeysResult
 import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo
 import im.vector.matrix.android.internal.crypto.model.MXEncryptEventContentResult
 import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap
+import im.vector.matrix.android.internal.crypto.model.event.EncryptedEventContent
 import im.vector.matrix.android.internal.crypto.model.event.RoomKeyContent
+import im.vector.matrix.android.internal.crypto.model.event.SecretSendEventContent
 import im.vector.matrix.android.internal.crypto.model.rest.DeviceInfo
 import im.vector.matrix.android.internal.crypto.model.rest.DevicesListResponse
 import im.vector.matrix.android.internal.crypto.model.rest.KeysUploadResponse
@@ -80,6 +85,7 @@ import im.vector.matrix.android.internal.session.room.membership.LoadRoomMembers
 import im.vector.matrix.android.internal.session.room.membership.RoomMemberHelper
 import im.vector.matrix.android.internal.session.sync.model.SyncResponse
 import im.vector.matrix.android.internal.task.TaskExecutor
+import im.vector.matrix.android.internal.task.TaskThread
 import im.vector.matrix.android.internal.task.configureWith
 import im.vector.matrix.android.internal.util.JsonCanonicalizer
 import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
@@ -137,7 +143,7 @@ internal class DefaultCryptoService @Inject constructor(
         //
         private val incomingRoomKeyRequestManager: IncomingRoomKeyRequestManager,
         //
-        private val outgoingRoomKeyRequestManager: OutgoingRoomKeyRequestManager,
+        private val outgoingGossipingRequestManager: OutgoingGossipingRequestManager,
         // Actions
         private val setDeviceVerificationAction: SetDeviceVerificationAction,
         private val megolmSessionDataImporter: MegolmSessionDataImporter,
@@ -189,6 +195,7 @@ internal class DefaultCryptoService @Inject constructor(
     override fun setDeviceName(deviceId: String, deviceName: String, callback: MatrixCallback<Unit>) {
         setDeviceNameTask
                 .configureWith(SetDeviceNameTask.Params(deviceId, deviceName)) {
+                    this.executionThread = TaskThread.CRYPTO
                     this.callback = object : MatrixCallback<Unit> {
                         override fun onSuccess(data: Unit) {
                             // bg refresh of crypto device
@@ -207,6 +214,7 @@ internal class DefaultCryptoService @Inject constructor(
     override fun deleteDevice(deviceId: String, callback: MatrixCallback<Unit>) {
         deleteDeviceTask
                 .configureWith(DeleteDeviceTask.Params(deviceId)) {
+                    this.executionThread = TaskThread.CRYPTO
                     this.callback = callback
                 }
                 .executeBy(taskExecutor)
@@ -215,6 +223,7 @@ internal class DefaultCryptoService @Inject constructor(
     override fun deleteDeviceWithUserPassword(deviceId: String, authSession: String?, password: String, callback: MatrixCallback<Unit>) {
         deleteDeviceWithUserPasswordTask
                 .configureWith(DeleteDeviceWithUserPasswordTask.Params(deviceId, authSession, password)) {
+                    this.executionThread = TaskThread.CRYPTO
                     this.callback = callback
                 }
                 .executeBy(taskExecutor)
@@ -231,6 +240,7 @@ internal class DefaultCryptoService @Inject constructor(
     override fun getDevicesList(callback: MatrixCallback<DevicesListResponse>) {
         getDevicesTask
                 .configureWith {
+                    this.executionThread = TaskThread.CRYPTO
                     this.callback = callback
                 }
                 .executeBy(taskExecutor)
@@ -239,6 +249,7 @@ internal class DefaultCryptoService @Inject constructor(
     override fun getDeviceInfo(deviceId: String, callback: MatrixCallback<DeviceInfo>) {
         getDeviceInfoTask
                 .configureWith(GetDeviceInfoTask.Params(deviceId)) {
+                    this.executionThread = TaskThread.CRYPTO
                     this.callback = callback
                 }
                 .executeBy(taskExecutor)
@@ -301,7 +312,6 @@ internal class DefaultCryptoService @Inject constructor(
         runCatching {
             uploadDeviceKeys()
             oneTimeKeysUploader.maybeUploadOneTimeKeys()
-            outgoingRoomKeyRequestManager.start()
             keysBackupService.checkAndStartKeysBackup()
             if (isInitialSync) {
                 // refresh the devices list for each known room members
@@ -329,8 +339,6 @@ internal class DefaultCryptoService @Inject constructor(
     fun close() = runBlocking(coroutineDispatchers.crypto) {
         cryptoCoroutineScope.coroutineContext.cancelChildren(CancellationException("Closing crypto module"))
 
-        outgoingRoomKeyRequestManager.stop()
-
         olmDevice.release()
         cryptoStore.close()
     }
@@ -689,18 +697,24 @@ internal class DefaultCryptoService @Inject constructor(
      * @param event the event
      */
     fun onToDeviceEvent(event: Event) {
+        // event have already been decrypted
         cryptoCoroutineScope.launch(coroutineDispatchers.crypto) {
             when (event.getClearType()) {
                 EventType.ROOM_KEY, EventType.FORWARDED_ROOM_KEY -> {
+                    cryptoStore.saveGossipingEvent(event)
+                    // Keys are imported directly, not waiting for end of sync
                     onRoomKeyEvent(event)
                 }
-
                 EventType.REQUEST_SECRET,
                 EventType.ROOM_KEY_REQUEST                       -> {
+                    // save audit trail
+                    cryptoStore.saveGossipingEvent(event)
+                    // Requests are stacked, and will be handled one by one at the end of the sync (onSyncComplete)
                     incomingRoomKeyRequestManager.onGossipingRequestEvent(event)
                 }
                 EventType.SEND_SECRET                            -> {
-                    // incomingRoomKeyRequestManager.onGossipingRequestEvent(event)
+                    cryptoStore.saveGossipingEvent(event)
+                    onSecretSendReceived(event)
                 }
                 else                                             -> {
                     // ignore
@@ -716,6 +730,7 @@ internal class DefaultCryptoService @Inject constructor(
      */
     private fun onRoomKeyEvent(event: Event) {
         val roomKeyContent = event.getClearContent().toModel<RoomKeyContent>() ?: return
+        Timber.v("## onRoomKeyEvent() : type<${event.type}> , sessionId<${roomKeyContent.sessionId}>")
         if (roomKeyContent.roomId.isNullOrEmpty() || roomKeyContent.algorithm.isNullOrEmpty()) {
             Timber.e("## onRoomKeyEvent() : missing fields")
             return
@@ -728,6 +743,78 @@ internal class DefaultCryptoService @Inject constructor(
         alg.onRoomKeyEvent(event, keysBackupService)
     }
 
+    private fun onSecretSendReceived(event: Event) {
+        if (!event.isEncrypted()) {
+            // secret send messages must be encrypted
+            Timber.e("## onSecretSend() :Received unencrypted secret send event")
+            return
+        }
+
+        // Was that sent by us?
+        if (event.senderId != credentials.userId) {
+            Timber.e("## onSecretSend() : Ignore secret from other user ${event.senderId}")
+            return
+        }
+
+        val encryptedEventContent = event.getClearContent().toModel<EncryptedEventContent>()
+                ?: return Unit.also {
+                    Timber.e("## onSecretSend() :Received malformed secret send event")
+                }
+
+        val senderDevice = encryptedEventContent.senderKey
+
+        val device = senderDevice?.let { cryptoStore.getUserDevice(event.senderId, it) }
+                ?: return Unit.also {
+                    Timber.e("## processIncomingSecretShareRequest() : Received secret share request from unknown device ${senderDevice}")
+                }
+
+        try {
+            val result = decryptEvent(event, "gossip")
+            event.mxDecryptionResult = OlmDecryptionResult(
+                    payload = result.clearEvent,
+                    senderKey = result.senderCurve25519Key,
+                    keysClaimed = result.claimedEd25519Key?.let { k -> mapOf("ed25519" to k) },
+                    forwardingCurve25519KeyChain = result.forwardingCurve25519KeyChain
+            )
+        } catch (failure: Throwable) {
+            Timber.i("## onSecretSend() :Failed to decrypt secret share: $device")
+        }
+
+        val secretContent = event.getClearContent().toModel<SecretSendEventContent>() ?: return
+
+        val existingRequest = cryptoStore
+                .getPendingIncomingGossipingRequests()
+                .firstOrNull { it.requestId == secretContent.requestId } as? IncomingSecretShareRequest
+
+        if (existingRequest == null) {
+            Timber.i("## onSecretSend() :Received secret from unknown request id: ${secretContent.requestId} from device ")
+            return
+        }
+
+        if (device.isBlocked || !device.isVerified) {
+            // Ignore secrets from this
+            Timber.i("## onSecretSend() :Received secret from untrusted/blocked device: ${device}")
+            return
+        }
+
+        when (existingRequest.secretName) {
+            SELF_SIGNING_KEY_SSSS_NAME -> {
+                if (device.trustLevel?.isLocallyVerified() == true) {
+                    crossSigningService.onSecretSSKGossip(secretContent.secretValue)
+                    return
+                }
+            }
+            USER_SIGNING_KEY_SSSS_NAME -> {
+                if (device.trustLevel?.isLocallyVerified() == true) {
+                    cryptoStore.storePrivateKeysInfo(null, null, secretContent.secretValue)
+                }
+            }
+            else                       -> {
+                // Ask to application layer?
+            }
+        }
+    }
+
     /**
      * Handle an m.room.encryption event.
      *
@@ -1003,14 +1090,14 @@ internal class DefaultCryptoService @Inject constructor(
         setRoomBlacklistUnverifiedDevices(roomId, false)
     }
 
-    // TODO Check if this method is still necessary
+// TODO Check if this method is still necessary
     /**
      * Cancel any earlier room key request
      *
      * @param requestBody requestBody
      */
     override fun cancelRoomKeyRequest(requestBody: RoomKeyRequestBody) {
-        outgoingRoomKeyRequestManager.cancelRoomKeyRequest(requestBody)
+        outgoingGossipingRequestManager.cancelRoomKeyRequest(requestBody)
     }
 
     /**
@@ -1019,20 +1106,36 @@ internal class DefaultCryptoService @Inject constructor(
      * @param event the event to decrypt again.
      */
     override fun reRequestRoomKeyForEvent(event: Event) {
-        val wireContent = event.content
-        if (wireContent == null) {
+        val wireContent = event.content.toModel<EncryptedEventContent>() ?: return Unit.also {
             Timber.e("## reRequestRoomKeyForEvent Failed to re-request key, null content")
-            return
         }
 
         val requestBody = RoomKeyRequestBody(
-                algorithm = wireContent["algorithm"]?.toString(),
+                algorithm = wireContent.algorithm,
                 roomId = event.roomId,
-                senderKey = wireContent["sender_key"]?.toString(),
-                sessionId = wireContent["session_id"]?.toString()
+                senderKey = wireContent.senderKey,
+                sessionId = wireContent.sessionId
         )
 
-        outgoingRoomKeyRequestManager.resendRoomKeyRequest(requestBody)
+        outgoingGossipingRequestManager.resendRoomKeyRequest(requestBody)
+    }
+
+    override fun requestRoomKeyForEvent(event: Event) {
+        val wireContent = event.content.toModel<EncryptedEventContent>() ?: return Unit.also {
+            Timber.e("## requestRoomKeyForEvent Failed to request key, null content eventId: ${event.eventId}")
+        }
+
+        cryptoCoroutineScope.launch(coroutineDispatchers.crypto) {
+            if (!isStarted()) {
+                Timber.v("## requestRoomKeyForEvent() : wait after e2e init")
+                internalStart(false)
+            }
+            roomDecryptorProvider
+                    .getOrCreateRoomDecryptor(event.roomId, wireContent.algorithm)
+                    ?.requestKeysForEvent(event) ?: run {
+                Timber.v("## requestRoomKeyForEvent() : No room decryptor for roomId:${event.roomId} algorithm:${wireContent.algorithm}")
+            }
+        }
     }
 
     /**
@@ -1090,9 +1193,9 @@ internal class DefaultCryptoService @Inject constructor(
     override fun removeSessionListener(listener: NewSessionListener) {
         roomDecryptorProvider.removeSessionListener(listener)
     }
-    /* ==========================================================================================
-     * DEBUG INFO
-     * ========================================================================================== */
+/* ==========================================================================================
+ * DEBUG INFO
+ * ========================================================================================== */
 
     override fun toString(): String {
         return "DefaultCryptoService of " + credentials.userId + " (" + credentials.deviceId + ")"
@@ -1101,4 +1204,12 @@ internal class DefaultCryptoService @Inject constructor(
     override fun getOutgoingRoomKeyRequest(): List<OutgoingRoomKeyRequest> {
         return cryptoStore.getOutgoingRoomKeyRequests()
     }
+
+    override fun getIncomingRoomKeyRequest(): List<IncomingRoomKeyRequest> {
+        return cryptoStore.getIncomingRoomKeyRequests()
+    }
+
+    override fun getGossipingEventsTrail(): List<Event> {
+        return cryptoStore.getGossipingEventsTrail()
+    }
 }
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/GossipingRequestState.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/GossipingRequestState.kt
new file mode 100644
index 0000000000..b218a2e387
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/GossipingRequestState.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 im.vector.matrix.android.internal.crypto
+
+enum class GossipRequestType {
+    KEY,
+    SECRET
+}
+
+enum class GossipingRequestState {
+    NONE,
+    PENDING,
+    REJECTED,
+    ACCEPTED,
+   // USER_REJECTED,
+    UNABLE_TO_PROCESS,
+    CANCELLED_BY_REQUESTER,
+    RE_REQUESTED
+}
+
+enum class OutgoingGossipingRequestState {
+    UNSENT,
+    SENDING,
+    SENT,
+    CANCELLING,
+    CANCELLED,
+    FAILED_TO_SEND,
+    FAILED_TO_CANCEL
+}
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/IncomingRequestCancellation.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/IncomingRequestCancellation.kt
index 7751f149f1..98e1e95423 100755
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/IncomingRequestCancellation.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/IncomingRequestCancellation.kt
@@ -37,7 +37,8 @@ data class IncomingRequestCancellation(
         /**
          * The request id
          */
-        override val requestId: String? = null
+        override val requestId: String? = null,
+        override val localCreationTimestamp: Long?
 ) : IncomingShareRequestCommon {
     companion object {
         /**
@@ -52,7 +53,8 @@ data class IncomingRequestCancellation(
                         IncomingRequestCancellation(
                                 userId = event.senderId,
                                 deviceId = it.requestingDeviceId,
-                                requestId = it.requestId
+                                requestId = it.requestId,
+                                localCreationTimestamp = event.ageLocalTs ?: System.currentTimeMillis()
                         )
                     }
         }
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/IncomingRoomKeyRequest.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/IncomingRoomKeyRequest.kt
index 8648041094..13f3d38677 100755
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/IncomingRoomKeyRequest.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/IncomingRoomKeyRequest.kt
@@ -46,6 +46,8 @@ data class IncomingRoomKeyRequest(
          */
         val requestBody: RoomKeyRequestBody? = null,
 
+        val state: GossipingRequestState = GossipingRequestState.NONE,
+
         /**
          * The runnable to call to accept to share the keys
          */
@@ -56,7 +58,8 @@ data class IncomingRoomKeyRequest(
          * The runnable to call to ignore the key share request.
          */
         @Transient
-        var ignore: Runnable? = null
+        var ignore: Runnable? = null,
+        override val localCreationTimestamp: Long?
 ) : IncomingShareRequestCommon {
     companion object {
         /**
@@ -72,7 +75,8 @@ data class IncomingRoomKeyRequest(
                                 userId = event.senderId,
                                 deviceId = it.requestingDeviceId,
                                 requestId = it.requestId,
-                                requestBody = it.body ?: RoomKeyRequestBody()
+                                requestBody = it.body ?: RoomKeyRequestBody(),
+                                localCreationTimestamp = event.ageLocalTs ?: System.currentTimeMillis()
                         )
                     }
         }
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/IncomingRoomKeyRequestManager.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/IncomingRoomKeyRequestManager.kt
index 98c73d75f5..4fdd011aee 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/IncomingRoomKeyRequestManager.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/IncomingRoomKeyRequestManager.kt
@@ -17,7 +17,7 @@
 package im.vector.matrix.android.internal.crypto
 
 import im.vector.matrix.android.api.auth.data.Credentials
-import im.vector.matrix.android.api.session.crypto.crosssigning.CrossSigningService
+import im.vector.matrix.android.api.crypto.MXCryptoConfig
 import im.vector.matrix.android.api.session.crypto.crosssigning.SELF_SIGNING_KEY_SSSS_NAME
 import im.vector.matrix.android.api.session.crypto.crosssigning.USER_SIGNING_KEY_SSSS_NAME
 import im.vector.matrix.android.api.session.crypto.keyshare.GossipingRequestListener
@@ -35,8 +35,8 @@ import javax.inject.Inject
 internal class IncomingRoomKeyRequestManager @Inject constructor(
         private val credentials: Credentials,
         private val cryptoStore: IMXCryptoStore,
-        private val crossSigningService: CrossSigningService,
-        private val secrSecretCryptoProvider: ShareSecretCryptoProvider,
+        private val cryptoConfig: MXCryptoConfig,
+        private val secretSecretCryptoProvider: ShareSecretCryptoProvider,
         private val roomDecryptorProvider: RoomDecryptorProvider) {
 
     // list of IncomingRoomKeyRequests/IncomingRoomKeyRequestCancellations
@@ -48,8 +48,7 @@ internal class IncomingRoomKeyRequestManager @Inject constructor(
     private val gossipingRequestListeners: MutableSet<GossipingRequestListener> = HashSet()
 
     init {
-        receiveGossipingRequests.addAll(cryptoStore.getPendingIncomingSecretShareRequests())
-        receiveGossipingRequests.addAll(cryptoStore.getPendingIncomingRoomKeyRequests())
+        receiveGossipingRequests.addAll(cryptoStore.getPendingIncomingGossipingRequests())
     }
 
     /**
@@ -59,21 +58,37 @@ internal class IncomingRoomKeyRequestManager @Inject constructor(
      * @param event the announcement event.
      */
     fun onGossipingRequestEvent(event: Event) {
+        Timber.v("## onGossipingRequestEvent type ${event.type} from user ${event.senderId}")
         val roomKeyShare = event.getClearContent().toModel<GossipingDefaultContent>()
+        val ageLocalTs = event.unsignedData?.age?.let { System.currentTimeMillis() - it }
         when (roomKeyShare?.action) {
             GossipingToDeviceObject.ACTION_SHARE_REQUEST      -> {
                 if (event.getClearType() == EventType.REQUEST_SECRET) {
                     IncomingSecretShareRequest.fromEvent(event)?.let {
+                        // save in DB
+                        cryptoStore.storeIncomingGossipingRequest(it, ageLocalTs)
                         receiveGossipingRequests.add(it)
                     }
                 } else if (event.getClearType() == EventType.ROOM_KEY_REQUEST) {
                     IncomingRoomKeyRequest.fromEvent(event)?.let {
-                        receiveGossipingRequests.add(it)
+                        if (event.senderId == credentials.userId && it.deviceId == credentials.deviceId) {
+                            // ignore, it was sent by me as *
+                            Timber.v("## onGossipingRequestEvent type ${event.type} ignore remote echo")
+                        } else {
+                            cryptoStore.storeIncomingGossipingRequest(it, ageLocalTs)
+                            receiveGossipingRequests.add(it)
+                        }
                     }
                 }
             }
-            GossipingToDeviceObject.ACTION_SHARE_CANCELLATION -> IncomingRequestCancellation.fromEvent(event)?.let { receivedRequestCancellations.add(it) }
-            else                                              -> Timber.e("## onGossipingRequestEvent() : unsupported action ${roomKeyShare?.action}")
+            GossipingToDeviceObject.ACTION_SHARE_CANCELLATION -> {
+                IncomingRequestCancellation.fromEvent(event)?.let {
+                    receivedRequestCancellations.add(it)
+                }
+            }
+            else                                              -> {
+                Timber.e("## onGossipingRequestEvent() : unsupported action ${roomKeyShare?.action}")
+            }
         }
     }
 
@@ -83,6 +98,8 @@ internal class IncomingRoomKeyRequestManager @Inject constructor(
      * It must be called on CryptoThread
      */
     fun processReceivedGossipingRequests() {
+        Timber.v("## processReceivedGossipingRequests()")
+
         val roomKeyRequestsToProcess = receiveGossipingRequests.toList()
         receiveGossipingRequests.clear()
         for (request in roomKeyRequestsToProcess) {
@@ -102,16 +119,25 @@ internal class IncomingRoomKeyRequestManager @Inject constructor(
             }
         }
 
-        if (null != receivedRequestCancellations) {
-            for (request in receivedRequestCancellations!!) {
-                Timber.v("## ## processReceivedGossipingRequests() : m.room_key_request cancellation for " + request.userId
-                        + ":" + request.deviceId + " id " + request.requestId)
-
-                // we should probably only notify the app of cancellations we told it
-                // about, but we don't currently have a record of that, so we just pass
-                // everything through.
-                onRoomKeyRequestCancellation(request)
-                cryptoStore.deleteIncomingRoomKeyRequest(request)
+        receivedRequestCancellations?.forEach { request ->
+            Timber.v("## processReceivedGossipingRequests() : m.room_key_request cancellation $request")
+            // we should probably only notify the app of cancellations we told it
+            // about, but we don't currently have a record of that, so we just pass
+            // everything through.
+            if (request.userId == credentials.userId && request.deviceId == credentials.deviceId) {
+                // ignore remote echo
+                return@forEach
+            }
+            val matchingIncoming = cryptoStore.getIncomingRoomKeyRequest(request.userId ?: "", request.deviceId ?: "", request.requestId ?: "")
+            if (matchingIncoming == null) {
+                // ignore that?
+                return@forEach
+            } else {
+                // If it was accepted from this device, keep the information, do not mark as cancelled
+                if (matchingIncoming.state != GossipingRequestState.ACCEPTED) {
+                    onRoomKeyRequestCancellation(request)
+                    cryptoStore.updateGossipingRequestState(request, GossipingRequestState.CANCELLED_BY_REQUESTER)
+                }
             }
         }
     }
@@ -123,11 +149,11 @@ internal class IncomingRoomKeyRequestManager @Inject constructor(
         val roomId = body!!.roomId
         val alg = body.algorithm
 
-        Timber.v("m.room_key_request from $userId:$deviceId for $roomId / ${body.sessionId} id ${request.requestId}")
+        Timber.v("## processIncomingRoomKeyRequest from $userId:$deviceId for $roomId / ${body.sessionId} id ${request.requestId}")
         if (userId == null || credentials.userId != userId) {
             // TODO: determine if we sent this device the keys already: in
             Timber.w("## processReceivedGossipingRequests() : Ignoring room key request from other user for now")
-            cryptoStore.deleteIncomingRoomKeyRequest(request)
+            cryptoStore.updateGossipingRequestState(request, GossipingRequestState.REJECTED)
             return
         }
         // TODO: should we queue up requests we don't yet have keys for, in case they turn up later?
@@ -136,89 +162,110 @@ internal class IncomingRoomKeyRequestManager @Inject constructor(
         val decryptor = roomDecryptorProvider.getRoomDecryptor(roomId, alg)
         if (null == decryptor) {
             Timber.w("## processReceivedGossipingRequests() : room key request for unknown $alg in room $roomId")
+            cryptoStore.updateGossipingRequestState(request, GossipingRequestState.REJECTED)
             return
         }
         if (!decryptor.hasKeysForKeyRequest(request)) {
             Timber.w("## processReceivedGossipingRequests() : room key request for unknown session ${body.sessionId!!}")
-            cryptoStore.deleteIncomingRoomKeyRequest(request)
+            cryptoStore.updateGossipingRequestState(request, GossipingRequestState.REJECTED)
             return
         }
 
         if (credentials.deviceId == deviceId && credentials.userId == userId) {
             Timber.v("## processReceivedGossipingRequests() : oneself device - ignored")
-            cryptoStore.deleteIncomingRoomKeyRequest(request)
+            cryptoStore.updateGossipingRequestState(request, GossipingRequestState.REJECTED)
             return
         }
         request.share = Runnable {
             decryptor.shareKeysWithDevice(request)
-            cryptoStore.deleteIncomingRoomKeyRequest(request)
+            cryptoStore.updateGossipingRequestState(request, GossipingRequestState.ACCEPTED)
         }
         request.ignore = Runnable {
-            cryptoStore.deleteIncomingRoomKeyRequest(request)
+            cryptoStore.updateGossipingRequestState(request, GossipingRequestState.REJECTED)
         }
         // if the device is verified already, share the keys
         val device = cryptoStore.getUserDevice(userId, deviceId!!)
         if (device != null) {
             if (device.isVerified) {
                 Timber.v("## processReceivedGossipingRequests() : device is already verified: sharing keys")
-                cryptoStore.deleteIncomingRoomKeyRequest(request)
                 request.share?.run()
                 return
             }
 
             if (device.isBlocked) {
                 Timber.v("## processReceivedGossipingRequests() : device is blocked -> ignored")
-                cryptoStore.deleteIncomingRoomKeyRequest(request)
+                cryptoStore.updateGossipingRequestState(request, GossipingRequestState.REJECTED)
                 return
             }
         }
 
-        // If cross signing is available on account we automatically discard untrust devices request
-        if (cryptoStore.getMyCrossSigningInfo() != null) {
+        // As per config we automatically discard untrusted devices request
+        if (cryptoConfig.discardRoomKeyRequestsFromUntrustedDevices) {
+            Timber.v("## processReceivedGossipingRequests() : discardRoomKeyRequestsFromUntrustedDevices")
             // At this point the device is unknown, we don't want to bother user with that
-            cryptoStore.deleteIncomingRoomKeyRequest(request)
+            cryptoStore.updateGossipingRequestState(request, GossipingRequestState.REJECTED)
             return
         }
 
-        cryptoStore.storeIncomingRoomKeyRequest(request)
-
-        // Legacy, pass to application layer to decide what to do
+        // Pass to application layer to decide what to do
         onRoomKeyRequest(request)
     }
 
     private fun processIncomingSecretShareRequest(request: IncomingSecretShareRequest) {
         val secretName = request.secretName ?: return Unit.also {
-            cryptoStore.deleteIncomingSecretRequest(request)
+            cryptoStore.updateGossipingRequestState(request, GossipingRequestState.REJECTED)
             Timber.v("## processIncomingSecretShareRequest() : Missing secret name")
         }
 
         val userId = request.userId
         if (userId == null || credentials.userId != userId) {
-            Timber.e("## processIncomingSecretShareRequest() : Ignoring secret share request from other user")
-            cryptoStore.deleteIncomingRoomKeyRequest(request)
+            Timber.e("## processIncomingSecretShareRequest() : Ignoring secret share request from other users")
+            cryptoStore.updateGossipingRequestState(request, GossipingRequestState.REJECTED)
             return
         }
 
+        val deviceId = request.deviceId
+                ?: return Unit.also {
+                    Timber.e("## processIncomingSecretShareRequest() : Malformed request, no ")
+                    cryptoStore.updateGossipingRequestState(request, GossipingRequestState.REJECTED)
+                }
+
+        val device = cryptoStore.getUserDevice(userId, deviceId)
+                ?: return Unit.also {
+                    Timber.e("## processIncomingSecretShareRequest() : Received secret share request from unknown device ${request.deviceId}")
+                    cryptoStore.updateGossipingRequestState(request, GossipingRequestState.REJECTED)
+                }
+
+        if (!device.isVerified || device.isBlocked) {
+            Timber.v("## processIncomingSecretShareRequest() : Ignoring secret share request from untrusted/blocked session $device")
+            cryptoStore.updateGossipingRequestState(request, GossipingRequestState.REJECTED)
+            return
+        }
+
+        val isDeviceLocallyVerified = cryptoStore.getUserDevice(userId, deviceId)?.trustLevel?.isLocallyVerified()
+
+        //Should SDK always Silently reject any request for the master key?
         when (secretName) {
             SELF_SIGNING_KEY_SSSS_NAME -> cryptoStore.getCrossSigningPrivateKeys()?.selfSigned
             USER_SIGNING_KEY_SSSS_NAME -> cryptoStore.getCrossSigningPrivateKeys()?.user
             else                       -> null
         }?.let { secretValue ->
             // TODO check if locally trusted and not outdated
-            if (cryptoStore.getUserDevice(userId, request.deviceId ?: "")?.trustLevel?.isLocallyVerified() == true) {
-                secrSecretCryptoProvider.shareSecretWithDevice(request, secretValue)
-                cryptoStore.deleteIncomingRoomKeyRequest(request)
+            Timber.i("## processIncomingSecretShareRequest() : Sharing secret $secretName with $device locally trusted")
+            if (isDeviceLocallyVerified == true) {
+                secretSecretCryptoProvider.shareSecretWithDevice(request, secretValue)
+                cryptoStore.updateGossipingRequestState(request, GossipingRequestState.ACCEPTED)
             }
             return
         }
 
         request.ignore = Runnable {
-            cryptoStore.deleteIncomingRoomKeyRequest(request)
+            cryptoStore.updateGossipingRequestState(request, GossipingRequestState.REJECTED)
         }
 
         request.share = { secretValue ->
-            secrSecretCryptoProvider.shareSecretWithDevice(request, secretValue)
-            cryptoStore.deleteIncomingRoomKeyRequest(request)
+            secretSecretCryptoProvider.shareSecretWithDevice(request, secretValue)
+            cryptoStore.updateGossipingRequestState(request, GossipingRequestState.ACCEPTED)
         }
 
         onShareRequest(request)
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/IncomingSecretShareRequest.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/IncomingSecretShareRequest.kt
index 6deb5b4692..2fcd3e22d5 100755
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/IncomingSecretShareRequest.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/IncomingSecretShareRequest.kt
@@ -54,7 +54,9 @@ data class IncomingSecretShareRequest(
          * The runnable to call to ignore the key share request.
          */
         @Transient
-        var ignore: Runnable? = null
+        var ignore: Runnable? = null,
+
+        override val localCreationTimestamp: Long?
 
 ) : IncomingShareRequestCommon {
     companion object {
@@ -71,7 +73,8 @@ data class IncomingSecretShareRequest(
                                 userId = event.senderId,
                                 deviceId = it.requestingDeviceId,
                                 requestId = it.requestId,
-                                secretName = it.secretName
+                                secretName = it.secretName,
+                                localCreationTimestamp = event.ageLocalTs ?: System.currentTimeMillis()
                         )
                     }
         }
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/IncomingShareRequestCommon.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/IncomingShareRequestCommon.kt
index 8999f835c8..f39a0d80d1 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/IncomingShareRequestCommon.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/IncomingShareRequestCommon.kt
@@ -31,4 +31,6 @@ interface IncomingShareRequestCommon {
      * The request id
      */
     val requestId: String?
+
+    val localCreationTimestamp: Long?
 }
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/OutgoingShareRequest.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/OutgoingGossipingRequest.kt
similarity index 81%
rename from matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/OutgoingShareRequest.kt
rename to matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/OutgoingGossipingRequest.kt
index b56ba22da6..30fc5fdb4a 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/OutgoingShareRequest.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/OutgoingGossipingRequest.kt
@@ -16,10 +16,10 @@
 
 package im.vector.matrix.android.internal.crypto
 
-interface OutgoingShareRequest {
-    var recipients: List<Map<String, String>>
+interface OutgoingGossipingRequest {
+    var recipients: Map<String, List<String>>
     var requestId: String
-    var state: ShareRequestState
+    var state: OutgoingGossipingRequestState
     // transaction id for the cancellation, if any
-    var cancellationTxnId: String?
+    //var cancellationTxnId: String?
 }
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/OutgoingGossipingRequestManager.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/OutgoingGossipingRequestManager.kt
new file mode 100755
index 0000000000..e7c5ef89c8
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/OutgoingGossipingRequestManager.kt
@@ -0,0 +1,185 @@
+/*
+ * Copyright 2016 OpenMarket Ltd
+ * Copyright 2018 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.matrix.android.internal.crypto
+
+import androidx.work.BackoffPolicy
+import androidx.work.Data
+import androidx.work.ExistingWorkPolicy
+import androidx.work.ListenableWorker
+import androidx.work.OneTimeWorkRequest
+import im.vector.matrix.android.api.session.events.model.LocalEcho
+import im.vector.matrix.android.api.util.Cancelable
+import im.vector.matrix.android.internal.crypto.model.rest.RoomKeyRequestBody
+import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore
+import im.vector.matrix.android.internal.di.SessionId
+import im.vector.matrix.android.internal.di.WorkManagerProvider
+import im.vector.matrix.android.internal.session.SessionScope
+import im.vector.matrix.android.internal.util.CancelableWork
+import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
+import im.vector.matrix.android.internal.worker.WorkerParamsFactory
+import im.vector.matrix.android.internal.worker.startChain
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
+import timber.log.Timber
+import java.util.concurrent.TimeUnit
+import javax.inject.Inject
+
+@SessionScope
+internal class OutgoingGossipingRequestManager @Inject constructor(
+        @SessionId private val sessionId: String,
+        private val cryptoStore: IMXCryptoStore,
+        private val coroutineDispatchers: MatrixCoroutineDispatchers,
+        private val cryptoCoroutineScope: CoroutineScope,
+        private val workManagerProvider: WorkManagerProvider) {
+
+    /**
+     * Send off a room key request, if we haven't already done so.
+     *
+     *
+     * The `requestBody` is compared (with a deep-equality check) against
+     * previous queued or sent requests and if it matches, no change is made.
+     * Otherwise, a request is added to the pending list, and a job is started
+     * in the background to send it.
+     *
+     * @param requestBody requestBody
+     * @param recipients  recipients
+     */
+    fun sendRoomKeyRequest(requestBody: RoomKeyRequestBody, recipients: Map<String, List<String>>) {
+        cryptoCoroutineScope.launch(coroutineDispatchers.crypto) {
+            cryptoStore.getOrAddOutgoingRoomKeyRequest(requestBody, recipients)?.let {
+                // Don't resend if it's already done, you need to cancel first (reRequest)
+                if (it.state == OutgoingGossipingRequestState.SENDING || it.state == OutgoingGossipingRequestState.SENT) {
+                    Timber.v("## sendOutgoingRoomKeyRequest() : we already request for that session: $it")
+                    return@launch
+                }
+
+                sendOutgoingGossipingRequest(it)
+            }
+        }
+    }
+
+    fun sendSecretShareRequest(secretName: String, recipients: Map<String, List<String>>) {
+        cryptoCoroutineScope.launch(coroutineDispatchers.crypto) {
+            cryptoStore.getOrAddOutgoingSecretShareRequest(secretName, recipients)?.let {
+                // TODO check if there is already one that is being sent?
+                if (it.state == OutgoingGossipingRequestState.SENDING || it.state == OutgoingGossipingRequestState.SENT) {
+                    Timber.v("## sendOutgoingRoomKeyRequest() : we already request for that session: $it")
+                    return@launch
+                }
+
+                sendOutgoingGossipingRequest(it)
+            }
+        }
+    }
+
+    /**
+     * Cancel room key requests, if any match the given details
+     *
+     * @param requestBody requestBody
+     */
+    fun cancelRoomKeyRequest(requestBody: RoomKeyRequestBody) {
+        cryptoCoroutineScope.launch(coroutineDispatchers.crypto) {
+            cancelRoomKeyRequest(requestBody, false)
+        }
+    }
+
+    /**
+     * Cancel room key requests, if any match the given details, and resend
+     *
+     * @param requestBody requestBody
+     */
+    fun resendRoomKeyRequest(requestBody: RoomKeyRequestBody) {
+        cryptoCoroutineScope.launch(coroutineDispatchers.crypto) {
+            cancelRoomKeyRequest(requestBody, true)
+        }
+    }
+
+    /**
+     * Cancel room key requests, if any match the given details, and resend
+     *
+     * @param requestBody requestBody
+     * @param andResend   true to resend the key request
+     */
+    private fun cancelRoomKeyRequest(requestBody: RoomKeyRequestBody, andResend: Boolean) {
+        val req = cryptoStore.getOutgoingRoomKeyRequest(requestBody)
+                ?: // no request was made for this key
+                return Unit.also {
+                    Timber.v("## cancelRoomKeyRequest() Unknown request")
+                }
+
+        sendOutgoingRoomKeyRequestCancellation(req, andResend)
+    }
+
+    /**
+     * Send the outgoing key request.
+     *
+     * @param request the request
+     */
+    private fun sendOutgoingGossipingRequest(request: OutgoingGossipingRequest) {
+        Timber.v("## sendOutgoingRoomKeyRequest() : Requesting keys ${request}")
+
+        val params = SendGossipRequestWorker.Params(
+                sessionId = sessionId,
+                keyShareRequest = request as? OutgoingRoomKeyRequest,
+                secretShareRequest = request as? OutgoingSecretRequest
+        )
+        cryptoStore.updateOutgoingGossipingRequestState(request.requestId, OutgoingGossipingRequestState.SENDING)
+        val workRequest = createWork<SendGossipRequestWorker>(WorkerParamsFactory.toData(params), true)
+        postWork(workRequest)
+    }
+
+    /**
+     * Given a OutgoingRoomKeyRequest, cancel it and delete the request record
+     *
+     * @param request the request
+     */
+    private fun sendOutgoingRoomKeyRequestCancellation(request: OutgoingRoomKeyRequest, resend: Boolean = false) {
+        Timber.v("$request")
+        val params = CancelGossipRequestWorker.Params.fromRequest(sessionId, request)
+        cryptoStore.updateOutgoingGossipingRequestState(request.requestId, OutgoingGossipingRequestState.CANCELLING)
+
+        val workRequest = createWork<CancelGossipRequestWorker>(WorkerParamsFactory.toData(params), true)
+        postWork(workRequest)
+
+        if (resend) {
+            val reSendParams = SendGossipRequestWorker.Params(
+                    sessionId = sessionId,
+                    keyShareRequest = request.copy(requestId = LocalEcho.createLocalEchoId())
+            )
+            val reSendWorkRequest = createWork<SendGossipRequestWorker>(WorkerParamsFactory.toData(reSendParams), true)
+            postWork(reSendWorkRequest)
+        }
+    }
+
+    private inline fun <reified W : ListenableWorker> createWork(data: Data, startChain: Boolean): OneTimeWorkRequest {
+        return workManagerProvider.matrixOneTimeWorkRequestBuilder<W>()
+                .setConstraints(WorkManagerProvider.workConstraints)
+                .startChain(startChain)
+                .setInputData(data)
+                .setBackoffCriteria(BackoffPolicy.LINEAR, 10_000L, TimeUnit.MILLISECONDS)
+                .build()
+    }
+
+    private fun postWork(workRequest: OneTimeWorkRequest, policy: ExistingWorkPolicy = ExistingWorkPolicy.APPEND): Cancelable {
+        workManagerProvider.workManager
+                .beginUniqueWork(this::class.java.name, policy, workRequest)
+                .enqueue()
+
+        return CancelableWork(workManagerProvider.workManager, workRequest.id)
+    }
+}
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/OutgoingRoomKeyRequest.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/OutgoingRoomKeyRequest.kt
index 2b9f8003ee..7e359a677c 100755
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/OutgoingRoomKeyRequest.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/OutgoingRoomKeyRequest.kt
@@ -17,22 +17,27 @@
 
 package im.vector.matrix.android.internal.crypto
 
+import com.squareup.moshi.JsonClass
 import im.vector.matrix.android.internal.crypto.model.rest.RoomKeyRequestBody
 
 /**
  * Represents an outgoing room key request
  */
-class OutgoingRoomKeyRequest(
+@JsonClass(generateAdapter = true)
+data class OutgoingRoomKeyRequest(
         // RequestBody
-        var requestBody: RoomKeyRequestBody?, // list of recipients for the request
-        override var recipients: List<Map<String, String>>, // Unique id for this request. Used for both
+        var requestBody: RoomKeyRequestBody?,
+        // list of recipients for the request
+        override var recipients: Map<String, List<String>>,
+        // Unique id for this request. Used for both
         // an id within the request for later pairing with a cancellation, and for
         // the transaction id when sending the to_device messages to our local
         override var requestId: String, // current state of this request
-        override var state: ShareRequestState) : OutgoingShareRequest {
+        override var state: OutgoingGossipingRequestState
+        // transaction id for the cancellation, if any
+        // override var cancellationTxnId: String? = null
+) : OutgoingGossipingRequest {
 
-    // transaction id for the cancellation, if any
-    override var cancellationTxnId: String? = null
 
     /**
      * Used only for log.
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/OutgoingRoomKeyRequestManager.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/OutgoingRoomKeyRequestManager.kt
deleted file mode 100755
index 5102afef7c..0000000000
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/OutgoingRoomKeyRequestManager.kt
+++ /dev/null
@@ -1,320 +0,0 @@
-/*
- * Copyright 2016 OpenMarket Ltd
- * Copyright 2018 New Vector Ltd
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package im.vector.matrix.android.internal.crypto
-
-import im.vector.matrix.android.api.MatrixCallback
-import im.vector.matrix.android.api.session.events.model.EventType
-import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap
-import im.vector.matrix.android.internal.crypto.model.rest.RoomKeyRequestBody
-import im.vector.matrix.android.internal.crypto.model.rest.ShareRequestCancellation
-import im.vector.matrix.android.internal.crypto.model.rest.RoomKeyShareRequest
-import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore
-import im.vector.matrix.android.internal.crypto.tasks.SendToDeviceTask
-import im.vector.matrix.android.internal.session.SessionScope
-import im.vector.matrix.android.internal.task.TaskExecutor
-import im.vector.matrix.android.internal.task.TaskThread
-import im.vector.matrix.android.internal.task.configureWith
-import im.vector.matrix.android.internal.util.createBackgroundHandler
-import timber.log.Timber
-import java.util.concurrent.atomic.AtomicBoolean
-import javax.inject.Inject
-
-@SessionScope
-internal class OutgoingRoomKeyRequestManager @Inject constructor(
-        private val cryptoStore: IMXCryptoStore,
-        private val sendToDeviceTask: SendToDeviceTask,
-        private val taskExecutor: TaskExecutor) {
-
-    // running
-    private var isClientRunning: Boolean = false
-
-    // transaction counter
-    private var txnCtr: Int = 0
-
-    // sanity check to ensure that we don't end up with two concurrent runs
-    // of sendOutgoingRoomKeyRequestsTimer
-    private val sendOutgoingRoomKeyRequestsRunning = AtomicBoolean(false)
-
-    /**
-     * Called when the client is started. Sets background processes running.
-     */
-    fun start() {
-        isClientRunning = true
-        startTimer()
-    }
-
-    /**
-     * Called when the client is stopped. Stops any running background processes.
-     */
-    fun stop() {
-        isClientRunning = false
-        stopTimer()
-    }
-
-    /**
-     * Make up a new transaction id
-     *
-     * @return {string} a new, unique, transaction id
-     */
-    private fun makeTxnId(): String {
-        return "m" + System.currentTimeMillis() + "." + txnCtr++
-    }
-
-    /**
-     * Send off a room key request, if we haven't already done so.
-     *
-     *
-     * The `requestBody` is compared (with a deep-equality check) against
-     * previous queued or sent requests and if it matches, no change is made.
-     * Otherwise, a request is added to the pending list, and a job is started
-     * in the background to send it.
-     *
-     * @param requestBody requestBody
-     * @param recipients  recipients
-     */
-    fun sendRoomKeyRequest(requestBody: RoomKeyRequestBody?, recipients: List<Map<String, String>>) {
-        val req = cryptoStore.getOrAddOutgoingRoomKeyRequest(
-                OutgoingRoomKeyRequest(requestBody, recipients, makeTxnId(), ShareRequestState.UNSENT))
-
-        if (req?.state == ShareRequestState.UNSENT) {
-            startTimer()
-        }
-    }
-
-    /**
-     * Cancel room key requests, if any match the given details
-     *
-     * @param requestBody requestBody
-     */
-    fun cancelRoomKeyRequest(requestBody: RoomKeyRequestBody) {
-        BACKGROUND_HANDLER.post {
-            cancelRoomKeyRequest(requestBody, false)
-        }
-    }
-
-    /**
-     * Cancel room key requests, if any match the given details, and resend
-     *
-     * @param requestBody requestBody
-     */
-    fun resendRoomKeyRequest(requestBody: RoomKeyRequestBody) {
-        BACKGROUND_HANDLER.post {
-            cancelRoomKeyRequest(requestBody, true)
-        }
-    }
-
-    /**
-     * Cancel room key requests, if any match the given details, and resend
-     *
-     * @param requestBody requestBody
-     * @param andResend   true to resend the key request
-     */
-    private fun cancelRoomKeyRequest(requestBody: RoomKeyRequestBody, andResend: Boolean) {
-        val req = cryptoStore.getOutgoingRoomKeyRequest(requestBody)
-                ?: // no request was made for this key
-                return
-
-        Timber.v("cancelRoomKeyRequest: requestId: " + req.requestId + " state: " + req.state + " andResend: " + andResend)
-
-        when (req.state) {
-            ShareRequestState.CANCELLATION_PENDING,
-            ShareRequestState.CANCELLATION_PENDING_AND_WILL_RESEND -> {
-                // nothing to do here
-            }
-            ShareRequestState.UNSENT,
-            ShareRequestState.FAILED                               -> {
-                Timber.v("## cancelRoomKeyRequest() : deleting unnecessary room key request for $requestBody")
-                cryptoStore.deleteOutgoingRoomKeyRequest(req.requestId)
-            }
-            ShareRequestState.SENT                                 -> {
-                if (andResend) {
-                    req.state = ShareRequestState.CANCELLATION_PENDING_AND_WILL_RESEND
-                } else {
-                    req.state = ShareRequestState.CANCELLATION_PENDING
-                }
-                req.cancellationTxnId = makeTxnId()
-                cryptoStore.updateOutgoingRoomKeyRequest(req)
-                sendOutgoingRoomKeyRequestCancellation(req)
-            }
-        }
-    }
-
-    /**
-     * Start the background timer to send queued requests, if the timer isn't already running.
-     */
-    private fun startTimer() {
-        if (sendOutgoingRoomKeyRequestsRunning.get()) {
-            return
-        }
-        BACKGROUND_HANDLER.postDelayed(Runnable {
-            if (sendOutgoingRoomKeyRequestsRunning.get()) {
-                Timber.v("## startTimer() : RoomKeyRequestSend already in progress!")
-                return@Runnable
-            }
-
-            sendOutgoingRoomKeyRequestsRunning.set(true)
-            sendOutgoingRoomKeyRequests()
-        }, SEND_KEY_REQUESTS_DELAY_MS.toLong())
-    }
-
-    private fun stopTimer() {
-        BACKGROUND_HANDLER.removeCallbacksAndMessages(null)
-    }
-
-    // look for and send any queued requests. Runs itself recursively until
-    // there are no more requests, or there is an error (in which case, the
-    // timer will be restarted before the promise resolves).
-    private fun sendOutgoingRoomKeyRequests() {
-        if (!isClientRunning) {
-            sendOutgoingRoomKeyRequestsRunning.set(false)
-            return
-        }
-
-        Timber.v("## sendOutgoingRoomKeyRequests() :  Looking for queued outgoing room key requests")
-        val outgoingRoomKeyRequest = cryptoStore.getOutgoingRoomKeyRequestByState(
-                setOf(ShareRequestState.UNSENT,
-                        ShareRequestState.CANCELLATION_PENDING,
-                        ShareRequestState.CANCELLATION_PENDING_AND_WILL_RESEND))
-
-        if (null == outgoingRoomKeyRequest) {
-            Timber.v("## sendOutgoingRoomKeyRequests() : No more outgoing room key requests")
-            sendOutgoingRoomKeyRequestsRunning.set(false)
-            return
-        }
-
-        if (ShareRequestState.UNSENT === outgoingRoomKeyRequest.state) {
-            sendOutgoingRoomKeyRequest(outgoingRoomKeyRequest)
-        } else {
-            sendOutgoingRoomKeyRequestCancellation(outgoingRoomKeyRequest)
-        }
-    }
-
-    /**
-     * Send the outgoing key request.
-     *
-     * @param request the request
-     */
-    private fun sendOutgoingRoomKeyRequest(request: OutgoingRoomKeyRequest) {
-        Timber.v("## sendOutgoingRoomKeyRequest() : Requesting keys " + request.requestBody
-                + " from " + request.recipients + " id " + request.requestId)
-
-        val requestMessage = RoomKeyShareRequest(
-                requestingDeviceId = cryptoStore.getDeviceId(),
-                requestId = request.requestId,
-                body = request.requestBody
-        )
-
-        sendMessageToDevices(requestMessage, request.recipients, request.requestId, object : MatrixCallback<Unit> {
-            private fun onDone(state: ShareRequestState) {
-                if (request.state !== ShareRequestState.UNSENT) {
-                    Timber.v("## sendOutgoingRoomKeyRequest() : Cannot update room key request from UNSENT as it was already updated to ${request.state}")
-                } else {
-                    request.state = state
-                    cryptoStore.updateOutgoingRoomKeyRequest(request)
-                }
-
-                sendOutgoingRoomKeyRequestsRunning.set(false)
-                startTimer()
-            }
-
-            override fun onSuccess(data: Unit) {
-                Timber.v("## sendOutgoingRoomKeyRequest succeed")
-                onDone(ShareRequestState.SENT)
-            }
-
-            override fun onFailure(failure: Throwable) {
-                Timber.e("## sendOutgoingRoomKeyRequest failed")
-                onDone(ShareRequestState.FAILED)
-            }
-        })
-    }
-
-    /**
-     * Given a OutgoingRoomKeyRequest, cancel it and delete the request record
-     *
-     * @param request the request
-     */
-    private fun sendOutgoingRoomKeyRequestCancellation(request: OutgoingRoomKeyRequest) {
-        Timber.v("## sendOutgoingRoomKeyRequestCancellation() : Sending cancellation for key request for " + request.requestBody
-                + " to " + request.recipients
-                + " cancellation id  " + request.cancellationTxnId)
-
-        val roomKeyShareCancellation = ShareRequestCancellation(
-                requestingDeviceId = cryptoStore.getDeviceId(),
-                requestId = request.cancellationTxnId
-        )
-
-        sendMessageToDevices(roomKeyShareCancellation, request.recipients, request.cancellationTxnId, object : MatrixCallback<Unit> {
-            private fun onDone() {
-                cryptoStore.deleteOutgoingRoomKeyRequest(request.requestId)
-                sendOutgoingRoomKeyRequestsRunning.set(false)
-                startTimer()
-            }
-
-            override fun onSuccess(data: Unit) {
-                Timber.v("## sendOutgoingRoomKeyRequestCancellation() : done")
-                val resend = request.state === ShareRequestState.CANCELLATION_PENDING_AND_WILL_RESEND
-
-                onDone()
-
-                // Resend the request with a new ID
-                if (resend) {
-                    sendRoomKeyRequest(request.requestBody, request.recipients)
-                }
-            }
-
-            override fun onFailure(failure: Throwable) {
-                Timber.e("## sendOutgoingRoomKeyRequestCancellation failed")
-                onDone()
-            }
-        })
-    }
-
-    /**
-     * Send a SendToDeviceObject to a list of recipients
-     *
-     * @param message       the message
-     * @param recipients    the recipients.
-     * @param transactionId the transaction id
-     * @param callback      the asynchronous callback.
-     */
-    private fun sendMessageToDevices(message: Any,
-                                     recipients: List<Map<String, String>>,
-                                     transactionId: String?,
-                                     callback: MatrixCallback<Unit>) {
-        val contentMap = MXUsersDevicesMap<Any>()
-
-        for (recipient in recipients) {
-            // TODO Change this two hard coded key to something better
-            contentMap.setObject(recipient["userId"], recipient["deviceId"], message)
-        }
-        sendToDeviceTask
-                .configureWith(SendToDeviceTask.Params(EventType.ROOM_KEY_REQUEST, contentMap, transactionId)) {
-                    this.callback = callback
-                    this.callbackThread = TaskThread.CALLER
-                    this.executionThread = TaskThread.CALLER
-                }
-                .executeBy(taskExecutor)
-    }
-
-    companion object {
-        private const val SEND_KEY_REQUESTS_DELAY_MS = 500
-
-        private val BACKGROUND_HANDLER = createBackgroundHandler("OutgoingRoomKeyRequest")
-    }
-}
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/OutgoingSecretRequest.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/OutgoingSecretRequest.kt
index a5305c1a25..1497796743 100755
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/OutgoingSecretRequest.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/OutgoingSecretRequest.kt
@@ -16,21 +16,23 @@
 
 package im.vector.matrix.android.internal.crypto
 
+import com.squareup.moshi.JsonClass
+
 /**
  * Represents an outgoing room key request
  */
+@JsonClass(generateAdapter = true)
 class OutgoingSecretRequest(
         // Secret Name
-        var secretName: String?,
+        val secretName: String?,
         // list of recipients for the request
-        override var recipients: List<Map<String, String>>,
+        override var recipients: Map<String, List<String>>,
         // Unique id for this request. Used for both
         // an id within the request for later pairing with a cancellation, and for
         // the transaction id when sending the to_device messages to our local
         override var requestId: String,
         // current state of this request
-        override var state: ShareRequestState) : OutgoingShareRequest {
+        override var state: OutgoingGossipingRequestState) : OutgoingGossipingRequest {
 
     // transaction id for the cancellation, if any
-    override var cancellationTxnId: String? = null
 }
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/SendGossipRequestWorker.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/SendGossipRequestWorker.kt
new file mode 100644
index 0000000000..cffd58cbf2
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/SendGossipRequestWorker.kt
@@ -0,0 +1,150 @@
+/*
+ * 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 im.vector.matrix.android.internal.crypto
+
+import android.content.Context
+import androidx.work.CoroutineWorker
+import androidx.work.Data
+import androidx.work.WorkerParameters
+import com.squareup.moshi.JsonClass
+import im.vector.matrix.android.api.auth.data.Credentials
+import im.vector.matrix.android.api.failure.shouldBeRetried
+import im.vector.matrix.android.api.session.events.model.Event
+import im.vector.matrix.android.api.session.events.model.EventType
+import im.vector.matrix.android.api.session.events.model.LocalEcho
+import im.vector.matrix.android.api.session.events.model.toContent
+import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap
+import im.vector.matrix.android.internal.crypto.model.rest.GossipingToDeviceObject
+import im.vector.matrix.android.internal.crypto.model.rest.RoomKeyShareRequest
+import im.vector.matrix.android.internal.crypto.model.rest.SecretShareRequest
+import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore
+import im.vector.matrix.android.internal.crypto.tasks.SendToDeviceTask
+import im.vector.matrix.android.internal.database.mapper.ContentMapper
+import im.vector.matrix.android.internal.worker.WorkerParamsFactory
+import im.vector.matrix.android.internal.worker.getSessionComponent
+import org.greenrobot.eventbus.EventBus
+import timber.log.Timber
+import javax.inject.Inject
+
+internal class SendGossipRequestWorker(context: Context,
+                                       params: WorkerParameters)
+    : CoroutineWorker(context, params) {
+
+    @JsonClass(generateAdapter = true)
+    internal data class Params(
+            val sessionId: String,
+            val keyShareRequest: OutgoingRoomKeyRequest? = null,
+            val secretShareRequest: OutgoingSecretRequest? = null
+    )
+
+    @Inject lateinit var sendToDeviceTask: SendToDeviceTask
+    @Inject lateinit var cryptoStore: IMXCryptoStore
+    @Inject lateinit var eventBus: EventBus
+    @Inject lateinit var credentials: Credentials
+
+    override suspend fun doWork(): Result {
+
+        val errorOutputData = Data.Builder().putBoolean("failed", true).build()
+        val params = WorkerParamsFactory.fromData<Params>(inputData)
+                ?: return Result.success(errorOutputData)
+
+        val sessionComponent = getSessionComponent(params.sessionId)
+                ?: return Result.success(errorOutputData).also {
+                    // TODO, can this happen? should I update local echo?
+                    Timber.e("Unknown Session, cannot send message, sessionId: ${params.sessionId}")
+                }
+        sessionComponent.inject(this)
+
+        val localId = LocalEcho.createLocalEchoId()
+        val contentMap = MXUsersDevicesMap<Any>()
+        val eventType: String
+        val requestId: String
+        when {
+            params.keyShareRequest != null    -> {
+                eventType = EventType.ROOM_KEY_REQUEST
+                requestId = params.keyShareRequest.requestId
+                val toDeviceContent = RoomKeyShareRequest(
+                        requestingDeviceId = credentials.deviceId,
+                        requestId = params.keyShareRequest.requestId,
+                        action = GossipingToDeviceObject.ACTION_SHARE_REQUEST,
+                        body = params.keyShareRequest.requestBody
+                )
+                cryptoStore.saveGossipingEvent(Event(
+                        type = eventType,
+                        content = toDeviceContent.toContent(),
+                        senderId = credentials.userId
+                ).also {
+                    it.ageLocalTs = System.currentTimeMillis()
+                })
+
+                params.keyShareRequest.recipients.forEach { userToDeviceMap ->
+                    userToDeviceMap.value.forEach { deviceId ->
+                        contentMap.setObject(userToDeviceMap.key, deviceId, toDeviceContent)
+                    }
+                }
+            }
+            params.secretShareRequest != null -> {
+                eventType = EventType.REQUEST_SECRET
+                requestId = params.secretShareRequest.requestId
+                val toDeviceContent = SecretShareRequest(
+                        requestingDeviceId = credentials.deviceId,
+                        requestId = params.secretShareRequest.requestId,
+                        action = GossipingToDeviceObject.ACTION_SHARE_REQUEST,
+                        secretName = params.secretShareRequest.secretName
+                )
+
+                cryptoStore.saveGossipingEvent(Event(
+                        type = eventType,
+                        content = toDeviceContent.toContent(),
+                        senderId = credentials.userId
+                ).also {
+                    it.ageLocalTs = System.currentTimeMillis()
+                })
+
+                params.secretShareRequest.recipients.forEach { userToDeviceMap ->
+                    userToDeviceMap.value.forEach { deviceId ->
+                        contentMap.setObject(userToDeviceMap.key, deviceId, toDeviceContent)
+                    }
+                }
+            }
+            else                              -> {
+                return Result.success(errorOutputData).also {
+                    Timber.e("Unknown empty gossiping request: ${params}")
+                }
+            }
+        }
+        try {
+            cryptoStore.updateOutgoingGossipingRequestState(requestId, OutgoingGossipingRequestState.SENDING)
+            sendToDeviceTask.execute(
+                    SendToDeviceTask.Params(
+                            eventType = eventType,
+                            contentMap = contentMap,
+                            transactionId = localId
+                    )
+            )
+            cryptoStore.updateOutgoingGossipingRequestState(requestId, OutgoingGossipingRequestState.SENT)
+            return Result.success()
+        } catch (exception: Throwable) {
+            return if (exception.shouldBeRetried()) {
+                Result.retry()
+            } else {
+                cryptoStore.updateOutgoingGossipingRequestState(requestId, OutgoingGossipingRequestState.FAILED_TO_SEND)
+                Result.success(errorOutputData)
+            }
+        }
+    }
+}
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/ShareSecretCryptoProvider.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/ShareSecretCryptoProvider.kt
index e40caf4eed..78e587c0f1 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/ShareSecretCryptoProvider.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/ShareSecretCryptoProvider.kt
@@ -16,8 +16,10 @@
 
 package im.vector.matrix.android.internal.crypto
 
+import im.vector.matrix.android.api.session.events.model.Event
 import im.vector.matrix.android.api.session.events.model.EventType
 import im.vector.matrix.android.internal.crypto.actions.MessageEncrypter
+import im.vector.matrix.android.internal.crypto.algorithms.olm.MXOlmDecryptionFactory
 import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap
 import im.vector.matrix.android.internal.crypto.model.event.SecretSendEventContent
 import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore
@@ -25,6 +27,7 @@ import im.vector.matrix.android.internal.crypto.tasks.SendToDeviceTask
 import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.launch
+import kotlinx.coroutines.runBlocking
 import timber.log.Timber
 import javax.inject.Inject
 
@@ -32,6 +35,7 @@ internal class ShareSecretCryptoProvider @Inject constructor(
         val messageEncrypter: MessageEncrypter,
         val sendToDeviceTask: SendToDeviceTask,
         val deviceListManager: DeviceListManager,
+        private val olmDecryptionFactory: MXOlmDecryptionFactory,
         val cryptoCoroutineScope: CoroutineScope,
         val cryptoStore: IMXCryptoStore,
         val coroutineDispatchers: MatrixCoroutineDispatchers
@@ -61,4 +65,10 @@ internal class ShareSecretCryptoProvider @Inject constructor(
                     }
         }
     }
+
+    fun decryptEvent(event: Event): MXEventDecryptionResult {
+        return runBlocking(coroutineDispatchers.crypto) {
+            olmDecryptionFactory.create().decryptEvent(event, ShareSecretCryptoProvider::class.java.name ?: "")
+        }
+    }
 }
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/actions/MegolmSessionDataImporter.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/actions/MegolmSessionDataImporter.kt
index 6f41116b90..cac4659ae5 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/actions/MegolmSessionDataImporter.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/actions/MegolmSessionDataImporter.kt
@@ -20,7 +20,7 @@ import androidx.annotation.WorkerThread
 import im.vector.matrix.android.api.listeners.ProgressListener
 import im.vector.matrix.android.internal.crypto.MXOlmDevice
 import im.vector.matrix.android.internal.crypto.MegolmSessionData
-import im.vector.matrix.android.internal.crypto.OutgoingRoomKeyRequestManager
+import im.vector.matrix.android.internal.crypto.OutgoingGossipingRequestManager
 import im.vector.matrix.android.internal.crypto.RoomDecryptorProvider
 import im.vector.matrix.android.internal.crypto.model.ImportRoomKeysResult
 import im.vector.matrix.android.internal.crypto.model.rest.RoomKeyRequestBody
@@ -30,7 +30,7 @@ import javax.inject.Inject
 
 internal class MegolmSessionDataImporter @Inject constructor(private val olmDevice: MXOlmDevice,
                                                              private val roomDecryptorProvider: RoomDecryptorProvider,
-                                                             private val outgoingRoomKeyRequestManager: OutgoingRoomKeyRequestManager,
+                                                             private val outgoingGossipingRequestManager: OutgoingGossipingRequestManager,
                                                              private val cryptoStore: IMXCryptoStore) {
 
     /**
@@ -73,7 +73,7 @@ internal class MegolmSessionDataImporter @Inject constructor(private val olmDevi
                             sessionId = megolmSessionData.sessionId
                     )
 
-                    outgoingRoomKeyRequestManager.cancelRoomKeyRequest(roomKeyRequestBody)
+                    outgoingGossipingRequestManager.cancelRoomKeyRequest(roomKeyRequestBody)
 
                     // Have another go at decrypting events sent with this session
                     decrypting.onNewSession(megolmSessionData.senderKey!!, sessionId!!)
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/IMXDecrypting.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/IMXDecrypting.kt
index ff740fb1f9..e9176ad6d9 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/IMXDecrypting.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/IMXDecrypting.kt
@@ -68,4 +68,6 @@ internal interface IMXDecrypting {
     fun shareKeysWithDevice(request: IncomingRoomKeyRequest) {}
 
     fun shareSecretWithDevice(request: IncomingSecretShareRequest, secretValue : String) {}
+
+    fun requestKeysForEvent(event: Event)
 }
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmDecryption.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmDecryption.kt
index c8a1d628d7..36b4e40957 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmDecryption.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmDecryption.kt
@@ -26,7 +26,7 @@ import im.vector.matrix.android.internal.crypto.IncomingRoomKeyRequest
 import im.vector.matrix.android.internal.crypto.MXEventDecryptionResult
 import im.vector.matrix.android.internal.crypto.MXOlmDevice
 import im.vector.matrix.android.internal.crypto.NewSessionListener
-import im.vector.matrix.android.internal.crypto.OutgoingRoomKeyRequestManager
+import im.vector.matrix.android.internal.crypto.OutgoingGossipingRequestManager
 import im.vector.matrix.android.internal.crypto.actions.EnsureOlmSessionsForDevicesAction
 import im.vector.matrix.android.internal.crypto.actions.MessageEncrypter
 import im.vector.matrix.android.internal.crypto.algorithms.IMXDecrypting
@@ -46,7 +46,7 @@ import timber.log.Timber
 internal class MXMegolmDecryption(private val userId: String,
                                   private val olmDevice: MXOlmDevice,
                                   private val deviceListManager: DeviceListManager,
-                                  private val outgoingRoomKeyRequestManager: OutgoingRoomKeyRequestManager,
+                                  private val outgoingGossipingRequestManager: OutgoingGossipingRequestManager,
                                   private val messageEncrypter: MessageEncrypter,
                                   private val ensureOlmSessionsForDevicesAction: EnsureOlmSessionsForDevicesAction,
                                   private val cryptoStore: IMXCryptoStore,
@@ -144,25 +144,26 @@ internal class MXMegolmDecryption(private val userId: String,
      *
      * @param event the event
      */
-    private fun requestKeysForEvent(event: Event) {
-        val sender = event.senderId!!
-        val encryptedEventContent = event.content.toModel<EncryptedEventContent>()!!
+    override fun requestKeysForEvent(event: Event) {
+        val sender = event.senderId ?: return
+        val encryptedEventContent = event.content.toModel<EncryptedEventContent>()
+        val senderDevice = encryptedEventContent?.deviceId ?: return
 
-        val recipients = ArrayList<Map<String, String>>()
-
-        val selfMap = HashMap<String, String>()
-        // TODO Replace this hard coded keys (see OutgoingRoomKeyRequestManager)
-        selfMap["userId"] = userId
-        selfMap["deviceId"] = "*"
-        recipients.add(selfMap)
-
-        if (sender != userId) {
-            val senderMap = HashMap<String, String>()
-            senderMap["userId"] = sender
-            senderMap["deviceId"] = encryptedEventContent.deviceId!!
-            recipients.add(senderMap)
+        val recipients = if (event.senderId != userId) {
+            mapOf(
+                    userId to listOf("*")
+            )
+        } else {
+            // for the case where you share the key with a device that has a broken olm session
+            // The other user might Re-shares a megolm session key with devices if the key has already been
+            // sent to them.
+            mapOf(
+                    userId to listOf("*"),
+                    sender to listOf(senderDevice)
+            )
         }
 
+
         val requestBody = RoomKeyRequestBody(
                 roomId = event.roomId,
                 algorithm = encryptedEventContent.algorithm,
@@ -170,7 +171,7 @@ internal class MXMegolmDecryption(private val userId: String,
                 sessionId = encryptedEventContent.sessionId
         )
 
-        outgoingRoomKeyRequestManager.sendRoomKeyRequest(requestBody, recipients)
+        outgoingGossipingRequestManager.sendRoomKeyRequest(requestBody, recipients)
     }
 
     /**
@@ -271,7 +272,7 @@ internal class MXMegolmDecryption(private val userId: String,
                     senderKey = senderKey
             )
 
-            outgoingRoomKeyRequestManager.cancelRoomKeyRequest(content)
+            outgoingGossipingRequestManager.cancelRoomKeyRequest(content)
 
             onNewSession(senderKey, roomKeyContent.sessionId)
         }
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmDecryptionFactory.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmDecryptionFactory.kt
index 7cddd27779..e8044186d8 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmDecryptionFactory.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmDecryptionFactory.kt
@@ -18,7 +18,7 @@ package im.vector.matrix.android.internal.crypto.algorithms.megolm
 
 import im.vector.matrix.android.internal.crypto.DeviceListManager
 import im.vector.matrix.android.internal.crypto.MXOlmDevice
-import im.vector.matrix.android.internal.crypto.OutgoingRoomKeyRequestManager
+import im.vector.matrix.android.internal.crypto.OutgoingGossipingRequestManager
 import im.vector.matrix.android.internal.crypto.actions.EnsureOlmSessionsForDevicesAction
 import im.vector.matrix.android.internal.crypto.actions.MessageEncrypter
 import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore
@@ -32,7 +32,7 @@ internal class MXMegolmDecryptionFactory @Inject constructor(
         @UserId private val userId: String,
         private val olmDevice: MXOlmDevice,
         private val deviceListManager: DeviceListManager,
-        private val outgoingRoomKeyRequestManager: OutgoingRoomKeyRequestManager,
+        private val outgoingGossipingRequestManager: OutgoingGossipingRequestManager,
         private val messageEncrypter: MessageEncrypter,
         private val ensureOlmSessionsForDevicesAction: EnsureOlmSessionsForDevicesAction,
         private val cryptoStore: IMXCryptoStore,
@@ -46,7 +46,7 @@ internal class MXMegolmDecryptionFactory @Inject constructor(
                 userId,
                 olmDevice,
                 deviceListManager,
-                outgoingRoomKeyRequestManager,
+                outgoingGossipingRequestManager,
                 messageEncrypter,
                 ensureOlmSessionsForDevicesAction,
                 cryptoStore,
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/olm/MXOlmDecryption.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/olm/MXOlmDecryption.kt
index e0454aea0d..0a8ef3993b 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/olm/MXOlmDecryption.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/olm/MXOlmDecryption.kt
@@ -210,4 +210,8 @@ internal class MXOlmDecryption(
 
         return res["payload"]
     }
+
+    override fun requestKeysForEvent(event: Event) {
+        // nop
+    }
 }
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/crosssigning/DefaultCrossSigningService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/crosssigning/DefaultCrossSigningService.kt
index 6251f72418..661af7fb49 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/crosssigning/DefaultCrossSigningService.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/crosssigning/DefaultCrossSigningService.kt
@@ -25,7 +25,7 @@ import im.vector.matrix.android.api.util.Optional
 import im.vector.matrix.android.internal.crypto.DeviceListManager
 import im.vector.matrix.android.internal.crypto.MXOlmDevice
 import im.vector.matrix.android.internal.crypto.MyDeviceInfoHolder
-import im.vector.matrix.android.internal.crypto.OutgoingRoomKeyRequestManager
+import im.vector.matrix.android.internal.crypto.OutgoingGossipingRequestManager
 import im.vector.matrix.android.internal.crypto.model.CryptoCrossSigningKey
 import im.vector.matrix.android.internal.crypto.model.KeyUsage
 import im.vector.matrix.android.internal.crypto.model.rest.UploadSignatureQueryBuilder
@@ -43,6 +43,7 @@ import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
 import im.vector.matrix.android.internal.util.withoutPrefix
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.launch
+import kotlinx.coroutines.runBlocking
 import org.greenrobot.eventbus.EventBus
 import org.matrix.olm.OlmPkSigning
 import org.matrix.olm.OlmUtility
@@ -62,7 +63,7 @@ internal class DefaultCrossSigningService @Inject constructor(
         private val taskExecutor: TaskExecutor,
         private val coroutineDispatchers: MatrixCoroutineDispatchers,
         private val cryptoCoroutineScope: CoroutineScope,
-        private val outgoingRoomKeyRequestManager: OutgoingRoomKeyRequestManager,
+        private val outgoingGossipingRequestManager: OutgoingGossipingRequestManager,
         private val eventBus: EventBus) : CrossSigningService, DeviceListManager.UserDevicesUpdateListener {
 
     private var olmUtility: OlmUtility? = null
@@ -299,6 +300,58 @@ internal class DefaultCrossSigningService @Inject constructor(
         cryptoStore.clearOtherUserTrust()
     }
 
+    override fun onSecretSSKGossip(sskPrivateKey: String): Boolean {
+        Timber.i("## CrossSigning - onSecretSSKGossip")
+        return runBlocking(coroutineDispatchers.crypto) {
+            val mxCrossSigningInfo = getMyCrossSigningKeys() ?: return@runBlocking false
+
+            sskPrivateKey.fromBase64NoPadding()
+                    .let { privateKeySeed ->
+                        val pkSigning = OlmPkSigning()
+                        try {
+                            if (pkSigning.initWithSeed(privateKeySeed) == mxCrossSigningInfo.selfSigningKey()?.unpaddedBase64PublicKey) {
+                                selfSigningPkSigning?.releaseSigning()
+                                selfSigningPkSigning = pkSigning
+                                Timber.i("## CrossSigning - Loading SSK success")
+                                cryptoStore.storePrivateKeysInfo(null, null, sskPrivateKey)
+                                return@runBlocking true
+                            } else {
+                                pkSigning.releaseSigning()
+                            }
+                        } catch (failure: Throwable) {
+                            pkSigning.releaseSigning()
+                        }
+                    }
+            return@runBlocking false
+        }
+    }
+
+    override fun onSecretUSKGossip(uskPrivateKey: String): Boolean {
+        Timber.i("## CrossSigning - onSecretUSKGossip")
+        return runBlocking(coroutineDispatchers.crypto) {
+            val mxCrossSigningInfo = getMyCrossSigningKeys() ?: return@runBlocking false
+
+            uskPrivateKey.fromBase64NoPadding()
+                    .let { privateKeySeed ->
+                        val pkSigning = OlmPkSigning()
+                        try {
+                            if (pkSigning.initWithSeed(privateKeySeed) == mxCrossSigningInfo.selfSigningKey()?.unpaddedBase64PublicKey) {
+                                userPkSigning?.releaseSigning()
+                                userPkSigning = pkSigning
+                                Timber.i("## CrossSigning - Loading USK success")
+                                cryptoStore.storePrivateKeysInfo(null, uskPrivateKey, null)
+                                return@runBlocking true
+                            } else {
+                                pkSigning.releaseSigning()
+                            }
+                        } catch (failure: Throwable) {
+                            pkSigning.releaseSigning()
+                        }
+                    }
+            return@runBlocking false
+        }
+    }
+
     override fun checkTrustFromPrivateKeys(masterKeyPrivateKey: String?,
                                            uskKeyPrivateKey: String?,
                                            sskPrivateKey: String?
@@ -549,97 +602,103 @@ internal class DefaultCrossSigningService @Inject constructor(
     }
 
     override fun trustUser(otherUserId: String, callback: MatrixCallback<Unit>) {
-        Timber.d("## CrossSigning - Mark user $userId as trusted ")
-        // We should have this user keys
-        val otherMasterKeys = getUserCrossSigningKeys(otherUserId)?.masterKey()
-        if (otherMasterKeys == null) {
-            callback.onFailure(Throwable("## CrossSigning - Other master signing key is not known"))
-            return
-        }
-        val myKeys = getUserCrossSigningKeys(userId)
-        if (myKeys == null) {
-            callback.onFailure(Throwable("## CrossSigning - CrossSigning is not setup for this account"))
-            return
-        }
-        val userPubKey = myKeys.userKey()?.unpaddedBase64PublicKey
-        if (userPubKey == null || userPkSigning == null) {
-            callback.onFailure(Throwable("## CrossSigning - Cannot sign from this account, privateKeyUnknown $userPubKey"))
-            return
-        }
+        cryptoCoroutineScope.launch(coroutineDispatchers.crypto) {
+            Timber.d("## CrossSigning - Mark user $userId as trusted ")
+            // We should have this user keys
+            val otherMasterKeys = getUserCrossSigningKeys(otherUserId)?.masterKey()
+            if (otherMasterKeys == null) {
+                callback.onFailure(Throwable("## CrossSigning - Other master signing key is not known"))
+                return@launch
+            }
+            val myKeys = getUserCrossSigningKeys(userId)
+            if (myKeys == null) {
+                callback.onFailure(Throwable("## CrossSigning - CrossSigning is not setup for this account"))
+                return@launch
+            }
+            val userPubKey = myKeys.userKey()?.unpaddedBase64PublicKey
+            if (userPubKey == null || userPkSigning == null) {
+                callback.onFailure(Throwable("## CrossSigning - Cannot sign from this account, privateKeyUnknown $userPubKey"))
+                return@launch
+            }
 
-        // Sign the other MasterKey with our UserSigning key
-        val newSignature = JsonCanonicalizer.getCanonicalJson(Map::class.java,
-                otherMasterKeys.signalableJSONDictionary()).let { userPkSigning?.sign(it) }
+            // Sign the other MasterKey with our UserSigning key
+            val newSignature = JsonCanonicalizer.getCanonicalJson(Map::class.java,
+                    otherMasterKeys.signalableJSONDictionary()).let { userPkSigning?.sign(it) }
 
-        if (newSignature == null) {
-            // race??
-            callback.onFailure(Throwable("## CrossSigning - Failed to sign"))
-            return
+            if (newSignature == null) {
+                // race??
+                callback.onFailure(Throwable("## CrossSigning - Failed to sign"))
+                return@launch
+            }
+
+            cryptoStore.setUserKeysAsTrusted(otherUserId, true)
+            // TODO update local copy with new signature directly here? kind of local echo of trust?
+
+            Timber.d("## CrossSigning - Upload signature of $userId MSK signed by USK")
+            val uploadQuery = UploadSignatureQueryBuilder()
+                    .withSigningKeyInfo(otherMasterKeys.copyForSignature(userId, userPubKey, newSignature))
+                    .build()
+            uploadSignaturesTask.configureWith(UploadSignaturesTask.Params(uploadQuery)) {
+                this.executionThread = TaskThread.CRYPTO
+                this.callback = callback
+            }.executeBy(taskExecutor)
         }
-
-        cryptoStore.setUserKeysAsTrusted(otherUserId, true)
-        // TODO update local copy with new signature directly here? kind of local echo of trust?
-
-        Timber.d("## CrossSigning - Upload signature of $userId MSK signed by USK")
-        val uploadQuery = UploadSignatureQueryBuilder()
-                .withSigningKeyInfo(otherMasterKeys.copyForSignature(userId, userPubKey, newSignature))
-                .build()
-        uploadSignaturesTask.configureWith(UploadSignaturesTask.Params(uploadQuery)) {
-            this.executionThread = TaskThread.CRYPTO
-            this.callback = callback
-        }.executeBy(taskExecutor)
     }
 
     override fun markMyMasterKeyAsTrusted() {
-        cryptoStore.markMyMasterKeyAsLocallyTrusted(true)
-        checkSelfTrust()
+        cryptoCoroutineScope.launch(coroutineDispatchers.crypto) {
+            cryptoStore.markMyMasterKeyAsLocallyTrusted(true)
+            checkSelfTrust()
+        }
     }
 
     override fun trustDevice(deviceId: String, callback: MatrixCallback<Unit>) {
-        // This device should be yours
-        val device = cryptoStore.getUserDevice(userId, deviceId)
-        if (device == null) {
-            callback.onFailure(IllegalArgumentException("This device [$deviceId] is not known, or not yours"))
-            return
+        cryptoCoroutineScope.launch(coroutineDispatchers.crypto) {
+            // This device should be yours
+            val device = cryptoStore.getUserDevice(userId, deviceId)
+            if (device == null) {
+                callback.onFailure(IllegalArgumentException("This device [$deviceId] is not known, or not yours"))
+                return@launch
+            }
+
+            val myKeys = getUserCrossSigningKeys(userId)
+            if (myKeys == null) {
+                callback.onFailure(Throwable("CrossSigning is not setup for this account"))
+                return@launch
+            }
+
+            val ssPubKey = myKeys.selfSigningKey()?.unpaddedBase64PublicKey
+            if (ssPubKey == null || selfSigningPkSigning == null) {
+                callback.onFailure(Throwable("Cannot sign from this account, public and/or privateKey Unknown $ssPubKey"))
+                return@launch
+            }
+
+            // Sign with self signing
+            val newSignature = selfSigningPkSigning?.sign(device.canonicalSignable())
+
+            if (newSignature == null) {
+                // race??
+                callback.onFailure(Throwable("Failed to sign"))
+                return@launch
+            }
+            val toUpload = device.copy(
+                    signatures = mapOf(
+                            userId
+                                    to
+                                    mapOf(
+                                            "ed25519:$ssPubKey" to newSignature
+                                    )
+                    )
+            )
+
+            val uploadQuery = UploadSignatureQueryBuilder()
+                    .withDeviceInfo(toUpload)
+                    .build()
+            uploadSignaturesTask.configureWith(UploadSignaturesTask.Params(uploadQuery)) {
+                this.executionThread = TaskThread.CRYPTO
+                this.callback = callback
+            }.executeBy(taskExecutor)
         }
-
-        val myKeys = getUserCrossSigningKeys(userId)
-        if (myKeys == null) {
-            callback.onFailure(Throwable("CrossSigning is not setup for this account"))
-            return
-        }
-
-        val ssPubKey = myKeys.selfSigningKey()?.unpaddedBase64PublicKey
-        if (ssPubKey == null || selfSigningPkSigning == null) {
-            callback.onFailure(Throwable("Cannot sign from this account, public and/or privateKey Unknown $ssPubKey"))
-            return
-        }
-
-        // Sign with self signing
-        val newSignature = selfSigningPkSigning?.sign(device.canonicalSignable())
-
-        if (newSignature == null) {
-            // race??
-            callback.onFailure(Throwable("Failed to sign"))
-            return
-        }
-        val toUpload = device.copy(
-                signatures = mapOf(
-                        userId
-                                to
-                                mapOf(
-                                        "ed25519:$ssPubKey" to newSignature
-                                )
-                )
-        )
-
-        val uploadQuery = UploadSignatureQueryBuilder()
-                .withDeviceInfo(toUpload)
-                .build()
-        uploadSignaturesTask.configureWith(UploadSignaturesTask.Params(uploadQuery)) {
-            this.executionThread = TaskThread.CRYPTO
-            this.callback = callback
-        }.executeBy(taskExecutor)
     }
 
     override fun checkDeviceTrust(otherUserId: String, otherDeviceId: String, locallyTrusted: Boolean?): DeviceTrustResult {
@@ -738,7 +797,7 @@ internal class DefaultCrossSigningService @Inject constructor(
             // If it's me, recheck trust of all users and devices?
             val users = ArrayList<String>()
             if (otherUserId == userId && currentTrust != trusted) {
-                reRequestAllPendingRoomKeyRequest()
+//                reRequestAllPendingRoomKeyRequest()
                 cryptoStore.updateUsersTrust {
                     users.add(it)
                     checkUserTrust(it).isVerified()
@@ -755,16 +814,18 @@ internal class DefaultCrossSigningService @Inject constructor(
         }
     }
 
-    private fun reRequestAllPendingRoomKeyRequest() {
-        Timber.d("## CrossSigning - reRequest pending outgoing room key requests")
-        cryptoStore.getOutgoingRoomKeyRequests().forEach {
-            it.requestBody?.let { requestBody ->
-                if (cryptoStore.getInboundGroupSession(requestBody.sessionId ?: "", requestBody.senderKey ?: "") == null) {
-                    outgoingRoomKeyRequestManager.resendRoomKeyRequest(requestBody)
-                } else {
-                    outgoingRoomKeyRequestManager.cancelRoomKeyRequest(requestBody)
-                }
-            }
-        }
-    }
+//    private fun reRequestAllPendingRoomKeyRequest() {
+//        cryptoCoroutineScope.launch(coroutineDispatchers.crypto) {
+//            Timber.d("## CrossSigning - reRequest pending outgoing room key requests")
+//            cryptoStore.getOutgoingRoomKeyRequests().forEach {
+//                it.requestBody?.let { requestBody ->
+//                    if (cryptoStore.getInboundGroupSession(requestBody.sessionId ?: "", requestBody.senderKey ?: "") == null) {
+//                        outgoingRoomKeyRequestManager.resendRoomKeyRequest(requestBody)
+//                    } else {
+//                        outgoingRoomKeyRequestManager.cancelRoomKeyRequest(requestBody)
+//                    }
+//                }
+//            }
+//        }
+//    }
 }
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/CryptoDeviceInfo.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/CryptoDeviceInfo.kt
index e3e8f3de27..b124f7590e 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/CryptoDeviceInfo.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/CryptoDeviceInfo.kt
@@ -33,7 +33,7 @@ data class CryptoDeviceInfo(
 ) : CryptoInfo {
 
     val isVerified: Boolean
-        get() = trustLevel?.isVerified() ?: false
+        get() = trustLevel?.isVerified() == true
 
     val isUnknown: Boolean
         get() = trustLevel == null
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/GossipingToDeviceObject.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/GossipingToDeviceObject.kt
index def8c7f620..deaccdef16 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/GossipingToDeviceObject.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/GossipingToDeviceObject.kt
@@ -22,7 +22,7 @@ import com.squareup.moshi.JsonClass
  * Interface representing an room key action request
  * Note: this class cannot be abstract because of [org.matrix.androidsdk.core.JsonUtils.toRoomKeyShare]
  */
-internal interface GossipingToDeviceObject : SendToDeviceObject {
+interface GossipingToDeviceObject : SendToDeviceObject {
 
     val action: String?
 
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/RoomKeyRequestBody.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/RoomKeyRequestBody.kt
index 3eb6600e5e..5def4ae2a3 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/RoomKeyRequestBody.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/RoomKeyRequestBody.kt
@@ -18,6 +18,7 @@ package im.vector.matrix.android.internal.crypto.model.rest
 
 import com.squareup.moshi.Json
 import com.squareup.moshi.JsonClass
+import im.vector.matrix.android.internal.di.MoshiProvider
 
 /**
  * Class representing an room key request body content
@@ -35,4 +36,16 @@ data class RoomKeyRequestBody(
 
         @Json(name = "session_id")
         val sessionId: String? = null
-)
+) {
+    fun toJson(): String {
+        return MoshiProvider.providesMoshi().adapter(RoomKeyRequestBody::class.java).toJson(this)
+    }
+
+    companion object {
+        fun fromJson(json: String?): RoomKeyRequestBody? {
+            return json?.let { MoshiProvider.providesMoshi().adapter(RoomKeyRequestBody::class.java).fromJson(it) }
+        }
+    }
+}
+
+
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/RoomKeyShareRequest.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/RoomKeyShareRequest.kt
index e61743251c..c2fc6fe96b 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/RoomKeyShareRequest.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/RoomKeyShareRequest.kt
@@ -23,7 +23,7 @@ import com.squareup.moshi.JsonClass
  * Class representing a room key request content
  */
 @JsonClass(generateAdapter = true)
-internal data class RoomKeyShareRequest(
+data class RoomKeyShareRequest(
         @Json(name = "action")
         override val action: String? = GossipingToDeviceObject.ACTION_SHARE_REQUEST,
 
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/secrets/DefaultSharedSecretStorageService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/secrets/DefaultSharedSecretStorageService.kt
index 649f60887d..1bd55dd35d 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/secrets/DefaultSharedSecretStorageService.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/secrets/DefaultSharedSecretStorageService.kt
@@ -33,6 +33,7 @@ import im.vector.matrix.android.api.session.securestorage.SharedSecretStorageSer
 import im.vector.matrix.android.api.session.securestorage.SsssKeyCreationInfo
 import im.vector.matrix.android.api.session.securestorage.SsssKeySpec
 import im.vector.matrix.android.api.session.securestorage.SsssPassphrase
+import im.vector.matrix.android.internal.crypto.OutgoingGossipingRequestManager
 import im.vector.matrix.android.internal.crypto.SSSS_ALGORITHM_AES_HMAC_SHA2
 import im.vector.matrix.android.internal.crypto.SSSS_ALGORITHM_CURVE25519_AES_SHA2
 import im.vector.matrix.android.internal.crypto.crosssigning.fromBase64
@@ -41,6 +42,7 @@ import im.vector.matrix.android.internal.crypto.keysbackup.generatePrivateKeyWit
 import im.vector.matrix.android.internal.crypto.keysbackup.util.computeRecoveryKey
 import im.vector.matrix.android.internal.crypto.tools.HkdfSha256
 import im.vector.matrix.android.internal.crypto.tools.withOlmDecryption
+import im.vector.matrix.android.internal.di.UserId
 import im.vector.matrix.android.internal.extensions.foldToCallback
 import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
 import kotlinx.coroutines.CoroutineScope
@@ -55,7 +57,9 @@ import javax.inject.Inject
 import kotlin.experimental.and
 
 internal class DefaultSharedSecretStorageService @Inject constructor(
+        @UserId private val userId: String,
         private val accountDataService: AccountDataService,
+        private val outgoingGossipingRequestManager: OutgoingGossipingRequestManager,
         private val coroutineDispatchers: MatrixCoroutineDispatchers,
         private val cryptoCoroutineScope: CoroutineScope
 ) : SharedSecretStorageService {
@@ -429,4 +433,11 @@ internal class DefaultSharedSecretStorageService @Inject constructor(
 
         return IntegrityResult.Success(keyInfo.content.passphrase != null)
     }
+
+    override fun requestSecret(name: String, myOtherDeviceId: String) {
+        outgoingGossipingRequestManager.sendSecretShareRequest(
+                name,
+                mapOf(userId to listOf(myOtherDeviceId))
+        )
+    }
 }
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/IMXCryptoStore.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/IMXCryptoStore.kt
index b05b6cfa59..c5d09bc111 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/IMXCryptoStore.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/IMXCryptoStore.kt
@@ -19,13 +19,15 @@ package im.vector.matrix.android.internal.crypto.store
 
 import androidx.lifecycle.LiveData
 import im.vector.matrix.android.api.session.crypto.crosssigning.MXCrossSigningInfo
+import im.vector.matrix.android.api.session.events.model.Event
 import im.vector.matrix.android.api.util.Optional
-import im.vector.matrix.android.internal.crypto.IncomingShareRequestCommon
+import im.vector.matrix.android.internal.crypto.GossipingRequestState
 import im.vector.matrix.android.internal.crypto.IncomingRoomKeyRequest
-import im.vector.matrix.android.internal.crypto.IncomingSecretShareRequest
+import im.vector.matrix.android.internal.crypto.IncomingShareRequestCommon
 import im.vector.matrix.android.internal.crypto.NewSessionListener
+import im.vector.matrix.android.internal.crypto.OutgoingGossipingRequestState
 import im.vector.matrix.android.internal.crypto.OutgoingRoomKeyRequest
-import im.vector.matrix.android.internal.crypto.ShareRequestState
+import im.vector.matrix.android.internal.crypto.OutgoingSecretRequest
 import im.vector.matrix.android.internal.crypto.model.CryptoCrossSigningKey
 import im.vector.matrix.android.internal.crypto.model.CryptoDeviceInfo
 import im.vector.matrix.android.internal.crypto.model.OlmInboundGroupSessionWrapper
@@ -118,7 +120,10 @@ internal interface IMXCryptoStore {
      * @return the pending IncomingRoomKeyRequest requests
      */
     fun getPendingIncomingRoomKeyRequests(): List<IncomingRoomKeyRequest>
-    fun getPendingIncomingSecretShareRequests(): List<IncomingSecretShareRequest>
+
+    fun getPendingIncomingGossipingRequests(): List<IncomingShareRequestCommon>
+    fun storeIncomingGossipingRequest(request: IncomingShareRequestCommon, ageLocalTS: Long?)
+//    fun getPendingIncomingSecretShareRequests(): List<IncomingSecretShareRequest>
 
     /**
      * Indicate if the store contains data for the passed account.
@@ -190,8 +195,8 @@ internal interface IMXCryptoStore {
     fun storeUserDevices(userId: String, devices: Map<String, CryptoDeviceInfo>?)
 
     fun storeUserCrossSigningKeys(userId: String, masterKey: CryptoCrossSigningKey?,
-                                 selfSigningKey: CryptoCrossSigningKey?,
-                                 userSigningKey: CryptoCrossSigningKey?)
+                                  selfSigningKey: CryptoCrossSigningKey?,
+                                  userSigningKey: CryptoCrossSigningKey?)
 
     /**
      * Retrieve the known devices for a user.
@@ -209,6 +214,7 @@ internal interface IMXCryptoStore {
 
     // TODO temp
     fun getLiveDeviceList(): LiveData<List<CryptoDeviceInfo>>
+
     /**
      * Store the crypto algorithm for a room.
      *
@@ -350,45 +356,49 @@ internal interface IMXCryptoStore {
      * @param request the request
      * @return either the same instance as passed in, or the existing one.
      */
-    fun getOrAddOutgoingRoomKeyRequest(request: OutgoingRoomKeyRequest): OutgoingRoomKeyRequest?
+    fun getOrAddOutgoingRoomKeyRequest(requestBody: RoomKeyRequestBody, recipients: Map<String, List<String>>): OutgoingRoomKeyRequest?
 
+    fun getOrAddOutgoingSecretShareRequest(secretName: String, recipients: Map<String, List<String>>): OutgoingSecretRequest?
+
+    fun saveGossipingEvent(event: Event)
     /**
      * Look for room key requests by state.
      *
      * @param states the states
      * @return an OutgoingRoomKeyRequest or null
      */
-    fun getOutgoingRoomKeyRequestByState(states: Set<ShareRequestState>): OutgoingRoomKeyRequest?
+//    fun getOutgoingRoomKeyRequestByState(states: Set<ShareRequestState>): OutgoingRoomKeyRequest?
+//    fun getOutgoingSecretShareRequestByState(states: Set<ShareRequestState>): OutgoingSecretRequest?
 
     /**
      * Update an existing outgoing request.
      *
      * @param request the request
      */
-    fun updateOutgoingRoomKeyRequest(request: OutgoingRoomKeyRequest)
+//    fun updateOutgoingRoomKeyRequest(request: OutgoingRoomKeyRequest)
 
     /**
      * Delete an outgoing room key request.
      *
      * @param transactionId the transaction id.
      */
-    fun deleteOutgoingRoomKeyRequest(transactionId: String)
+//    fun deleteOutgoingRoomKeyRequest(transactionId: String)
 
     /**
      * Store an incomingRoomKeyRequest instance
      *
      * @param incomingRoomKeyRequest the incoming key request
      */
-    fun storeIncomingRoomKeyRequest(incomingRoomKeyRequest: IncomingRoomKeyRequest?)
+//    fun storeIncomingRoomKeyRequest(incomingRoomKeyRequest: IncomingRoomKeyRequest?)
 
     /**
      * Delete an incomingRoomKeyRequest instance
      *
      * @param incomingRoomKeyRequest the incoming key request
      */
-    fun deleteIncomingRoomKeyRequest(incomingRoomKeyRequest: IncomingShareRequestCommon)
+//    fun deleteIncomingRoomKeyRequest(incomingRoomKeyRequest: IncomingShareRequestCommon)
 
-    fun deleteIncomingSecretRequest(request: IncomingSecretShareRequest)
+    fun updateGossipingRequestState(request: IncomingShareRequestCommon, state: GossipingRequestState)
 
     /**
      * Search an IncomingRoomKeyRequest
@@ -400,6 +410,8 @@ internal interface IMXCryptoStore {
      */
     fun getIncomingRoomKeyRequest(userId: String, deviceId: String, requestId: String): IncomingRoomKeyRequest?
 
+    fun updateOutgoingGossipingRequestState(requestId: String, state: OutgoingGossipingRequestState)
+
     fun addNewSessionListener(listener: NewSessionListener)
 
     fun removeSessionListener(listener: NewSessionListener)
@@ -411,20 +423,21 @@ internal interface IMXCryptoStore {
     /**
      * Gets the current crosssigning info
      */
-    fun getMyCrossSigningInfo() : MXCrossSigningInfo?
+    fun getMyCrossSigningInfo(): MXCrossSigningInfo?
+
     fun setMyCrossSigningInfo(info: MXCrossSigningInfo?)
 
-    fun getCrossSigningInfo(userId: String) : MXCrossSigningInfo?
-    fun getLiveCrossSigningInfo(userId: String) : LiveData<Optional<MXCrossSigningInfo>>
+    fun getCrossSigningInfo(userId: String): MXCrossSigningInfo?
+    fun getLiveCrossSigningInfo(userId: String): LiveData<Optional<MXCrossSigningInfo>>
     fun setCrossSigningInfo(userId: String, info: MXCrossSigningInfo?)
 
     fun markMyMasterKeyAsLocallyTrusted(trusted: Boolean)
 
     fun storePrivateKeysInfo(msk: String?, usk: String?, ssk: String?)
-    fun getCrossSigningPrivateKeys() : PrivateKeysInfo?
+    fun getCrossSigningPrivateKeys(): PrivateKeysInfo?
 
     fun setUserKeysAsTrusted(userId: String, trusted: Boolean = true)
-    fun setDeviceTrust(userId: String, deviceId: String, crossSignedVerified: Boolean, locallyVerified : Boolean)
+    fun setDeviceTrust(userId: String, deviceId: String, crossSignedVerified: Boolean, locallyVerified: Boolean)
 
     fun clearOtherUserTrust()
 
@@ -432,5 +445,8 @@ internal interface IMXCryptoStore {
 
     // Dev tools
 
-    fun getOutgoingRoomKeyRequests() : List<OutgoingRoomKeyRequest>
+    fun getOutgoingRoomKeyRequests(): List<OutgoingRoomKeyRequest>
+    fun getOutgoingSecretRequest(secretName: String): OutgoingSecretRequest?
+    fun getIncomingRoomKeyRequests(): List<IncomingRoomKeyRequest>
+    fun getGossipingEventsTrail(): List<Event>
 }
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/Helper.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/Helper.kt
index 81988fe209..642c466e42 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/Helper.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/Helper.kt
@@ -59,7 +59,12 @@ fun <T : RealmObject> doRealmQueryAndCopyList(realmConfiguration: RealmConfigura
  */
 fun doRealmTransaction(realmConfiguration: RealmConfiguration, action: (Realm) -> Unit) {
     Realm.getInstance(realmConfiguration).use { realm ->
-        realm.executeTransaction { action.invoke(realm) }
+        realm.executeTransaction { action.invoke(it) }
+    }
+}
+fun doRealmTransactionAsync(realmConfiguration: RealmConfiguration, action: (Realm) -> Unit) {
+    Realm.getInstance(realmConfiguration).use { realm ->
+        realm.executeTransactionAsync { action.invoke(it) }
     }
 }
 
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/RealmCryptoStore.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/RealmCryptoStore.kt
index 1b4410d48c..83b8459d72 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/RealmCryptoStore.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/RealmCryptoStore.kt
@@ -21,14 +21,21 @@ import androidx.lifecycle.Transformations
 import com.zhuinden.monarchy.Monarchy
 import im.vector.matrix.android.api.auth.data.Credentials
 import im.vector.matrix.android.api.session.crypto.crosssigning.MXCrossSigningInfo
+import im.vector.matrix.android.api.session.events.model.Event
+import im.vector.matrix.android.api.session.events.model.LocalEcho
+import im.vector.matrix.android.api.session.room.send.SendState
 import im.vector.matrix.android.api.util.Optional
 import im.vector.matrix.android.api.util.toOptional
+import im.vector.matrix.android.internal.crypto.GossipRequestType
+import im.vector.matrix.android.internal.crypto.GossipingRequestState
 import im.vector.matrix.android.internal.crypto.IncomingRoomKeyRequest
 import im.vector.matrix.android.internal.crypto.IncomingSecretShareRequest
 import im.vector.matrix.android.internal.crypto.IncomingShareRequestCommon
 import im.vector.matrix.android.internal.crypto.NewSessionListener
+import im.vector.matrix.android.internal.crypto.OutgoingGossipingRequestState
 import im.vector.matrix.android.internal.crypto.OutgoingRoomKeyRequest
-import im.vector.matrix.android.internal.crypto.ShareRequestState
+import im.vector.matrix.android.internal.crypto.OutgoingSecretRequest
+import im.vector.matrix.android.internal.crypto.algorithms.olm.OlmDecryptionResult
 import im.vector.matrix.android.internal.crypto.crosssigning.DeviceTrustLevel
 import im.vector.matrix.android.internal.crypto.model.CryptoCrossSigningKey
 import im.vector.matrix.android.internal.crypto.model.CryptoDeviceInfo
@@ -46,18 +53,17 @@ import im.vector.matrix.android.internal.crypto.store.db.model.CryptoRoomEntity
 import im.vector.matrix.android.internal.crypto.store.db.model.CryptoRoomEntityFields
 import im.vector.matrix.android.internal.crypto.store.db.model.DeviceInfoEntity
 import im.vector.matrix.android.internal.crypto.store.db.model.DeviceInfoEntityFields
-import im.vector.matrix.android.internal.crypto.store.db.model.IncomingRoomKeyRequestEntity
-import im.vector.matrix.android.internal.crypto.store.db.model.IncomingRoomKeyRequestEntityFields
-import im.vector.matrix.android.internal.crypto.store.db.model.IncomingSecretRequestEntity
-import im.vector.matrix.android.internal.crypto.store.db.model.IncomingSecretRequestEntityFields
+import im.vector.matrix.android.internal.crypto.store.db.model.GossipingEventEntity
+import im.vector.matrix.android.internal.crypto.store.db.model.IncomingGossipingRequestEntity
+import im.vector.matrix.android.internal.crypto.store.db.model.IncomingGossipingRequestEntityFields
 import im.vector.matrix.android.internal.crypto.store.db.model.KeyInfoEntity
 import im.vector.matrix.android.internal.crypto.store.db.model.KeysBackupDataEntity
 import im.vector.matrix.android.internal.crypto.store.db.model.OlmInboundGroupSessionEntity
 import im.vector.matrix.android.internal.crypto.store.db.model.OlmInboundGroupSessionEntityFields
 import im.vector.matrix.android.internal.crypto.store.db.model.OlmSessionEntity
 import im.vector.matrix.android.internal.crypto.store.db.model.OlmSessionEntityFields
-import im.vector.matrix.android.internal.crypto.store.db.model.OutgoingRoomKeyRequestEntity
-import im.vector.matrix.android.internal.crypto.store.db.model.OutgoingRoomKeyRequestEntityFields
+import im.vector.matrix.android.internal.crypto.store.db.model.OutgoingGossipingRequestEntity
+import im.vector.matrix.android.internal.crypto.store.db.model.OutgoingGossipingRequestEntityFields
 import im.vector.matrix.android.internal.crypto.store.db.model.TrustLevelEntity
 import im.vector.matrix.android.internal.crypto.store.db.model.UserEntity
 import im.vector.matrix.android.internal.crypto.store.db.model.UserEntityFields
@@ -66,7 +72,9 @@ import im.vector.matrix.android.internal.crypto.store.db.query.delete
 import im.vector.matrix.android.internal.crypto.store.db.query.get
 import im.vector.matrix.android.internal.crypto.store.db.query.getById
 import im.vector.matrix.android.internal.crypto.store.db.query.getOrCreate
+import im.vector.matrix.android.internal.database.mapper.ContentMapper
 import im.vector.matrix.android.internal.di.CryptoDatabase
+import im.vector.matrix.android.internal.di.MoshiProvider
 import im.vector.matrix.android.internal.session.SessionScope
 import io.realm.Realm
 import io.realm.RealmConfiguration
@@ -801,152 +809,334 @@ internal class RealmCryptoStore @Inject constructor(
     }
 
     override fun getOutgoingRoomKeyRequest(requestBody: RoomKeyRequestBody): OutgoingRoomKeyRequest? {
-        return doRealmQueryAndCopy(realmConfiguration) {
-            it.where<OutgoingRoomKeyRequestEntity>()
-                    .equalTo(OutgoingRoomKeyRequestEntityFields.REQUEST_BODY_ALGORITHM, requestBody.algorithm)
-                    .equalTo(OutgoingRoomKeyRequestEntityFields.REQUEST_BODY_ROOM_ID, requestBody.roomId)
-                    .equalTo(OutgoingRoomKeyRequestEntityFields.REQUEST_BODY_SENDER_KEY, requestBody.senderKey)
-                    .equalTo(OutgoingRoomKeyRequestEntityFields.REQUEST_BODY_SESSION_ID, requestBody.sessionId)
-                    .findFirst()
+        return monarchy.fetchAllCopiedSync { realm ->
+            realm.where<OutgoingGossipingRequestEntity>()
+                    .equalTo(OutgoingGossipingRequestEntityFields.TYPE_STR, GossipRequestType.KEY.name)
+        }.mapNotNull {
+            it.toOutgoingGossipingRequest() as? OutgoingRoomKeyRequest
+        }.firstOrNull {
+            it.requestBody?.algorithm == requestBody.algorithm
+            it.requestBody?.roomId == requestBody.roomId
+            it.requestBody?.senderKey == requestBody.senderKey
+            it.requestBody?.sessionId == requestBody.sessionId
         }
-                ?.toOutgoingRoomKeyRequest()
     }
 
-    override fun getOrAddOutgoingRoomKeyRequest(request: OutgoingRoomKeyRequest): OutgoingRoomKeyRequest? {
-        if (request.requestBody == null) {
-            return null
-        }
+    override fun getOutgoingSecretRequest(secretName: String): OutgoingSecretRequest? {
+//        return monarchy.fetchAllCopiedSync { realm ->
+////            realm.where<OutgoingGossipingRequestEntity>()
+////                    .equalTo(OutgoingGossipingRequestEntityFields.TYPE_STR, GossipRequestType.SECRET.name)
+////                    .equalTo(GossipingEventEntityFields.SENDER, credentials.userId)
+////        }.mapNotNull {
+////            ContentMapper.map(it.content)?.toModel<OutgoingSecretRequest>()
+////        }.firstOrNull {
+////            it.secretName == secretName
+////        }
+        TODO("not implemented")
+    }
 
-        val existingOne = getOutgoingRoomKeyRequest(request.requestBody!!)
-
-        if (existingOne != null) {
-            return existingOne
+    override fun getIncomingRoomKeyRequests(): List<IncomingRoomKeyRequest> {
+        return monarchy.fetchAllCopiedSync { realm ->
+            realm.where<IncomingGossipingRequestEntity>()
+                    .equalTo(IncomingGossipingRequestEntityFields.TYPE_STR, GossipRequestType.KEY.name)
+        }.mapNotNull {
+            it.toIncomingGossipingRequest() as? IncomingRoomKeyRequest
         }
+    }
+
+    override fun getGossipingEventsTrail(): List<Event> {
+        return monarchy.fetchAllCopiedSync { realm ->
+            realm.where<GossipingEventEntity>()
+        }.map {
+            it.toModel()
+        }
+    }
+
+    override fun getOrAddOutgoingRoomKeyRequest(requestBody: RoomKeyRequestBody, recipients: Map<String, List<String>>): OutgoingRoomKeyRequest? {
+        // Insert the request and return the one passed in parameter
+        var request: OutgoingRoomKeyRequest? = null
+        doRealmTransaction(realmConfiguration) { realm ->
+
+            val existing = realm.where<OutgoingGossipingRequestEntity>()
+                    .equalTo(OutgoingGossipingRequestEntityFields.TYPE_STR, GossipRequestType.KEY.name)
+                    .findAll()
+                    .mapNotNull {
+                        it.toOutgoingGossipingRequest() as? OutgoingRoomKeyRequest
+                    }.firstOrNull {
+                        it.requestBody?.algorithm == requestBody.algorithm
+                                && it.requestBody?.sessionId == requestBody.sessionId
+                                && it.requestBody?.senderKey == requestBody.senderKey
+                                && it.requestBody?.roomId == requestBody.roomId
+                    }
+
+            if (existing == null) {
+                request = realm.createObject(OutgoingGossipingRequestEntity::class.java).apply {
+                    this.requestId = LocalEcho.createLocalEchoId()
+                    this.setRecipients(recipients)
+                    this.requestState = OutgoingGossipingRequestState.UNSENT
+                    this.type = GossipRequestType.KEY
+                    this.requestedInfoStr = requestBody.toJson()
+                }.toOutgoingGossipingRequest() as? OutgoingRoomKeyRequest
+            } else {
+                request = existing
+            }
+
+        }
+        return request
+    }
+
+    override fun getOrAddOutgoingSecretShareRequest(secretName: String, recipients: Map<String, List<String>>): OutgoingSecretRequest? {
+
+        var request: OutgoingSecretRequest? = null
 
         // Insert the request and return the one passed in parameter
-        doRealmTransaction(realmConfiguration) {
-            it.createObject(OutgoingRoomKeyRequestEntity::class.java, request.requestId).apply {
-                putRequestBody(request.requestBody)
-                putRecipients(request.recipients)
-                cancellationTxnId = request.cancellationTxnId
-                state = request.state.ordinal
+        doRealmTransaction(realmConfiguration) { realm ->
+            val existing = realm.where<OutgoingGossipingRequestEntity>()
+                    .equalTo(OutgoingGossipingRequestEntityFields.TYPE_STR, GossipRequestType.SECRET.name)
+                    .equalTo(OutgoingGossipingRequestEntityFields.REQUESTED_INFO_STR, secretName)
+                    .findAll()
+                    .mapNotNull {
+                        it.toOutgoingGossipingRequest() as? OutgoingSecretRequest
+                    }.firstOrNull()
+            if (existing == null) {
+                request = realm.createObject(OutgoingGossipingRequestEntity::class.java).apply {
+                    this.type = GossipRequestType.SECRET
+                    setRecipients(recipients)
+                    this.requestState = OutgoingGossipingRequestState.UNSENT
+                    this.requestId = LocalEcho.createLocalEchoId()
+                    this.requestedInfoStr = secretName
+                }.toOutgoingGossipingRequest() as? OutgoingSecretRequest
+            } else {
+                request = existing
             }
         }
 
         return request
     }
 
-    override fun getOutgoingRoomKeyRequestByState(states: Set<ShareRequestState>): OutgoingRoomKeyRequest? {
-        val statesIndex = states.map { it.ordinal }.toTypedArray()
-        return doRealmQueryAndCopy(realmConfiguration) {
-            it.where<OutgoingRoomKeyRequestEntity>()
-                    .`in`(OutgoingRoomKeyRequestEntityFields.STATE, statesIndex)
-                    .findFirst()
+    override fun saveGossipingEvent(event: Event) {
+        val now = System.currentTimeMillis()
+        val ageLocalTs = event.unsignedData?.age?.let { now - it } ?: now
+        val entity = GossipingEventEntity(
+                type = event.type,
+                sender = event.senderId,
+                ageLocalTs = ageLocalTs,
+                content = ContentMapper.map(event.content)
+        ).apply {
+            sendState = SendState.SYNCED
+            decryptionResultJson = MoshiProvider.providesMoshi().adapter<OlmDecryptionResult>(OlmDecryptionResult::class.java).toJson(event.mxDecryptionResult)
+            decryptionErrorCode = event.mCryptoError?.name
         }
-                ?.toOutgoingRoomKeyRequest()
-    }
-
-    override fun updateOutgoingRoomKeyRequest(request: OutgoingRoomKeyRequest) {
-        doRealmTransaction(realmConfiguration) {
-            val obj = OutgoingRoomKeyRequestEntity().apply {
-                requestId = request.requestId
-                cancellationTxnId = request.cancellationTxnId
-                state = request.state.ordinal
-                putRecipients(request.recipients)
-                putRequestBody(request.requestBody)
-            }
-
-            it.insertOrUpdate(obj)
+        doRealmTransaction(realmConfiguration) { realm ->
+            realm.insertOrUpdate(entity)
         }
     }
 
-    override fun deleteOutgoingRoomKeyRequest(transactionId: String) {
-        doRealmTransaction(realmConfiguration) {
-            it.where<OutgoingRoomKeyRequestEntity>()
-                    .equalTo(OutgoingRoomKeyRequestEntityFields.REQUEST_ID, transactionId)
-                    .findFirst()
-                    ?.deleteFromRealm()
+//    override fun getOutgoingRoomKeyRequestByState(states: Set<ShareRequestState>): OutgoingRoomKeyRequest? {
+//        val statesIndex = states.map { it.ordinal }.toTypedArray()
+//        return doRealmQueryAndCopy(realmConfiguration) { realm ->
+//            realm.where<GossipingEventEntity>()
+//                    .equalTo(GossipingEventEntityFields.SENDER, credentials.userId)
+//                    .findAll()
+//                    .filter {entity ->
+//                        states.any { it == entity.requestState}
+//                    }
+//        }.mapNotNull {
+//            ContentMapper.map(it.content)?.toModel<OutgoingSecretRequest>()
+//        }
+//                ?.toOutgoingRoomKeyRequest()
+//    }
+//
+//    override fun getOutgoingSecretShareRequestByState(states: Set<ShareRequestState>): OutgoingSecretRequest? {
+//        val statesIndex = states.map { it.ordinal }.toTypedArray()
+//        return doRealmQueryAndCopy(realmConfiguration) {
+//            it.where<OutgoingSecretRequestEntity>()
+//                    .`in`(OutgoingSecretRequestEntityFields.STATE, statesIndex)
+//                    .findFirst()
+//        }
+//                ?.toOutgoingSecretRequest()
+//    }
+
+//    override fun updateOutgoingRoomKeyRequest(request: OutgoingRoomKeyRequest) {
+//        doRealmTransaction(realmConfiguration) {
+//            val obj = OutgoingRoomKeyRequestEntity().apply {
+//                requestId = request.requestId
+//                cancellationTxnId = request.cancellationTxnId
+//                state = request.state.ordinal
+//                putRecipients(request.recipients)
+//                putRequestBody(request.requestBody)
+//            }
+//
+//            it.insertOrUpdate(obj)
+//        }
+//    }
+
+//    override fun deleteOutgoingRoomKeyRequest(transactionId: String) {
+//        doRealmTransaction(realmConfiguration) {
+//            it.where<OutgoingRoomKeyRequestEntity>()
+//                    .equalTo(OutgoingRoomKeyRequestEntityFields.REQUEST_ID, transactionId)
+//                    .findFirst()
+//                    ?.deleteFromRealm()
+//        }
+//    }
+
+//    override fun storeIncomingRoomKeyRequest(incomingRoomKeyRequest: IncomingRoomKeyRequest?) {
+//        if (incomingRoomKeyRequest == null) {
+//            return
+//        }
+//
+//        doRealmTransaction(realmConfiguration) {
+//            // Delete any previous store request with the same parameters
+//            it.where<IncomingRoomKeyRequestEntity>()
+//                    .equalTo(IncomingRoomKeyRequestEntityFields.USER_ID, incomingRoomKeyRequest.userId)
+//                    .equalTo(IncomingRoomKeyRequestEntityFields.DEVICE_ID, incomingRoomKeyRequest.deviceId)
+//                    .equalTo(IncomingRoomKeyRequestEntityFields.REQUEST_ID, incomingRoomKeyRequest.requestId)
+//                    .findAll()
+//                    .deleteAllFromRealm()
+//
+//            // Then store it
+//            it.createObject(IncomingRoomKeyRequestEntity::class.java).apply {
+//                userId = incomingRoomKeyRequest.userId
+//                deviceId = incomingRoomKeyRequest.deviceId
+//                requestId = incomingRoomKeyRequest.requestId
+//                putRequestBody(incomingRoomKeyRequest.requestBody)
+//            }
+//        }
+//    }
+
+//    override fun deleteIncomingRoomKeyRequest(incomingRoomKeyRequest: IncomingShareRequestCommon) {
+//        doRealmTransaction(realmConfiguration) {
+//            it.where<GossipingEventEntity>()
+//                    .equalTo(GossipingEventEntityFields.TYPE, EventType.ROOM_KEY_REQUEST)
+//                    .notEqualTo(GossipingEventEntityFields.SENDER, credentials.userId)
+//                    .findAll()
+//                    .filter {
+//                        ContentMapper.map(it.content).toModel<IncomingRoomKeyRequest>()?.let {
+//
+//                        }
+//                    }
+////                    .equalTo(IncomingRoomKeyRequestEntityFields.USER_ID, incomingRoomKeyRequest.userId)
+////                    .equalTo(IncomingRoomKeyRequestEntityFields.DEVICE_ID, incomingRoomKeyRequest.deviceId)
+////                    .equalTo(IncomingRoomKeyRequestEntityFields.REQUEST_ID, incomingRoomKeyRequest.requestId)
+////                    .findAll()
+////                    .deleteAllFromRealm()
+//        }
+//    }
+
+    override fun updateGossipingRequestState(request: IncomingShareRequestCommon, state: GossipingRequestState) {
+        doRealmTransaction(realmConfiguration) { realm ->
+            realm.where<IncomingGossipingRequestEntity>()
+                    .equalTo(IncomingGossipingRequestEntityFields.OTHER_USER_ID, request.userId)
+                    .equalTo(IncomingGossipingRequestEntityFields.OTHER_DEVICE_ID, request.deviceId)
+                    .equalTo(IncomingGossipingRequestEntityFields.REQUEST_ID, request.requestId)
+                    .findAll().forEach {
+                        it.requestState = state
+                    }
         }
     }
 
-    override fun storeIncomingRoomKeyRequest(incomingRoomKeyRequest: IncomingRoomKeyRequest?) {
-        if (incomingRoomKeyRequest == null) {
-            return
-        }
-
-        doRealmTransaction(realmConfiguration) {
-            // Delete any previous store request with the same parameters
-            it.where<IncomingRoomKeyRequestEntity>()
-                    .equalTo(IncomingRoomKeyRequestEntityFields.USER_ID, incomingRoomKeyRequest.userId)
-                    .equalTo(IncomingRoomKeyRequestEntityFields.DEVICE_ID, incomingRoomKeyRequest.deviceId)
-                    .equalTo(IncomingRoomKeyRequestEntityFields.REQUEST_ID, incomingRoomKeyRequest.requestId)
-                    .findAll()
-                    .deleteAllFromRealm()
-
-            // Then store it
-            it.createObject(IncomingRoomKeyRequestEntity::class.java).apply {
-                userId = incomingRoomKeyRequest.userId
-                deviceId = incomingRoomKeyRequest.deviceId
-                requestId = incomingRoomKeyRequest.requestId
-                putRequestBody(incomingRoomKeyRequest.requestBody)
-            }
-        }
-    }
-
-    override fun deleteIncomingRoomKeyRequest(incomingRoomKeyRequest: IncomingShareRequestCommon) {
-        doRealmTransaction(realmConfiguration) {
-            it.where<IncomingRoomKeyRequestEntity>()
-                    .equalTo(IncomingRoomKeyRequestEntityFields.USER_ID, incomingRoomKeyRequest.userId)
-                    .equalTo(IncomingRoomKeyRequestEntityFields.DEVICE_ID, incomingRoomKeyRequest.deviceId)
-                    .equalTo(IncomingRoomKeyRequestEntityFields.REQUEST_ID, incomingRoomKeyRequest.requestId)
-                    .findAll()
-                    .deleteAllFromRealm()
-        }
-    }
-
-    override fun deleteIncomingSecretRequest(request: IncomingSecretShareRequest) {
-        doRealmTransaction(realmConfiguration) {
-            it.where<IncomingSecretRequestEntity>()
-                    .equalTo(IncomingSecretRequestEntityFields.USER_ID, request.userId)
-                    .equalTo(IncomingSecretRequestEntityFields.DEVICE_ID, request.deviceId)
-                    .equalTo(IncomingSecretRequestEntityFields.REQUEST_ID, request.requestId)
-                    .equalTo(IncomingSecretRequestEntityFields.SECRET_NAME, request.secretName)
-                    .findAll()
-                    .deleteAllFromRealm()
+    override fun updateOutgoingGossipingRequestState(requestId: String, state: OutgoingGossipingRequestState) {
+        doRealmTransaction(realmConfiguration) { realm ->
+            realm.where<OutgoingGossipingRequestEntity>()
+                    .equalTo(OutgoingGossipingRequestEntityFields.REQUEST_ID, requestId)
+                    .findAll().forEach {
+                        it.requestState = state
+                    }
         }
     }
 
     override fun getIncomingRoomKeyRequest(userId: String, deviceId: String, requestId: String): IncomingRoomKeyRequest? {
-        return doRealmQueryAndCopy(realmConfiguration) {
-            it.where<IncomingRoomKeyRequestEntity>()
-                    .equalTo(IncomingRoomKeyRequestEntityFields.USER_ID, userId)
-                    .equalTo(IncomingRoomKeyRequestEntityFields.DEVICE_ID, deviceId)
-                    .equalTo(IncomingRoomKeyRequestEntityFields.REQUEST_ID, requestId)
-                    .findFirst()
-        }
-                ?.toIncomingRoomKeyRequest()
+        return doRealmQueryAndCopyList(realmConfiguration) { realm ->
+            realm.where<IncomingGossipingRequestEntity>()
+                    .equalTo(IncomingGossipingRequestEntityFields.TYPE_STR, GossipRequestType.KEY.name)
+                    .equalTo(IncomingGossipingRequestEntityFields.OTHER_DEVICE_ID, deviceId)
+                    .equalTo(IncomingGossipingRequestEntityFields.OTHER_USER_ID, userId)
+                    .findAll()
+        }.mapNotNull { entity ->
+            entity.toIncomingGossipingRequest() as? IncomingRoomKeyRequest
+        }.firstOrNull()
     }
 
-    override fun getPendingIncomingRoomKeyRequests(): MutableList<IncomingRoomKeyRequest> {
+    override fun getPendingIncomingRoomKeyRequests(): List<IncomingRoomKeyRequest> {
         return doRealmQueryAndCopyList(realmConfiguration) {
-            it.where<IncomingRoomKeyRequestEntity>()
+            it.where<IncomingGossipingRequestEntity>()
+                    .equalTo(IncomingGossipingRequestEntityFields.TYPE_STR, GossipRequestType.KEY.name)
+                    .equalTo(IncomingGossipingRequestEntityFields.REQUEST_STATE_STR, GossipingRequestState.PENDING.name)
                     .findAll()
         }
-                .map {
-                    it.toIncomingRoomKeyRequest()
+                .map { entity ->
+                    IncomingRoomKeyRequest(
+                            userId = entity.otherUserId,
+                            deviceId = entity.otherDeviceId,
+                            requestId = entity.requestId,
+                            requestBody = entity.getRequestedKeyInfo(),
+                            localCreationTimestamp = entity.localCreationTimestamp
+                    )
                 }
-                .toMutableList()
     }
 
-    override fun getPendingIncomingSecretShareRequests(): List<IncomingSecretShareRequest> {
+    override fun getPendingIncomingGossipingRequests(): List<IncomingShareRequestCommon> {
         return doRealmQueryAndCopyList(realmConfiguration) {
-            it.where<IncomingSecretRequestEntity>()
+            it.where<IncomingGossipingRequestEntity>()
+                    .equalTo(IncomingGossipingRequestEntityFields.REQUEST_STATE_STR, GossipingRequestState.PENDING.name)
                     .findAll()
-        }.map {
-            it.toIncomingSecretShareRequest()
+        }
+                .mapNotNull { entity ->
+                    when (entity.type) {
+                        GossipRequestType.KEY    -> {
+                            IncomingRoomKeyRequest(
+                                    userId = entity.otherUserId,
+                                    deviceId = entity.otherDeviceId,
+                                    requestId = entity.requestId,
+                                    requestBody = entity.getRequestedKeyInfo(),
+                                    localCreationTimestamp = entity.localCreationTimestamp
+                            )
+                        }
+                        GossipRequestType.SECRET -> {
+                            IncomingSecretShareRequest(
+                                    userId = entity.otherUserId,
+                                    deviceId = entity.otherDeviceId,
+                                    requestId = entity.requestId,
+                                    secretName = entity.getRequestedSecretName(),
+                                    localCreationTimestamp = entity.localCreationTimestamp
+                            )
+                        }
+                    }
+
+                }
+    }
+
+    override fun storeIncomingGossipingRequest(request: IncomingShareRequestCommon, ageLocalTS: Long?) {
+        doRealmTransactionAsync(realmConfiguration) { realm ->
+
+            // After a clear cache, we might have a
+
+            realm.createObject(IncomingGossipingRequestEntity::class.java).let {
+                it.otherDeviceId = request.deviceId
+                it.otherUserId = request.userId
+                it.requestId = request.requestId ?: ""
+                it.requestState = GossipingRequestState.PENDING
+                it.localCreationTimestamp = ageLocalTS ?: System.currentTimeMillis()
+                if (request is IncomingSecretShareRequest) {
+                    it.type = GossipRequestType.SECRET
+                    it.requestedInfoStr = request.secretName
+                } else if (request is IncomingRoomKeyRequest) {
+                    it.type = GossipRequestType.KEY
+                    it.requestedInfoStr = request.requestBody?.toJson()
+                }
+            }
         }
     }
 
+//    override fun getPendingIncomingSecretShareRequests(): List<IncomingSecretShareRequest> {
+//        return doRealmQueryAndCopyList(realmConfiguration) {
+//            it.where<GossipingEventEntity>()
+//                    .findAll()
+//        }.map {
+//            it.toIncomingSecretShareRequest()
+//        }
+//    }
+
     /* ==========================================================================================
      * Cross Signing
      * ========================================================================================== */
@@ -1051,10 +1241,12 @@ internal class RealmCryptoStore @Inject constructor(
 
     override fun getOutgoingRoomKeyRequests(): List<OutgoingRoomKeyRequest> {
         return monarchy.fetchAllMappedSync({ realm ->
-            realm.where(OutgoingRoomKeyRequestEntity::class.java)
-        }, {
-            it.toOutgoingRoomKeyRequest()
-        })
+            realm
+                    .where(OutgoingGossipingRequestEntity::class.java)
+                    .equalTo(OutgoingGossipingRequestEntityFields.TYPE_STR, GossipRequestType.KEY.name)
+        }, { entity ->
+            entity.toOutgoingGossipingRequest() as? OutgoingRoomKeyRequest
+        }).filterNotNull()
     }
 
     override fun getCrossSigningInfo(userId: String): MXCrossSigningInfo? {
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/RealmCryptoStoreMigration.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/RealmCryptoStoreMigration.kt
index 6d10e9d8aa..80173c050c 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/RealmCryptoStoreMigration.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/RealmCryptoStoreMigration.kt
@@ -23,9 +23,10 @@ import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo
 import im.vector.matrix.android.internal.crypto.store.db.model.CrossSigningInfoEntityFields
 import im.vector.matrix.android.internal.crypto.store.db.model.CryptoMetadataEntityFields
 import im.vector.matrix.android.internal.crypto.store.db.model.DeviceInfoEntityFields
-import im.vector.matrix.android.internal.crypto.store.db.model.IncomingSecretRequestEntityFields
+import im.vector.matrix.android.internal.crypto.store.db.model.GossipingEventEntityFields
+import im.vector.matrix.android.internal.crypto.store.db.model.IncomingGossipingRequestEntityFields
 import im.vector.matrix.android.internal.crypto.store.db.model.KeyInfoEntityFields
-import im.vector.matrix.android.internal.crypto.store.db.model.OutgoingSecretRequestEntityFields
+import im.vector.matrix.android.internal.crypto.store.db.model.OutgoingGossipingRequestEntityFields
 import im.vector.matrix.android.internal.crypto.store.db.model.TrustLevelEntityFields
 import im.vector.matrix.android.internal.crypto.store.db.model.UserEntityFields
 import im.vector.matrix.android.internal.di.SerializeNulls
@@ -140,21 +141,38 @@ internal object RealmCryptoStoreMigration : RealmMigration {
 
     private fun migrateTo2(realm: DynamicRealm) {
         Timber.d("Step 1 -> 2")
+        realm.schema.remove("OutgoingRoomKeyRequestEntity")
+        realm.schema.remove("IncomingRoomKeyRequestEntity")
 
-        realm.schema.create("IncomingSecretRequestEntity")
-                .addField(IncomingSecretRequestEntityFields.DEVICE_ID, String::class.java)
-                .addField(IncomingSecretRequestEntityFields.SECRET_NAME, String::class.java)
-                .addField(IncomingSecretRequestEntityFields.REQUEST_ID, String::class.java)
-                .addField(IncomingSecretRequestEntityFields.USER_ID, String::class.java)
+        //Not need to migrate existing request, just start fresh?
+
+        realm.schema.create("GossipingEventEntity")
+                .addField(GossipingEventEntityFields.TYPE, String::class.java)
+                .addIndex(GossipingEventEntityFields.TYPE)
+                .addField(GossipingEventEntityFields.CONTENT, String::class.java)
+                .addField(GossipingEventEntityFields.SENDER, String::class.java)
+                .addField(GossipingEventEntityFields.DECRYPTION_RESULT_JSON, String::class.java)
+                .addField(GossipingEventEntityFields.DECRYPTION_ERROR_CODE, String::class.java)
+                .addField(GossipingEventEntityFields.AGE_LOCAL_TS, Long::class.java)
+                .addField(GossipingEventEntityFields.SEND_STATE_STR, Long::class.java)
 
 
-        realm.schema.create("OutgoingSecretRequestEntity")
-                .addField(OutgoingSecretRequestEntityFields.REQUEST_ID, String::class.java)
-                .addPrimaryKey(OutgoingSecretRequestEntityFields.REQUEST_ID)
-                .addField(OutgoingSecretRequestEntityFields.SECRET_NAME, String::class.java)
-                .addField(OutgoingSecretRequestEntityFields.CANCELLATION_TXN_ID, String::class.java)
-                .addField(OutgoingSecretRequestEntityFields.RECIPIENTS_DATA, String::class.java)
-                .addField(OutgoingSecretRequestEntityFields.STATE, Int::class.java)
+        realm.schema.create("IncomingGossipingRequestEntity")
+                .addField(IncomingGossipingRequestEntityFields.REQUEST_ID, String::class.java)
+                .addField(IncomingGossipingRequestEntityFields.TYPE_STR, String::class.java)
+                .addField(IncomingGossipingRequestEntityFields.OTHER_USER_ID, String::class.java)
+                .addField(IncomingGossipingRequestEntityFields.REQUESTED_INFO_STR, String::class.java)
+                .addField(IncomingGossipingRequestEntityFields.OTHER_DEVICE_ID, String::class.java)
+                .addField(IncomingGossipingRequestEntityFields.REQUEST_STATE_STR, String::class.java)
+                .addField(IncomingGossipingRequestEntityFields.LOCAL_CREATION_TIMESTAMP, Long::class.java)
+                .setNullable(IncomingGossipingRequestEntityFields.LOCAL_CREATION_TIMESTAMP, true)
 
+
+        realm.schema.create("OutgoingGossipingRequestEntity")
+                .addField(OutgoingGossipingRequestEntityFields.REQUEST_ID, String::class.java)
+                .addField(OutgoingGossipingRequestEntityFields.TYPE_STR, String::class.java)
+                .addField(OutgoingGossipingRequestEntityFields.REQUESTED_INFO_STR, String::class.java)
+                .addField(OutgoingGossipingRequestEntityFields.REQUEST_STATE_STR, String::class.java)
+                .addField(OutgoingGossipingRequestEntityFields.RECIPIENTS_DATA, String::class.java)
     }
 }
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/RealmCryptoStoreModule.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/RealmCryptoStoreModule.kt
index d9d4496d4e..0d48f4671b 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/RealmCryptoStoreModule.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/RealmCryptoStoreModule.kt
@@ -20,14 +20,13 @@ import im.vector.matrix.android.internal.crypto.store.db.model.CrossSigningInfoE
 import im.vector.matrix.android.internal.crypto.store.db.model.CryptoMetadataEntity
 import im.vector.matrix.android.internal.crypto.store.db.model.CryptoRoomEntity
 import im.vector.matrix.android.internal.crypto.store.db.model.DeviceInfoEntity
-import im.vector.matrix.android.internal.crypto.store.db.model.IncomingRoomKeyRequestEntity
-import im.vector.matrix.android.internal.crypto.store.db.model.IncomingSecretRequestEntity
+import im.vector.matrix.android.internal.crypto.store.db.model.GossipingEventEntity
+import im.vector.matrix.android.internal.crypto.store.db.model.IncomingGossipingRequestEntity
 import im.vector.matrix.android.internal.crypto.store.db.model.KeyInfoEntity
 import im.vector.matrix.android.internal.crypto.store.db.model.KeysBackupDataEntity
 import im.vector.matrix.android.internal.crypto.store.db.model.OlmInboundGroupSessionEntity
 import im.vector.matrix.android.internal.crypto.store.db.model.OlmSessionEntity
-import im.vector.matrix.android.internal.crypto.store.db.model.OutgoingRoomKeyRequestEntity
-import im.vector.matrix.android.internal.crypto.store.db.model.OutgoingSecretRequestEntity
+import im.vector.matrix.android.internal.crypto.store.db.model.OutgoingGossipingRequestEntity
 import im.vector.matrix.android.internal.crypto.store.db.model.TrustLevelEntity
 import im.vector.matrix.android.internal.crypto.store.db.model.UserEntity
 import io.realm.annotations.RealmModule
@@ -40,16 +39,19 @@ import io.realm.annotations.RealmModule
             CryptoMetadataEntity::class,
             CryptoRoomEntity::class,
             DeviceInfoEntity::class,
-            IncomingRoomKeyRequestEntity::class,
+//            IncomingRoomKeyRequestEntity::class,
             KeysBackupDataEntity::class,
             OlmInboundGroupSessionEntity::class,
             OlmSessionEntity::class,
-            OutgoingRoomKeyRequestEntity::class,
+//            OutgoingRoomKeyRequestEntity::class,
             UserEntity::class,
             KeyInfoEntity::class,
             CrossSigningInfoEntity::class,
             TrustLevelEntity::class,
-            IncomingSecretRequestEntity::class,
-            OutgoingSecretRequestEntity::class
+//            IncomingSecretRequestEntity::class,
+//            OutgoingSecretRequestEntity::class,
+            GossipingEventEntity::class,
+            IncomingGossipingRequestEntity::class,
+            OutgoingGossipingRequestEntity::class
         ])
 internal class RealmCryptoStoreModule
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/GossipingEventEntity.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/GossipingEventEntity.kt
new file mode 100644
index 0000000000..1c5c4d267b
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/GossipingEventEntity.kt
@@ -0,0 +1,88 @@
+/*
+ * 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 im.vector.matrix.android.internal.crypto.store.db.model
+
+import com.squareup.moshi.JsonDataException
+import im.vector.matrix.android.api.session.crypto.MXCryptoError
+import im.vector.matrix.android.api.session.events.model.Event
+import im.vector.matrix.android.api.session.room.send.SendState
+import im.vector.matrix.android.internal.crypto.MXEventDecryptionResult
+import im.vector.matrix.android.internal.crypto.algorithms.olm.OlmDecryptionResult
+import im.vector.matrix.android.internal.database.mapper.ContentMapper
+import im.vector.matrix.android.internal.di.MoshiProvider
+import io.realm.RealmObject
+import io.realm.annotations.Index
+import timber.log.Timber
+
+/**
+ * Keep track of gossiping event received in toDevice messages
+ * (room key request, or sss secret sharing, as well as cancellations)
+ *
+ */
+internal open class GossipingEventEntity(@Index var type: String = "",
+                                         var content: String? = null,
+                                         @Index var sender: String? = null,
+                                         var decryptionResultJson: String? = null,
+                                         var decryptionErrorCode: String? = null,
+                                         var ageLocalTs: Long? = null) : RealmObject() {
+
+    private var sendStateStr: String = SendState.UNKNOWN.name
+
+    var sendState: SendState
+        get() {
+            return SendState.valueOf(sendStateStr)
+        }
+        set(value) {
+            sendStateStr = value.name
+        }
+
+    companion object
+
+    fun setDecryptionResult(result: MXEventDecryptionResult) {
+        val decryptionResult = OlmDecryptionResult(
+                payload = result.clearEvent,
+                senderKey = result.senderCurve25519Key,
+                keysClaimed = result.claimedEd25519Key?.let { mapOf("ed25519" to it) },
+                forwardingCurve25519KeyChain = result.forwardingCurve25519KeyChain
+        )
+        val adapter = MoshiProvider.providesMoshi().adapter<OlmDecryptionResult>(OlmDecryptionResult::class.java)
+        decryptionResultJson = adapter.toJson(decryptionResult)
+        decryptionErrorCode = null
+    }
+
+    fun toModel(): Event {
+        return Event(
+                type = this.type,
+                content = ContentMapper.map(this.content),
+                senderId = this.sender
+        ).also {
+            it.ageLocalTs = this.ageLocalTs
+            it.sendState = this.sendState
+            this.decryptionResultJson?.let { json ->
+                try {
+                    it.mxDecryptionResult = MoshiProvider.providesMoshi().adapter(OlmDecryptionResult::class.java).fromJson(json)
+                } catch (t: JsonDataException) {
+                    Timber.e(t, "Failed to parse decryption result")
+                }
+            }
+            // TODO get the full crypto error object
+            it.mCryptoError = this.decryptionErrorCode?.let { errorCode ->
+                MXCryptoError.ErrorType.valueOf(errorCode)
+            }
+        }
+    }
+}
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/IncomingGossipingRequestEntity.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/IncomingGossipingRequestEntity.kt
new file mode 100644
index 0000000000..1482a18d74
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/IncomingGossipingRequestEntity.kt
@@ -0,0 +1,90 @@
+/*
+ * 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 im.vector.matrix.android.internal.crypto.store.db.model
+
+import im.vector.matrix.android.api.extensions.tryThis
+import im.vector.matrix.android.internal.crypto.GossipRequestType
+import im.vector.matrix.android.internal.crypto.GossipingRequestState
+import im.vector.matrix.android.internal.crypto.IncomingRoomKeyRequest
+import im.vector.matrix.android.internal.crypto.IncomingSecretShareRequest
+import im.vector.matrix.android.internal.crypto.IncomingShareRequestCommon
+import im.vector.matrix.android.internal.crypto.model.rest.RoomKeyRequestBody
+import io.realm.RealmList
+import io.realm.RealmObject
+import io.realm.annotations.Index
+
+internal open class IncomingGossipingRequestEntity(@Index var requestId: String = "",
+                                                   @Index var typeStr: String? = null,
+                                                   var otherUserId: String? = null,
+                                                   var requestedInfoStr: String? = null,
+                                                   var otherDeviceId: String? = null,
+                                                   var localCreationTimestamp: Long? = null
+) : RealmObject() {
+
+    fun getRequestedSecretName(): String? = if (type == GossipRequestType.SECRET) {
+        requestedInfoStr
+    } else null
+
+    fun getRequestedKeyInfo(): RoomKeyRequestBody? = if (type == GossipRequestType.KEY) {
+        RoomKeyRequestBody.fromJson(requestedInfoStr)
+    } else null
+
+    var type: GossipRequestType
+        get() {
+            return tryThis { typeStr?.let { GossipRequestType.valueOf(it) } } ?: GossipRequestType.KEY
+        }
+        set(value) {
+            typeStr = value.name
+        }
+
+    private var requestStateStr: String = GossipingRequestState.NONE.name
+
+    var requestState: GossipingRequestState
+        get() {
+            return tryThis { GossipingRequestState.valueOf(requestStateStr) }
+                    ?: GossipingRequestState.NONE
+        }
+        set(value) {
+            requestStateStr = value.name
+        }
+
+    companion object
+
+    fun toIncomingGossipingRequest(): IncomingShareRequestCommon {
+        return when (type) {
+            GossipRequestType.KEY    -> {
+                IncomingRoomKeyRequest(
+                        requestBody = getRequestedKeyInfo(),
+                        deviceId = otherDeviceId,
+                        userId = otherUserId,
+                        requestId = requestId,
+                        state = requestState,
+                        localCreationTimestamp = localCreationTimestamp
+                )
+            }
+            GossipRequestType.SECRET -> {
+                IncomingSecretShareRequest(
+                        secretName = getRequestedSecretName(),
+                        deviceId = otherDeviceId,
+                        userId = otherUserId,
+                        requestId = requestId,
+                        localCreationTimestamp = localCreationTimestamp
+                )
+            }
+        }
+    }
+}
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/IncomingRoomKeyRequestEntity.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/IncomingRoomKeyRequestEntity.kt
index 38cece99ac..ef3ba75f23 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/IncomingRoomKeyRequestEntity.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/IncomingRoomKeyRequestEntity.kt
@@ -1,56 +1,56 @@
-/*
- * Copyright 2018 New Vector Ltd
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package im.vector.matrix.android.internal.crypto.store.db.model
-
-import im.vector.matrix.android.internal.crypto.IncomingRoomKeyRequest
-import im.vector.matrix.android.internal.crypto.model.rest.RoomKeyRequestBody
-import io.realm.RealmObject
-
-internal open class IncomingRoomKeyRequestEntity(
-        var requestId: String? = null,
-        var userId: String? = null,
-        var deviceId: String? = null,
-        // RoomKeyRequestBody fields
-        var requestBodyAlgorithm: String? = null,
-        var requestBodyRoomId: String? = null,
-        var requestBodySenderKey: String? = null,
-        var requestBodySessionId: String? = null
-) : RealmObject() {
-
-    fun toIncomingRoomKeyRequest(): IncomingRoomKeyRequest {
-        return IncomingRoomKeyRequest(
-                requestId = requestId,
-                userId = userId,
-                deviceId = deviceId,
-                requestBody = RoomKeyRequestBody(
-                        algorithm = requestBodyAlgorithm,
-                        roomId = requestBodyRoomId,
-                        senderKey = requestBodySenderKey,
-                        sessionId = requestBodySessionId
-                )
-        )
-    }
-
-    fun putRequestBody(requestBody: RoomKeyRequestBody?) {
-        requestBody?.let {
-            requestBodyAlgorithm = it.algorithm
-            requestBodyRoomId = it.roomId
-            requestBodySenderKey = it.senderKey
-            requestBodySessionId = it.sessionId
-        }
-    }
-}
+///*
+// * Copyright 2018 New Vector Ltd
+// *
+// * Licensed under the Apache License, Version 2.0 (the "License");
+// * you may not use this file except in compliance with the License.
+// * You may obtain a copy of the License at
+// *
+// *     http://www.apache.org/licenses/LICENSE-2.0
+// *
+// * Unless required by applicable law or agreed to in writing, software
+// * distributed under the License is distributed on an "AS IS" BASIS,
+// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// * See the License for the specific language governing permissions and
+// * limitations under the License.
+// */
+//
+//package im.vector.matrix.android.internal.crypto.store.db.model
+//
+//import im.vector.matrix.android.internal.crypto.IncomingRoomKeyRequest
+//import im.vector.matrix.android.internal.crypto.model.rest.RoomKeyRequestBody
+//import io.realm.RealmObject
+//
+//internal open class IncomingRoomKeyRequestEntity(
+//        var requestId: String? = null,
+//        var userId: String? = null,
+//        var deviceId: String? = null,
+//        // RoomKeyRequestBody fields
+//        var requestBodyAlgorithm: String? = null,
+//        var requestBodyRoomId: String? = null,
+//        var requestBodySenderKey: String? = null,
+//        var requestBodySessionId: String? = null
+//) : RealmObject() {
+//
+//    fun toIncomingRoomKeyRequest(): IncomingRoomKeyRequest {
+//        return IncomingRoomKeyRequest(
+//                requestId = requestId,
+//                userId = userId,
+//                deviceId = deviceId,
+//                requestBody = RoomKeyRequestBody(
+//                        algorithm = requestBodyAlgorithm,
+//                        roomId = requestBodyRoomId,
+//                        senderKey = requestBodySenderKey,
+//                        sessionId = requestBodySessionId
+//                )
+//        )
+//    }
+//
+//    fun putRequestBody(requestBody: RoomKeyRequestBody?) {
+//        requestBody?.let {
+//            requestBodyAlgorithm = it.algorithm
+//            requestBodyRoomId = it.roomId
+//            requestBodySenderKey = it.senderKey
+//            requestBodySessionId = it.sessionId
+//        }
+//    }
+//}
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/IncomingSecretRequestEntity.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/IncomingSecretRequestEntity.kt
index e1eb274e3d..81095dfb52 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/IncomingSecretRequestEntity.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/IncomingSecretRequestEntity.kt
@@ -1,37 +1,37 @@
-/*
- * 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 im.vector.matrix.android.internal.crypto.store.db.model
-
-import im.vector.matrix.android.internal.crypto.IncomingSecretShareRequest
-import io.realm.RealmObject
-
-internal open class IncomingSecretRequestEntity(
-        var requestId: String? = null,
-        var userId: String? = null,
-        var deviceId: String? = null,
-        var secretName: String? = null
-) : RealmObject() {
-
-    fun toIncomingSecretShareRequest(): IncomingSecretShareRequest {
-        return IncomingSecretShareRequest(
-                requestId = requestId,
-                userId = userId,
-                deviceId = deviceId,
-                secretName = secretName
-        )
-    }
-}
+///*
+// * 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 im.vector.matrix.android.internal.crypto.store.db.model
+//
+//import im.vector.matrix.android.internal.crypto.IncomingSecretShareRequest
+//import io.realm.RealmObject
+//
+//internal open class IncomingSecretRequestEntity(
+//        var requestId: String? = null,
+//        var userId: String? = null,
+//        var deviceId: String? = null,
+//        var secretName: String? = null
+//) : RealmObject() {
+//
+//    fun toIncomingSecretShareRequest(): IncomingSecretShareRequest {
+//        return IncomingSecretShareRequest(
+//                requestId = requestId,
+//                userId = userId,
+//                deviceId = deviceId,
+//                secretName = secretName
+//        )
+//    }
+//}
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/OutgoingGossipingRequestEntity.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/OutgoingGossipingRequestEntity.kt
new file mode 100644
index 0000000000..e6be88790d
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/OutgoingGossipingRequestEntity.kt
@@ -0,0 +1,107 @@
+/*
+ * 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 im.vector.matrix.android.internal.crypto.store.db.model
+
+import com.squareup.moshi.JsonAdapter
+import com.squareup.moshi.Types
+import im.vector.matrix.android.api.extensions.tryThis
+import im.vector.matrix.android.api.session.events.model.toContent
+import im.vector.matrix.android.api.session.events.model.toModel
+import im.vector.matrix.android.internal.crypto.GossipRequestType
+import im.vector.matrix.android.internal.crypto.GossipingRequestState
+import im.vector.matrix.android.internal.crypto.OutgoingGossipingRequest
+import im.vector.matrix.android.internal.crypto.OutgoingGossipingRequestState
+import im.vector.matrix.android.internal.crypto.OutgoingRoomKeyRequest
+import im.vector.matrix.android.internal.crypto.OutgoingSecretRequest
+import im.vector.matrix.android.internal.crypto.model.rest.RoomKeyRequestBody
+import im.vector.matrix.android.internal.di.MoshiProvider
+import io.realm.RealmList
+import io.realm.RealmObject
+import io.realm.annotations.Index
+
+internal open class OutgoingGossipingRequestEntity(
+        @Index var requestId: String? = null,
+//        var cancellationTxnId: String? = null,
+        // Serialized Json
+        var recipientsData: String? = null,
+        var requestedInfoStr: String? = null,
+        @Index var typeStr: String? = null,
+        var sourceEvents: RealmList<String> = RealmList()
+) : RealmObject() {
+
+    fun getRequestedSecretName(): String? = if (type == GossipRequestType.SECRET) {
+        requestedInfoStr
+    } else null
+
+    fun getRequestedKeyInfo(): RoomKeyRequestBody? = if (type == GossipRequestType.KEY) {
+        RoomKeyRequestBody.fromJson(requestedInfoStr)
+    } else null
+
+    var type: GossipRequestType
+        get() {
+            return tryThis { typeStr?.let { GossipRequestType.valueOf(it) } } ?: GossipRequestType.KEY
+        }
+        set(value) {
+            typeStr = value.name
+        }
+
+    private var requestStateStr: String = OutgoingGossipingRequestState.UNSENT.name
+
+    var requestState: OutgoingGossipingRequestState
+        get() {
+            return tryThis { OutgoingGossipingRequestState.valueOf(requestStateStr) }
+                    ?: OutgoingGossipingRequestState.UNSENT
+        }
+        set(value) {
+            requestStateStr = value.name
+        }
+
+    companion object {
+
+        private val recipientsDataMapper: JsonAdapter<Map<String, List<String>>> =
+                MoshiProvider.providesMoshi().adapter<Map<String, List<String>>>(Types.newParameterizedType(Map::class.java, String::class.java, List::class.java))
+    }
+
+    fun toOutgoingGossipingRequest(): OutgoingGossipingRequest {
+        return when (type) {
+            GossipRequestType.KEY    -> {
+                OutgoingRoomKeyRequest(
+                        requestBody = getRequestedKeyInfo(),
+                        recipients = getRecipients() ?: emptyMap(),
+                        requestId = requestId ?: "",
+                        state = requestState
+                )
+            }
+            GossipRequestType.SECRET -> {
+                OutgoingSecretRequest(
+                        secretName = getRequestedSecretName(),
+                        recipients = getRecipients() ?: emptyMap(),
+                        requestId = requestId ?: "",
+                        state = requestState
+                )
+            }
+        }
+    }
+
+    private fun getRecipients(): Map<String, List<String>>? {
+        return this.recipientsData?.let { recipientsDataMapper.fromJson(it) }
+    }
+
+    fun setRecipients(recipients: Map<String, List<String>>) {
+        this.recipientsData = recipientsDataMapper.toJson(recipients)
+    }
+}
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/OutgoingRoomKeyRequestEntity.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/OutgoingRoomKeyRequestEntity.kt
index 7b4b515c85..9e1ac89bd3 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/OutgoingRoomKeyRequestEntity.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/OutgoingRoomKeyRequestEntity.kt
@@ -1,77 +1,77 @@
-/*
- * Copyright 2018 New Vector Ltd
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package im.vector.matrix.android.internal.crypto.store.db.model
-
-import im.vector.matrix.android.internal.crypto.OutgoingRoomKeyRequest
-import im.vector.matrix.android.internal.crypto.ShareRequestState
-import im.vector.matrix.android.internal.crypto.model.rest.RoomKeyRequestBody
-import im.vector.matrix.android.internal.crypto.store.db.deserializeFromRealm
-import im.vector.matrix.android.internal.crypto.store.db.serializeForRealm
-import io.realm.RealmObject
-import io.realm.annotations.PrimaryKey
-
-internal open class OutgoingRoomKeyRequestEntity(
-        @PrimaryKey var requestId: String? = null,
-        var cancellationTxnId: String? = null,
-        // Serialized Json
-        var recipientsData: String? = null,
-        // RoomKeyRequestBody fields
-        var requestBodyAlgorithm: String? = null,
-        var requestBodyRoomId: String? = null,
-        var requestBodySenderKey: String? = null,
-        var requestBodySessionId: String? = null,
-        // State
-        var state: Int = 0
-) : RealmObject() {
-
-    /**
-     * Convert to OutgoingRoomKeyRequest
-     */
-    fun toOutgoingRoomKeyRequest(): OutgoingRoomKeyRequest {
-        val cancellationTxnId = this.cancellationTxnId
-        return OutgoingRoomKeyRequest(
-                RoomKeyRequestBody(
-                        algorithm = requestBodyAlgorithm,
-                        roomId = requestBodyRoomId,
-                        senderKey = requestBodySenderKey,
-                        sessionId = requestBodySessionId
-                ),
-                getRecipients()!!,
-                requestId!!,
-                ShareRequestState.from(state)
-        ).apply {
-            this.cancellationTxnId = cancellationTxnId
-        }
-    }
-
-    private fun getRecipients(): List<Map<String, String>>? {
-        return deserializeFromRealm(recipientsData)
-    }
-
-    fun putRecipients(recipients: List<Map<String, String>>?) {
-        recipientsData = serializeForRealm(recipients)
-    }
-
-    fun putRequestBody(requestBody: RoomKeyRequestBody?) {
-        requestBody?.let {
-            requestBodyAlgorithm = it.algorithm
-            requestBodyRoomId = it.roomId
-            requestBodySenderKey = it.senderKey
-            requestBodySessionId = it.sessionId
-        }
-    }
-}
+///*
+// * Copyright 2018 New Vector Ltd
+// *
+// * Licensed under the Apache License, Version 2.0 (the "License");
+// * you may not use this file except in compliance with the License.
+// * You may obtain a copy of the License at
+// *
+// *     http://www.apache.org/licenses/LICENSE-2.0
+// *
+// * Unless required by applicable law or agreed to in writing, software
+// * distributed under the License is distributed on an "AS IS" BASIS,
+// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// * See the License for the specific language governing permissions and
+// * limitations under the License.
+// */
+//
+//package im.vector.matrix.android.internal.crypto.store.db.model
+//
+//import im.vector.matrix.android.internal.crypto.OutgoingRoomKeyRequest
+//import im.vector.matrix.android.internal.crypto.ShareRequestState
+//import im.vector.matrix.android.internal.crypto.model.rest.RoomKeyRequestBody
+//import im.vector.matrix.android.internal.crypto.store.db.deserializeFromRealm
+//import im.vector.matrix.android.internal.crypto.store.db.serializeForRealm
+//import io.realm.RealmObject
+//import io.realm.annotations.PrimaryKey
+//
+//internal open class OutgoingRoomKeyRequestEntity(
+//        @PrimaryKey var requestId: String? = null,
+//        var cancellationTxnId: String? = null,
+//        // Serialized Json
+//        var recipientsData: String? = null,
+//        // RoomKeyRequestBody fields
+//        var requestBodyAlgorithm: String? = null,
+//        var requestBodyRoomId: String? = null,
+//        var requestBodySenderKey: String? = null,
+//        var requestBodySessionId: String? = null,
+//        // State
+//        var state: Int = 0
+//) : RealmObject() {
+//
+//    /**
+//     * Convert to OutgoingRoomKeyRequest
+//     */
+//    fun toOutgoingRoomKeyRequest(): OutgoingRoomKeyRequest {
+//        val cancellationTxnId = this.cancellationTxnId
+//        return OutgoingRoomKeyRequest(
+//                RoomKeyRequestBody(
+//                        algorithm = requestBodyAlgorithm,
+//                        roomId = requestBodyRoomId,
+//                        senderKey = requestBodySenderKey,
+//                        sessionId = requestBodySessionId
+//                ),
+//                getRecipients()!!,
+//                requestId!!,
+//                ShareRequestState.from(state)
+//        ).apply {
+//            this.cancellationTxnId = cancellationTxnId
+//        }
+//    }
+//
+//    private fun getRecipients(): List<Map<String, String>>? {
+//        return deserializeFromRealm(recipientsData)
+//    }
+//
+//    fun putRecipients(recipients: List<Map<String, String>>?) {
+//        recipientsData = serializeForRealm(recipients)
+//    }
+//
+//    fun putRequestBody(requestBody: RoomKeyRequestBody?) {
+//        requestBody?.let {
+//            requestBodyAlgorithm = it.algorithm
+//            requestBodyRoomId = it.roomId
+//            requestBodySenderKey = it.senderKey
+//            requestBodySessionId = it.sessionId
+//        }
+//    }
+//}
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/OutgoingSecretRequestEntity.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/OutgoingSecretRequestEntity.kt
index e6ce91fc10..4a4a2d8155 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/OutgoingSecretRequestEntity.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/OutgoingSecretRequestEntity.kt
@@ -1,63 +1,63 @@
-/*
- * 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 im.vector.matrix.android.internal.crypto.store.db.model
-
-import im.vector.matrix.android.internal.crypto.OutgoingSecretRequest
-import im.vector.matrix.android.internal.crypto.ShareRequestState
-import im.vector.matrix.android.internal.crypto.store.db.deserializeFromRealm
-import im.vector.matrix.android.internal.crypto.store.db.serializeForRealm
-import io.realm.RealmObject
-import io.realm.annotations.PrimaryKey
-
-internal open class OutgoingSecretRequestEntity(
-        @PrimaryKey var requestId: String? = null,
-        var cancellationTxnId: String? = null,
-        // Serialized Json
-        var recipientsData: String? = null,
-        // RoomKeyRequestBody fields
-        var secretName: String? = null,
-        // State
-        var state: Int = 0
-) : RealmObject() {
-
-    /**
-     * Convert to OutgoingRoomKeyRequest
-     */
-    fun toOutgoingSecretRequest(): OutgoingSecretRequest {
-        val cancellationTxnId = this.cancellationTxnId
-        return OutgoingSecretRequest(
-                secretName,
-                getRecipients() ?: emptyList(),
-                requestId!!,
-                ShareRequestState.from(state)
-        ).apply {
-            this.cancellationTxnId = cancellationTxnId
-        }
-    }
-
-    private fun getRecipients(): List<Map<String, String>>? {
-        return try {
-            deserializeFromRealm(recipientsData)
-        } catch (failure: Throwable) {
-            null
-        }
-    }
-
-    fun putRecipients(recipients: List<Map<String, String>>?) {
-        recipientsData = serializeForRealm(recipients)
-    }
-}
+///*
+// * 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 im.vector.matrix.android.internal.crypto.store.db.model
+//
+//import im.vector.matrix.android.internal.crypto.OutgoingSecretRequest
+//import im.vector.matrix.android.internal.crypto.ShareRequestState
+//import im.vector.matrix.android.internal.crypto.store.db.deserializeFromRealm
+//import im.vector.matrix.android.internal.crypto.store.db.serializeForRealm
+//import io.realm.RealmObject
+//import io.realm.annotations.PrimaryKey
+//
+//internal open class OutgoingSecretRequestEntity(
+//        @PrimaryKey var requestId: String? = null,
+//        var cancellationTxnId: String? = null,
+//        // Serialized Json
+//        var recipientsData: String? = null,
+//        // RoomKeyRequestBody fields
+//        var secretName: String? = null,
+//        // State
+//        var state: Int = 0
+//) : RealmObject() {
+//
+//    /**
+//     * Convert to OutgoingRoomKeyRequest
+//     */
+//    fun toOutgoingSecretRequest(): OutgoingSecretRequest {
+//        val cancellationTxnId = this.cancellationTxnId
+//        return OutgoingSecretRequest(
+//                secretName,
+//                getRecipients() ?: emptyList(),
+//                requestId!!,
+//                ShareRequestState.from(state)
+//        ).apply {
+//            this.cancellationTxnId = cancellationTxnId
+//        }
+//    }
+//
+//    private fun getRecipients(): List<Map<String, String>>? {
+//        return try {
+//            deserializeFromRealm(recipientsData)
+//        } catch (failure: Throwable) {
+//            null
+//        }
+//    }
+//
+//    fun putRecipients(recipients: List<Map<String, String>>?) {
+//        recipientsData = serializeForRealm(recipients)
+//    }
+//}
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/RoomVerificationUpdateTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/RoomVerificationUpdateTask.kt
index 3fb2573bed..53ccb8b6f0 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/RoomVerificationUpdateTask.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/RoomVerificationUpdateTask.kt
@@ -61,7 +61,6 @@ internal class DefaultRoomVerificationUpdateTask @Inject constructor(
 
         params.events.forEach { event ->
             Timber.d("## SAS Verification live observer: received msgId: ${event.eventId} msgtype: ${event.type} from ${event.senderId}")
-            Timber.v("## SAS Verification live observer: received msgId: $event")
 
             // If the request is in the future by more than 5 minutes or more than 10 minutes in the past,
             // the message should be ignored by the receiver.
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/qrcode/DefaultQrCodeVerificationTransaction.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/qrcode/DefaultQrCodeVerificationTransaction.kt
index f56e261416..f6c9b3d50b 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/qrcode/DefaultQrCodeVerificationTransaction.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/qrcode/DefaultQrCodeVerificationTransaction.kt
@@ -22,6 +22,7 @@ import im.vector.matrix.android.api.session.crypto.verification.QrCodeVerificati
 import im.vector.matrix.android.api.session.crypto.verification.VerificationTxState
 import im.vector.matrix.android.api.session.events.model.EventType
 import im.vector.matrix.android.internal.crypto.actions.SetDeviceVerificationAction
+import im.vector.matrix.android.internal.crypto.crosssigning.DeviceTrustLevel
 import im.vector.matrix.android.internal.crypto.crosssigning.fromBase64
 import im.vector.matrix.android.internal.crypto.crosssigning.fromBase64Safe
 import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/SessionComponent.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/SessionComponent.kt
index 1b07377fa1..2185d3b278 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/SessionComponent.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/SessionComponent.kt
@@ -20,7 +20,9 @@ import dagger.BindsInstance
 import dagger.Component
 import im.vector.matrix.android.api.auth.data.SessionParams
 import im.vector.matrix.android.api.session.Session
+import im.vector.matrix.android.internal.crypto.CancelGossipRequestWorker
 import im.vector.matrix.android.internal.crypto.CryptoModule
+import im.vector.matrix.android.internal.crypto.SendGossipRequestWorker
 import im.vector.matrix.android.internal.crypto.verification.SendVerificationMessageWorker
 import im.vector.matrix.android.internal.di.MatrixComponent
 import im.vector.matrix.android.internal.di.SessionAssistedInjectModule
@@ -106,6 +108,9 @@ internal interface SessionComponent {
 
     fun inject(worker: SendVerificationMessageWorker)
 
+    fun inject(worker: SendGossipRequestWorker)
+    fun inject(worker: CancelGossipRequestWorker)
+
     @Component.Factory
     interface Factory {
         fun create(
diff --git a/vector/build.gradle b/vector/build.gradle
index 2aae593271..f26ad40279 100644
--- a/vector/build.gradle
+++ b/vector/build.gradle
@@ -249,6 +249,7 @@ dependencies {
     def moshi_version = '1.8.0'
     def daggerVersion = '2.25.4'
     def autofill_version = "1.0.0"
+    def work_version = '2.3.2'
 
     implementation project(":matrix-sdk-android")
     implementation project(":matrix-sdk-android-rx")
@@ -296,7 +297,7 @@ dependencies {
     implementation 'com.airbnb.android:mvrx:1.3.0'
 
     // Work
-    implementation "androidx.work:work-runtime-ktx:2.3.3"
+    implementation "androidx.work:work-runtime-ktx:$work_version"
 
     // Paging
     implementation "androidx.paging:paging-runtime-ktx:2.1.1"
diff --git a/vector/src/main/java/im/vector/riotx/core/di/FragmentModule.kt b/vector/src/main/java/im/vector/riotx/core/di/FragmentModule.kt
index 1bccdb6c25..f112fae83c 100644
--- a/vector/src/main/java/im/vector/riotx/core/di/FragmentModule.kt
+++ b/vector/src/main/java/im/vector/riotx/core/di/FragmentModule.kt
@@ -74,7 +74,9 @@ import im.vector.riotx.features.settings.VectorSettingsSecurityPrivacyFragment
 import im.vector.riotx.features.settings.crosssigning.CrossSigningSettingsFragment
 import im.vector.riotx.features.settings.devices.VectorSettingsDevicesFragment
 import im.vector.riotx.features.settings.devtools.AccountDataFragment
-import im.vector.riotx.features.settings.devtools.KeyRequestListFragment
+import im.vector.riotx.features.settings.devtools.GossipingEventsPaperTrailFragment
+import im.vector.riotx.features.settings.devtools.IncomingKeyRequestListFragment
+import im.vector.riotx.features.settings.devtools.OutgoingKeyRequestListFragment
 import im.vector.riotx.features.settings.devtools.KeyRequestsFragment
 import im.vector.riotx.features.settings.ignored.VectorSettingsIgnoredUsersFragment
 import im.vector.riotx.features.settings.push.PushGatewaysFragment
@@ -371,11 +373,24 @@ interface FragmentModule {
 
     @Binds
     @IntoMap
-    @FragmentKey(KeyRequestListFragment::class)
-    fun bindKeyRequestListFragment(fragment: KeyRequestListFragment): Fragment
+    @FragmentKey(OutgoingKeyRequestListFragment::class)
+    fun bindOutgoingKeyRequestListFragment(fragment: OutgoingKeyRequestListFragment): Fragment
+
+    @Binds
+    @IntoMap
+    @FragmentKey(IncomingKeyRequestListFragment::class)
+    fun bindIncomingKeyRequestListFragment(fragment: IncomingKeyRequestListFragment): Fragment
 
     @Binds
     @IntoMap
     @FragmentKey(KeyRequestsFragment::class)
     fun bindKeyRequestsFragment(fragment: KeyRequestsFragment): Fragment
+
+
+    @Binds
+    @IntoMap
+    @FragmentKey(GossipingEventsPaperTrailFragment::class)
+    fun bindGossipingEventsPaperTrailFragment(fragment: GossipingEventsPaperTrailFragment): Fragment
+
+
 }
diff --git a/vector/src/main/java/im/vector/riotx/features/settings/devtools/GossipingEventsEpoxyController.kt b/vector/src/main/java/im/vector/riotx/features/settings/devtools/GossipingEventsEpoxyController.kt
new file mode 100644
index 0000000000..f9224b5a4b
--- /dev/null
+++ b/vector/src/main/java/im/vector/riotx/features/settings/devtools/GossipingEventsEpoxyController.kt
@@ -0,0 +1,203 @@
+/*
+ * 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 im.vector.riotx.features.settings.devtools
+
+import com.airbnb.epoxy.TypedEpoxyController
+import com.airbnb.mvrx.Fail
+import com.airbnb.mvrx.Loading
+import com.airbnb.mvrx.Success
+import com.airbnb.mvrx.Uninitialized
+import im.vector.matrix.android.api.session.events.model.Event
+import im.vector.matrix.android.api.session.events.model.EventType
+import im.vector.matrix.android.api.session.events.model.toModel
+import im.vector.matrix.android.internal.crypto.model.event.EncryptedEventContent
+import im.vector.matrix.android.internal.crypto.model.event.OlmEventContent
+import im.vector.matrix.android.internal.crypto.model.rest.ForwardedRoomKeyContent
+import im.vector.matrix.android.internal.crypto.model.rest.GossipingToDeviceObject
+import im.vector.matrix.android.internal.crypto.model.rest.RoomKeyShareRequest
+import im.vector.riotx.R
+import im.vector.riotx.core.date.VectorDateFormatter
+import im.vector.riotx.core.epoxy.loadingItem
+import im.vector.riotx.core.extensions.exhaustive
+import im.vector.riotx.core.resources.ColorProvider
+import im.vector.riotx.core.resources.DateProvider
+import im.vector.riotx.core.resources.StringProvider
+import im.vector.riotx.core.ui.list.genericFooterItem
+import im.vector.riotx.core.ui.list.genericItem
+import im.vector.riotx.core.ui.list.genericItemHeader
+import me.gujun.android.span.span
+import javax.inject.Inject
+
+class GossipingEventsEpoxyController @Inject constructor(
+        private val stringProvider: StringProvider,
+        private val vectorDateFormatter: VectorDateFormatter,
+        private val colorProvider: ColorProvider
+) : TypedEpoxyController<GossipingEventsPaperTrailState>() {
+
+    interface InteractionListener {
+        fun didTap(event: Event)
+    }
+
+    var interactionListener: InteractionListener? = null
+
+    override fun buildModels(data: GossipingEventsPaperTrailState?) {
+        when (val async = data?.events) {
+            is Uninitialized,
+            is Loading -> {
+                loadingItem {
+                    id("loadingOutgoing")
+                    loadingText(stringProvider.getString(R.string.loading))
+                }
+            }
+            is Fail    -> {
+                genericItem {
+                    id("failOutgoing")
+                    title(async.error.localizedMessage)
+                }
+            }
+            is Success -> {
+                val eventList = async.invoke()
+                if (eventList.isEmpty()) {
+                    genericFooterItem {
+                        id("empty")
+                        text(stringProvider.getString(R.string.no_result_placeholder))
+                    }
+                    return
+                }
+
+                eventList.forEachIndexed { _, event ->
+                    genericItem {
+                        id(event.hashCode())
+                        title(
+                                if (event.isEncrypted()) {
+                                    "${event.getClearType()} [encrypted]"
+                                } else {
+                                    event.type
+                                }
+                        )
+                        description(
+                                span {
+                                    +vectorDateFormatter.formatMessageDay(DateProvider.toLocalDateTime(event.ageLocalTs))
+                                    +" ${vectorDateFormatter.formatMessageHour(DateProvider.toLocalDateTime(event.ageLocalTs))}"
+                                    span("\nfrom: ") {
+                                        textStyle = "bold"
+                                    }
+                                    +"${event.senderId}"
+                                    apply {
+                                        if (event.getClearType() == EventType.ROOM_KEY_REQUEST) {
+                                            val content = event.getClearContent().toModel<RoomKeyShareRequest>()
+                                            span("\nreqId:") {
+                                                textStyle = "bold"
+                                            }
+                                            +" ${content?.requestId}"
+                                            span("\naction:") {
+                                                textStyle = "bold"
+                                            }
+                                            +" ${content?.action}"
+                                            if (content?.action == GossipingToDeviceObject.ACTION_SHARE_REQUEST) {
+                                                span("\nsessionId:") {
+                                                    textStyle = "bold"
+                                                }
+                                                +" ${content.body?.sessionId}"
+                                            }
+                                            span("\nrequestedBy: ") {
+                                                textStyle = "bold"
+                                            }
+                                            +"${content?.requestingDeviceId}"
+                                        } else if (event.getClearType() == EventType.FORWARDED_ROOM_KEY) {
+                                            val encryptedContent = event.content.toModel<OlmEventContent>()
+                                            val content = event.getClearContent().toModel<ForwardedRoomKeyContent>()
+                                            if (event.mxDecryptionResult == null) {
+                                                span("**Failed to Decrypt** ${event.mCryptoError}") {
+                                                    textColor = colorProvider.getColor(R.color.vector_error_color)
+                                                }
+                                            }
+                                            span("\nsessionId:") {
+                                                textStyle = "bold"
+                                            }
+                                            +" ${content?.sessionId}"
+                                            span("\nFrom Device (sender key):") {
+                                                textStyle = "bold"
+                                            }
+                                            +" ${encryptedContent?.senderKey}"
+                                        }
+                                    }
+                                }
+                        )
+                    }
+                }
+            }
+        }
+    }
+
+    private fun buildOutgoing(data: KeyRequestListViewState?) {
+        data?.outgoingRoomKeyRequest?.let { async ->
+            when (async) {
+                is Uninitialized,
+                is Loading -> {
+                    loadingItem {
+                        id("loadingOutgoing")
+                        loadingText(stringProvider.getString(R.string.loading))
+                    }
+                }
+                is Fail    -> {
+                    genericItem {
+                        id("failOutgoing")
+                        title(async.error.localizedMessage)
+                    }
+                }
+                is Success -> {
+
+                    if (async.invoke().isEmpty()) {
+                        genericFooterItem {
+                            id("empty")
+                            text(stringProvider.getString(R.string.no_result_placeholder))
+                        }
+                        return
+                    }
+
+                    val requestList = async.invoke().groupBy { it.roomId }
+
+                    requestList.forEach {
+                        genericItemHeader {
+                            id(it.key)
+                            text("roomId: ${it.key}")
+                        }
+                        it.value.forEach { roomKeyRequest ->
+                            genericItem {
+                                id(roomKeyRequest.requestId)
+                                title(roomKeyRequest.requestId)
+                                description(
+                                        span {
+                                            span("sessionId:\n") {
+                                                textStyle = "bold"
+                                            }
+                                            +"${roomKeyRequest.sessionId}"
+                                            span("\nstate:") {
+                                                textStyle = "bold"
+                                            }
+                                            +"\n${roomKeyRequest.state.name}"
+                                        }
+                                )
+                            }
+                        }
+                    }
+                }
+            }.exhaustive
+        }
+    }
+}
diff --git a/vector/src/main/java/im/vector/riotx/features/settings/devtools/GossipingEventsPaperTrailFragment.kt b/vector/src/main/java/im/vector/riotx/features/settings/devtools/GossipingEventsPaperTrailFragment.kt
new file mode 100644
index 0000000000..e845061a70
--- /dev/null
+++ b/vector/src/main/java/im/vector/riotx/features/settings/devtools/GossipingEventsPaperTrailFragment.kt
@@ -0,0 +1,68 @@
+/*
+ * 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 im.vector.riotx.features.settings.devtools
+
+import android.os.Bundle
+import android.view.MenuItem
+import android.view.View
+import com.airbnb.mvrx.fragmentViewModel
+import com.airbnb.mvrx.withState
+import im.vector.riotx.R
+import im.vector.riotx.core.extensions.cleanup
+import im.vector.riotx.core.extensions.configureWith
+import im.vector.riotx.core.platform.VectorBaseFragment
+import im.vector.riotx.core.resources.ColorProvider
+import kotlinx.android.synthetic.main.fragment_generic_recycler.*
+import javax.inject.Inject
+
+class GossipingEventsPaperTrailFragment @Inject constructor(
+        val viewModelFactory: GossipingEventsPaperTrailViewModel.Factory,
+        private val epoxyController: GossipingEventsEpoxyController
+) : VectorBaseFragment() {
+
+    override fun getLayoutResId() = R.layout.fragment_generic_recycler
+
+    override fun getMenuRes(): Int = R.menu.menu_common_gossiping
+
+    private val viewModel: GossipingEventsPaperTrailViewModel by fragmentViewModel(GossipingEventsPaperTrailViewModel::class)
+
+    override fun invalidate() = withState(viewModel) { state ->
+        epoxyController.setData(state)
+    }
+
+    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+        super.onViewCreated(view, savedInstanceState)
+        recyclerView.configureWith(epoxyController, showDivider = true)
+//        epoxyController.interactionListener = this
+    }
+
+    override fun onDestroyView() {
+        super.onDestroyView()
+        recyclerView.cleanup()
+//        epoxyController.interactionListener = null
+    }
+
+    override fun onOptionsItemSelected(item: MenuItem): Boolean {
+        if (item.itemId == R.id.refresh) {
+            viewModel.refresh()
+            return true
+        } else {
+            return super.onOptionsItemSelected(item)
+        }
+    }
+
+}
diff --git a/vector/src/main/java/im/vector/riotx/features/settings/devtools/GossipingEventsPaperTrailViewModel.kt b/vector/src/main/java/im/vector/riotx/features/settings/devtools/GossipingEventsPaperTrailViewModel.kt
new file mode 100644
index 0000000000..f248ab1482
--- /dev/null
+++ b/vector/src/main/java/im/vector/riotx/features/settings/devtools/GossipingEventsPaperTrailViewModel.kt
@@ -0,0 +1,79 @@
+/*
+ * 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 im.vector.riotx.features.settings.devtools
+
+import com.airbnb.mvrx.Async
+import com.airbnb.mvrx.FragmentViewModelContext
+import com.airbnb.mvrx.Loading
+import com.airbnb.mvrx.MvRxState
+import com.airbnb.mvrx.MvRxViewModelFactory
+import com.airbnb.mvrx.Success
+import com.airbnb.mvrx.Uninitialized
+import com.airbnb.mvrx.ViewModelContext
+import com.squareup.inject.assisted.Assisted
+import com.squareup.inject.assisted.AssistedInject
+import im.vector.matrix.android.api.session.Session
+import im.vector.matrix.android.api.session.events.model.Event
+import im.vector.riotx.core.platform.EmptyAction
+import im.vector.riotx.core.platform.EmptyViewEvents
+import im.vector.riotx.core.platform.VectorViewModel
+import kotlinx.coroutines.GlobalScope
+import kotlinx.coroutines.launch
+
+data class GossipingEventsPaperTrailState(
+        val events: Async<List<Event>> = Uninitialized
+) : MvRxState
+
+class GossipingEventsPaperTrailViewModel @AssistedInject constructor(@Assisted initialState: GossipingEventsPaperTrailState,
+                                                                     private val session: Session)
+    : VectorViewModel<GossipingEventsPaperTrailState, EmptyAction, EmptyViewEvents>(initialState) {
+
+    init {
+        refresh()
+    }
+
+    fun refresh() {
+        setState {
+            copy(events = Loading())
+        }
+        GlobalScope.launch {
+            session.cryptoService().getGossipingEventsTrail().let {
+                val sorted = it.sortedByDescending { it.ageLocalTs }
+                setState {
+                    copy(events = Success(sorted))
+                }
+            }
+        }
+    }
+
+    override fun handle(action: EmptyAction) {}
+
+    @AssistedInject.Factory
+    interface Factory {
+        fun create(initialState: GossipingEventsPaperTrailState): GossipingEventsPaperTrailViewModel
+    }
+
+    companion object : MvRxViewModelFactory<GossipingEventsPaperTrailViewModel, GossipingEventsPaperTrailState> {
+
+        @JvmStatic
+        override fun create(viewModelContext: ViewModelContext, state: GossipingEventsPaperTrailState): GossipingEventsPaperTrailViewModel? {
+            val fragment: GossipingEventsPaperTrailFragment = (viewModelContext as FragmentViewModelContext).fragment()
+
+            return fragment.viewModelFactory.create(state)
+        }
+    }
+}
diff --git a/vector/src/main/java/im/vector/riotx/features/settings/devtools/IncomingKeyRequestListFragment.kt b/vector/src/main/java/im/vector/riotx/features/settings/devtools/IncomingKeyRequestListFragment.kt
new file mode 100644
index 0000000000..653fbe36af
--- /dev/null
+++ b/vector/src/main/java/im/vector/riotx/features/settings/devtools/IncomingKeyRequestListFragment.kt
@@ -0,0 +1,57 @@
+/*
+ * 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 im.vector.riotx.features.settings.devtools
+
+import android.os.Bundle
+import android.view.View
+import com.airbnb.mvrx.fragmentViewModel
+import com.airbnb.mvrx.withState
+import im.vector.riotx.R
+import im.vector.riotx.core.extensions.cleanup
+import im.vector.riotx.core.extensions.configureWith
+import im.vector.riotx.core.platform.VectorBaseFragment
+import im.vector.riotx.core.resources.ColorProvider
+import kotlinx.android.synthetic.main.fragment_generic_recycler.*
+import javax.inject.Inject
+
+class IncomingKeyRequestListFragment @Inject constructor(
+        val viewModelFactory: KeyRequestListViewModel.Factory,
+        private val epoxyController: KeyRequestEpoxyController,
+        private val colorProvider: ColorProvider
+) : VectorBaseFragment() {
+
+    override fun getLayoutResId() = R.layout.fragment_generic_recycler
+
+    private val viewModel: KeyRequestListViewModel by fragmentViewModel(KeyRequestListViewModel::class)
+
+    override fun invalidate() = withState(viewModel) { state ->
+        epoxyController.outgoing = false
+        epoxyController.setData(state)
+    }
+
+    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+        super.onViewCreated(view, savedInstanceState)
+        recyclerView.configureWith(epoxyController, showDivider = true)
+//        epoxyController.interactionListener = this
+    }
+
+    override fun onDestroyView() {
+        super.onDestroyView()
+        recyclerView.cleanup()
+//        epoxyController.interactionListener = null
+    }
+}
diff --git a/vector/src/main/java/im/vector/riotx/features/settings/devtools/KeyRequestEpoxyController.kt b/vector/src/main/java/im/vector/riotx/features/settings/devtools/KeyRequestEpoxyController.kt
index 4169ee71f4..f1c18806d9 100644
--- a/vector/src/main/java/im/vector/riotx/features/settings/devtools/KeyRequestEpoxyController.kt
+++ b/vector/src/main/java/im/vector/riotx/features/settings/devtools/KeyRequestEpoxyController.kt
@@ -25,6 +25,7 @@ import im.vector.riotx.R
 import im.vector.riotx.core.epoxy.loadingItem
 import im.vector.riotx.core.extensions.exhaustive
 import im.vector.riotx.core.resources.StringProvider
+import im.vector.riotx.core.ui.list.genericFooterItem
 import im.vector.riotx.core.ui.list.genericItem
 import im.vector.riotx.core.ui.list.genericItemHeader
 import me.gujun.android.span.span
@@ -38,9 +39,75 @@ class KeyRequestEpoxyController @Inject constructor(
         // fun didTap(data: UserAccountData)
     }
 
+    var outgoing = true
+
     var interactionListener: InteractionListener? = null
 
     override fun buildModels(data: KeyRequestListViewState?) {
+        if (outgoing) {
+            buildOutgoing(data)
+        } else {
+            buildIncoming(data)
+        }
+    }
+
+    private fun buildIncoming(data: KeyRequestListViewState?) {
+        data?.incomingRequests?.let { async ->
+            when (async) {
+                is Uninitialized,
+                is Loading -> {
+                    loadingItem {
+                        id("loadingOutgoing")
+                        loadingText(stringProvider.getString(R.string.loading))
+                    }
+                }
+                is Fail    -> {
+                    genericItem {
+                        id("failOutgoing")
+                        title(async.error.localizedMessage)
+                    }
+                }
+                is Success -> {
+
+                    if (async.invoke().isEmpty()) {
+                        genericFooterItem {
+                            id("empty")
+                            text(stringProvider.getString(R.string.no_result_placeholder))
+                        }
+                        return
+                    }
+                    val requestList = async.invoke().groupBy { it.userId }
+
+                    requestList.forEach {
+                        genericItemHeader {
+                            id(it.key)
+                            text("From user: ${it.key}")
+                        }
+                        it.value.forEach { roomKeyRequest ->
+                            genericItem {
+                                id(roomKeyRequest.requestId)
+                                title(roomKeyRequest.requestId)
+                                description(
+                                        span {
+                                            span("sessionId:") {
+                                                textStyle = "bold"
+                                            }
+                                            span("\nFrom device:") {
+                                                textStyle = "bold"
+                                            }
+                                            +"${roomKeyRequest.deviceId}"
+                                            +"\n${roomKeyRequest.state.name}"
+                                        }
+                                )
+                            }
+                        }
+                    }
+                }
+            }.exhaustive
+        }
+    }
+
+    private fun buildOutgoing(data: KeyRequestListViewState?) {
         data?.outgoingRoomKeyRequest?.let { async ->
             when (async) {
                 is Uninitialized,
@@ -57,6 +124,15 @@ class KeyRequestEpoxyController @Inject constructor(
                     }
                 }
                 is Success -> {
+
+                    if (async.invoke().isEmpty()) {
+                        genericFooterItem {
+                            id("empty")
+                            text(stringProvider.getString(R.string.no_result_placeholder))
+                        }
+                        return
+                    }
+
                     val requestList = async.invoke().groupBy { it.roomId }
 
                     requestList.forEach {
@@ -70,7 +146,7 @@ class KeyRequestEpoxyController @Inject constructor(
                                 title(roomKeyRequest.requestId)
                                 description(
                                         span {
-                                            span("sessionId:") {
+                                            span("sessionId:\n") {
                                                 textStyle = "bold"
                                             }
                                             +"${roomKeyRequest.sessionId}"
diff --git a/vector/src/main/java/im/vector/riotx/features/settings/devtools/KeyRequestListViewModel.kt b/vector/src/main/java/im/vector/riotx/features/settings/devtools/KeyRequestListViewModel.kt
index 6b09273f93..6812467b96 100644
--- a/vector/src/main/java/im/vector/riotx/features/settings/devtools/KeyRequestListViewModel.kt
+++ b/vector/src/main/java/im/vector/riotx/features/settings/devtools/KeyRequestListViewModel.kt
@@ -31,6 +31,8 @@ import im.vector.matrix.android.internal.crypto.OutgoingRoomKeyRequest
 import im.vector.riotx.core.platform.EmptyAction
 import im.vector.riotx.core.platform.EmptyViewEvents
 import im.vector.riotx.core.platform.VectorViewModel
+import kotlinx.coroutines.GlobalScope
+import kotlinx.coroutines.launch
 
 data class KeyRequestListViewState(
         val incomingRequests: Async<List<IncomingRoomKeyRequest>> = Uninitialized,
@@ -42,11 +44,24 @@ class KeyRequestListViewModel @AssistedInject constructor(@Assisted initialState
     : VectorViewModel<KeyRequestListViewState, EmptyAction, EmptyViewEvents>(initialState) {
 
     init {
-        session.cryptoService().getOutgoingRoomKeyRequest().let {
-            setState {
-                copy(
-                        outgoingRoomKeyRequest = Success(it)
-                )
+        refresh()
+    }
+
+    fun refresh() {
+        GlobalScope.launch {
+            session.cryptoService().getOutgoingRoomKeyRequest().let {
+                setState {
+                    copy(
+                            outgoingRoomKeyRequest = Success(it)
+                    )
+                }
+            }
+            session.cryptoService().getIncomingRoomKeyRequest().let {
+                setState {
+                    copy(
+                            incomingRequests = Success(it)
+                    )
+                }
             }
         }
     }
@@ -58,12 +73,16 @@ class KeyRequestListViewModel @AssistedInject constructor(@Assisted initialState
         fun create(initialState: KeyRequestListViewState): KeyRequestListViewModel
     }
 
+
     companion object : MvRxViewModelFactory<KeyRequestListViewModel, KeyRequestListViewState> {
 
         @JvmStatic
         override fun create(viewModelContext: ViewModelContext, state: KeyRequestListViewState): KeyRequestListViewModel? {
-            val fragment: KeyRequestListFragment = (viewModelContext as FragmentViewModelContext).fragment()
-            return fragment.viewModelFactory.create(state)
+            val context = viewModelContext as FragmentViewModelContext
+            val factory = (context.fragment as? IncomingKeyRequestListFragment)?.viewModelFactory
+                    ?: (context.fragment as? OutgoingKeyRequestListFragment)?.viewModelFactory
+
+            return factory?.create(state)
         }
     }
 }
diff --git a/vector/src/main/java/im/vector/riotx/features/settings/devtools/KeyRequestsFragment.kt b/vector/src/main/java/im/vector/riotx/features/settings/devtools/KeyRequestsFragment.kt
index 8b78f52c1e..b15d754181 100644
--- a/vector/src/main/java/im/vector/riotx/features/settings/devtools/KeyRequestsFragment.kt
+++ b/vector/src/main/java/im/vector/riotx/features/settings/devtools/KeyRequestsFragment.kt
@@ -16,11 +16,14 @@
 
 package im.vector.riotx.features.settings.devtools
 
+import android.content.Context
 import android.os.Bundle
+import android.view.MenuItem
 import android.view.View
 import androidx.fragment.app.Fragment
-import androidx.fragment.app.FragmentActivity
 import androidx.viewpager2.adapter.FragmentStateAdapter
+import androidx.viewpager2.widget.ViewPager2
+import androidx.viewpager2.widget.ViewPager2.SCROLL_STATE_IDLE
 import com.google.android.material.tabs.TabLayoutMediator
 import im.vector.riotx.R
 import im.vector.riotx.core.platform.VectorBaseActivity
@@ -31,26 +34,79 @@ import javax.inject.Inject
 class KeyRequestsFragment @Inject constructor() : VectorBaseFragment() {
 
     override fun getLayoutResId(): Int = R.layout.fragment_devtool_keyrequests
+    override fun getMenuRes(): Int = R.menu.menu_common_gossiping
 
     override fun onResume() {
         super.onResume()
         (activity as? VectorBaseActivity)?.supportActionBar?.setTitle(R.string.key_share_request)
     }
 
+    private var mPagerAdapter: KeyReqPagerAdapter? = null
+
+    private val pageAdapterListener = object : ViewPager2.OnPageChangeCallback() {
+        override fun onPageSelected(position: Int) {
+
+            invalidateOptionsMenu()
+        }
+
+        override fun onPageScrollStateChanged(state: Int) {
+            childFragmentManager.fragments.forEach {
+                setHasOptionsMenu(state == SCROLL_STATE_IDLE)
+            }
+            invalidateOptionsMenu()
+        }
+    }
+
+    override fun onDestroy() {
+        invalidateOptionsMenu()
+        super.onDestroy()
+    }
+
     override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
         super.onViewCreated(view, savedInstanceState)
-        devToolKeyRequestPager.adapter = KeyReqPagerAdapter(requireActivity())
+        mPagerAdapter = KeyReqPagerAdapter(this)
+        devToolKeyRequestPager.adapter = mPagerAdapter
+        devToolKeyRequestPager.registerOnPageChangeCallback(pageAdapterListener)
 
-        TabLayoutMediator(devToolKeyRequestTabs, devToolKeyRequestPager) { tab, _ ->
-            tab.text = "Outgoing"
+        TabLayoutMediator(devToolKeyRequestTabs, devToolKeyRequestPager) { tab, position ->
+            when (position) {
+                0 -> {
+                    tab.text = "Outgoing"
+                }
+                1 -> {
+                    tab.text = "Incoming"
+                }
+                2 -> {
+                    tab.text = "Audit Trail"
+                }
+            }
         }.attach()
     }
 
-    private inner class KeyReqPagerAdapter(fa: FragmentActivity) : FragmentStateAdapter(fa) {
-        override fun getItemCount(): Int = 1
+    override fun onDestroyView() {
+        devToolKeyRequestPager.unregisterOnPageChangeCallback(pageAdapterListener)
+        mPagerAdapter = null
+        super.onDestroyView()
+    }
+
+    private inner class KeyReqPagerAdapter(fa: Fragment) : FragmentStateAdapter(fa) {
+        override fun getItemCount(): Int = 3
+
+
 
         override fun createFragment(position: Int): Fragment {
-            return childFragmentManager.fragmentFactory.instantiate(requireContext().classLoader, KeyRequestListFragment::class.java.name)
+            return when (position) {
+                0    -> {
+                    childFragmentManager.fragmentFactory.instantiate(requireContext().classLoader, OutgoingKeyRequestListFragment::class.java.name)
+                }
+                1    -> {
+                    childFragmentManager.fragmentFactory.instantiate(requireContext().classLoader, IncomingKeyRequestListFragment::class.java.name)
+                }
+                else -> {
+                    childFragmentManager.fragmentFactory.instantiate(requireContext().classLoader, GossipingEventsPaperTrailFragment::class.java.name)
+                }
+            }
         }
     }
+
 }
diff --git a/vector/src/main/java/im/vector/riotx/features/settings/devtools/KeyRequestListFragment.kt b/vector/src/main/java/im/vector/riotx/features/settings/devtools/OutgoingKeyRequestListFragment.kt
similarity index 95%
rename from vector/src/main/java/im/vector/riotx/features/settings/devtools/KeyRequestListFragment.kt
rename to vector/src/main/java/im/vector/riotx/features/settings/devtools/OutgoingKeyRequestListFragment.kt
index 71a4e19343..d79bb9934b 100644
--- a/vector/src/main/java/im/vector/riotx/features/settings/devtools/KeyRequestListFragment.kt
+++ b/vector/src/main/java/im/vector/riotx/features/settings/devtools/OutgoingKeyRequestListFragment.kt
@@ -17,6 +17,7 @@
 package im.vector.riotx.features.settings.devtools
 
 import android.os.Bundle
+import android.view.MenuItem
 import android.view.View
 import com.airbnb.mvrx.fragmentViewModel
 import com.airbnb.mvrx.withState
@@ -28,14 +29,13 @@ import im.vector.riotx.core.resources.ColorProvider
 import kotlinx.android.synthetic.main.fragment_generic_recycler.*
 import javax.inject.Inject
 
-class KeyRequestListFragment @Inject constructor(
+class OutgoingKeyRequestListFragment @Inject constructor(
         val viewModelFactory: KeyRequestListViewModel.Factory,
         private val epoxyController: KeyRequestEpoxyController,
         private val colorProvider: ColorProvider
 ) : VectorBaseFragment() {
 
     override fun getLayoutResId() = R.layout.fragment_generic_recycler
-
     private val viewModel: KeyRequestListViewModel by fragmentViewModel(KeyRequestListViewModel::class)
 
     override fun invalidate() = withState(viewModel) { state ->
@@ -53,4 +53,5 @@ class KeyRequestListFragment @Inject constructor(
         recyclerView.cleanup()
 //        epoxyController.interactionListener = null
     }
+
 }
diff --git a/vector/src/main/res/menu/menu_common_gossiping.xml b/vector/src/main/res/menu/menu_common_gossiping.xml
new file mode 100644
index 0000000000..08b23c760c
--- /dev/null
+++ b/vector/src/main/res/menu/menu_common_gossiping.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<menu xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:android="http://schemas.android.com/apk/res/android">
+    <item
+        android:id="@+id/refresh"
+        android:icon="@drawable/ic_refresh_cw"
+        app:iconTint="?riotx_text_primary"
+        android:title="@string/refresh"
+        app:showAsAction="collapseActionView|ifRoom" />
+</menu>
diff --git a/vector/src/main/res/values/strings_riotX.xml b/vector/src/main/res/values/strings_riotX.xml
index 7105d56a5c..643160ef21 100644
--- a/vector/src/main/res/values/strings_riotX.xml
+++ b/vector/src/main/res/values/strings_riotX.xml
@@ -12,6 +12,8 @@
 
     <string name="e2e_use_keybackup">Unlock encrypted messages history</string>
 
+    <string name="refresh">Refresh</string>
+
     <!-- END Strings added by Valere -->