From 21ae4c6ddbf29bd33f3888788717f0cbd06d74b5 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Thu, 13 Oct 2022 15:11:15 +0100
Subject: [PATCH 01/93] Support for login by m.login.token during QR code sign
 in

---
 .../sdk/api/auth/AuthenticationService.kt     | 14 +++
 .../matrix/android/sdk/api/auth/LoginType.kt  |  4 +-
 .../auth/DefaultAuthenticationService.kt      | 15 +++-
 .../sdk/internal/auth/data/LoginParams.kt     |  2 +
 .../internal/auth/data/PasswordLoginParams.kt |  4 +-
 .../internal/auth/data/TokenLoginParams.kt    |  4 +-
 .../internal/auth/login/QrLoginTokenTask.kt   | 88 +++++++++++++++++++
 7 files changed, 126 insertions(+), 5 deletions(-)
 create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/login/QrLoginTokenTask.kt

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/AuthenticationService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/AuthenticationService.kt
index 5ae70e1978..8f2a784d49 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/AuthenticationService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/AuthenticationService.kt
@@ -124,4 +124,18 @@ interface AuthenticationService {
             initialDeviceName: String,
             deviceId: String? = null
     ): Session
+
+    /**
+     * Authenticate using m.login.token method during sign in with QR code.
+     * @param homeServerConnectionConfig the information about the homeserver and other configuration
+     * @param loginToken the m.login.token
+     * @param initialDeviceName the initial device name
+     * @param deviceId the device id, optional. If not provided or null, the server will generate one.
+     */
+    suspend fun loginUsingQrLoginToken(
+            homeServerConnectionConfig: HomeServerConnectionConfig,
+            loginToken: String,
+            initialDeviceName: String,
+            deviceId: String? = null
+    ): Session
 }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/LoginType.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/LoginType.kt
index 627a825679..991b7b654d 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/LoginType.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/LoginType.kt
@@ -22,7 +22,8 @@ enum class LoginType {
     UNSUPPORTED,
     CUSTOM,
     DIRECT,
-    UNKNOWN;
+    UNKNOWN,
+    QR;
 
     companion object {
 
@@ -32,6 +33,7 @@ enum class LoginType {
             UNSUPPORTED.name -> UNSUPPORTED
             CUSTOM.name -> CUSTOM
             DIRECT.name -> DIRECT
+            QR.name -> QR
             else -> UNKNOWN
         }
     }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt
index 446f931847..90dc57b4f0 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt
@@ -39,6 +39,7 @@ import org.matrix.android.sdk.internal.auth.data.WebClientConfig
 import org.matrix.android.sdk.internal.auth.db.PendingSessionData
 import org.matrix.android.sdk.internal.auth.login.DefaultLoginWizard
 import org.matrix.android.sdk.internal.auth.login.DirectLoginTask
+import org.matrix.android.sdk.internal.auth.login.QrLoginTokenTask
 import org.matrix.android.sdk.internal.auth.registration.DefaultRegistrationWizard
 import org.matrix.android.sdk.internal.auth.version.Versions
 import org.matrix.android.sdk.internal.auth.version.doesServerSupportLogoutDevices
@@ -62,7 +63,8 @@ internal class DefaultAuthenticationService @Inject constructor(
         private val sessionCreator: SessionCreator,
         private val pendingSessionStore: PendingSessionStore,
         private val getWellknownTask: GetWellknownTask,
-        private val directLoginTask: DirectLoginTask
+        private val directLoginTask: DirectLoginTask,
+        private val loginTokenAuthTask: QrLoginTokenTask
 ) : AuthenticationService {
 
     private var pendingSessionData: PendingSessionData? = pendingSessionStore.getPendingSessionData()
@@ -404,6 +406,17 @@ internal class DefaultAuthenticationService @Inject constructor(
         )
     }
 
+    override suspend fun loginUsingQrLoginToken(homeServerConnectionConfig: HomeServerConnectionConfig, loginToken: String, initialDeviceName: String, deviceId: String?): Session {
+        return loginTokenAuthTask.execute(
+                QrLoginTokenTask.Params(
+                        homeServerConnectionConfig = homeServerConnectionConfig,
+                        loginToken = loginToken,
+                        deviceName = initialDeviceName,
+                        deviceId = deviceId
+                )
+        )
+    }
+
     private fun buildAuthAPI(homeServerConnectionConfig: HomeServerConnectionConfig): AuthAPI {
         val retrofit = retrofitFactory.create(buildClient(homeServerConnectionConfig), homeServerConnectionConfig.homeServerUriBase.toString())
         return retrofit.create(AuthAPI::class.java)
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/LoginParams.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/LoginParams.kt
index ea8578ed8c..8646752083 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/LoginParams.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/LoginParams.kt
@@ -18,4 +18,6 @@ package org.matrix.android.sdk.internal.auth.data
 
 internal interface LoginParams {
     val type: String
+    val deviceDisplayName: String?
+    val deviceId: String?
 }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/PasswordLoginParams.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/PasswordLoginParams.kt
index 5f0a2298cb..062b2466e5 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/PasswordLoginParams.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/PasswordLoginParams.kt
@@ -30,8 +30,8 @@ internal data class PasswordLoginParams(
         @Json(name = "identifier") val identifier: Map<String, String>,
         @Json(name = "password") val password: String,
         @Json(name = "type") override val type: String,
-        @Json(name = "initial_device_display_name") val deviceDisplayName: String?,
-        @Json(name = "device_id") val deviceId: String?
+        @Json(name = "initial_device_display_name") override val deviceDisplayName: String?,
+        @Json(name = "device_id") override val deviceId: String?
 ) : LoginParams {
 
     companion object {
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/TokenLoginParams.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/TokenLoginParams.kt
index 0c6fb45e78..22cc185fa7 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/TokenLoginParams.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/TokenLoginParams.kt
@@ -23,5 +23,7 @@ import org.matrix.android.sdk.api.auth.data.LoginFlowTypes
 @JsonClass(generateAdapter = true)
 internal data class TokenLoginParams(
         @Json(name = "type") override val type: String = LoginFlowTypes.TOKEN,
-        @Json(name = "token") val token: String
+        @Json(name = "token") val token: String,
+        @Json(name = "initial_device_display_name") override val deviceDisplayName: String?,
+        @Json(name = "device_id") override val deviceId: String?
 ) : LoginParams
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/login/QrLoginTokenTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/login/QrLoginTokenTask.kt
new file mode 100644
index 0000000000..477719f607
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/login/QrLoginTokenTask.kt
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2022 The Matrix.org Foundation C.I.C.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.matrix.android.sdk.internal.auth.login
+
+import dagger.Lazy
+import okhttp3.OkHttpClient
+import org.matrix.android.sdk.api.auth.LoginType
+import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig
+import org.matrix.android.sdk.api.failure.Failure
+import org.matrix.android.sdk.api.session.Session
+import org.matrix.android.sdk.internal.auth.AuthAPI
+import org.matrix.android.sdk.internal.auth.SessionCreator
+import org.matrix.android.sdk.internal.auth.data.TokenLoginParams
+import org.matrix.android.sdk.internal.di.Unauthenticated
+import org.matrix.android.sdk.internal.network.RetrofitFactory
+import org.matrix.android.sdk.internal.network.executeRequest
+import org.matrix.android.sdk.internal.network.httpclient.addSocketFactory
+import org.matrix.android.sdk.internal.network.ssl.UnrecognizedCertificateException
+import org.matrix.android.sdk.internal.task.Task
+import javax.inject.Inject
+
+internal interface QrLoginTokenTask : Task<QrLoginTokenTask.Params, Session> {
+    data class Params(
+            val homeServerConnectionConfig: HomeServerConnectionConfig,
+            val loginToken: String,
+            val deviceName: String?,
+            val deviceId: String?
+    )
+}
+
+internal class DefaultQrLoginTokenTask @Inject constructor(
+        @Unauthenticated
+        private val okHttpClient: Lazy<OkHttpClient>,
+        private val retrofitFactory: RetrofitFactory,
+        private val sessionCreator: SessionCreator,
+) : QrLoginTokenTask {
+
+    override suspend fun execute(params: QrLoginTokenTask.Params): Session {
+        val client = buildClient(params.homeServerConnectionConfig)
+        val homeServerUrl = params.homeServerConnectionConfig.homeServerUriBase.toString()
+
+        val authAPI = retrofitFactory.create(client, homeServerUrl)
+                .create(AuthAPI::class.java)
+
+        val loginParams = TokenLoginParams(
+                token = params.loginToken,
+                deviceDisplayName = params.deviceName,
+                deviceId = params.deviceId
+        )
+
+        val credentials = try {
+            executeRequest(null) {
+                authAPI.login(loginParams)
+            }
+        } catch (throwable: Throwable) {
+            throw when (throwable) {
+                is UnrecognizedCertificateException -> Failure.UnrecognizedCertificateFailure(
+                        homeServerUrl,
+                        throwable.fingerprint
+                )
+                else -> throwable
+            }
+        }
+
+        return sessionCreator.createSession(credentials, params.homeServerConnectionConfig, LoginType.QR)
+    }
+
+    private fun buildClient(homeServerConnectionConfig: HomeServerConnectionConfig): OkHttpClient {
+        return okHttpClient.get()
+                .newBuilder()
+                .addSocketFactory(homeServerConnectionConfig)
+                .build()
+    }
+}

From 098c268af3c348ce8ce7ef088047e907cd1efe83 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Thu, 13 Oct 2022 15:18:31 +0100
Subject: [PATCH 02/93] Changelog

---
 changelog.d/7358.sdk | 1 +
 1 file changed, 1 insertion(+)
 create mode 100644 changelog.d/7358.sdk

diff --git a/changelog.d/7358.sdk b/changelog.d/7358.sdk
new file mode 100644
index 0000000000..3d17076a44
--- /dev/null
+++ b/changelog.d/7358.sdk
@@ -0,0 +1 @@
+Add support for `m.login.token` auth during QR code based sign in

From a71ecee44a5b39d1ac13faa0078e0349d0bbf901 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Thu, 13 Oct 2022 15:19:39 +0100
Subject: [PATCH 03/93] Linting

---
 .../sdk/internal/auth/DefaultAuthenticationService.kt      | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt
index 90dc57b4f0..7fd730bece 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt
@@ -406,7 +406,12 @@ internal class DefaultAuthenticationService @Inject constructor(
         )
     }
 
-    override suspend fun loginUsingQrLoginToken(homeServerConnectionConfig: HomeServerConnectionConfig, loginToken: String, initialDeviceName: String, deviceId: String?): Session {
+    override suspend fun loginUsingQrLoginToken(
+            homeServerConnectionConfig: HomeServerConnectionConfig,
+            loginToken: String,
+            initialDeviceName: String,
+            deviceId: String?,
+    ): Session {
         return loginTokenAuthTask.execute(
                 QrLoginTokenTask.Params(
                         homeServerConnectionConfig = homeServerConnectionConfig,

From 0d245657e1fd50596397e4dd48d70c24c8661fba Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Tue, 11 Oct 2022 23:32:49 +0100
Subject: [PATCH 04/93] Retry scanning if not a QR code


From 1c70d455fbfda7a301ed01f1f18eef1ddf2aa902 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Tue, 11 Oct 2022 23:33:30 +0100
Subject: [PATCH 05/93] Implementations of MSC3886 and MSC3903


From b192fdb0a89b1aa922440ae3dda3bf288a8b7611 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Tue, 11 Oct 2022 23:34:05 +0100
Subject: [PATCH 06/93] Partial implementation of QR login logic

---
 .../features/login/qr/QrCodeLoginViewModel.kt |  2 +-
 .../voicebroadcast/VoiceBroadcastConstants.kt | 20 -------------------
 2 files changed, 1 insertion(+), 21 deletions(-)
 delete mode 100644 vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastConstants.kt

diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
index f97a59b432..d0c34b83af 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
@@ -85,8 +85,8 @@ class QrCodeLoginViewModel @AssistedInject constructor(
             Timber.tag(TAG).i("Established secure channel with checksum: $confirmationCode")
             confirmationCode ?.let {
                 onConnectionEstablished(it)
-                rendezvous.completeOnNewDevice()
             }
+            rendezvous.completeOnNewDevice()
         }
     //        if (isValidQrCode(action.qrCode)) {
 //            setState {
diff --git a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastConstants.kt b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastConstants.kt
deleted file mode 100644
index d7d74b08e9..0000000000
--- a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastConstants.kt
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Copyright (c) 2022 New Vector Ltd
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package im.vector.app.features.voicebroadcast
-
-/** Voice Broadcast State Event. */
-const val STATE_ROOM_VOICE_BROADCAST_INFO = "io.element.voice_broadcast_info"

From 6e09d900070319b356a2b10a1769af8da97a2aef Mon Sep 17 00:00:00 2001
From: Onuray Sahin <onurays@element.io>
Date: Wed, 12 Oct 2022 14:32:09 +0300
Subject: [PATCH 07/93] Merge branch 'develop' into
 feature/ons/qr_code_login_ui

# Conflicts:
#	library/ui-strings/src/main/res/values/strings.xml
#	library/ui-styles/src/main/res/values/stylable_sessions_list_header_view.xml
#	vector-app/src/debug/java/im/vector/app/features/debug/features/DebugFeaturesStateFactory.kt
#	vector-app/src/debug/java/im/vector/app/features/debug/features/DebugVectorFeatures.kt
#	vector/src/main/java/im/vector/app/core/di/MavericksViewModelModule.kt
#	vector/src/main/java/im/vector/app/features/VectorFeatures.kt
#	vector/src/main/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesFragment.kt
#	vector/src/main/java/im/vector/app/features/settings/devices/v2/list/SessionsListHeaderView.kt
#	vector/src/main/res/layout/fragment_other_sessions.xml
#	vector/src/main/res/layout/fragment_settings_devices.xml
---
 .../voicebroadcast/VoiceBroadcastConstants.kt | 20 +++++++++++++++++++
 1 file changed, 20 insertions(+)
 create mode 100644 vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastConstants.kt

diff --git a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastConstants.kt b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastConstants.kt
new file mode 100644
index 0000000000..d7d74b08e9
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastConstants.kt
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2022 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.app.features.voicebroadcast
+
+/** Voice Broadcast State Event. */
+const val STATE_ROOM_VOICE_BROADCAST_INFO = "io.element.voice_broadcast_info"

From 86090086b1344a8e80f8bd3accb1cbbf26a2eab2 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Wed, 12 Oct 2022 13:08:01 +0100
Subject: [PATCH 08/93] Only do completeOnNewDevice if we received a
 confirmation code

---
 .../im/vector/app/features/login/qr/QrCodeLoginViewModel.kt     | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
index d0c34b83af..f97a59b432 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
@@ -85,8 +85,8 @@ class QrCodeLoginViewModel @AssistedInject constructor(
             Timber.tag(TAG).i("Established secure channel with checksum: $confirmationCode")
             confirmationCode ?.let {
                 onConnectionEstablished(it)
+                rendezvous.completeOnNewDevice()
             }
-            rendezvous.completeOnNewDevice()
         }
     //        if (isValidQrCode(action.qrCode)) {
 //            setState {

From ebb3d201c18efba47ec546d778c2f311f266ff9e Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Thu, 13 Oct 2022 15:35:45 +0100
Subject: [PATCH 09/93] Make initialDeviceName optional

---
 .../org/matrix/android/sdk/api/auth/AuthenticationService.kt    | 2 +-
 .../android/sdk/internal/auth/DefaultAuthenticationService.kt   | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/AuthenticationService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/AuthenticationService.kt
index 8f2a784d49..c8065e4524 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/AuthenticationService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/AuthenticationService.kt
@@ -135,7 +135,7 @@ interface AuthenticationService {
     suspend fun loginUsingQrLoginToken(
             homeServerConnectionConfig: HomeServerConnectionConfig,
             loginToken: String,
-            initialDeviceName: String,
+            initialDeviceName: String?,
             deviceId: String? = null
     ): Session
 }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt
index 7fd730bece..6c3622ed5d 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt
@@ -409,7 +409,7 @@ internal class DefaultAuthenticationService @Inject constructor(
     override suspend fun loginUsingQrLoginToken(
             homeServerConnectionConfig: HomeServerConnectionConfig,
             loginToken: String,
-            initialDeviceName: String,
+            initialDeviceName: String?,
             deviceId: String?,
     ): Session {
         return loginTokenAuthTask.execute(

From 5843c3832b6e654a813dde273c75cf9e68497e63 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Thu, 13 Oct 2022 15:58:19 +0100
Subject: [PATCH 10/93] Use correct var name

---
 .../android/sdk/internal/auth/DefaultAuthenticationService.kt | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt
index 6c3622ed5d..5b12e3bdc3 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt
@@ -64,7 +64,7 @@ internal class DefaultAuthenticationService @Inject constructor(
         private val pendingSessionStore: PendingSessionStore,
         private val getWellknownTask: GetWellknownTask,
         private val directLoginTask: DirectLoginTask,
-        private val loginTokenAuthTask: QrLoginTokenTask
+        private val qrLoginTokenTask: QrLoginTokenTask
 ) : AuthenticationService {
 
     private var pendingSessionData: PendingSessionData? = pendingSessionStore.getPendingSessionData()
@@ -412,7 +412,7 @@ internal class DefaultAuthenticationService @Inject constructor(
             initialDeviceName: String?,
             deviceId: String?,
     ): Session {
-        return loginTokenAuthTask.execute(
+        return qrLoginTokenTask.execute(
                 QrLoginTokenTask.Params(
                         homeServerConnectionConfig = homeServerConnectionConfig,
                         loginToken = loginToken,

From 579df742579f19fda49913d031759f26ea61c841 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Thu, 13 Oct 2022 16:02:57 +0100
Subject: [PATCH 11/93] Add missing binding

---
 .../java/org/matrix/android/sdk/internal/auth/AuthModule.kt  | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/AuthModule.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/AuthModule.kt
index 463692e574..b1f65194f1 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/AuthModule.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/AuthModule.kt
@@ -29,7 +29,9 @@ import org.matrix.android.sdk.internal.auth.db.AuthRealmModule
 import org.matrix.android.sdk.internal.auth.db.RealmPendingSessionStore
 import org.matrix.android.sdk.internal.auth.db.RealmSessionParamsStore
 import org.matrix.android.sdk.internal.auth.login.DefaultDirectLoginTask
+import org.matrix.android.sdk.internal.auth.login.DefaultQrLoginTokenTask
 import org.matrix.android.sdk.internal.auth.login.DirectLoginTask
+import org.matrix.android.sdk.internal.auth.login.QrLoginTokenTask
 import org.matrix.android.sdk.internal.database.RealmKeysUtils
 import org.matrix.android.sdk.internal.di.AuthDatabase
 import org.matrix.android.sdk.internal.legacy.DefaultLegacySessionImporter
@@ -94,4 +96,7 @@ internal abstract class AuthModule {
 
     @Binds
     abstract fun bindHomeServerHistoryService(service: DefaultHomeServerHistoryService): HomeServerHistoryService
+
+    @Binds
+    abstract fun bindQrLoginTokenTask(task: DefaultQrLoginTokenTask): QrLoginTokenTask
 }

From b5e81d27d65bb8dc5819250c29035e004c24121f Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Thu, 13 Oct 2022 16:08:50 +0100
Subject: [PATCH 12/93] Set default value for optional params

---
 .../matrix/android/sdk/internal/auth/data/TokenLoginParams.kt | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/TokenLoginParams.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/TokenLoginParams.kt
index 22cc185fa7..52045a1d7a 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/TokenLoginParams.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/TokenLoginParams.kt
@@ -24,6 +24,6 @@ import org.matrix.android.sdk.api.auth.data.LoginFlowTypes
 internal data class TokenLoginParams(
         @Json(name = "type") override val type: String = LoginFlowTypes.TOKEN,
         @Json(name = "token") val token: String,
-        @Json(name = "initial_device_display_name") override val deviceDisplayName: String?,
-        @Json(name = "device_id") override val deviceId: String?
+        @Json(name = "initial_device_display_name") override val deviceDisplayName: String? = null,
+        @Json(name = "device_id") override val deviceId: String? = null
 ) : LoginParams

From 22b344c43a224a52d45be8087a7ead5678d6b38f Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Thu, 13 Oct 2022 16:11:41 +0100
Subject: [PATCH 13/93] Another default value fix

---
 .../org/matrix/android/sdk/api/auth/AuthenticationService.kt    | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/AuthenticationService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/AuthenticationService.kt
index c8065e4524..e490311b91 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/AuthenticationService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/AuthenticationService.kt
@@ -135,7 +135,7 @@ interface AuthenticationService {
     suspend fun loginUsingQrLoginToken(
             homeServerConnectionConfig: HomeServerConnectionConfig,
             loginToken: String,
-            initialDeviceName: String?,
+            initialDeviceName: String? = null,
             deviceId: String? = null
     ): Session
 }

From 8dbb1b830ef8c508190b0367501a1ef441c717c6 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Thu, 13 Oct 2022 16:28:47 +0100
Subject: [PATCH 14/93] Map for soft logout

---
 .../im/vector/app/features/signout/soft/SoftLogoutController.kt  | 1 +
 1 file changed, 1 insertion(+)

diff --git a/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutController.kt b/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutController.kt
index b1a240e942..a1ed27df1d 100644
--- a/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutController.kt
+++ b/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutController.kt
@@ -152,6 +152,7 @@ class SoftLogoutController @Inject constructor(
             LoginType.SSO -> buildLoginSSOForm()
             LoginType.DIRECT,
             LoginType.CUSTOM,
+            LoginType.QR,
             LoginType.UNSUPPORTED -> buildLoginUnsupportedForm()
             LoginType.UNKNOWN -> Unit
         }

From 0111b932dea08e98362182165a23d717bea68592 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Thu, 13 Oct 2022 21:08:45 +0100
Subject: [PATCH 15/93] Support for navigation to home screen

---
 .../im/vector/app/features/login/qr/QrCodeLoginActivity.kt | 7 +++++++
 .../vector/app/features/login/qr/QrCodeLoginViewEvents.kt  | 1 +
 2 files changed, 8 insertions(+)

diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginActivity.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginActivity.kt
index e0323fdc2d..3c7b0a4729 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginActivity.kt
@@ -26,6 +26,7 @@ import dagger.hilt.android.AndroidEntryPoint
 import im.vector.app.core.extensions.addFragment
 import im.vector.app.core.extensions.addFragmentToBackstack
 import im.vector.app.core.platform.SimpleFragmentActivity
+import im.vector.app.features.home.HomeActivity
 import org.matrix.android.sdk.api.extensions.orFalse
 import timber.log.Timber
 
@@ -75,6 +76,7 @@ class QrCodeLoginActivity : SimpleFragmentActivity() {
             when (it) {
                 QrCodeLoginViewEvents.NavigateToStatusScreen -> handleNavigateToStatusScreen()
                 QrCodeLoginViewEvents.NavigateToShowQrCodeScreen -> handleNavigateToShowQrCodeScreen()
+                QrCodeLoginViewEvents.NavigateToHomeScreen -> handleNavigateToHomeScreen()
             }
         }
     }
@@ -95,6 +97,11 @@ class QrCodeLoginActivity : SimpleFragmentActivity() {
         )
     }
 
+    private fun handleNavigateToHomeScreen() {
+        val intent = HomeActivity.newIntent(this, firstStartMainActivity = false, existingSession = true)
+        startActivity(intent)
+    }
+
     companion object {
 
         private const val FRAGMENT_QR_CODE_INSTRUCTIONS_TAG = "FRAGMENT_QR_CODE_INSTRUCTIONS_TAG"
diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewEvents.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewEvents.kt
index dc258408e7..0f282fee38 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewEvents.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewEvents.kt
@@ -21,4 +21,5 @@ import im.vector.app.core.platform.VectorViewEvents
 sealed class QrCodeLoginViewEvents : VectorViewEvents {
     object NavigateToStatusScreen : QrCodeLoginViewEvents()
     object NavigateToShowQrCodeScreen : QrCodeLoginViewEvents()
+    object NavigateToHomeScreen : QrCodeLoginViewEvents()
 }

From 1ed082d3cb16943a15664dbb862126942f5539d3 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Thu, 13 Oct 2022 21:15:52 +0100
Subject: [PATCH 16/93] QR login + E2EE set up

---
 .../sdk/internal/rendezvous/Rendezvous.kt     | 95 +++++++++----------
 .../features/login/qr/QrCodeLoginViewModel.kt | 73 ++++++--------
 2 files changed, 74 insertions(+), 94 deletions(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/Rendezvous.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/Rendezvous.kt
index dd7b91582b..2f85a97c55 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/Rendezvous.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/Rendezvous.kt
@@ -16,8 +16,11 @@
 
 package org.matrix.android.sdk.internal.rendezvous
 
+import android.net.Uri
 import com.squareup.moshi.Json
 import com.squareup.moshi.JsonClass
+import org.matrix.android.sdk.api.auth.AuthenticationService
+import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig
 import org.matrix.android.sdk.api.logger.LoggerTag
 import org.matrix.android.sdk.api.session.Session
 import org.matrix.android.sdk.api.session.crypto.crosssigning.DeviceTrustLevel
@@ -56,9 +59,12 @@ internal data class Payload(
 
 private val TAG = LoggerTag(Rendezvous::class.java.simpleName, LoggerTag.RENDEZVOUS).value
 
-data class Rendezvous(
+/**
+ * Implementation of MSC3906 to sign in + E2EE set up using a QR code.
+ */
+class Rendezvous(
         val channel: RendezvousChannel,
-        val theirIntent: RendezvousIntent
+        val theirIntent: RendezvousIntent,
 ) {
     companion object {
         fun buildChannelFromCode(code: String, onCancelled: (reason: RendezvousFailureReason) -> Unit): Rendezvous {
@@ -116,7 +122,7 @@ data class Rendezvous(
         return checksum
     }
 
-    suspend fun completeOnNewDevice(): Session? {
+    suspend fun waitForLoginOnNewDevice(authenticationService: AuthenticationService): Session? {
         Timber.tag(TAG).i("Waiting for login_token");
 
         val loginToken = receive()
@@ -143,59 +149,46 @@ data class Rendezvous(
 
         Timber.tag(TAG).i("Got login_token: $login_token for $homeserver");
 
-        // TODO: set view to be state logging in?
+        val hsConfig = HomeServerConnectionConfig(homeServerUri = Uri.parse(homeserver))
+        return authenticationService.loginUsingQrLoginToken(hsConfig, login_token)
+    }
 
-        // use token to login
-//        const login = await sendLoginRequest(homeserver, undefined, "m.login.token", { token: login_token });
-//
-//        await setLoggedIn(login);
-//
-//        const { deviceId, userId } = login;
-//
-//        const client = MatrixClientPeg.get();
-//
+    suspend fun completeVerificationOnNewDevice(session: Session) {
+        val userId = session.myUserId
+        val crypto = session.cryptoService()
+        val deviceId = crypto.getMyDevice().deviceId
+        val deviceKey = crypto.getMyDevice().fingerprint()
+        send(Payload(PayloadType.Progress, outcome = "success", device_id = deviceId, device_key = deviceKey))
 
-        val newSession: Session? = null
+        // await confirmation of verification
 
-        newSession ?.let {
-            session ->
-            val userId = session.myUserId
-            val crypto = session.cryptoService()
-            val deviceId = crypto.getMyDevice().deviceId
-            val deviceKey = crypto.getMyDevice().fingerprint()
-            send(Payload(PayloadType.Progress, outcome = "success", device_id = deviceId, device_key = deviceKey))
-
-            // await confirmation of verification
-
-            val verificationResponse = receive()
-            val verifyingDeviceId = verificationResponse?.verifying_device_id ?: throw RuntimeException("No verifying device id returned")
-            val verifyingDeviceFromServer = crypto.getCryptoDeviceInfo(userId, verifyingDeviceId)
-            if (verifyingDeviceFromServer?.fingerprint() == verificationResponse.verifying_device_key) {
-                // set other device as verified
-                Timber.tag(TAG).i("Setting device $verifyingDeviceId as verified");
-                crypto.setDeviceVerification(DeviceTrustLevel(locallyVerified = true, crossSigningVerified = false), userId, verifyingDeviceId)
-
-                verificationResponse.master_key ?.let {
-                    // set master key as trusted
-                    crypto.setDeviceVerification(DeviceTrustLevel(locallyVerified = true, crossSigningVerified = false), userId, it)
-
-                }
-
-                // request secrets from the verifying device
-                Timber.tag(TAG).i("Requesting secrets from $verifyingDeviceId")
-
-                session.sharedSecretStorageService() .let {
-                    it.requestSecret(verifyingDeviceId, MASTER_KEY_SSSS_NAME)
-                    it.requestSecret(verifyingDeviceId, SELF_SIGNING_KEY_SSSS_NAME)
-                    it.requestSecret(verifyingDeviceId, USER_SIGNING_KEY_SSSS_NAME)
-                    it.requestSecret(verifyingDeviceId, KEYBACKUP_SECRET_SSSS_NAME)
-                }
-            } else {
-                Timber.tag(TAG).i("Verifying device $verifyingDeviceId doesn't match: $verifyingDeviceFromServer")
-            }
+        val verificationResponse = receive()
+        val verifyingDeviceId = verificationResponse?.verifying_device_id ?: throw RuntimeException("No verifying device id returned")
+        val verifyingDeviceFromServer = crypto.getCryptoDeviceInfo(userId, verifyingDeviceId)
+        if (verifyingDeviceFromServer?.fingerprint() != verificationResponse.verifying_device_key) {
+            Timber.tag(TAG).w("Verifying device $verifyingDeviceId doesn't match: $verifyingDeviceFromServer")
+            return;
         }
 
-        return newSession
+        // set other device as verified
+        Timber.tag(TAG).i("Setting device $verifyingDeviceId as verified");
+        crypto.setDeviceVerification(DeviceTrustLevel(locallyVerified = true, crossSigningVerified = false), userId, verifyingDeviceId)
+
+        // TODO: what do we do with the master key?
+//        verificationResponse.master_key ?.let {
+//            // set master key as trusted
+//            crypto.setDeviceVerification(DeviceTrustLevel(locallyVerified = true, crossSigningVerified = false), userId, it)
+//        }
+
+        // request secrets from the verifying device
+        Timber.tag(TAG).i("Requesting secrets from $verifyingDeviceId")
+
+        session.sharedSecretStorageService() .let {
+            it.requestSecret(MASTER_KEY_SSSS_NAME, verifyingDeviceId)
+            it.requestSecret(SELF_SIGNING_KEY_SSSS_NAME, verifyingDeviceId)
+            it.requestSecret(USER_SIGNING_KEY_SSSS_NAME, verifyingDeviceId)
+            it.requestSecret(KEYBACKUP_SECRET_SSSS_NAME, verifyingDeviceId)
+        }
     }
 
     private suspend fun receive(): Payload? {
diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
index f97a59b432..c729152e44 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
@@ -16,24 +16,31 @@
 
 package im.vector.app.features.login.qr
 
+import android.content.Context
 import com.airbnb.mvrx.MavericksViewModelFactory
 import dagger.assisted.Assisted
 import dagger.assisted.AssistedFactory
 import dagger.assisted.AssistedInject
+import im.vector.app.core.di.ActiveSessionHolder
 import im.vector.app.core.di.MavericksAssistedViewModelFactory
 import im.vector.app.core.di.hiltMavericksViewModelFactory
+import im.vector.app.core.extensions.configureAndStart
 import im.vector.app.core.platform.VectorViewModel
+import im.vector.app.features.home.HomeActivity
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.launch
+import org.matrix.android.sdk.api.auth.AuthenticationService
 import org.matrix.android.sdk.api.session.Session
 import org.matrix.android.sdk.internal.rendezvous.Rendezvous
 import org.matrix.android.sdk.internal.rendezvous.RendezvousFailureReason
 import timber.log.Timber
 
 class QrCodeLoginViewModel @AssistedInject constructor(
-        @Assisted private val initialState: QrCodeLoginViewState
+        @Assisted private val initialState: QrCodeLoginViewState,
+        private val applicationContext: Context,
+        private val authenticationService: AuthenticationService,
+        private val activeSessionHolder: ActiveSessionHolder,
 ) : VectorViewModel<QrCodeLoginViewState, QrCodeLoginAction, QrCodeLoginViewEvents>(initialState) {
-
     val TAG: String = QrCodeLoginViewModel::class.java.simpleName
 
     @AssistedFactory
@@ -81,44 +88,31 @@ class QrCodeLoginViewModel @AssistedInject constructor(
         _viewEvents.post(QrCodeLoginViewEvents.NavigateToStatusScreen)
 
         viewModelScope.launch(Dispatchers.IO) {
-            val confirmationCode = rendezvous.startAfterScanningCode()
-            Timber.tag(TAG).i("Established secure channel with checksum: $confirmationCode")
-            confirmationCode ?.let {
-                onConnectionEstablished(it)
-                rendezvous.completeOnNewDevice()
+            try {
+                val confirmationCode = rendezvous.startAfterScanningCode()
+                Timber.tag(TAG).i("Established secure channel with checksum: $confirmationCode")
+                confirmationCode?.let {
+                    onConnectionEstablished(it)
+                    val session = rendezvous.waitForLoginOnNewDevice(authenticationService)
+                    onSigningIn()
+                    session?.let {
+                        activeSessionHolder.setActiveSession(session)
+                        authenticationService.reset()
+
+                        session.configureAndStart(applicationContext)
+
+                        rendezvous.completeVerificationOnNewDevice(session)
+
+                        _viewEvents.post(QrCodeLoginViewEvents.NavigateToHomeScreen)
+                    }
+                }
+            } catch (failure: Throwable) {
+                Timber.tag(TAG).e(failure, "Error occurred during sign in")
+                onFailed(RendezvousFailureReason.Unknown)
             }
         }
-    //        if (isValidQrCode(action.qrCode)) {
-//            setState {
-//                copy(
-//                        connectionStatus = QrCodeLoginConnectionStatus.ConnectingToDevice
-//                )
-//            }
-//            _viewEvents.post(QrCodeLoginViewEvents.NavigateToStatusScreen)
-//        }
-//
-
-//        // TODO. UI test purpose. Fixme remove!
-//        viewModelScope.launch {
-//            delay(3000)
-//            onFailed(QrCodeLoginErrorType.TIMEOUT, true)
-//            delay(3000)
-//            onConnectionEstablished("1234-ABCD-5678-EFGH")
-//            delay(3000)
-//            onSigningIn()
-//            delay(3000)
-//            onFailed(QrCodeLoginErrorType.DEVICE_IS_NOT_SUPPORTED, false)
-//        }
-//        // TODO. UI test purpose. Fixme remove!
-//        viewModelScope.launch {
-//            delay(3000)
-//            onConnectionEstablished("1234-ABCD-5678-EFGH")
-//            delay(3000)
-//            onSigningIn()
-//        }
     }
 
-
     private fun onFailed(reason: RendezvousFailureReason) {
         setState {
             copy(
@@ -144,13 +138,6 @@ class QrCodeLoginViewModel @AssistedInject constructor(
         }
     }
 
-    /**
-     * TODO. UI test purpose. Fixme accordingly.
-     */
-    private fun isValidQrCode(qrCode: String): Boolean {
-        return qrCode.startsWith("http")
-    }
-
     /**
      * TODO. UI test purpose. Fixme accordingly.
      */

From 560fda51d163695d1c4878751d9f2f0ab92014b7 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Thu, 13 Oct 2022 21:26:22 +0100
Subject: [PATCH 17/93] Reduce logging

---
 .../rendezvous/channels/ECDHRendezvousChannel.kt | 16 ++++++++--------
 .../transports/SimpleHttpRendezvousTransport.kt  |  8 ++++----
 2 files changed, 12 insertions(+), 12 deletions(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/channels/ECDHRendezvousChannel.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/channels/ECDHRendezvousChannel.kt
index 33837bc425..cced29aab4 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/channels/ECDHRendezvousChannel.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/channels/ECDHRendezvousChannel.kt
@@ -94,7 +94,7 @@ class ECDHRendezvousChannel(override var transport: RendezvousTransport, theirPu
         val isInitiator = theirPublicKey == null
 
         if (isInitiator) {
-            Timber.tag(TAG).i("Waiting for other device to send their public key")
+//            Timber.tag(TAG).i("Waiting for other device to send their public key")
             val res = this.receiveAsPayload() ?: throw RuntimeException("No reply from other device")
 
             if (res.key == null) {
@@ -106,7 +106,7 @@ class ECDHRendezvousChannel(override var transport: RendezvousTransport, theirPu
             theirPublicKey = Base64.decode(res.key, Base64.NO_WRAP)
         } else {
             // send our public key unencrypted
-            Timber.tag(TAG).i("Sending public key")
+//            Timber.tag(TAG).i("Sending public key")
             send(ECDHPayload(
                     algorithm = SecureRendezvousChannelAlgorithm.ECDH_V1,
                     key = Base64.encodeToString(ourPublicKey, Base64.NO_WRAP)
@@ -121,10 +121,10 @@ class ECDHRendezvousChannel(override var transport: RendezvousTransport, theirPu
 
         aesKey = olmSAS!!.generateShortCode(aesInfo, 32)
 
-        Timber.tag(TAG).i("Our public key: ${Base64.encodeToString(ourPublicKey, Base64.NO_WRAP)}")
-        Timber.tag(TAG).i("Their public key: ${Base64.encodeToString(theirPublicKey, Base64.NO_WRAP)}")
-        Timber.tag(TAG).i("AES info: $aesInfo")
-        Timber.tag(TAG).i("AES key: ${Base64.encodeToString(aesKey, Base64.NO_WRAP)}")
+//        Timber.tag(TAG).i("Our public key: ${Base64.encodeToString(ourPublicKey, Base64.NO_WRAP)}")
+//        Timber.tag(TAG).i("Their public key: ${Base64.encodeToString(theirPublicKey, Base64.NO_WRAP)}")
+//        Timber.tag(TAG).i("AES info: $aesInfo")
+//        Timber.tag(TAG).i("AES key: ${Base64.encodeToString(aesKey, Base64.NO_WRAP)}")
 
         val rawChecksum = olmSAS!!.generateShortCode(aesInfo, 5)
         return getDecimalCodeRepresentation(rawChecksum)
@@ -180,7 +180,7 @@ class ECDHRendezvousChannel(override var transport: RendezvousTransport, theirPu
     }
 
     private fun encrypt(plainText: ByteArray): ECDHPayload {
-        Timber.tag(TAG).i("Encrypting: ${plainText.toString(Charsets.UTF_8)}")
+//        Timber.tag(TAG).d("Encrypting: ${plainText.toString(Charsets.UTF_8)}")
         val iv = ByteArray(16)
         SecureRandom().nextBytes(iv)
 
@@ -212,7 +212,7 @@ class ECDHRendezvousChannel(override var transport: RendezvousTransport, theirPu
 
         val plainTextBytes = plainText.toByteArray()
 
-        Timber.tag(TAG).i("Decrypted: ${plainTextBytes.toString(Charsets.UTF_8)}")
+//        Timber.tag(TAG).d("Decrypted: ${plainTextBytes.toString(Charsets.UTF_8)}")
         return plainTextBytes
     }
 }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/transports/SimpleHttpRendezvousTransport.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/transports/SimpleHttpRendezvousTransport.kt
index 3e5e1121ea..cc4346d55e 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/transports/SimpleHttpRendezvousTransport.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/transports/SimpleHttpRendezvousTransport.kt
@@ -67,7 +67,7 @@ class SimpleHttpRendezvousTransport(override var onCancelled: ((reason: Rendezvo
         // TODO: properly determine endpoint
         val uri = if (uri != null) uri!! else "https://rendezvous.lab.element.dev"
 
-        Timber.tag(TAG).i("Sending data: ${data.toString(Charsets.UTF_8)} to $uri")
+//        Timber.tag(TAG).i("Sending data: ${data.toString(Charsets.UTF_8)} to $uri")
 
         val httpClient = okhttp3.OkHttpClient.Builder().build()
 
@@ -123,8 +123,7 @@ class SimpleHttpRendezvousTransport(override var onCancelled: ((reason: Rendezvo
             val response = httpClient.newCall(request.build()).execute()
 
             try {
-
-                Timber.tag(TAG).i("Received polling response: ${response.code} from $uri")
+//                Timber.tag(TAG).d("Received polling response: ${response.code} from $uri")
 
                 if (response.code == 404) {
                     cancel(RendezvousFailureReason.Unknown)
@@ -142,7 +141,7 @@ class SimpleHttpRendezvousTransport(override var onCancelled: ((reason: Rendezvo
                         etag = it
                     }
                     val data = response.body?.bytes()
-                    Timber.tag(TAG).i("Received data: ${data?.toString(Charsets.UTF_8)} from $uri with etag $etag")
+//                    Timber.tag(TAG).d("Received data: ${data?.toString(Charsets.UTF_8)} from $uri with etag $etag")
                     return data
                 }
 
@@ -158,6 +157,7 @@ class SimpleHttpRendezvousTransport(override var onCancelled: ((reason: Rendezvo
 
     override suspend fun cancel(reason: RendezvousFailureReason) {
         var mappedReason = reason
+        Timber.tag(TAG).i("$expiresAt")
         if (mappedReason == RendezvousFailureReason.Unknown &&
                 expiresAt != null && Date() > expiresAt) {
             mappedReason = RendezvousFailureReason.Expired

From 88238c0f04648a7468c2ee33d86068fa79e4fe5f Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Thu, 13 Oct 2022 15:11:15 +0100
Subject: [PATCH 18/93] Support for login by m.login.token during QR code sign
 in

---
 .../sdk/api/auth/AuthenticationService.kt     | 13 +++
 .../matrix/android/sdk/api/auth/LoginType.kt  |  4 +-
 .../auth/DefaultAuthenticationService.kt      | 15 +++-
 .../sdk/internal/auth/data/LoginParams.kt     |  2 +
 .../internal/auth/data/PasswordLoginParams.kt |  4 +-
 .../internal/auth/data/TokenLoginParams.kt    |  4 +-
 .../internal/auth/login/QrLoginTokenTask.kt   | 88 +++++++++++++++++++
 7 files changed, 125 insertions(+), 5 deletions(-)
 create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/login/QrLoginTokenTask.kt

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/AuthenticationService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/AuthenticationService.kt
index d040f9c67b..6bb47dda5b 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/AuthenticationService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/AuthenticationService.kt
@@ -130,4 +130,17 @@ interface AuthenticationService {
      * Return true if qr code login is supported by the server, false otherwise.
      */
     suspend fun isQrLoginSupported(homeServerConnectionConfig: HomeServerConnectionConfig): Boolean
+     * Authenticate using m.login.token method during sign in with QR code.
+     * @param homeServerConnectionConfig the information about the homeserver and other configuration
+     * @param loginToken the m.login.token
+     * @param initialDeviceName the initial device name
+     * @param deviceId the device id, optional. If not provided or null, the server will generate one.
+     */
+
+    suspend fun loginUsingQrLoginToken(
+            homeServerConnectionConfig: HomeServerConnectionConfig,
+            loginToken: String,
+            initialDeviceName: String,
+            deviceId: String? = null
+    ): Session
 }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/LoginType.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/LoginType.kt
index 627a825679..991b7b654d 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/LoginType.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/LoginType.kt
@@ -22,7 +22,8 @@ enum class LoginType {
     UNSUPPORTED,
     CUSTOM,
     DIRECT,
-    UNKNOWN;
+    UNKNOWN,
+    QR;
 
     companion object {
 
@@ -32,6 +33,7 @@ enum class LoginType {
             UNSUPPORTED.name -> UNSUPPORTED
             CUSTOM.name -> CUSTOM
             DIRECT.name -> DIRECT
+            QR.name -> QR
             else -> UNKNOWN
         }
     }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt
index 4f45f807df..5b6f0686c4 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt
@@ -40,6 +40,7 @@ import org.matrix.android.sdk.internal.auth.data.WebClientConfig
 import org.matrix.android.sdk.internal.auth.db.PendingSessionData
 import org.matrix.android.sdk.internal.auth.login.DefaultLoginWizard
 import org.matrix.android.sdk.internal.auth.login.DirectLoginTask
+import org.matrix.android.sdk.internal.auth.login.QrLoginTokenTask
 import org.matrix.android.sdk.internal.auth.registration.DefaultRegistrationWizard
 import org.matrix.android.sdk.internal.auth.version.Versions
 import org.matrix.android.sdk.internal.auth.version.doesServerSupportLogoutDevices
@@ -64,7 +65,8 @@ internal class DefaultAuthenticationService @Inject constructor(
         private val sessionCreator: SessionCreator,
         private val pendingSessionStore: PendingSessionStore,
         private val getWellknownTask: GetWellknownTask,
-        private val directLoginTask: DirectLoginTask
+        private val directLoginTask: DirectLoginTask,
+        private val loginTokenAuthTask: QrLoginTokenTask
 ) : AuthenticationService {
 
     private var pendingSessionData: PendingSessionData? = pendingSessionStore.getPendingSessionData()
@@ -420,6 +422,17 @@ internal class DefaultAuthenticationService @Inject constructor(
         }
     }
 
+    override suspend fun loginUsingQrLoginToken(homeServerConnectionConfig: HomeServerConnectionConfig, loginToken: String, initialDeviceName: String, deviceId: String?): Session {
+        return loginTokenAuthTask.execute(
+                QrLoginTokenTask.Params(
+                        homeServerConnectionConfig = homeServerConnectionConfig,
+                        loginToken = loginToken,
+                        deviceName = initialDeviceName,
+                        deviceId = deviceId
+                )
+        )
+    }
+
     private fun buildAuthAPI(homeServerConnectionConfig: HomeServerConnectionConfig): AuthAPI {
         val retrofit = retrofitFactory.create(buildClient(homeServerConnectionConfig), homeServerConnectionConfig.homeServerUriBase.toString())
         return retrofit.create(AuthAPI::class.java)
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/LoginParams.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/LoginParams.kt
index ea8578ed8c..8646752083 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/LoginParams.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/LoginParams.kt
@@ -18,4 +18,6 @@ package org.matrix.android.sdk.internal.auth.data
 
 internal interface LoginParams {
     val type: String
+    val deviceDisplayName: String?
+    val deviceId: String?
 }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/PasswordLoginParams.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/PasswordLoginParams.kt
index 5f0a2298cb..062b2466e5 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/PasswordLoginParams.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/PasswordLoginParams.kt
@@ -30,8 +30,8 @@ internal data class PasswordLoginParams(
         @Json(name = "identifier") val identifier: Map<String, String>,
         @Json(name = "password") val password: String,
         @Json(name = "type") override val type: String,
-        @Json(name = "initial_device_display_name") val deviceDisplayName: String?,
-        @Json(name = "device_id") val deviceId: String?
+        @Json(name = "initial_device_display_name") override val deviceDisplayName: String?,
+        @Json(name = "device_id") override val deviceId: String?
 ) : LoginParams {
 
     companion object {
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/TokenLoginParams.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/TokenLoginParams.kt
index 0c6fb45e78..22cc185fa7 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/TokenLoginParams.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/TokenLoginParams.kt
@@ -23,5 +23,7 @@ import org.matrix.android.sdk.api.auth.data.LoginFlowTypes
 @JsonClass(generateAdapter = true)
 internal data class TokenLoginParams(
         @Json(name = "type") override val type: String = LoginFlowTypes.TOKEN,
-        @Json(name = "token") val token: String
+        @Json(name = "token") val token: String,
+        @Json(name = "initial_device_display_name") override val deviceDisplayName: String?,
+        @Json(name = "device_id") override val deviceId: String?
 ) : LoginParams
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/login/QrLoginTokenTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/login/QrLoginTokenTask.kt
new file mode 100644
index 0000000000..477719f607
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/login/QrLoginTokenTask.kt
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2022 The Matrix.org Foundation C.I.C.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.matrix.android.sdk.internal.auth.login
+
+import dagger.Lazy
+import okhttp3.OkHttpClient
+import org.matrix.android.sdk.api.auth.LoginType
+import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig
+import org.matrix.android.sdk.api.failure.Failure
+import org.matrix.android.sdk.api.session.Session
+import org.matrix.android.sdk.internal.auth.AuthAPI
+import org.matrix.android.sdk.internal.auth.SessionCreator
+import org.matrix.android.sdk.internal.auth.data.TokenLoginParams
+import org.matrix.android.sdk.internal.di.Unauthenticated
+import org.matrix.android.sdk.internal.network.RetrofitFactory
+import org.matrix.android.sdk.internal.network.executeRequest
+import org.matrix.android.sdk.internal.network.httpclient.addSocketFactory
+import org.matrix.android.sdk.internal.network.ssl.UnrecognizedCertificateException
+import org.matrix.android.sdk.internal.task.Task
+import javax.inject.Inject
+
+internal interface QrLoginTokenTask : Task<QrLoginTokenTask.Params, Session> {
+    data class Params(
+            val homeServerConnectionConfig: HomeServerConnectionConfig,
+            val loginToken: String,
+            val deviceName: String?,
+            val deviceId: String?
+    )
+}
+
+internal class DefaultQrLoginTokenTask @Inject constructor(
+        @Unauthenticated
+        private val okHttpClient: Lazy<OkHttpClient>,
+        private val retrofitFactory: RetrofitFactory,
+        private val sessionCreator: SessionCreator,
+) : QrLoginTokenTask {
+
+    override suspend fun execute(params: QrLoginTokenTask.Params): Session {
+        val client = buildClient(params.homeServerConnectionConfig)
+        val homeServerUrl = params.homeServerConnectionConfig.homeServerUriBase.toString()
+
+        val authAPI = retrofitFactory.create(client, homeServerUrl)
+                .create(AuthAPI::class.java)
+
+        val loginParams = TokenLoginParams(
+                token = params.loginToken,
+                deviceDisplayName = params.deviceName,
+                deviceId = params.deviceId
+        )
+
+        val credentials = try {
+            executeRequest(null) {
+                authAPI.login(loginParams)
+            }
+        } catch (throwable: Throwable) {
+            throw when (throwable) {
+                is UnrecognizedCertificateException -> Failure.UnrecognizedCertificateFailure(
+                        homeServerUrl,
+                        throwable.fingerprint
+                )
+                else -> throwable
+            }
+        }
+
+        return sessionCreator.createSession(credentials, params.homeServerConnectionConfig, LoginType.QR)
+    }
+
+    private fun buildClient(homeServerConnectionConfig: HomeServerConnectionConfig): OkHttpClient {
+        return okHttpClient.get()
+                .newBuilder()
+                .addSocketFactory(homeServerConnectionConfig)
+                .build()
+    }
+}

From 282825db7952fd6516a3b1531fb709ca6831cd78 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Thu, 13 Oct 2022 15:18:31 +0100
Subject: [PATCH 19/93] Changelog

---
 changelog.d/7358.sdk | 1 +
 1 file changed, 1 insertion(+)
 create mode 100644 changelog.d/7358.sdk

diff --git a/changelog.d/7358.sdk b/changelog.d/7358.sdk
new file mode 100644
index 0000000000..3d17076a44
--- /dev/null
+++ b/changelog.d/7358.sdk
@@ -0,0 +1 @@
+Add support for `m.login.token` auth during QR code based sign in

From d0898a2b89e7ff0c2858fece93656a2b0427a19e Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Thu, 13 Oct 2022 15:19:39 +0100
Subject: [PATCH 20/93] Linting

---
 .../sdk/internal/auth/DefaultAuthenticationService.kt      | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt
index 5b6f0686c4..773f2118b1 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt
@@ -422,7 +422,12 @@ internal class DefaultAuthenticationService @Inject constructor(
         }
     }
 
-    override suspend fun loginUsingQrLoginToken(homeServerConnectionConfig: HomeServerConnectionConfig, loginToken: String, initialDeviceName: String, deviceId: String?): Session {
+    override suspend fun loginUsingQrLoginToken(
+            homeServerConnectionConfig: HomeServerConnectionConfig,
+            loginToken: String,
+            initialDeviceName: String,
+            deviceId: String?,
+    ): Session {
         return loginTokenAuthTask.execute(
                 QrLoginTokenTask.Params(
                         homeServerConnectionConfig = homeServerConnectionConfig,

From 3b3e11e5681874a7540e0885ee44da1885d29e30 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Tue, 11 Oct 2022 23:32:49 +0100
Subject: [PATCH 21/93] Retry scanning if not a QR code


From de611ca81a1c9f16ded4b6b8d3d48b12b7edd68e Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Tue, 11 Oct 2022 23:33:30 +0100
Subject: [PATCH 22/93] Implementations of MSC3886 and MSC3903


From bfab07d716bddf91b2e7cab707dd2fe8e5ffb982 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Tue, 11 Oct 2022 23:34:05 +0100
Subject: [PATCH 23/93] Partial implementation of QR login logic

---
 .../features/login/qr/QrCodeLoginViewModel.kt | 27 ++++++++++++++-----
 .../voicebroadcast/VoiceBroadcastConstants.kt | 20 --------------
 2 files changed, 21 insertions(+), 26 deletions(-)
 delete mode 100644 vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastConstants.kt

diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
index da3348653c..05344f0a67 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
@@ -59,13 +59,28 @@ class QrCodeLoginViewModel @AssistedInject constructor(
     }
 
     private fun handleOnQrCodeScanned(action: QrCodeLoginAction.OnQrCodeScanned) {
-        if (isValidQrCode(action.qrCode)) {
-            setState {
-                copy(
-                        connectionStatus = QrCodeLoginConnectionStatus.ConnectingToDevice
-                )
+        Timber.tag(TAG).d("Scanned code: ${action.qrCode}")
+
+        val rendezvous = Rendezvous.buildChannelFromCode(action.qrCode) { reason ->
+            Timber.tag(TAG).d("Rendezvous cancelled: $reason")
+            onFailed(reason)
+        }
+
+        setState {
+            copy(
+                    connectionStatus = QrCodeLoginConnectionStatus.ConnectingToDevice
+            )
+        }
+
+        _viewEvents.post(QrCodeLoginViewEvents.NavigateToStatusScreen)
+
+        viewModelScope.launch(Dispatchers.IO) {
+            val confirmationCode = rendezvous.startAfterScanningCode()
+            Timber.tag(TAG).i("Established secure channel with checksum: $confirmationCode")
+            confirmationCode ?.let {
+                onConnectionEstablished(it)
             }
-            _viewEvents.post(QrCodeLoginViewEvents.NavigateToStatusScreen)
+            rendezvous.completeOnNewDevice()
         }
 
         // TODO. UI test purpose. Fixme remove!
diff --git a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastConstants.kt b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastConstants.kt
deleted file mode 100644
index d7d74b08e9..0000000000
--- a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastConstants.kt
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Copyright (c) 2022 New Vector Ltd
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package im.vector.app.features.voicebroadcast
-
-/** Voice Broadcast State Event. */
-const val STATE_ROOM_VOICE_BROADCAST_INFO = "io.element.voice_broadcast_info"

From ef574bd82fbc5e33f0d4da51958158de9773a45c Mon Sep 17 00:00:00 2001
From: Onuray Sahin <onurays@element.io>
Date: Wed, 12 Oct 2022 14:32:09 +0300
Subject: [PATCH 24/93] Merge branch 'develop' into
 feature/ons/qr_code_login_ui

# Conflicts:
#	library/ui-strings/src/main/res/values/strings.xml
#	library/ui-styles/src/main/res/values/stylable_sessions_list_header_view.xml
#	vector-app/src/debug/java/im/vector/app/features/debug/features/DebugFeaturesStateFactory.kt
#	vector-app/src/debug/java/im/vector/app/features/debug/features/DebugVectorFeatures.kt
#	vector/src/main/java/im/vector/app/core/di/MavericksViewModelModule.kt
#	vector/src/main/java/im/vector/app/features/VectorFeatures.kt
#	vector/src/main/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesFragment.kt
#	vector/src/main/java/im/vector/app/features/settings/devices/v2/list/SessionsListHeaderView.kt
#	vector/src/main/res/layout/fragment_other_sessions.xml
#	vector/src/main/res/layout/fragment_settings_devices.xml
---
 .../voicebroadcast/VoiceBroadcastConstants.kt | 20 +++++++++++++++++++
 1 file changed, 20 insertions(+)
 create mode 100644 vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastConstants.kt

diff --git a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastConstants.kt b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastConstants.kt
new file mode 100644
index 0000000000..d7d74b08e9
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastConstants.kt
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2022 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.app.features.voicebroadcast
+
+/** Voice Broadcast State Event. */
+const val STATE_ROOM_VOICE_BROADCAST_INFO = "io.element.voice_broadcast_info"

From b03240330d9905fc422a2fd3d166376c5d80f3a7 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Wed, 12 Oct 2022 13:08:01 +0100
Subject: [PATCH 25/93] Only do completeOnNewDevice if we received a
 confirmation code

---
 .../im/vector/app/features/login/qr/QrCodeLoginViewModel.kt     | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
index 05344f0a67..877d32a11d 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
@@ -79,8 +79,8 @@ class QrCodeLoginViewModel @AssistedInject constructor(
             Timber.tag(TAG).i("Established secure channel with checksum: $confirmationCode")
             confirmationCode ?.let {
                 onConnectionEstablished(it)
+                rendezvous.completeOnNewDevice()
             }
-            rendezvous.completeOnNewDevice()
         }
 
         // TODO. UI test purpose. Fixme remove!

From 1e60f3c25b457e2cb40e5c8770e673c25f2fa941 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Thu, 13 Oct 2022 15:35:45 +0100
Subject: [PATCH 26/93] Make initialDeviceName optional

---
 .../org/matrix/android/sdk/api/auth/AuthenticationService.kt    | 2 +-
 .../android/sdk/internal/auth/DefaultAuthenticationService.kt   | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/AuthenticationService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/AuthenticationService.kt
index 6bb47dda5b..9fde6d9326 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/AuthenticationService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/AuthenticationService.kt
@@ -140,7 +140,7 @@ interface AuthenticationService {
     suspend fun loginUsingQrLoginToken(
             homeServerConnectionConfig: HomeServerConnectionConfig,
             loginToken: String,
-            initialDeviceName: String,
+            initialDeviceName: String?,
             deviceId: String? = null
     ): Session
 }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt
index 773f2118b1..7aeeacd10b 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt
@@ -425,7 +425,7 @@ internal class DefaultAuthenticationService @Inject constructor(
     override suspend fun loginUsingQrLoginToken(
             homeServerConnectionConfig: HomeServerConnectionConfig,
             loginToken: String,
-            initialDeviceName: String,
+            initialDeviceName: String?,
             deviceId: String?,
     ): Session {
         return loginTokenAuthTask.execute(

From e2f3dde5c1579c88521ae7b2e16b725f7e7c48e5 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Thu, 13 Oct 2022 15:58:19 +0100
Subject: [PATCH 27/93] Use correct var name

---
 .../android/sdk/internal/auth/DefaultAuthenticationService.kt | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt
index 7aeeacd10b..5449c0a735 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt
@@ -66,7 +66,7 @@ internal class DefaultAuthenticationService @Inject constructor(
         private val pendingSessionStore: PendingSessionStore,
         private val getWellknownTask: GetWellknownTask,
         private val directLoginTask: DirectLoginTask,
-        private val loginTokenAuthTask: QrLoginTokenTask
+        private val qrLoginTokenTask: QrLoginTokenTask
 ) : AuthenticationService {
 
     private var pendingSessionData: PendingSessionData? = pendingSessionStore.getPendingSessionData()
@@ -428,7 +428,7 @@ internal class DefaultAuthenticationService @Inject constructor(
             initialDeviceName: String?,
             deviceId: String?,
     ): Session {
-        return loginTokenAuthTask.execute(
+        return qrLoginTokenTask.execute(
                 QrLoginTokenTask.Params(
                         homeServerConnectionConfig = homeServerConnectionConfig,
                         loginToken = loginToken,

From ca7a6efadee7430c4ac431a7cb89733db7486cc6 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Thu, 13 Oct 2022 16:02:57 +0100
Subject: [PATCH 28/93] Add missing binding

---
 .../java/org/matrix/android/sdk/internal/auth/AuthModule.kt  | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/AuthModule.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/AuthModule.kt
index 463692e574..b1f65194f1 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/AuthModule.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/AuthModule.kt
@@ -29,7 +29,9 @@ import org.matrix.android.sdk.internal.auth.db.AuthRealmModule
 import org.matrix.android.sdk.internal.auth.db.RealmPendingSessionStore
 import org.matrix.android.sdk.internal.auth.db.RealmSessionParamsStore
 import org.matrix.android.sdk.internal.auth.login.DefaultDirectLoginTask
+import org.matrix.android.sdk.internal.auth.login.DefaultQrLoginTokenTask
 import org.matrix.android.sdk.internal.auth.login.DirectLoginTask
+import org.matrix.android.sdk.internal.auth.login.QrLoginTokenTask
 import org.matrix.android.sdk.internal.database.RealmKeysUtils
 import org.matrix.android.sdk.internal.di.AuthDatabase
 import org.matrix.android.sdk.internal.legacy.DefaultLegacySessionImporter
@@ -94,4 +96,7 @@ internal abstract class AuthModule {
 
     @Binds
     abstract fun bindHomeServerHistoryService(service: DefaultHomeServerHistoryService): HomeServerHistoryService
+
+    @Binds
+    abstract fun bindQrLoginTokenTask(task: DefaultQrLoginTokenTask): QrLoginTokenTask
 }

From ac80ae5632c4543b199189b90662ed4c971373eb Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Thu, 13 Oct 2022 16:08:50 +0100
Subject: [PATCH 29/93] Set default value for optional params

---
 .../matrix/android/sdk/internal/auth/data/TokenLoginParams.kt | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/TokenLoginParams.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/TokenLoginParams.kt
index 22cc185fa7..52045a1d7a 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/TokenLoginParams.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/TokenLoginParams.kt
@@ -24,6 +24,6 @@ import org.matrix.android.sdk.api.auth.data.LoginFlowTypes
 internal data class TokenLoginParams(
         @Json(name = "type") override val type: String = LoginFlowTypes.TOKEN,
         @Json(name = "token") val token: String,
-        @Json(name = "initial_device_display_name") override val deviceDisplayName: String?,
-        @Json(name = "device_id") override val deviceId: String?
+        @Json(name = "initial_device_display_name") override val deviceDisplayName: String? = null,
+        @Json(name = "device_id") override val deviceId: String? = null
 ) : LoginParams

From bc0843eddf1712983dd4990ac6835dd68fc0bbb5 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Thu, 13 Oct 2022 16:11:41 +0100
Subject: [PATCH 30/93] Another default value fix

---
 .../org/matrix/android/sdk/api/auth/AuthenticationService.kt    | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/AuthenticationService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/AuthenticationService.kt
index 9fde6d9326..1cc53b311c 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/AuthenticationService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/AuthenticationService.kt
@@ -140,7 +140,7 @@ interface AuthenticationService {
     suspend fun loginUsingQrLoginToken(
             homeServerConnectionConfig: HomeServerConnectionConfig,
             loginToken: String,
-            initialDeviceName: String?,
+            initialDeviceName: String? = null,
             deviceId: String? = null
     ): Session
 }

From 991eeb1de6c29515aa43d4c551b8cf12580f5f53 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Thu, 13 Oct 2022 16:28:47 +0100
Subject: [PATCH 31/93] Map for soft logout

---
 .../im/vector/app/features/signout/soft/SoftLogoutController.kt  | 1 +
 1 file changed, 1 insertion(+)

diff --git a/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutController.kt b/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutController.kt
index b1a240e942..a1ed27df1d 100644
--- a/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutController.kt
+++ b/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutController.kt
@@ -152,6 +152,7 @@ class SoftLogoutController @Inject constructor(
             LoginType.SSO -> buildLoginSSOForm()
             LoginType.DIRECT,
             LoginType.CUSTOM,
+            LoginType.QR,
             LoginType.UNSUPPORTED -> buildLoginUnsupportedForm()
             LoginType.UNKNOWN -> Unit
         }

From 9a72d6529b0a76bc83c28ed217f79c8a69c6acc4 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Thu, 13 Oct 2022 21:08:45 +0100
Subject: [PATCH 32/93] Support for navigation to home screen

---
 .../im/vector/app/features/login/qr/QrCodeLoginActivity.kt | 7 +++++++
 .../vector/app/features/login/qr/QrCodeLoginViewEvents.kt  | 1 +
 2 files changed, 8 insertions(+)

diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginActivity.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginActivity.kt
index 042f885231..fac31ce4e3 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginActivity.kt
@@ -25,6 +25,7 @@ import com.airbnb.mvrx.viewModel
 import dagger.hilt.android.AndroidEntryPoint
 import im.vector.app.core.extensions.addFragment
 import im.vector.app.core.platform.SimpleFragmentActivity
+import im.vector.app.features.home.HomeActivity
 import org.matrix.android.sdk.api.extensions.orFalse
 import timber.log.Timber
 
@@ -74,6 +75,7 @@ class QrCodeLoginActivity : SimpleFragmentActivity() {
             when (it) {
                 QrCodeLoginViewEvents.NavigateToStatusScreen -> handleNavigateToStatusScreen()
                 QrCodeLoginViewEvents.NavigateToShowQrCodeScreen -> handleNavigateToShowQrCodeScreen()
+                QrCodeLoginViewEvents.NavigateToHomeScreen -> handleNavigateToHomeScreen()
             }
         }
     }
@@ -94,6 +96,11 @@ class QrCodeLoginActivity : SimpleFragmentActivity() {
         )
     }
 
+    private fun handleNavigateToHomeScreen() {
+        val intent = HomeActivity.newIntent(this, firstStartMainActivity = false, existingSession = true)
+        startActivity(intent)
+    }
+
     companion object {
 
         private const val FRAGMENT_QR_CODE_INSTRUCTIONS_TAG = "FRAGMENT_QR_CODE_INSTRUCTIONS_TAG"
diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewEvents.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewEvents.kt
index dc258408e7..0f282fee38 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewEvents.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewEvents.kt
@@ -21,4 +21,5 @@ import im.vector.app.core.platform.VectorViewEvents
 sealed class QrCodeLoginViewEvents : VectorViewEvents {
     object NavigateToStatusScreen : QrCodeLoginViewEvents()
     object NavigateToShowQrCodeScreen : QrCodeLoginViewEvents()
+    object NavigateToHomeScreen : QrCodeLoginViewEvents()
 }

From dd47297dfd4bc774c509d06ccc1316fbb70e6514 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Thu, 13 Oct 2022 21:15:52 +0100
Subject: [PATCH 33/93] QR login + E2EE set up

---
 .../sdk/internal/rendezvous/Rendezvous.kt     | 210 ++++++++++++++++++
 .../features/login/qr/QrCodeLoginViewModel.kt |  62 +++---
 2 files changed, 246 insertions(+), 26 deletions(-)
 create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/Rendezvous.kt

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/Rendezvous.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/Rendezvous.kt
new file mode 100644
index 0000000000..2f85a97c55
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/Rendezvous.kt
@@ -0,0 +1,210 @@
+/*
+ * Copyright (c) 2022 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.matrix.android.sdk.internal.rendezvous
+
+import android.net.Uri
+import com.squareup.moshi.Json
+import com.squareup.moshi.JsonClass
+import org.matrix.android.sdk.api.auth.AuthenticationService
+import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig
+import org.matrix.android.sdk.api.logger.LoggerTag
+import org.matrix.android.sdk.api.session.Session
+import org.matrix.android.sdk.api.session.crypto.crosssigning.DeviceTrustLevel
+import org.matrix.android.sdk.api.session.crypto.crosssigning.KEYBACKUP_SECRET_SSSS_NAME
+import org.matrix.android.sdk.api.session.crypto.crosssigning.MASTER_KEY_SSSS_NAME
+import org.matrix.android.sdk.api.session.crypto.crosssigning.SELF_SIGNING_KEY_SSSS_NAME
+import org.matrix.android.sdk.api.session.crypto.crosssigning.USER_SIGNING_KEY_SSSS_NAME
+import org.matrix.android.sdk.api.util.MatrixJsonParser
+import org.matrix.android.sdk.internal.rendezvous.channels.ECDHRendezvousChannel
+import org.matrix.android.sdk.internal.rendezvous.model.ECDHRendezvousCode
+import org.matrix.android.sdk.internal.rendezvous.model.RendezvousIntent
+import org.matrix.android.sdk.internal.rendezvous.transports.SimpleHttpRendezvousTransport
+import timber.log.Timber
+
+internal enum class PayloadType(val value: String) {
+    @Json(name = "m.login.start") Start("m.login.start"),
+    @Json(name = "m.login.finish") Finish("m.login.finish"),
+    @Json(name = "m.login.progress") Progress("m.login.progress")
+}
+
+@JsonClass(generateAdapter = true)
+internal data class Payload(
+        @Json val type: PayloadType,
+        @Json val intent: RendezvousIntent? = null,
+        @Json val outcome: String? = null,
+        @Json val protocols: List<String>? = null,
+        @Json val protocol: String? = null,
+        @Json val homeserver: String? = null,
+        @Json val login_token: String? = null,
+        @Json val device_id: String? = null,
+        @Json val device_key: String? = null,
+        @Json val verifying_device_id: String? = null,
+        @Json val verifying_device_key: String? = null,
+        @Json val master_key: String? = null
+)
+
+private val TAG = LoggerTag(Rendezvous::class.java.simpleName, LoggerTag.RENDEZVOUS).value
+
+/**
+ * Implementation of MSC3906 to sign in + E2EE set up using a QR code.
+ */
+class Rendezvous(
+        val channel: RendezvousChannel,
+        val theirIntent: RendezvousIntent,
+) {
+    companion object {
+        fun buildChannelFromCode(code: String, onCancelled: (reason: RendezvousFailureReason) -> Unit): Rendezvous {
+            val parsed = MatrixJsonParser.getMoshi().adapter(ECDHRendezvousCode::class.java).fromJson(code) ?: throw RuntimeException("Invalid code")
+
+            val transport = SimpleHttpRendezvousTransport(onCancelled, parsed.rendezvous.transport.uri)
+
+            return Rendezvous(
+                    ECDHRendezvousChannel(transport, parsed.rendezvous.key),
+                    parsed.intent
+            )
+        }
+    }
+
+    private val adapter = MatrixJsonParser.getMoshi().adapter(Payload::class.java)
+    // not yet implemented: RendezvousIntent.RECIPROCATE_LOGIN_ON_EXISTING_DEVICE
+    val ourIntent: RendezvousIntent = RendezvousIntent.LOGIN_ON_NEW_DEVICE
+
+    private suspend fun areIntentsIncompatible(): Boolean {
+        val incompatible = theirIntent == ourIntent
+
+        Timber.tag(TAG).d("ourIntent: $ourIntent, theirIntent: $theirIntent, incompatible: $incompatible")
+
+        if (incompatible) {
+            send(Payload(PayloadType.Finish, intent = ourIntent))
+            val reason = if (ourIntent == RendezvousIntent.LOGIN_ON_NEW_DEVICE) RendezvousFailureReason.OtherDeviceNotSignedIn else RendezvousFailureReason.OtherDeviceAlreadySignedIn
+            channel.cancel(reason)
+        }
+
+        return incompatible
+    }
+
+    suspend fun startAfterScanningCode(): String? {
+        val checksum = channel.connect();
+
+        Timber.tag(TAG).i("Connected to secure channel with checksum: $checksum")
+
+        if (areIntentsIncompatible()) {
+            return null
+        }
+
+        // get protocols
+        Timber.tag(TAG).i("Waiting for protocols");
+        val protocolsResponse = receive()
+
+        if (protocolsResponse?.protocols == null || !protocolsResponse.protocols.contains("login_token")) {
+            send(Payload(PayloadType.Finish, outcome = "unsupported"))
+            Timber.tag(TAG).i("No supported protocol")
+            cancel(RendezvousFailureReason.Unknown)
+            return null
+        }
+
+        send(Payload(PayloadType.Progress, protocol = "login_token"))
+
+        return checksum
+    }
+
+    suspend fun waitForLoginOnNewDevice(authenticationService: AuthenticationService): Session? {
+        Timber.tag(TAG).i("Waiting for login_token");
+
+        val loginToken = receive()
+
+        if (loginToken?.type == PayloadType.Finish) {
+            when (loginToken.outcome) {
+                "declined" -> {
+                    Timber.tag(TAG).i("Login declined by other device")
+                    channel.cancel(RendezvousFailureReason.UserDeclined)
+                    return null
+                }
+                "unsupported" -> {
+                    Timber.tag(TAG).i("Not supported")
+                    channel.cancel(RendezvousFailureReason.HomeserverLacksSupport)
+                    return null
+                }
+            }
+            channel.cancel(RendezvousFailureReason.Unknown)
+            return null
+        }
+
+        val homeserver = loginToken?.homeserver ?: throw RuntimeException("No homeserver returned")
+        val login_token = loginToken.login_token ?: throw RuntimeException("No login token returned")
+
+        Timber.tag(TAG).i("Got login_token: $login_token for $homeserver");
+
+        val hsConfig = HomeServerConnectionConfig(homeServerUri = Uri.parse(homeserver))
+        return authenticationService.loginUsingQrLoginToken(hsConfig, login_token)
+    }
+
+    suspend fun completeVerificationOnNewDevice(session: Session) {
+        val userId = session.myUserId
+        val crypto = session.cryptoService()
+        val deviceId = crypto.getMyDevice().deviceId
+        val deviceKey = crypto.getMyDevice().fingerprint()
+        send(Payload(PayloadType.Progress, outcome = "success", device_id = deviceId, device_key = deviceKey))
+
+        // await confirmation of verification
+
+        val verificationResponse = receive()
+        val verifyingDeviceId = verificationResponse?.verifying_device_id ?: throw RuntimeException("No verifying device id returned")
+        val verifyingDeviceFromServer = crypto.getCryptoDeviceInfo(userId, verifyingDeviceId)
+        if (verifyingDeviceFromServer?.fingerprint() != verificationResponse.verifying_device_key) {
+            Timber.tag(TAG).w("Verifying device $verifyingDeviceId doesn't match: $verifyingDeviceFromServer")
+            return;
+        }
+
+        // set other device as verified
+        Timber.tag(TAG).i("Setting device $verifyingDeviceId as verified");
+        crypto.setDeviceVerification(DeviceTrustLevel(locallyVerified = true, crossSigningVerified = false), userId, verifyingDeviceId)
+
+        // TODO: what do we do with the master key?
+//        verificationResponse.master_key ?.let {
+//            // set master key as trusted
+//            crypto.setDeviceVerification(DeviceTrustLevel(locallyVerified = true, crossSigningVerified = false), userId, it)
+//        }
+
+        // request secrets from the verifying device
+        Timber.tag(TAG).i("Requesting secrets from $verifyingDeviceId")
+
+        session.sharedSecretStorageService() .let {
+            it.requestSecret(MASTER_KEY_SSSS_NAME, verifyingDeviceId)
+            it.requestSecret(SELF_SIGNING_KEY_SSSS_NAME, verifyingDeviceId)
+            it.requestSecret(USER_SIGNING_KEY_SSSS_NAME, verifyingDeviceId)
+            it.requestSecret(KEYBACKUP_SECRET_SSSS_NAME, verifyingDeviceId)
+        }
+    }
+
+    private suspend fun receive(): Payload? {
+        val data = channel.receive()?: return null
+        return adapter.fromJson(data.toString(Charsets.UTF_8))
+    }
+
+    private suspend fun send(payload: Payload) {
+        channel.send(adapter.toJson(payload).toByteArray(Charsets.UTF_8));
+    }
+
+    suspend fun cancel(reason: RendezvousFailureReason) {
+        channel.cancel(reason)
+    }
+
+    suspend fun close() {
+        channel.close()
+    }
+}
diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
index 877d32a11d..d9c30690a7 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
@@ -16,19 +16,32 @@
 
 package im.vector.app.features.login.qr
 
+import android.content.Context
 import com.airbnb.mvrx.MavericksViewModelFactory
 import dagger.assisted.Assisted
 import dagger.assisted.AssistedFactory
 import dagger.assisted.AssistedInject
+import im.vector.app.core.di.ActiveSessionHolder
 import im.vector.app.core.di.MavericksAssistedViewModelFactory
 import im.vector.app.core.di.hiltMavericksViewModelFactory
+import im.vector.app.core.extensions.configureAndStart
 import im.vector.app.core.platform.VectorViewModel
-import kotlinx.coroutines.delay
+import im.vector.app.features.home.HomeActivity
+import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.launch
+import org.matrix.android.sdk.api.auth.AuthenticationService
+import org.matrix.android.sdk.api.session.Session
+import org.matrix.android.sdk.internal.rendezvous.Rendezvous
+import org.matrix.android.sdk.internal.rendezvous.RendezvousFailureReason
+import timber.log.Timber
 
 class QrCodeLoginViewModel @AssistedInject constructor(
         @Assisted private val initialState: QrCodeLoginViewState,
+        private val applicationContext: Context,
+        private val authenticationService: AuthenticationService,
+        private val activeSessionHolder: ActiveSessionHolder,
 ) : VectorViewModel<QrCodeLoginViewState, QrCodeLoginAction, QrCodeLoginViewEvents>(initialState) {
+    val TAG: String = QrCodeLoginViewModel::class.java.simpleName
 
     @AssistedFactory
     interface Factory : MavericksAssistedViewModelFactory<QrCodeLoginViewModel, QrCodeLoginViewState> {
@@ -75,28 +88,32 @@ class QrCodeLoginViewModel @AssistedInject constructor(
         _viewEvents.post(QrCodeLoginViewEvents.NavigateToStatusScreen)
 
         viewModelScope.launch(Dispatchers.IO) {
-            val confirmationCode = rendezvous.startAfterScanningCode()
-            Timber.tag(TAG).i("Established secure channel with checksum: $confirmationCode")
-            confirmationCode ?.let {
-                onConnectionEstablished(it)
-                rendezvous.completeOnNewDevice()
-            }
-        }
+            try {
+                val confirmationCode = rendezvous.startAfterScanningCode()
+                Timber.tag(TAG).i("Established secure channel with checksum: $confirmationCode")
+                confirmationCode?.let {
+                    onConnectionEstablished(it)
+                    val session = rendezvous.waitForLoginOnNewDevice(authenticationService)
+                    onSigningIn()
+                    session?.let {
+                        activeSessionHolder.setActiveSession(session)
+                        authenticationService.reset()
 
-        // TODO. UI test purpose. Fixme remove!
-        viewModelScope.launch {
-            delay(3000)
-            onFailed(QrCodeLoginErrorType.TIMEOUT, true)
-            delay(3000)
-            onConnectionEstablished("1234-ABCD-5678-EFGH")
-            delay(3000)
-            onSigningIn()
-            delay(3000)
-            onFailed(QrCodeLoginErrorType.DEVICE_IS_NOT_SUPPORTED, false)
+                        session.configureAndStart(applicationContext)
+
+                        rendezvous.completeVerificationOnNewDevice(session)
+
+                        _viewEvents.post(QrCodeLoginViewEvents.NavigateToHomeScreen)
+                    }
+                }
+            } catch (failure: Throwable) {
+                Timber.tag(TAG).e(failure, "Error occurred during sign in")
+                onFailed(RendezvousFailureReason.Unknown)
+            }
         }
     }
 
-    private fun onFailed(errorType: QrCodeLoginErrorType, canTryAgain: Boolean) {
+    private fun onFailed(reason: RendezvousFailureReason) {
         setState {
             copy(
                     connectionStatus = QrCodeLoginConnectionStatus.Failed(errorType, canTryAgain)
@@ -121,13 +138,6 @@ class QrCodeLoginViewModel @AssistedInject constructor(
         }
     }
 
-    /**
-     * TODO. UI test purpose. Fixme accordingly.
-     */
-    private fun isValidQrCode(qrCode: String): Boolean {
-        return qrCode.startsWith("http")
-    }
-
     /**
      * TODO. UI test purpose. Fixme accordingly.
      */

From 7bc0bd3b57d1db71742051fe65d6b1003ad2037e Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Thu, 13 Oct 2022 21:26:22 +0100
Subject: [PATCH 34/93] Reduce logging

---
 .../channels/ECDHRendezvousChannel.kt         | 218 ++++++++++++++++++
 .../SimpleHttpRendezvousTransport.kt          | 185 +++++++++++++++
 2 files changed, 403 insertions(+)
 create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/channels/ECDHRendezvousChannel.kt
 create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/transports/SimpleHttpRendezvousTransport.kt

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/channels/ECDHRendezvousChannel.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/channels/ECDHRendezvousChannel.kt
new file mode 100644
index 0000000000..cced29aab4
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/channels/ECDHRendezvousChannel.kt
@@ -0,0 +1,218 @@
+/*
+ * Copyright (c) 2022 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.matrix.android.sdk.internal.rendezvous.channels
+
+import android.util.Base64
+import com.squareup.moshi.Json
+import com.squareup.moshi.JsonClass
+import okhttp3.MediaType.Companion.toMediaType
+import org.matrix.android.sdk.api.logger.LoggerTag
+import org.matrix.android.sdk.api.util.MatrixJsonParser
+import org.matrix.android.sdk.internal.extensions.toUnsignedInt
+import org.matrix.android.sdk.internal.rendezvous.RendezvousFailureReason
+import org.matrix.android.sdk.internal.rendezvous.RendezvousChannel
+import org.matrix.android.sdk.internal.rendezvous.RendezvousTransport
+import org.matrix.android.sdk.internal.rendezvous.model.ECDHRendezvous
+import org.matrix.android.sdk.internal.rendezvous.model.ECDHRendezvousCode
+import org.matrix.android.sdk.internal.rendezvous.model.RendezvousError
+import org.matrix.android.sdk.internal.rendezvous.model.RendezvousIntent
+import org.matrix.android.sdk.internal.rendezvous.model.SecureRendezvousChannelAlgorithm
+import org.matrix.android.sdk.internal.rendezvous.transports.SimpleHttpRendezvousTransportDetails
+import org.matrix.olm.OlmSAS
+import timber.log.Timber
+import java.security.SecureRandom
+import java.util.LinkedList
+import javax.crypto.Cipher
+import javax.crypto.spec.IvParameterSpec
+import javax.crypto.spec.SecretKeySpec
+
+@JsonClass(generateAdapter = true)
+data class ECDHPayload(
+        @Json val algorithm: SecureRendezvousChannelAlgorithm? = null,
+        @Json val key: String? = null,
+        @Json val ciphertext: String? = null,
+        @Json val iv: String? = null
+)
+
+private val TAG = LoggerTag(ECDHRendezvousChannel::class.java.simpleName, LoggerTag.RENDEZVOUS).value
+
+fun getDecimalCodeRepresentation(byteArray: ByteArray): String {
+    val b0 = byteArray[0].toUnsignedInt() // need unsigned byte
+    val b1 = byteArray[1].toUnsignedInt() // need unsigned byte
+    val b2 = byteArray[2].toUnsignedInt() // need unsigned byte
+    val b3 = byteArray[3].toUnsignedInt() // need unsigned byte
+    val b4 = byteArray[4].toUnsignedInt() // need unsigned byte
+    // (B0 << 5 | B1 >> 3) + 1000
+    val first = (b0.shl(5) or b1.shr(3)) + 1000
+    // ((B1 & 0x7) << 10 | B2 << 2 | B3 >> 6) + 1000
+    val second = ((b1 and 0x7).shl(10) or b2.shl(2) or b3.shr(6)) + 1000
+    // ((B3 & 0x3f) << 7 | B4 >> 1) + 1000
+    val third = ((b3 and 0x3f).shl(7) or b4.shr(1)) + 1000
+    return "$first-$second-$third"
+}
+
+const val ALGORITHM_SPEC = "AES/GCM/NoPadding"
+const val KEY_SPEC = "AES"
+
+/**
+ *  Implements X25519 ECDH key agreement and AES-256-GCM encryption channel as per MSC3903:
+ *  https://github.com/matrix-org/matrix-spec-proposals/pull/3903
+ */
+class ECDHRendezvousChannel(override var transport: RendezvousTransport, theirPublicKeyBase64: String?): RendezvousChannel {
+    private var olmSAS: OlmSAS?
+    private val ourPublicKey: ByteArray
+    private val ecdhAdapter = MatrixJsonParser.getMoshi().adapter(ECDHPayload::class.java)
+    private var theirPublicKey: ByteArray? = null
+    private var aesKey: ByteArray? = null
+
+    init {
+        theirPublicKeyBase64 ?.let {
+            theirPublicKey = Base64.decode(it, Base64.NO_WRAP)
+        }
+        olmSAS = OlmSAS()
+        ourPublicKey = Base64.decode(olmSAS!!.publicKey, Base64.NO_WRAP)
+    }
+
+    override suspend fun connect(): String {
+        if (olmSAS == null) {
+            throw RuntimeException("Channel closed")
+        }
+        val isInitiator = theirPublicKey == null
+
+        if (isInitiator) {
+//            Timber.tag(TAG).i("Waiting for other device to send their public key")
+            val res = this.receiveAsPayload() ?: throw RuntimeException("No reply from other device")
+
+            if (res.key == null) {
+                throw RendezvousError(
+                        "Unsupported algorithm: ${res.algorithm}",
+                        RendezvousFailureReason.UnsupportedAlgorithm,
+                )
+            }
+            theirPublicKey = Base64.decode(res.key, Base64.NO_WRAP)
+        } else {
+            // send our public key unencrypted
+//            Timber.tag(TAG).i("Sending public key")
+            send(ECDHPayload(
+                    algorithm = SecureRendezvousChannelAlgorithm.ECDH_V1,
+                    key = Base64.encodeToString(ourPublicKey, Base64.NO_WRAP)
+            ))
+        }
+
+        olmSAS!!.setTheirPublicKey(Base64.encodeToString(theirPublicKey, Base64.NO_WRAP))
+
+        val initiatorKey = Base64.encodeToString(if (isInitiator) ourPublicKey else theirPublicKey, Base64.NO_WRAP)
+        val recipientKey = Base64.encodeToString(if (isInitiator) theirPublicKey else ourPublicKey, Base64.NO_WRAP)
+        val aesInfo = "${SecureRendezvousChannelAlgorithm.ECDH_V1.value}|$initiatorKey|$recipientKey"
+
+        aesKey = olmSAS!!.generateShortCode(aesInfo, 32)
+
+//        Timber.tag(TAG).i("Our public key: ${Base64.encodeToString(ourPublicKey, Base64.NO_WRAP)}")
+//        Timber.tag(TAG).i("Their public key: ${Base64.encodeToString(theirPublicKey, Base64.NO_WRAP)}")
+//        Timber.tag(TAG).i("AES info: $aesInfo")
+//        Timber.tag(TAG).i("AES key: ${Base64.encodeToString(aesKey, Base64.NO_WRAP)}")
+
+        val rawChecksum = olmSAS!!.generateShortCode(aesInfo, 5)
+        return getDecimalCodeRepresentation(rawChecksum)
+    }
+
+    private suspend fun send(payload: ECDHPayload) {
+        transport.send("application/json".toMediaType(), ecdhAdapter.toJson(payload).toByteArray(Charsets.UTF_8))
+    }
+
+    override suspend fun send(data: ByteArray) {
+        if (aesKey == null) {
+            throw RuntimeException("Shared secret not established")
+        }
+        send(encrypt(data))
+    }
+
+    private suspend fun receiveAsPayload(): ECDHPayload? {
+        transport.receive()?.toString(Charsets.UTF_8) ?.let {
+            return ecdhAdapter.fromJson(it)
+        } ?: return null
+    }
+
+    override suspend fun receive(): ByteArray? {
+        if (aesKey == null) {
+            throw RuntimeException("Shared secret not established")
+        }
+        val payload = receiveAsPayload() ?: return null
+        return decrypt(payload)
+    }
+
+    override suspend fun generateCode(intent: RendezvousIntent): ECDHRendezvousCode {
+        return ECDHRendezvousCode(
+                intent,
+                rendezvous = ECDHRendezvous(
+                        transport.details() as SimpleHttpRendezvousTransportDetails,
+                        SecureRendezvousChannelAlgorithm.ECDH_V1,
+                        key = Base64.encodeToString(ourPublicKey, Base64.NO_WRAP)
+                )
+        )
+    }
+
+    override suspend fun cancel(reason: RendezvousFailureReason) {
+        try {
+            transport.cancel(reason)
+        } finally {
+            close()
+        }
+    }
+
+    override suspend fun close() {
+        olmSAS?.releaseSas()
+        olmSAS = null
+    }
+
+    private fun encrypt(plainText: ByteArray): ECDHPayload {
+//        Timber.tag(TAG).d("Encrypting: ${plainText.toString(Charsets.UTF_8)}")
+        val iv = ByteArray(16)
+        SecureRandom().nextBytes(iv)
+
+        val cipherText = LinkedList<Byte>()
+
+        val encryptCipher = Cipher.getInstance(ALGORITHM_SPEC)
+        val secretKeySpec = SecretKeySpec(aesKey, KEY_SPEC)
+        val ivParameterSpec = IvParameterSpec(iv)
+        encryptCipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec)
+        cipherText.addAll(encryptCipher.update(plainText).toList())
+        cipherText.addAll(encryptCipher.doFinal().toList())
+
+        return ECDHPayload(
+                ciphertext = Base64.encodeToString(cipherText.toByteArray(), Base64.NO_WRAP),
+                iv = Base64.encodeToString(iv, Base64.NO_WRAP)
+        )
+    }
+
+    private fun decrypt(payload: ECDHPayload): ByteArray {
+        val iv = Base64.decode(payload.iv, Base64.NO_WRAP)
+        val encryptCipher = Cipher.getInstance(ALGORITHM_SPEC)
+        val secretKeySpec = SecretKeySpec(aesKey, KEY_SPEC)
+        val ivParameterSpec = IvParameterSpec(iv)
+        encryptCipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec)
+
+        val plainText = LinkedList<Byte>()
+        plainText.addAll(encryptCipher.update(Base64.decode(payload.ciphertext, Base64.NO_WRAP)).toList())
+        plainText.addAll(encryptCipher.doFinal().toList())
+
+        val plainTextBytes = plainText.toByteArray()
+
+//        Timber.tag(TAG).d("Decrypted: ${plainTextBytes.toString(Charsets.UTF_8)}")
+        return plainTextBytes
+    }
+}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/transports/SimpleHttpRendezvousTransport.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/transports/SimpleHttpRendezvousTransport.kt
new file mode 100644
index 0000000000..cc4346d55e
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/transports/SimpleHttpRendezvousTransport.kt
@@ -0,0 +1,185 @@
+/*
+ * Copyright (c) 2022 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.matrix.android.sdk.internal.rendezvous.transports
+
+import com.squareup.moshi.Json
+import com.squareup.moshi.JsonClass
+import kotlinx.coroutines.delay
+import okhttp3.MediaType
+import okhttp3.Request
+import okhttp3.RequestBody.Companion.toRequestBody
+import org.matrix.android.sdk.api.logger.LoggerTag
+import org.matrix.android.sdk.internal.rendezvous.RendezvousFailureReason
+import org.matrix.android.sdk.internal.rendezvous.RendezvousTransport
+import org.matrix.android.sdk.internal.rendezvous.model.RendezvousTransportDetails
+import org.matrix.android.sdk.internal.rendezvous.model.RendezvousTransportType
+import timber.log.Timber
+import java.text.SimpleDateFormat
+import java.util.Date
+
+private val TAG = LoggerTag(SimpleHttpRendezvousTransport::class.java.simpleName, LoggerTag.RENDEZVOUS).value
+
+@JsonClass(generateAdapter = true)
+data class SimpleHttpRendezvousTransportDetails(
+        @Json val uri: String
+): RendezvousTransportDetails(type = RendezvousTransportType.MSC3886_SIMPLE_HTTP_V1)
+
+/**
+ * Implementation of the Simple HTTP transport MSC3886: https://github.com/matrix-org/matrix-spec-proposals/pull/3886
+ */
+class SimpleHttpRendezvousTransport(override var onCancelled: ((reason: RendezvousFailureReason) -> Unit)?, rendezvousUri: String?) : RendezvousTransport {
+    override var ready = false
+    private var cancelled = false
+    private var uri: String?
+    private var etag: String? = null
+    private var expiresAt: Date? = null
+
+    init {
+        uri = rendezvousUri
+    }
+
+    override suspend fun details(): RendezvousTransportDetails {
+        val uri = uri ?: throw IllegalStateException("Rendezvous not set up")
+
+        return SimpleHttpRendezvousTransportDetails(uri)
+    }
+
+    override suspend fun send(contentType: MediaType, data: ByteArray) {
+        if (cancelled) {
+            return
+        }
+
+        val method = if (uri != null) "PUT" else "POST"
+        // TODO: properly determine endpoint
+        val uri = if (uri != null) uri!! else "https://rendezvous.lab.element.dev"
+
+//        Timber.tag(TAG).i("Sending data: ${data.toString(Charsets.UTF_8)} to $uri")
+
+        val httpClient = okhttp3.OkHttpClient.Builder().build()
+
+        val request = Request.Builder()
+                .url(uri)
+                .method(method, data.toRequestBody())
+                .header("content-type", contentType.toString())
+
+        etag ?.let {
+            request.header("if-match", it)
+        }
+
+        val response = httpClient.newCall(request.build()).execute()
+
+        if (response.code == 404) {
+            cancel(RendezvousFailureReason.Unknown)
+        }
+        etag = response.header("etag")
+
+        Timber.tag(TAG).i("Sent data to $uri new etag $etag")
+
+        if (method == "POST") {
+            val location = response.header("location") ?: throw RuntimeException("No rendezvous URI found in response")
+
+            response.header("expires") ?.let {
+                val format = SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz")
+                expiresAt = format.parse(it)
+            }
+
+            // resolve location header which could be relative or absolute
+            this.uri = response.request.url.toUri().resolve(location).toString()
+            ready = true
+        }
+    }
+
+    override suspend fun receive(): ByteArray? {
+        val uri = uri ?: throw IllegalStateException("Rendezvous not set up")
+        var done = false
+        val httpClient = okhttp3.OkHttpClient.Builder().build()
+        while (!done) {
+            if (cancelled) {
+                return null
+            }
+            Timber.tag(TAG).i("Polling: $uri after etag $etag")
+            val request = Request.Builder()
+                    .url(uri)
+                    .get()
+
+            etag ?.let {
+                request.header("if-none-match", it)
+            }
+
+            val response = httpClient.newCall(request.build()).execute()
+
+            try {
+//                Timber.tag(TAG).d("Received polling response: ${response.code} from $uri")
+
+                if (response.code == 404) {
+                    cancel(RendezvousFailureReason.Unknown)
+                    return null
+                }
+
+                // rely on server expiring the channel rather than checking ourselves
+
+                if (response.header("content-type") != "application/json") {
+                    response.header("etag")?.let {
+                        etag = it
+                    }
+                } else if (response.code == 200) {
+                    response.header("etag")?.let {
+                        etag = it
+                    }
+                    val data = response.body?.bytes()
+//                    Timber.tag(TAG).d("Received data: ${data?.toString(Charsets.UTF_8)} from $uri with etag $etag")
+                    return data
+                }
+
+                done = false
+                delay(1000)
+            } finally {
+                response.close()
+            }
+        }
+
+        return null
+    }
+
+    override suspend fun cancel(reason: RendezvousFailureReason) {
+        var mappedReason = reason
+        Timber.tag(TAG).i("$expiresAt")
+        if (mappedReason == RendezvousFailureReason.Unknown &&
+                expiresAt != null && Date() > expiresAt) {
+            mappedReason = RendezvousFailureReason.Expired
+        }
+
+        cancelled = true
+        ready = false
+        onCancelled ?.let { it(mappedReason) }
+
+        if (mappedReason == RendezvousFailureReason.UserDeclined) {
+            uri ?.let {
+                try {
+                    val httpClient = okhttp3.OkHttpClient.Builder().build()
+                    val request = Request.Builder()
+                            .url(it)
+                            .delete()
+                            .build()
+                    httpClient.newCall(request).execute()
+                } catch (e: Exception) {
+                    Timber.tag(TAG).w(e, "Failed to delete channel")
+                }
+            }
+        }
+    }
+}

From 6399032312c743793fc79ec1f7863c95aec0db1a Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Thu, 13 Oct 2022 22:23:26 +0100
Subject: [PATCH 35/93] Fix bad merge

---
 .../org/matrix/android/sdk/api/auth/AuthenticationService.kt   | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/AuthenticationService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/AuthenticationService.kt
index 1cc53b311c..252c33a8c4 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/AuthenticationService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/AuthenticationService.kt
@@ -130,13 +130,14 @@ interface AuthenticationService {
      * Return true if qr code login is supported by the server, false otherwise.
      */
     suspend fun isQrLoginSupported(homeServerConnectionConfig: HomeServerConnectionConfig): Boolean
+
+    /**
      * Authenticate using m.login.token method during sign in with QR code.
      * @param homeServerConnectionConfig the information about the homeserver and other configuration
      * @param loginToken the m.login.token
      * @param initialDeviceName the initial device name
      * @param deviceId the device id, optional. If not provided or null, the server will generate one.
      */
-
     suspend fun loginUsingQrLoginToken(
             homeServerConnectionConfig: HomeServerConnectionConfig,
             loginToken: String,

From 958ee2d3561fb4b871ba83c6cb3be2ce4fbeb943 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Thu, 13 Oct 2022 22:32:02 +0100
Subject: [PATCH 36/93] Revert "Revert "Retry scanning if not a QR code""

This reverts commit 9429a4f22aabfbff23ffc223c3b090626be553dc.
---
 .../app/features/login/qr/QrCodeLoginInstructionsFragment.kt    | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginInstructionsFragment.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginInstructionsFragment.kt
index ae3ba9574b..17b4ff2409 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginInstructionsFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginInstructionsFragment.kt
@@ -83,6 +83,7 @@ class QrCodeLoginInstructionsFragment : VectorBaseFragment<FragmentQrCodeLoginIn
         if (activityResult.resultCode == Activity.RESULT_OK) {
             val scannedQrCode = QrCodeScannerActivity.getResultText(activityResult.data)
             val wasQrCode = QrCodeScannerActivity.getResultIsQrCode(activityResult.data)
+            Timber.d("Scanned QR code: $scannedQrCode $wasQrCode")
 
             if (wasQrCode && !scannedQrCode.isNullOrBlank()) {
                 onQrCodeScanned(scannedQrCode)
@@ -98,5 +99,6 @@ class QrCodeLoginInstructionsFragment : VectorBaseFragment<FragmentQrCodeLoginIn
 
     private fun onQrCodeScannerFailed() {
         Timber.d("QrCodeLoginInstructionsFragment.onQrCodeScannerFailed")
+        QrCodeScannerActivity.startForResult(requireActivity(), scanActivityResultLauncher)
     }
 }

From 370652c04b684b5436d3e03a94ed41913efc6523 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Thu, 13 Oct 2022 22:37:19 +0100
Subject: [PATCH 37/93] Revert "Revert "Implementations of MSC3886 and
 MSC3903""

This reverts commit 489dfd73546a78246b1f18faf97e7a36bb4e0241.
---
 .../android/sdk/api/logger/LoggerTag.kt       |  1 +
 .../internal/rendezvous/RendezvousChannel.kt  | 45 +++++++++++++++++++
 .../rendezvous/RendezvousFailureReason.kt     | 31 +++++++++++++
 .../rendezvous/RendezvousTransport.kt         | 29 ++++++++++++
 .../rendezvous/model/ECDHRendezvous.kt        | 34 ++++++++++++++
 .../rendezvous/model/EmbeddedRendezvous.kt    | 26 +++++++++++
 .../rendezvous/model/RendezvousError.kt       | 22 +++++++++
 .../rendezvous/model/RendezvousIntent.kt      | 24 ++++++++++
 .../model/RendezvousTransportDetails.kt       | 25 +++++++++++
 .../model/RendezvousTransportType.kt          | 23 ++++++++++
 .../model/SecureRendezvousChannelAlgorithm.kt | 23 ++++++++++
 11 files changed, 283 insertions(+)
 create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousChannel.kt
 create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousFailureReason.kt
 create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousTransport.kt
 create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/ECDHRendezvous.kt
 create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/EmbeddedRendezvous.kt
 create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousError.kt
 create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousIntent.kt
 create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousTransportDetails.kt
 create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousTransportType.kt
 create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/SecureRendezvousChannelAlgorithm.kt

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/logger/LoggerTag.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/logger/LoggerTag.kt
index ae65963f37..22af8cebbd 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/logger/LoggerTag.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/logger/LoggerTag.kt
@@ -27,6 +27,7 @@ open class LoggerTag(name: String, parentTag: LoggerTag? = null) {
     object SYNC : LoggerTag("SYNC")
     object VOIP : LoggerTag("VOIP")
     object CRYPTO : LoggerTag("CRYPTO")
+    object RENDEZVOUS : LoggerTag("RZ")
 
     val value: String = if (parentTag == null) {
         name
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousChannel.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousChannel.kt
new file mode 100644
index 0000000000..43552f46be
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousChannel.kt
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2022 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.matrix.android.sdk.internal.rendezvous
+
+import org.matrix.android.sdk.internal.rendezvous.model.ECDHRendezvousCode
+import org.matrix.android.sdk.internal.rendezvous.model.RendezvousIntent
+
+interface RendezvousChannel {
+    var transport: RendezvousTransport;
+    /**
+     * @returns the checksum/confirmation digits to be shown to the user
+     */
+    suspend fun connect(): String
+    /**
+     * Send a payload via the channel.
+     * @param data payload to send
+     */
+    suspend fun send(data: ByteArray)
+    /**
+     * Receive a payload from the channel.
+     * @returns the received payload
+     */
+    suspend fun receive(): ByteArray?
+    /**
+     * @returns a representation of the channel that can be encoded in a QR or similar
+     */
+    suspend fun close()
+    // TODO: this should be transport independent in the future
+    suspend fun generateCode(intent: RendezvousIntent): ECDHRendezvousCode
+    suspend fun cancel(reason: RendezvousFailureReason)
+}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousFailureReason.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousFailureReason.kt
new file mode 100644
index 0000000000..0e2ea8c758
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousFailureReason.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2022 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.matrix.android.sdk.internal.rendezvous
+
+enum class RendezvousFailureReason(val value: String, val canRetry: Boolean = true) {
+    UserDeclined("user_declined"),
+    OtherDeviceNotSignedIn("other_device_not_signed_in"),
+    OtherDeviceAlreadySignedIn("other_device_already_signed_in"),
+    Unknown("unknown"),
+    Expired("expired"),
+    UserCancelled("user_cancelled"),
+    InvalidCode("invalid_code"),
+    UnsupportedAlgorithm("unsupported_algorithm", false),
+    DataMismatch("data_mismatch"),
+    UnsupportedTransport("unsupported_transport", false),
+    HomeserverLacksSupport("homeserver_lacks_support", false)
+}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousTransport.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousTransport.kt
new file mode 100644
index 0000000000..753b0bc6fa
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousTransport.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2022 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.matrix.android.sdk.internal.rendezvous
+
+import okhttp3.MediaType
+import org.matrix.android.sdk.internal.rendezvous.model.RendezvousTransportDetails
+
+interface RendezvousTransport {
+    var ready: Boolean;
+    var onCancelled: ((reason: RendezvousFailureReason) -> Unit)?;
+    suspend fun details(): RendezvousTransportDetails;
+    suspend fun send(contentType: MediaType, data: ByteArray);
+    suspend fun receive(): ByteArray?;
+    suspend fun cancel(reason: RendezvousFailureReason);
+}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/ECDHRendezvous.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/ECDHRendezvous.kt
new file mode 100644
index 0000000000..e296dce09d
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/ECDHRendezvous.kt
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2022 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.matrix.android.sdk.internal.rendezvous.model
+
+import com.squareup.moshi.Json
+import com.squareup.moshi.JsonClass
+import org.matrix.android.sdk.internal.rendezvous.transports.SimpleHttpRendezvousTransportDetails
+
+@JsonClass(generateAdapter = true)
+data class ECDHRendezvous(
+        @Json val transport: SimpleHttpRendezvousTransportDetails,
+        @Json val algorithm: SecureRendezvousChannelAlgorithm,
+        @Json val key: String
+)
+
+@JsonClass(generateAdapter = true)
+data class ECDHRendezvousCode(
+        @Json val intent: RendezvousIntent,
+        @Json val rendezvous: ECDHRendezvous
+)
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/EmbeddedRendezvous.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/EmbeddedRendezvous.kt
new file mode 100644
index 0000000000..d490de0133
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/EmbeddedRendezvous.kt
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2022 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.matrix.android.sdk.internal.rendezvous.model
+
+import com.squareup.moshi.Json
+import com.squareup.moshi.JsonClass
+
+@JsonClass(generateAdapter = true)
+open class EmbeddedRendezvous(
+    @Json(name = "transport") val transport: RendezvousTransportDetails,
+    @Json(name = "algorithm") val algorithm: SecureRendezvousChannelAlgorithm
+)
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousError.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousError.kt
new file mode 100644
index 0000000000..ead273e8ce
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousError.kt
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2022 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.matrix.android.sdk.internal.rendezvous.model
+
+import org.matrix.android.sdk.internal.rendezvous.RendezvousFailureReason
+
+class RendezvousError(val description: String, val reason: RendezvousFailureReason): RuntimeException(description) {
+}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousIntent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousIntent.kt
new file mode 100644
index 0000000000..6285c1e57a
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousIntent.kt
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2022 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.matrix.android.sdk.internal.rendezvous.model
+
+import com.squareup.moshi.Json
+
+enum class RendezvousIntent {
+    @Json(name = "login.start") LOGIN_ON_NEW_DEVICE,
+    @Json(name = "login.reciprocate") RECIPROCATE_LOGIN_ON_EXISTING_DEVICE
+}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousTransportDetails.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousTransportDetails.kt
new file mode 100644
index 0000000000..1b1826194f
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousTransportDetails.kt
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2022 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.matrix.android.sdk.internal.rendezvous.model
+
+import com.squareup.moshi.Json
+import com.squareup.moshi.JsonClass
+
+@JsonClass(generateAdapter = true)
+open class RendezvousTransportDetails(
+    @Json val type: RendezvousTransportType
+)
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousTransportType.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousTransportType.kt
new file mode 100644
index 0000000000..c3b6ba7ac8
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousTransportType.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2022 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.matrix.android.sdk.internal.rendezvous.model
+
+import com.squareup.moshi.Json
+
+enum class RendezvousTransportType(val value: String) {
+    @Json(name = "http.v1") MSC3886_SIMPLE_HTTP_V1("http.v1")
+}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/SecureRendezvousChannelAlgorithm.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/SecureRendezvousChannelAlgorithm.kt
new file mode 100644
index 0000000000..ddc0ae20e7
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/SecureRendezvousChannelAlgorithm.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2022 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.matrix.android.sdk.internal.rendezvous.model
+
+import com.squareup.moshi.Json
+
+enum class SecureRendezvousChannelAlgorithm(val value: String) {
+    @Json(name = "m.rendezvous.v1.curve25519-aes-sha256") ECDH_V1("m.rendezvous.v1.curve25519-aes-sha256")
+}

From f04f0e6fac25345b7cdc39181e0ac4743fdb37c5 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Thu, 13 Oct 2022 22:42:58 +0100
Subject: [PATCH 38/93] Revert "Revert "Partial implementation of QR login
 logic""

This reverts commit e305478ddabf39c6e2a272485a1b154b3ad12263.
---
 .../src/main/res/values/strings.xml           |  1 +
 .../login/qr/QrCodeLoginConnectionStatus.kt   |  4 +++-
 .../features/login/qr/QrCodeLoginErrorType.kt | 23 -------------------
 .../login/qr/QrCodeLoginStatusFragment.kt     | 12 ++++++----
 .../features/login/qr/QrCodeLoginViewModel.kt |  4 ++--
 5 files changed, 13 insertions(+), 31 deletions(-)
 delete mode 100644 vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginErrorType.kt

diff --git a/library/ui-strings/src/main/res/values/strings.xml b/library/ui-strings/src/main/res/values/strings.xml
index 822e9d3865..108fe7db7c 100644
--- a/library/ui-strings/src/main/res/values/strings.xml
+++ b/library/ui-strings/src/main/res/values/strings.xml
@@ -3368,6 +3368,7 @@
     <string name="qr_code_login_header_failed_device_is_not_supported_description">Linking with this device is not supported.</string>
     <string name="qr_code_login_header_failed_timeout_description">The linking wasn’t completed in the required time.</string>
     <string name="qr_code_login_header_failed_denied_description">The request was denied on the other device.</string>
+    <string name="qr_code_login_header_failed_other_description">The request failed.</string>
     <string name="qr_code_login_new_device_instruction_1">Open ${app_name} on your other device</string>
     <string name="qr_code_login_new_device_instruction_2">Go to Settings -> Security &amp; Privacy -> Show All Sessions</string>
     <string name="qr_code_login_new_device_instruction_3">Select \'Show QR code\'</string>
diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginConnectionStatus.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginConnectionStatus.kt
index 330562b874..4de191f863 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginConnectionStatus.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginConnectionStatus.kt
@@ -16,9 +16,11 @@
 
 package im.vector.app.features.login.qr
 
+import org.matrix.android.sdk.internal.rendezvous.RendezvousFailureReason
+
 sealed class QrCodeLoginConnectionStatus {
     object ConnectingToDevice : QrCodeLoginConnectionStatus()
     data class Connected(val securityCode: String, val canConfirmSecurityCode: Boolean) : QrCodeLoginConnectionStatus()
     object SigningIn : QrCodeLoginConnectionStatus()
-    data class Failed(val errorType: QrCodeLoginErrorType, val canTryAgain: Boolean) : QrCodeLoginConnectionStatus()
+    data class Failed(val errorType: RendezvousFailureReason, val canTryAgain: Boolean) : QrCodeLoginConnectionStatus()
 }
diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginErrorType.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginErrorType.kt
deleted file mode 100644
index 9a6cc13de0..0000000000
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginErrorType.kt
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Copyright (c) 2022 New Vector Ltd
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package im.vector.app.features.login.qr
-
-enum class QrCodeLoginErrorType {
-    DEVICE_IS_NOT_SUPPORTED,
-    TIMEOUT,
-    REQUEST_WAS_DENIED,
-}
diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt
index 1c0841aa11..fb372cbb2f 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt
@@ -27,6 +27,7 @@ import im.vector.app.R
 import im.vector.app.core.platform.VectorBaseFragment
 import im.vector.app.databinding.FragmentQrCodeLoginStatusBinding
 import im.vector.app.features.themes.ThemeUtils
+import org.matrix.android.sdk.internal.rendezvous.RendezvousFailureReason
 
 @AndroidEntryPoint
 class QrCodeLoginStatusFragment : VectorBaseFragment<FragmentQrCodeLoginStatusBinding>() {
@@ -77,11 +78,12 @@ class QrCodeLoginStatusFragment : VectorBaseFragment<FragmentQrCodeLoginStatusBi
         )
     }
 
-    private fun getErrorCode(errorType: QrCodeLoginErrorType): String {
-        return when (errorType) {
-            QrCodeLoginErrorType.DEVICE_IS_NOT_SUPPORTED -> getString(R.string.qr_code_login_header_failed_device_is_not_supported_description)
-            QrCodeLoginErrorType.TIMEOUT -> getString(R.string.qr_code_login_header_failed_timeout_description)
-            QrCodeLoginErrorType.REQUEST_WAS_DENIED -> getString(R.string.qr_code_login_header_failed_denied_description)
+    private fun getErrorCode(reason: RendezvousFailureReason): String {
+        return when (reason) {
+            RendezvousFailureReason.UnsupportedAlgorithm, RendezvousFailureReason.UnsupportedTransport -> getString(R.string.qr_code_login_header_failed_device_is_not_supported_description)
+            RendezvousFailureReason.Expired -> getString(R.string.qr_code_login_header_failed_timeout_description)
+            RendezvousFailureReason.UserDeclined -> getString(R.string.qr_code_login_header_failed_denied_description)
+            else -> getString(R.string.qr_code_login_header_failed_other_description)
         }
     }
 
diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
index d9c30690a7..8461d8d88f 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
@@ -39,7 +39,7 @@ class QrCodeLoginViewModel @AssistedInject constructor(
         @Assisted private val initialState: QrCodeLoginViewState,
         private val applicationContext: Context,
         private val authenticationService: AuthenticationService,
-        private val activeSessionHolder: ActiveSessionHolder,
+        private val activeSessionHolder: ActiveSessionHolder
 ) : VectorViewModel<QrCodeLoginViewState, QrCodeLoginAction, QrCodeLoginViewEvents>(initialState) {
     val TAG: String = QrCodeLoginViewModel::class.java.simpleName
 
@@ -116,7 +116,7 @@ class QrCodeLoginViewModel @AssistedInject constructor(
     private fun onFailed(reason: RendezvousFailureReason) {
         setState {
             copy(
-                    connectionStatus = QrCodeLoginConnectionStatus.Failed(errorType, canTryAgain)
+                    connectionStatus = QrCodeLoginConnectionStatus.Failed(reason, reason.canRetry)
             )
         }
     }

From 5abb786b6b792ea70f38ca616bd5925a329af719 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Fri, 14 Oct 2022 00:59:31 +0100
Subject: [PATCH 39/93] Fix copyright on SDK

---
 .../org/matrix/android/sdk/internal/rendezvous/Rendezvous.kt    | 2 +-
 .../matrix/android/sdk/internal/rendezvous/RendezvousChannel.kt | 2 +-
 .../android/sdk/internal/rendezvous/RendezvousFailureReason.kt  | 2 +-
 .../android/sdk/internal/rendezvous/RendezvousTransport.kt      | 2 +-
 .../sdk/internal/rendezvous/channels/ECDHRendezvousChannel.kt   | 2 +-
 .../android/sdk/internal/rendezvous/model/ECDHRendezvous.kt     | 2 +-
 .../android/sdk/internal/rendezvous/model/EmbeddedRendezvous.kt | 2 +-
 .../android/sdk/internal/rendezvous/model/RendezvousError.kt    | 2 +-
 .../android/sdk/internal/rendezvous/model/RendezvousIntent.kt   | 2 +-
 .../sdk/internal/rendezvous/model/RendezvousTransportDetails.kt | 2 +-
 .../sdk/internal/rendezvous/model/RendezvousTransportType.kt    | 2 +-
 .../rendezvous/model/SecureRendezvousChannelAlgorithm.kt        | 2 +-
 .../rendezvous/transports/SimpleHttpRendezvousTransport.kt      | 2 +-
 13 files changed, 13 insertions(+), 13 deletions(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/Rendezvous.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/Rendezvous.kt
index 2f85a97c55..d8fd323056 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/Rendezvous.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/Rendezvous.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022 New Vector Ltd
+ * Copyright 2020 The Matrix.org Foundation C.I.C.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousChannel.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousChannel.kt
index 43552f46be..6655468808 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousChannel.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousChannel.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022 New Vector Ltd
+ * Copyright 2020 The Matrix.org Foundation C.I.C.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousFailureReason.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousFailureReason.kt
index 0e2ea8c758..0920bf224f 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousFailureReason.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousFailureReason.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022 New Vector Ltd
+ * Copyright 2020 The Matrix.org Foundation C.I.C.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousTransport.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousTransport.kt
index 753b0bc6fa..f054b7cb73 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousTransport.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousTransport.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022 New Vector Ltd
+ * Copyright 2020 The Matrix.org Foundation C.I.C.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/channels/ECDHRendezvousChannel.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/channels/ECDHRendezvousChannel.kt
index cced29aab4..9f347b8536 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/channels/ECDHRendezvousChannel.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/channels/ECDHRendezvousChannel.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022 New Vector Ltd
+ * Copyright 2020 The Matrix.org Foundation C.I.C.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/ECDHRendezvous.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/ECDHRendezvous.kt
index e296dce09d..14976ec836 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/ECDHRendezvous.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/ECDHRendezvous.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022 New Vector Ltd
+ * Copyright 2020 The Matrix.org Foundation C.I.C.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/EmbeddedRendezvous.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/EmbeddedRendezvous.kt
index d490de0133..5a609d497e 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/EmbeddedRendezvous.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/EmbeddedRendezvous.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022 New Vector Ltd
+ * Copyright 2020 The Matrix.org Foundation C.I.C.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousError.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousError.kt
index ead273e8ce..feb7d0cf35 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousError.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousError.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022 New Vector Ltd
+ * Copyright 2020 The Matrix.org Foundation C.I.C.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousIntent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousIntent.kt
index 6285c1e57a..d317531835 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousIntent.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousIntent.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022 New Vector Ltd
+ * Copyright 2020 The Matrix.org Foundation C.I.C.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousTransportDetails.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousTransportDetails.kt
index 1b1826194f..ee9058daa3 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousTransportDetails.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousTransportDetails.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022 New Vector Ltd
+ * Copyright 2020 The Matrix.org Foundation C.I.C.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousTransportType.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousTransportType.kt
index c3b6ba7ac8..51371ddb2f 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousTransportType.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousTransportType.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022 New Vector Ltd
+ * Copyright 2020 The Matrix.org Foundation C.I.C.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/SecureRendezvousChannelAlgorithm.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/SecureRendezvousChannelAlgorithm.kt
index ddc0ae20e7..122b6ede82 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/SecureRendezvousChannelAlgorithm.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/SecureRendezvousChannelAlgorithm.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022 New Vector Ltd
+ * Copyright 2020 The Matrix.org Foundation C.I.C.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/transports/SimpleHttpRendezvousTransport.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/transports/SimpleHttpRendezvousTransport.kt
index cc4346d55e..7918946d75 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/transports/SimpleHttpRendezvousTransport.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/transports/SimpleHttpRendezvousTransport.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022 New Vector Ltd
+ * Copyright 2020 The Matrix.org Foundation C.I.C.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.

From c18439f99b7056619fcf1d7f7df3697d1ee5b63c Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Fri, 14 Oct 2022 01:07:19 +0100
Subject: [PATCH 40/93] Refactor code into api from internal

---
 .../rendezvous/Rendezvous.kt                  | 12 ++++----
 .../rendezvous/RendezvousChannel.kt           |  8 +++---
 .../rendezvous/RendezvousFailureReason.kt     |  4 +--
 .../rendezvous/RendezvousTransport.kt         |  6 ++--
 .../channels/ECDHRendezvousChannel.kt         | 28 ++++++++++---------
 .../rendezvous/model/ECDHRendezvous.kt        |  6 ++--
 .../rendezvous/model/EmbeddedRendezvous.kt    |  8 +++---
 .../rendezvous/model/RendezvousError.kt       |  6 ++--
 .../rendezvous/model/RendezvousIntent.kt      |  4 +--
 .../model/RendezvousTransportDetails.kt       |  4 +--
 .../model/RendezvousTransportType.kt          |  4 +--
 .../model/SecureRendezvousChannelAlgorithm.kt |  4 +--
 .../SimpleHttpRendezvousTransport.kt          | 12 ++++----
 .../login/qr/QrCodeLoginConnectionStatus.kt   |  2 +-
 .../login/qr/QrCodeLoginStatusFragment.kt     |  2 +-
 .../features/login/qr/QrCodeLoginViewModel.kt |  4 +--
 16 files changed, 58 insertions(+), 56 deletions(-)
 rename matrix-sdk-android/src/main/java/org/matrix/android/sdk/{internal => api}/rendezvous/Rendezvous.kt (95%)
 rename matrix-sdk-android/src/main/java/org/matrix/android/sdk/{internal => api}/rendezvous/RendezvousChannel.kt (84%)
 rename matrix-sdk-android/src/main/java/org/matrix/android/sdk/{internal => api}/rendezvous/RendezvousFailureReason.kt (91%)
 rename matrix-sdk-android/src/main/java/org/matrix/android/sdk/{internal => api}/rendezvous/RendezvousTransport.kt (83%)
 rename matrix-sdk-android/src/main/java/org/matrix/android/sdk/{internal => api}/rendezvous/channels/ECDHRendezvousChannel.kt (90%)
 rename matrix-sdk-android/src/main/java/org/matrix/android/sdk/{internal => api}/rendezvous/model/ECDHRendezvous.kt (83%)
 rename matrix-sdk-android/src/main/java/org/matrix/android/sdk/{internal => api}/rendezvous/model/EmbeddedRendezvous.kt (72%)
 rename matrix-sdk-android/src/main/java/org/matrix/android/sdk/{internal => api}/rendezvous/model/RendezvousError.kt (79%)
 rename matrix-sdk-android/src/main/java/org/matrix/android/sdk/{internal => api}/rendezvous/model/RendezvousIntent.kt (87%)
 rename matrix-sdk-android/src/main/java/org/matrix/android/sdk/{internal => api}/rendezvous/model/RendezvousTransportDetails.kt (87%)
 rename matrix-sdk-android/src/main/java/org/matrix/android/sdk/{internal => api}/rendezvous/model/RendezvousTransportType.kt (86%)
 rename matrix-sdk-android/src/main/java/org/matrix/android/sdk/{internal => api}/rendezvous/model/SecureRendezvousChannelAlgorithm.kt (87%)
 rename matrix-sdk-android/src/main/java/org/matrix/android/sdk/{internal => api}/rendezvous/transports/SimpleHttpRendezvousTransport.kt (93%)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/Rendezvous.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/Rendezvous.kt
similarity index 95%
rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/Rendezvous.kt
rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/Rendezvous.kt
index d8fd323056..534d5df1ca 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/Rendezvous.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/Rendezvous.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2020 The Matrix.org Foundation C.I.C.
+ * Copyright 2022 The Matrix.org Foundation C.I.C.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package org.matrix.android.sdk.internal.rendezvous
+package org.matrix.android.sdk.api.rendezvous
 
 import android.net.Uri
 import com.squareup.moshi.Json
@@ -29,10 +29,10 @@ import org.matrix.android.sdk.api.session.crypto.crosssigning.MASTER_KEY_SSSS_NA
 import org.matrix.android.sdk.api.session.crypto.crosssigning.SELF_SIGNING_KEY_SSSS_NAME
 import org.matrix.android.sdk.api.session.crypto.crosssigning.USER_SIGNING_KEY_SSSS_NAME
 import org.matrix.android.sdk.api.util.MatrixJsonParser
-import org.matrix.android.sdk.internal.rendezvous.channels.ECDHRendezvousChannel
-import org.matrix.android.sdk.internal.rendezvous.model.ECDHRendezvousCode
-import org.matrix.android.sdk.internal.rendezvous.model.RendezvousIntent
-import org.matrix.android.sdk.internal.rendezvous.transports.SimpleHttpRendezvousTransport
+import org.matrix.android.sdk.api.rendezvous.channels.ECDHRendezvousChannel
+import org.matrix.android.sdk.api.rendezvous.model.ECDHRendezvousCode
+import org.matrix.android.sdk.api.rendezvous.model.RendezvousIntent
+import org.matrix.android.sdk.api.rendezvous.transports.SimpleHttpRendezvousTransport
 import timber.log.Timber
 
 internal enum class PayloadType(val value: String) {
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousChannel.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousChannel.kt
similarity index 84%
rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousChannel.kt
rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousChannel.kt
index 6655468808..2a73e8f112 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousChannel.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousChannel.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2020 The Matrix.org Foundation C.I.C.
+ * Copyright 2022 The Matrix.org Foundation C.I.C.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,10 +14,10 @@
  * limitations under the License.
  */
 
-package org.matrix.android.sdk.internal.rendezvous
+package org.matrix.android.sdk.api.rendezvous
 
-import org.matrix.android.sdk.internal.rendezvous.model.ECDHRendezvousCode
-import org.matrix.android.sdk.internal.rendezvous.model.RendezvousIntent
+import org.matrix.android.sdk.api.rendezvous.model.ECDHRendezvousCode
+import org.matrix.android.sdk.api.rendezvous.model.RendezvousIntent
 
 interface RendezvousChannel {
     var transport: RendezvousTransport;
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousFailureReason.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousFailureReason.kt
similarity index 91%
rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousFailureReason.kt
rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousFailureReason.kt
index 0920bf224f..a607dc7f38 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousFailureReason.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousFailureReason.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2020 The Matrix.org Foundation C.I.C.
+ * Copyright 2022 The Matrix.org Foundation C.I.C.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package org.matrix.android.sdk.internal.rendezvous
+package org.matrix.android.sdk.api.rendezvous
 
 enum class RendezvousFailureReason(val value: String, val canRetry: Boolean = true) {
     UserDeclined("user_declined"),
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousTransport.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousTransport.kt
similarity index 83%
rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousTransport.kt
rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousTransport.kt
index f054b7cb73..11471288f6 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousTransport.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousTransport.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2020 The Matrix.org Foundation C.I.C.
+ * Copyright 2022 The Matrix.org Foundation C.I.C.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,10 +14,10 @@
  * limitations under the License.
  */
 
-package org.matrix.android.sdk.internal.rendezvous
+package org.matrix.android.sdk.api.rendezvous
 
 import okhttp3.MediaType
-import org.matrix.android.sdk.internal.rendezvous.model.RendezvousTransportDetails
+import org.matrix.android.sdk.api.rendezvous.model.RendezvousTransportDetails
 
 interface RendezvousTransport {
     var ready: Boolean;
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/channels/ECDHRendezvousChannel.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt
similarity index 90%
rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/channels/ECDHRendezvousChannel.kt
rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt
index 9f347b8536..0f69bd7eda 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/channels/ECDHRendezvousChannel.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2020 The Matrix.org Foundation C.I.C.
+ * Copyright 2022 The Matrix.org Foundation C.I.C.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package org.matrix.android.sdk.internal.rendezvous.channels
+package org.matrix.android.sdk.api.rendezvous.channels
 
 import android.util.Base64
 import com.squareup.moshi.Json
@@ -23,15 +23,15 @@ import okhttp3.MediaType.Companion.toMediaType
 import org.matrix.android.sdk.api.logger.LoggerTag
 import org.matrix.android.sdk.api.util.MatrixJsonParser
 import org.matrix.android.sdk.internal.extensions.toUnsignedInt
-import org.matrix.android.sdk.internal.rendezvous.RendezvousFailureReason
-import org.matrix.android.sdk.internal.rendezvous.RendezvousChannel
-import org.matrix.android.sdk.internal.rendezvous.RendezvousTransport
-import org.matrix.android.sdk.internal.rendezvous.model.ECDHRendezvous
-import org.matrix.android.sdk.internal.rendezvous.model.ECDHRendezvousCode
-import org.matrix.android.sdk.internal.rendezvous.model.RendezvousError
-import org.matrix.android.sdk.internal.rendezvous.model.RendezvousIntent
-import org.matrix.android.sdk.internal.rendezvous.model.SecureRendezvousChannelAlgorithm
-import org.matrix.android.sdk.internal.rendezvous.transports.SimpleHttpRendezvousTransportDetails
+import org.matrix.android.sdk.api.rendezvous.RendezvousFailureReason
+import org.matrix.android.sdk.api.rendezvous.RendezvousChannel
+import org.matrix.android.sdk.api.rendezvous.RendezvousTransport
+import org.matrix.android.sdk.api.rendezvous.model.ECDHRendezvous
+import org.matrix.android.sdk.api.rendezvous.model.ECDHRendezvousCode
+import org.matrix.android.sdk.api.rendezvous.model.RendezvousError
+import org.matrix.android.sdk.api.rendezvous.model.RendezvousIntent
+import org.matrix.android.sdk.api.rendezvous.model.SecureRendezvousChannelAlgorithm
+import org.matrix.android.sdk.api.rendezvous.transports.SimpleHttpRendezvousTransportDetails
 import org.matrix.olm.OlmSAS
 import timber.log.Timber
 import java.security.SecureRandom
@@ -107,10 +107,12 @@ class ECDHRendezvousChannel(override var transport: RendezvousTransport, theirPu
         } else {
             // send our public key unencrypted
 //            Timber.tag(TAG).i("Sending public key")
-            send(ECDHPayload(
+            send(
+                    ECDHPayload(
                     algorithm = SecureRendezvousChannelAlgorithm.ECDH_V1,
                     key = Base64.encodeToString(ourPublicKey, Base64.NO_WRAP)
-            ))
+            )
+            )
         }
 
         olmSAS!!.setTheirPublicKey(Base64.encodeToString(theirPublicKey, Base64.NO_WRAP))
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/ECDHRendezvous.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/ECDHRendezvous.kt
similarity index 83%
rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/ECDHRendezvous.kt
rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/ECDHRendezvous.kt
index 14976ec836..b203101b66 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/ECDHRendezvous.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/ECDHRendezvous.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2020 The Matrix.org Foundation C.I.C.
+ * Copyright 2022 The Matrix.org Foundation C.I.C.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,11 +14,11 @@
  * limitations under the License.
  */
 
-package org.matrix.android.sdk.internal.rendezvous.model
+package org.matrix.android.sdk.api.rendezvous.model
 
 import com.squareup.moshi.Json
 import com.squareup.moshi.JsonClass
-import org.matrix.android.sdk.internal.rendezvous.transports.SimpleHttpRendezvousTransportDetails
+import org.matrix.android.sdk.api.rendezvous.transports.SimpleHttpRendezvousTransportDetails
 
 @JsonClass(generateAdapter = true)
 data class ECDHRendezvous(
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/EmbeddedRendezvous.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/EmbeddedRendezvous.kt
similarity index 72%
rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/EmbeddedRendezvous.kt
rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/EmbeddedRendezvous.kt
index 5a609d497e..785ce1fed7 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/EmbeddedRendezvous.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/EmbeddedRendezvous.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2020 The Matrix.org Foundation C.I.C.
+ * Copyright 2022 The Matrix.org Foundation C.I.C.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,13 +14,13 @@
  * limitations under the License.
  */
 
-package org.matrix.android.sdk.internal.rendezvous.model
+package org.matrix.android.sdk.api.rendezvous.model
 
 import com.squareup.moshi.Json
 import com.squareup.moshi.JsonClass
 
 @JsonClass(generateAdapter = true)
 open class EmbeddedRendezvous(
-    @Json(name = "transport") val transport: RendezvousTransportDetails,
-    @Json(name = "algorithm") val algorithm: SecureRendezvousChannelAlgorithm
+        @Json(name = "transport") val transport: RendezvousTransportDetails,
+        @Json(name = "algorithm") val algorithm: SecureRendezvousChannelAlgorithm
 )
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousError.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousError.kt
similarity index 79%
rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousError.kt
rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousError.kt
index feb7d0cf35..f731c89649 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousError.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousError.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2020 The Matrix.org Foundation C.I.C.
+ * Copyright 2022 The Matrix.org Foundation C.I.C.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,9 +14,9 @@
  * limitations under the License.
  */
 
-package org.matrix.android.sdk.internal.rendezvous.model
+package org.matrix.android.sdk.api.rendezvous.model
 
-import org.matrix.android.sdk.internal.rendezvous.RendezvousFailureReason
+import org.matrix.android.sdk.api.rendezvous.RendezvousFailureReason
 
 class RendezvousError(val description: String, val reason: RendezvousFailureReason): RuntimeException(description) {
 }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousIntent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousIntent.kt
similarity index 87%
rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousIntent.kt
rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousIntent.kt
index d317531835..1c070599b0 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousIntent.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousIntent.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2020 The Matrix.org Foundation C.I.C.
+ * Copyright 2022 The Matrix.org Foundation C.I.C.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package org.matrix.android.sdk.internal.rendezvous.model
+package org.matrix.android.sdk.api.rendezvous.model
 
 import com.squareup.moshi.Json
 
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousTransportDetails.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousTransportDetails.kt
similarity index 87%
rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousTransportDetails.kt
rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousTransportDetails.kt
index ee9058daa3..55b3bbb5d9 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousTransportDetails.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousTransportDetails.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2020 The Matrix.org Foundation C.I.C.
+ * Copyright 2022 The Matrix.org Foundation C.I.C.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package org.matrix.android.sdk.internal.rendezvous.model
+package org.matrix.android.sdk.api.rendezvous.model
 
 import com.squareup.moshi.Json
 import com.squareup.moshi.JsonClass
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousTransportType.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousTransportType.kt
similarity index 86%
rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousTransportType.kt
rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousTransportType.kt
index 51371ddb2f..9c3e44f25b 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousTransportType.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousTransportType.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2020 The Matrix.org Foundation C.I.C.
+ * Copyright 2022 The Matrix.org Foundation C.I.C.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package org.matrix.android.sdk.internal.rendezvous.model
+package org.matrix.android.sdk.api.rendezvous.model
 
 import com.squareup.moshi.Json
 
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/SecureRendezvousChannelAlgorithm.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/SecureRendezvousChannelAlgorithm.kt
similarity index 87%
rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/SecureRendezvousChannelAlgorithm.kt
rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/SecureRendezvousChannelAlgorithm.kt
index 122b6ede82..9a9db58a41 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/SecureRendezvousChannelAlgorithm.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/SecureRendezvousChannelAlgorithm.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2020 The Matrix.org Foundation C.I.C.
+ * Copyright 2022 The Matrix.org Foundation C.I.C.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package org.matrix.android.sdk.internal.rendezvous.model
+package org.matrix.android.sdk.api.rendezvous.model
 
 import com.squareup.moshi.Json
 
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/transports/SimpleHttpRendezvousTransport.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt
similarity index 93%
rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/transports/SimpleHttpRendezvousTransport.kt
rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt
index 7918946d75..ba60459544 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/transports/SimpleHttpRendezvousTransport.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2020 The Matrix.org Foundation C.I.C.
+ * Copyright 2022 The Matrix.org Foundation C.I.C.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package org.matrix.android.sdk.internal.rendezvous.transports
+package org.matrix.android.sdk.api.rendezvous.transports
 
 import com.squareup.moshi.Json
 import com.squareup.moshi.JsonClass
@@ -23,10 +23,10 @@ import okhttp3.MediaType
 import okhttp3.Request
 import okhttp3.RequestBody.Companion.toRequestBody
 import org.matrix.android.sdk.api.logger.LoggerTag
-import org.matrix.android.sdk.internal.rendezvous.RendezvousFailureReason
-import org.matrix.android.sdk.internal.rendezvous.RendezvousTransport
-import org.matrix.android.sdk.internal.rendezvous.model.RendezvousTransportDetails
-import org.matrix.android.sdk.internal.rendezvous.model.RendezvousTransportType
+import org.matrix.android.sdk.api.rendezvous.RendezvousFailureReason
+import org.matrix.android.sdk.api.rendezvous.RendezvousTransport
+import org.matrix.android.sdk.api.rendezvous.model.RendezvousTransportDetails
+import org.matrix.android.sdk.api.rendezvous.model.RendezvousTransportType
 import timber.log.Timber
 import java.text.SimpleDateFormat
 import java.util.Date
diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginConnectionStatus.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginConnectionStatus.kt
index 4de191f863..4bef41b6c1 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginConnectionStatus.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginConnectionStatus.kt
@@ -16,7 +16,7 @@
 
 package im.vector.app.features.login.qr
 
-import org.matrix.android.sdk.internal.rendezvous.RendezvousFailureReason
+import org.matrix.android.sdk.api.rendezvous.RendezvousFailureReason
 
 sealed class QrCodeLoginConnectionStatus {
     object ConnectingToDevice : QrCodeLoginConnectionStatus()
diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt
index fb372cbb2f..5451a16b44 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt
@@ -27,7 +27,7 @@ import im.vector.app.R
 import im.vector.app.core.platform.VectorBaseFragment
 import im.vector.app.databinding.FragmentQrCodeLoginStatusBinding
 import im.vector.app.features.themes.ThemeUtils
-import org.matrix.android.sdk.internal.rendezvous.RendezvousFailureReason
+import org.matrix.android.sdk.api.rendezvous.RendezvousFailureReason
 
 @AndroidEntryPoint
 class QrCodeLoginStatusFragment : VectorBaseFragment<FragmentQrCodeLoginStatusBinding>() {
diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
index 8461d8d88f..276cedad43 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
@@ -31,8 +31,8 @@ import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.launch
 import org.matrix.android.sdk.api.auth.AuthenticationService
 import org.matrix.android.sdk.api.session.Session
-import org.matrix.android.sdk.internal.rendezvous.Rendezvous
-import org.matrix.android.sdk.internal.rendezvous.RendezvousFailureReason
+import org.matrix.android.sdk.api.rendezvous.Rendezvous
+import org.matrix.android.sdk.api.rendezvous.RendezvousFailureReason
 import timber.log.Timber
 
 class QrCodeLoginViewModel @AssistedInject constructor(

From c00ce91214cea1a0308d7885d46b5ea5224f5aea Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Fri, 14 Oct 2022 01:11:25 +0100
Subject: [PATCH 41/93] Linting

---
 .../android/sdk/api/rendezvous/Rendezvous.kt  | 27 ++++++++++---------
 .../sdk/api/rendezvous/RendezvousChannel.kt   |  7 ++++-
 .../sdk/api/rendezvous/RendezvousTransport.kt | 12 ++++-----
 .../channels/ECDHRendezvousChannel.kt         |  9 +++----
 .../api/rendezvous/model/RendezvousError.kt   |  3 +--
 .../SimpleHttpRendezvousTransport.kt          |  2 +-
 .../features/login/qr/QrCodeLoginViewModel.kt |  2 --
 7 files changed, 32 insertions(+), 30 deletions(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/Rendezvous.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/Rendezvous.kt
index 534d5df1ca..e33130e529 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/Rendezvous.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/Rendezvous.kt
@@ -22,6 +22,10 @@ import com.squareup.moshi.JsonClass
 import org.matrix.android.sdk.api.auth.AuthenticationService
 import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig
 import org.matrix.android.sdk.api.logger.LoggerTag
+import org.matrix.android.sdk.api.rendezvous.channels.ECDHRendezvousChannel
+import org.matrix.android.sdk.api.rendezvous.model.ECDHRendezvousCode
+import org.matrix.android.sdk.api.rendezvous.model.RendezvousIntent
+import org.matrix.android.sdk.api.rendezvous.transports.SimpleHttpRendezvousTransport
 import org.matrix.android.sdk.api.session.Session
 import org.matrix.android.sdk.api.session.crypto.crosssigning.DeviceTrustLevel
 import org.matrix.android.sdk.api.session.crypto.crosssigning.KEYBACKUP_SECRET_SSSS_NAME
@@ -29,10 +33,6 @@ import org.matrix.android.sdk.api.session.crypto.crosssigning.MASTER_KEY_SSSS_NA
 import org.matrix.android.sdk.api.session.crypto.crosssigning.SELF_SIGNING_KEY_SSSS_NAME
 import org.matrix.android.sdk.api.session.crypto.crosssigning.USER_SIGNING_KEY_SSSS_NAME
 import org.matrix.android.sdk.api.util.MatrixJsonParser
-import org.matrix.android.sdk.api.rendezvous.channels.ECDHRendezvousChannel
-import org.matrix.android.sdk.api.rendezvous.model.ECDHRendezvousCode
-import org.matrix.android.sdk.api.rendezvous.model.RendezvousIntent
-import org.matrix.android.sdk.api.rendezvous.transports.SimpleHttpRendezvousTransport
 import timber.log.Timber
 
 internal enum class PayloadType(val value: String) {
@@ -80,6 +80,7 @@ class Rendezvous(
     }
 
     private val adapter = MatrixJsonParser.getMoshi().adapter(Payload::class.java)
+
     // not yet implemented: RendezvousIntent.RECIPROCATE_LOGIN_ON_EXISTING_DEVICE
     val ourIntent: RendezvousIntent = RendezvousIntent.LOGIN_ON_NEW_DEVICE
 
@@ -98,7 +99,7 @@ class Rendezvous(
     }
 
     suspend fun startAfterScanningCode(): String? {
-        val checksum = channel.connect();
+        val checksum = channel.connect()
 
         Timber.tag(TAG).i("Connected to secure channel with checksum: $checksum")
 
@@ -107,7 +108,7 @@ class Rendezvous(
         }
 
         // get protocols
-        Timber.tag(TAG).i("Waiting for protocols");
+        Timber.tag(TAG).i("Waiting for protocols")
         val protocolsResponse = receive()
 
         if (protocolsResponse?.protocols == null || !protocolsResponse.protocols.contains("login_token")) {
@@ -123,7 +124,7 @@ class Rendezvous(
     }
 
     suspend fun waitForLoginOnNewDevice(authenticationService: AuthenticationService): Session? {
-        Timber.tag(TAG).i("Waiting for login_token");
+        Timber.tag(TAG).i("Waiting for login_token")
 
         val loginToken = receive()
 
@@ -147,7 +148,7 @@ class Rendezvous(
         val homeserver = loginToken?.homeserver ?: throw RuntimeException("No homeserver returned")
         val login_token = loginToken.login_token ?: throw RuntimeException("No login token returned")
 
-        Timber.tag(TAG).i("Got login_token: $login_token for $homeserver");
+        Timber.tag(TAG).i("Got login_token: $login_token for $homeserver")
 
         val hsConfig = HomeServerConnectionConfig(homeServerUri = Uri.parse(homeserver))
         return authenticationService.loginUsingQrLoginToken(hsConfig, login_token)
@@ -167,11 +168,11 @@ class Rendezvous(
         val verifyingDeviceFromServer = crypto.getCryptoDeviceInfo(userId, verifyingDeviceId)
         if (verifyingDeviceFromServer?.fingerprint() != verificationResponse.verifying_device_key) {
             Timber.tag(TAG).w("Verifying device $verifyingDeviceId doesn't match: $verifyingDeviceFromServer")
-            return;
+            return
         }
 
         // set other device as verified
-        Timber.tag(TAG).i("Setting device $verifyingDeviceId as verified");
+        Timber.tag(TAG).i("Setting device $verifyingDeviceId as verified")
         crypto.setDeviceVerification(DeviceTrustLevel(locallyVerified = true, crossSigningVerified = false), userId, verifyingDeviceId)
 
         // TODO: what do we do with the master key?
@@ -183,7 +184,7 @@ class Rendezvous(
         // request secrets from the verifying device
         Timber.tag(TAG).i("Requesting secrets from $verifyingDeviceId")
 
-        session.sharedSecretStorageService() .let {
+        session.sharedSecretStorageService().let {
             it.requestSecret(MASTER_KEY_SSSS_NAME, verifyingDeviceId)
             it.requestSecret(SELF_SIGNING_KEY_SSSS_NAME, verifyingDeviceId)
             it.requestSecret(USER_SIGNING_KEY_SSSS_NAME, verifyingDeviceId)
@@ -192,12 +193,12 @@ class Rendezvous(
     }
 
     private suspend fun receive(): Payload? {
-        val data = channel.receive()?: return null
+        val data = channel.receive() ?: return null
         return adapter.fromJson(data.toString(Charsets.UTF_8))
     }
 
     private suspend fun send(payload: Payload) {
-        channel.send(adapter.toJson(payload).toByteArray(Charsets.UTF_8));
+        channel.send(adapter.toJson(payload).toByteArray(Charsets.UTF_8))
     }
 
     suspend fun cancel(reason: RendezvousFailureReason) {
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousChannel.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousChannel.kt
index 2a73e8f112..588d034f10 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousChannel.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousChannel.kt
@@ -20,25 +20,30 @@ import org.matrix.android.sdk.api.rendezvous.model.ECDHRendezvousCode
 import org.matrix.android.sdk.api.rendezvous.model.RendezvousIntent
 
 interface RendezvousChannel {
-    var transport: RendezvousTransport;
+    var transport: RendezvousTransport
+
     /**
      * @returns the checksum/confirmation digits to be shown to the user
      */
     suspend fun connect(): String
+
     /**
      * Send a payload via the channel.
      * @param data payload to send
      */
     suspend fun send(data: ByteArray)
+
     /**
      * Receive a payload from the channel.
      * @returns the received payload
      */
     suspend fun receive(): ByteArray?
+
     /**
      * @returns a representation of the channel that can be encoded in a QR or similar
      */
     suspend fun close()
+
     // TODO: this should be transport independent in the future
     suspend fun generateCode(intent: RendezvousIntent): ECDHRendezvousCode
     suspend fun cancel(reason: RendezvousFailureReason)
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousTransport.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousTransport.kt
index 11471288f6..de0aed7efc 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousTransport.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousTransport.kt
@@ -20,10 +20,10 @@ import okhttp3.MediaType
 import org.matrix.android.sdk.api.rendezvous.model.RendezvousTransportDetails
 
 interface RendezvousTransport {
-    var ready: Boolean;
-    var onCancelled: ((reason: RendezvousFailureReason) -> Unit)?;
-    suspend fun details(): RendezvousTransportDetails;
-    suspend fun send(contentType: MediaType, data: ByteArray);
-    suspend fun receive(): ByteArray?;
-    suspend fun cancel(reason: RendezvousFailureReason);
+    var ready: Boolean
+    var onCancelled: ((reason: RendezvousFailureReason) -> Unit)?
+    suspend fun details(): RendezvousTransportDetails
+    suspend fun send(contentType: MediaType, data: ByteArray)
+    suspend fun receive(): ByteArray?
+    suspend fun cancel(reason: RendezvousFailureReason)
 }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt
index 0f69bd7eda..1c8bca5d1c 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt
@@ -21,10 +21,8 @@ import com.squareup.moshi.Json
 import com.squareup.moshi.JsonClass
 import okhttp3.MediaType.Companion.toMediaType
 import org.matrix.android.sdk.api.logger.LoggerTag
-import org.matrix.android.sdk.api.util.MatrixJsonParser
-import org.matrix.android.sdk.internal.extensions.toUnsignedInt
-import org.matrix.android.sdk.api.rendezvous.RendezvousFailureReason
 import org.matrix.android.sdk.api.rendezvous.RendezvousChannel
+import org.matrix.android.sdk.api.rendezvous.RendezvousFailureReason
 import org.matrix.android.sdk.api.rendezvous.RendezvousTransport
 import org.matrix.android.sdk.api.rendezvous.model.ECDHRendezvous
 import org.matrix.android.sdk.api.rendezvous.model.ECDHRendezvousCode
@@ -32,8 +30,9 @@ import org.matrix.android.sdk.api.rendezvous.model.RendezvousError
 import org.matrix.android.sdk.api.rendezvous.model.RendezvousIntent
 import org.matrix.android.sdk.api.rendezvous.model.SecureRendezvousChannelAlgorithm
 import org.matrix.android.sdk.api.rendezvous.transports.SimpleHttpRendezvousTransportDetails
+import org.matrix.android.sdk.api.util.MatrixJsonParser
+import org.matrix.android.sdk.internal.extensions.toUnsignedInt
 import org.matrix.olm.OlmSAS
-import timber.log.Timber
 import java.security.SecureRandom
 import java.util.LinkedList
 import javax.crypto.Cipher
@@ -72,7 +71,7 @@ const val KEY_SPEC = "AES"
  *  Implements X25519 ECDH key agreement and AES-256-GCM encryption channel as per MSC3903:
  *  https://github.com/matrix-org/matrix-spec-proposals/pull/3903
  */
-class ECDHRendezvousChannel(override var transport: RendezvousTransport, theirPublicKeyBase64: String?): RendezvousChannel {
+class ECDHRendezvousChannel(override var transport: RendezvousTransport, theirPublicKeyBase64: String?) : RendezvousChannel {
     private var olmSAS: OlmSAS?
     private val ourPublicKey: ByteArray
     private val ecdhAdapter = MatrixJsonParser.getMoshi().adapter(ECDHPayload::class.java)
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousError.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousError.kt
index f731c89649..fec55ffb67 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousError.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousError.kt
@@ -18,5 +18,4 @@ package org.matrix.android.sdk.api.rendezvous.model
 
 import org.matrix.android.sdk.api.rendezvous.RendezvousFailureReason
 
-class RendezvousError(val description: String, val reason: RendezvousFailureReason): RuntimeException(description) {
-}
+class RendezvousError(val description: String, val reason: RendezvousFailureReason) : RuntimeException(description)
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt
index ba60459544..475a4fbe6c 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt
@@ -36,7 +36,7 @@ private val TAG = LoggerTag(SimpleHttpRendezvousTransport::class.java.simpleName
 @JsonClass(generateAdapter = true)
 data class SimpleHttpRendezvousTransportDetails(
         @Json val uri: String
-): RendezvousTransportDetails(type = RendezvousTransportType.MSC3886_SIMPLE_HTTP_V1)
+) : RendezvousTransportDetails(type = RendezvousTransportType.MSC3886_SIMPLE_HTTP_V1)
 
 /**
  * Implementation of the Simple HTTP transport MSC3886: https://github.com/matrix-org/matrix-spec-proposals/pull/3886
diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
index 276cedad43..7a51098439 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
@@ -26,11 +26,9 @@ import im.vector.app.core.di.MavericksAssistedViewModelFactory
 import im.vector.app.core.di.hiltMavericksViewModelFactory
 import im.vector.app.core.extensions.configureAndStart
 import im.vector.app.core.platform.VectorViewModel
-import im.vector.app.features.home.HomeActivity
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.launch
 import org.matrix.android.sdk.api.auth.AuthenticationService
-import org.matrix.android.sdk.api.session.Session
 import org.matrix.android.sdk.api.rendezvous.Rendezvous
 import org.matrix.android.sdk.api.rendezvous.RendezvousFailureReason
 import timber.log.Timber

From efa70fa0ff92b56d15f638dbd240a050f9b3ff25 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Thu, 13 Oct 2022 21:48:33 +0100
Subject: [PATCH 42/93] Revert "Retry scanning if not a QR code"

This reverts commit 87956e943897333f3f7e9be9a6e59d9a7f0c4547.
---
 .../app/features/login/qr/QrCodeLoginInstructionsFragment.kt    | 2 --
 1 file changed, 2 deletions(-)

diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginInstructionsFragment.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginInstructionsFragment.kt
index 17b4ff2409..ae3ba9574b 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginInstructionsFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginInstructionsFragment.kt
@@ -83,7 +83,6 @@ class QrCodeLoginInstructionsFragment : VectorBaseFragment<FragmentQrCodeLoginIn
         if (activityResult.resultCode == Activity.RESULT_OK) {
             val scannedQrCode = QrCodeScannerActivity.getResultText(activityResult.data)
             val wasQrCode = QrCodeScannerActivity.getResultIsQrCode(activityResult.data)
-            Timber.d("Scanned QR code: $scannedQrCode $wasQrCode")
 
             if (wasQrCode && !scannedQrCode.isNullOrBlank()) {
                 onQrCodeScanned(scannedQrCode)
@@ -99,6 +98,5 @@ class QrCodeLoginInstructionsFragment : VectorBaseFragment<FragmentQrCodeLoginIn
 
     private fun onQrCodeScannerFailed() {
         Timber.d("QrCodeLoginInstructionsFragment.onQrCodeScannerFailed")
-        QrCodeScannerActivity.startForResult(requireActivity(), scanActivityResultLauncher)
     }
 }

From d72371906e08b040e57020dc63eabac48eaf73e6 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Fri, 14 Oct 2022 01:45:03 +0100
Subject: [PATCH 43/93] Add flag to allow QR login on all servers + split flag
 for showing in device manager

---
 .../debug/features/DebugFeaturesStateFactory.kt        | 10 ++++++++++
 .../app/features/debug/features/DebugVectorFeatures.kt |  8 ++++++++
 .../main/java/im/vector/app/features/VectorFeatures.kt |  4 ++++
 .../app/features/onboarding/OnboardingViewModel.kt     |  9 +++++++++
 .../devices/v2/VectorSettingsDevicesFragment.kt        |  2 +-
 5 files changed, 32 insertions(+), 1 deletion(-)

diff --git a/vector-app/src/debug/java/im/vector/app/features/debug/features/DebugFeaturesStateFactory.kt b/vector-app/src/debug/java/im/vector/app/features/debug/features/DebugFeaturesStateFactory.kt
index 3a302feba0..1d8171a9e5 100644
--- a/vector-app/src/debug/java/im/vector/app/features/debug/features/DebugFeaturesStateFactory.kt
+++ b/vector-app/src/debug/java/im/vector/app/features/debug/features/DebugFeaturesStateFactory.kt
@@ -95,6 +95,16 @@ class DebugFeaturesStateFactory @Inject constructor(
                                 key = DebugFeatureKeys.qrCodeLoginEnabled,
                                 factory = VectorFeatures::isQrCodeLoginEnabled
                         ),
+                        createBooleanFeature(
+                                label = "Allow QR Code Login for all servers",
+                                key = DebugFeatureKeys.allowQrCodeLoginForAllServers,
+                                factory = VectorFeatures::allowQrCodeLoginForAllServers
+                        ),
+                        createBooleanFeature(
+                                label = "Show QR Code Login in Device Manager",
+                                key = DebugFeatureKeys.allowReciprocateQrCodeLogin,
+                                factory = VectorFeatures::allowReciprocateQrCodeLogin
+                        ),
                         createBooleanFeature(
                                 label = "Enable Voice Broadcast",
                                 key = DebugFeatureKeys.voiceBroadcastEnabled,
diff --git a/vector-app/src/debug/java/im/vector/app/features/debug/features/DebugVectorFeatures.kt b/vector-app/src/debug/java/im/vector/app/features/debug/features/DebugVectorFeatures.kt
index b5ffa28db7..701f2dcab8 100644
--- a/vector-app/src/debug/java/im/vector/app/features/debug/features/DebugVectorFeatures.kt
+++ b/vector-app/src/debug/java/im/vector/app/features/debug/features/DebugVectorFeatures.kt
@@ -82,6 +82,12 @@ class DebugVectorFeatures(
     override fun isQrCodeLoginEnabled() = read(DebugFeatureKeys.qrCodeLoginEnabled)
             ?: vectorFeatures.isQrCodeLoginEnabled()
 
+    override fun allowQrCodeLoginForAllServers() = read(DebugFeatureKeys.allowQrCodeLoginForAllServers)
+            ?: vectorFeatures.allowQrCodeLoginForAllServers()
+
+    override fun allowReciprocateQrCodeLogin() = read(DebugFeatureKeys.allowReciprocateQrCodeLogin)
+            ?: vectorFeatures.allowReciprocateQrCodeLogin()
+
     override fun isVoiceBroadcastEnabled(): Boolean = read(DebugFeatureKeys.voiceBroadcastEnabled)
             ?: vectorFeatures.isVoiceBroadcastEnabled()
 
@@ -147,5 +153,7 @@ object DebugFeatureKeys {
     val newAppLayoutEnabled = booleanPreferencesKey("new-app-layout-enabled")
     val newDeviceManagementEnabled = booleanPreferencesKey("new-device-management-enabled")
     val qrCodeLoginEnabled = booleanPreferencesKey("qr-code-login-enabled")
+    val allowQrCodeLoginForAllServers = booleanPreferencesKey("allow-qr-code-login-for-all-servers")
+    val allowReciprocateQrCodeLogin = booleanPreferencesKey("allow-reciprocate-qr-code-login")
     val voiceBroadcastEnabled = booleanPreferencesKey("voice-broadcast-enabled")
 }
diff --git a/vector/src/main/java/im/vector/app/features/VectorFeatures.kt b/vector/src/main/java/im/vector/app/features/VectorFeatures.kt
index 62eb0523b0..061276ba21 100644
--- a/vector/src/main/java/im/vector/app/features/VectorFeatures.kt
+++ b/vector/src/main/java/im/vector/app/features/VectorFeatures.kt
@@ -42,6 +42,8 @@ interface VectorFeatures {
     fun isNewAppLayoutFeatureEnabled(): Boolean
     fun isNewDeviceManagementEnabled(): Boolean
     fun isQrCodeLoginEnabled(): Boolean
+    fun allowQrCodeLoginForAllServers(): Boolean
+    fun allowReciprocateQrCodeLogin(): Boolean
     fun isVoiceBroadcastEnabled(): Boolean
 }
 
@@ -60,5 +62,7 @@ class DefaultVectorFeatures : VectorFeatures {
     override fun isNewAppLayoutFeatureEnabled(): Boolean = true
     override fun isNewDeviceManagementEnabled(): Boolean = false
     override fun isQrCodeLoginEnabled(): Boolean = false
+    override fun allowQrCodeLoginForAllServers(): Boolean = false
+    override fun allowReciprocateQrCodeLogin(): Boolean = false
     override fun isVoiceBroadcastEnabled(): Boolean = false
 }
diff --git a/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt b/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt
index 03cf2f43e6..8e64948c6a 100644
--- a/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt
@@ -124,7 +124,16 @@ class OnboardingViewModel @AssistedInject constructor(
                         canLoginWithQrCode = false
                 )
             }
+        } else if (vectorFeatures.allowQrCodeLoginForAllServers()) {
+            // allow for all servers
+            setState {
+                copy(
+                        canLoginWithQrCode = true
+                )
+            }
         } else {
+            // check if selected server supports MSC3882 first
+            // FIXME: this should be checking the selected homeserver not defaultHomeserverUrl
             homeServerConnectionConfigFactory.create(defaultHomeserverUrl)?.let {
                 val canLoginWithQrCode = authenticationService.isQrLoginSupported(it)
                 setState {
diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesFragment.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesFragment.kt
index 48f66cfc75..7e6da851dd 100644
--- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesFragment.kt
@@ -158,7 +158,7 @@ class VectorSettingsDevicesFragment :
     }
 
     private fun initQrLoginView() {
-        if (!vectorFeatures.isQrCodeLoginEnabled()) {
+        if (!vectorFeatures.allowReciprocateQrCodeLogin()) {
             views.deviceListHeaderSignInWithQrCode.isVisible = false
             views.deviceListHeaderScanQrCodeButton.isVisible = false
             views.deviceListHeaderShowQrCodeButton.isVisible = false

From de4232dff519b05063e0e671789802bbd96830cd Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Fri, 14 Oct 2022 02:04:08 +0100
Subject: [PATCH 44/93] Fix logic for showing confirm button

---
 .../vector/app/features/login/qr/QrCodeLoginStatusFragment.kt   | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt
index 5451a16b44..05ed070915 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt
@@ -115,7 +115,7 @@ class QrCodeLoginStatusFragment : VectorBaseFragment<FragmentQrCodeLoginStatusBi
     }
 
     private fun handleConnectionEstablished(connectionStatus: QrCodeLoginConnectionStatus.Connected, loginType: QrCodeLoginType) {
-        views.qrCodeLoginConfirmSecurityCodeLayout.isVisible = true
+        views.qrCodeLoginConfirmSecurityCodeLayout.isVisible = loginType == QrCodeLoginType.LINK_A_DEVICE
         views.qrCodeLoginStatusLoadingLayout.isVisible = false
         views.qrCodeLoginStatusHeaderView.isVisible = true
         views.qrCodeLoginStatusSecurityCode.isVisible = true

From e439b72e48170c9bfcd0b57157c60816655d52e8 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Fri, 14 Oct 2022 13:46:57 +0100
Subject: [PATCH 45/93] Handle master key trust during E2EE set up

---
 .../android/sdk/api/rendezvous/Rendezvous.kt      | 15 +++++++++++----
 1 file changed, 11 insertions(+), 4 deletions(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/Rendezvous.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/Rendezvous.kt
index e33130e529..17f3a73181 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/Rendezvous.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/Rendezvous.kt
@@ -176,10 +176,17 @@ class Rendezvous(
         crypto.setDeviceVerification(DeviceTrustLevel(locallyVerified = true, crossSigningVerified = false), userId, verifyingDeviceId)
 
         // TODO: what do we do with the master key?
-//        verificationResponse.master_key ?.let {
-//            // set master key as trusted
-//            crypto.setDeviceVerification(DeviceTrustLevel(locallyVerified = true, crossSigningVerified = false), userId, it)
-//        }
+        verificationResponse.master_key ?.let { masterKeyFromVerifyingDevice ->
+            // set master key as trusted
+            crypto.crossSigningService().getMyCrossSigningKeys()?.masterKey()?.let { localMasterKey ->
+                if (localMasterKey.unpaddedBase64PublicKey == masterKeyFromVerifyingDevice) {
+                    Timber.tag(TAG).i("Setting master key as trusted")
+                    crypto.crossSigningService().markMyMasterKeyAsTrusted()
+                } else {
+                    Timber.tag(TAG).w("Master key from verifying device doesn't match: $masterKeyFromVerifyingDevice vs $localMasterKey")
+                }
+            } ?: Timber.tag(TAG).i("No local master key")
+        } ?: Timber.tag(TAG).i("No master key given by verifying device")
 
         // request secrets from the verifying device
         Timber.tag(TAG).i("Requesting secrets from $verifyingDeviceId")

From f999e7275952633a87d9cff123a4211546e7e735 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Fri, 14 Oct 2022 14:01:13 +0100
Subject: [PATCH 46/93] Changelog

---
 changelog.d/7369.feature | 1 +
 1 file changed, 1 insertion(+)
 create mode 100644 changelog.d/7369.feature

diff --git a/changelog.d/7369.feature b/changelog.d/7369.feature
new file mode 100644
index 0000000000..240fac3516
--- /dev/null
+++ b/changelog.d/7369.feature
@@ -0,0 +1 @@
+Add logic for sign in with QR code

From 411b766890ab33722b10be177bb1f3e64d963e75 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Fri, 14 Oct 2022 14:17:19 +0100
Subject: [PATCH 47/93] Refactor to camelcase

---
 .../android/sdk/api/rendezvous/Rendezvous.kt  | 22 +++++++++----------
 1 file changed, 11 insertions(+), 11 deletions(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/Rendezvous.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/Rendezvous.kt
index 17f3a73181..67bdeb2235 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/Rendezvous.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/Rendezvous.kt
@@ -49,12 +49,12 @@ internal data class Payload(
         @Json val protocols: List<String>? = null,
         @Json val protocol: String? = null,
         @Json val homeserver: String? = null,
-        @Json val login_token: String? = null,
-        @Json val device_id: String? = null,
-        @Json val device_key: String? = null,
-        @Json val verifying_device_id: String? = null,
-        @Json val verifying_device_key: String? = null,
-        @Json val master_key: String? = null
+        @Json(name = "login_token") val loginToken: String? = null,
+        @Json(name = "device_id") val deviceId: String? = null,
+        @Json(name = "device_key") val deviceKey: String? = null,
+        @Json(name = "verifying_device_id") val verifyingDeviceId: String? = null,
+        @Json(name = "verifying_device_key") val verifyingDeviceKey: String? = null,
+        @Json(name = "master_key") val masterKey: String? = null
 )
 
 private val TAG = LoggerTag(Rendezvous::class.java.simpleName, LoggerTag.RENDEZVOUS).value
@@ -146,7 +146,7 @@ class Rendezvous(
         }
 
         val homeserver = loginToken?.homeserver ?: throw RuntimeException("No homeserver returned")
-        val login_token = loginToken.login_token ?: throw RuntimeException("No login token returned")
+        val login_token = loginToken.loginToken ?: throw RuntimeException("No login token returned")
 
         Timber.tag(TAG).i("Got login_token: $login_token for $homeserver")
 
@@ -159,14 +159,14 @@ class Rendezvous(
         val crypto = session.cryptoService()
         val deviceId = crypto.getMyDevice().deviceId
         val deviceKey = crypto.getMyDevice().fingerprint()
-        send(Payload(PayloadType.Progress, outcome = "success", device_id = deviceId, device_key = deviceKey))
+        send(Payload(PayloadType.Progress, outcome = "success", deviceId = deviceId, deviceKey = deviceKey))
 
         // await confirmation of verification
 
         val verificationResponse = receive()
-        val verifyingDeviceId = verificationResponse?.verifying_device_id ?: throw RuntimeException("No verifying device id returned")
+        val verifyingDeviceId = verificationResponse?.verifyingDeviceId ?: throw RuntimeException("No verifying device id returned")
         val verifyingDeviceFromServer = crypto.getCryptoDeviceInfo(userId, verifyingDeviceId)
-        if (verifyingDeviceFromServer?.fingerprint() != verificationResponse.verifying_device_key) {
+        if (verifyingDeviceFromServer?.fingerprint() != verificationResponse.verifyingDeviceKey) {
             Timber.tag(TAG).w("Verifying device $verifyingDeviceId doesn't match: $verifyingDeviceFromServer")
             return
         }
@@ -176,7 +176,7 @@ class Rendezvous(
         crypto.setDeviceVerification(DeviceTrustLevel(locallyVerified = true, crossSigningVerified = false), userId, verifyingDeviceId)
 
         // TODO: what do we do with the master key?
-        verificationResponse.master_key ?.let { masterKeyFromVerifyingDevice ->
+        verificationResponse.masterKey ?.let { masterKeyFromVerifyingDevice ->
             // set master key as trusted
             crypto.crossSigningService().getMyCrossSigningKeys()?.masterKey()?.let { localMasterKey ->
                 if (localMasterKey.unpaddedBase64PublicKey == masterKeyFromVerifyingDevice) {

From 6426ff40d351396741ebf8aa71da1b87d9661673 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Fri, 14 Oct 2022 14:18:50 +0100
Subject: [PATCH 48/93] Linting

---
 .../org/matrix/android/sdk/api/rendezvous/Rendezvous.kt    | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/Rendezvous.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/Rendezvous.kt
index 67bdeb2235..6403d17031 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/Rendezvous.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/Rendezvous.kt
@@ -91,7 +91,11 @@ class Rendezvous(
 
         if (incompatible) {
             send(Payload(PayloadType.Finish, intent = ourIntent))
-            val reason = if (ourIntent == RendezvousIntent.LOGIN_ON_NEW_DEVICE) RendezvousFailureReason.OtherDeviceNotSignedIn else RendezvousFailureReason.OtherDeviceAlreadySignedIn
+            val reason = if (ourIntent == RendezvousIntent.LOGIN_ON_NEW_DEVICE) {
+                RendezvousFailureReason.OtherDeviceNotSignedIn
+            } else {
+                RendezvousFailureReason.OtherDeviceAlreadySignedIn
+            }
             channel.cancel(reason)
         }
 
@@ -175,7 +179,6 @@ class Rendezvous(
         Timber.tag(TAG).i("Setting device $verifyingDeviceId as verified")
         crypto.setDeviceVerification(DeviceTrustLevel(locallyVerified = true, crossSigningVerified = false), userId, verifyingDeviceId)
 
-        // TODO: what do we do with the master key?
         verificationResponse.masterKey ?.let { masterKeyFromVerifyingDevice ->
             // set master key as trusted
             crypto.crossSigningService().getMyCrossSigningKeys()?.masterKey()?.let { localMasterKey ->

From fdc55965ca0e3328d6ed4f32b00bde97f00941b9 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Fri, 14 Oct 2022 17:23:26 +0100
Subject: [PATCH 49/93] Linting

---
 .../matrix/android/sdk/api/rendezvous/RendezvousChannel.kt   | 5 ++++-
 .../app/features/login/qr/QrCodeLoginStatusFragment.kt       | 3 ++-
 2 files changed, 6 insertions(+), 2 deletions(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousChannel.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousChannel.kt
index 588d034f10..9d2843ce66 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousChannel.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousChannel.kt
@@ -19,6 +19,9 @@ package org.matrix.android.sdk.api.rendezvous
 import org.matrix.android.sdk.api.rendezvous.model.ECDHRendezvousCode
 import org.matrix.android.sdk.api.rendezvous.model.RendezvousIntent
 
+/**
+ * Representation of a rendezvous channel such as that described by MSC3903
+ */
 interface RendezvousChannel {
     var transport: RendezvousTransport
 
@@ -44,7 +47,7 @@ interface RendezvousChannel {
      */
     suspend fun close()
 
-    // TODO: this should be transport independent in the future
+    // In future we probably want this to be a more generic RendezvousCode but it is suffice for now
     suspend fun generateCode(intent: RendezvousIntent): ECDHRendezvousCode
     suspend fun cancel(reason: RendezvousFailureReason)
 }
diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt
index 556be3af4d..f0030d6763 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt
@@ -80,7 +80,8 @@ class QrCodeLoginStatusFragment : VectorBaseFragment<FragmentQrCodeLoginStatusBi
 
     private fun getErrorCode(reason: RendezvousFailureReason): String {
         return when (reason) {
-            RendezvousFailureReason.UnsupportedAlgorithm, RendezvousFailureReason.UnsupportedTransport -> getString(R.string.qr_code_login_header_failed_device_is_not_supported_description)
+            RendezvousFailureReason.UnsupportedAlgorithm,
+            RendezvousFailureReason.UnsupportedTransport -> getString(R.string.qr_code_login_header_failed_device_is_not_supported_description)
             RendezvousFailureReason.Expired -> getString(R.string.qr_code_login_header_failed_timeout_description)
             RendezvousFailureReason.UserDeclined -> getString(R.string.qr_code_login_header_failed_denied_description)
             else -> getString(R.string.qr_code_login_header_failed_other_description)

From bfe3daa37fda9cd42bc541b5c13c4f75bb58e221 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Fri, 14 Oct 2022 17:25:06 +0100
Subject: [PATCH 50/93] Fix compile error from bad merge

---
 .../vector/app/features/login/qr/QrCodeLoginViewModel.kt   | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
index 7a51098439..af0df22ca3 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
@@ -24,8 +24,8 @@ import dagger.assisted.AssistedInject
 import im.vector.app.core.di.ActiveSessionHolder
 import im.vector.app.core.di.MavericksAssistedViewModelFactory
 import im.vector.app.core.di.hiltMavericksViewModelFactory
-import im.vector.app.core.extensions.configureAndStart
 import im.vector.app.core.platform.VectorViewModel
+import im.vector.app.core.session.ConfigureAndStartSessionUseCase
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.launch
 import org.matrix.android.sdk.api.auth.AuthenticationService
@@ -37,7 +37,8 @@ class QrCodeLoginViewModel @AssistedInject constructor(
         @Assisted private val initialState: QrCodeLoginViewState,
         private val applicationContext: Context,
         private val authenticationService: AuthenticationService,
-        private val activeSessionHolder: ActiveSessionHolder
+        private val activeSessionHolder: ActiveSessionHolder,
+        private val configureAndStartSessionUseCase: ConfigureAndStartSessionUseCase
 ) : VectorViewModel<QrCodeLoginViewState, QrCodeLoginAction, QrCodeLoginViewEvents>(initialState) {
     val TAG: String = QrCodeLoginViewModel::class.java.simpleName
 
@@ -97,7 +98,7 @@ class QrCodeLoginViewModel @AssistedInject constructor(
                         activeSessionHolder.setActiveSession(session)
                         authenticationService.reset()
 
-                        session.configureAndStart(applicationContext)
+                        configureAndStartSessionUseCase.execute(session, startSyncing)
 
                         rendezvous.completeVerificationOnNewDevice(session)
 

From a3fc78594502953d86944d3f1ec0509c13921e7d Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Fri, 14 Oct 2022 18:52:42 +0100
Subject: [PATCH 51/93] Fix missing param

---
 .../im/vector/app/features/login/qr/QrCodeLoginViewModel.kt     | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
index af0df22ca3..81a00cf548 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
@@ -98,7 +98,7 @@ class QrCodeLoginViewModel @AssistedInject constructor(
                         activeSessionHolder.setActiveSession(session)
                         authenticationService.reset()
 
-                        configureAndStartSessionUseCase.execute(session, startSyncing)
+                        configureAndStartSessionUseCase.execute(session)
 
                         rendezvous.completeVerificationOnNewDevice(session)
 

From d979b50b2cd8b098b5bd3e353264e7e76be5dabe Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Mon, 17 Oct 2022 10:52:24 +0100
Subject: [PATCH 52/93] Logging cleanup

---
 .../android/sdk/api/rendezvous/Rendezvous.kt  | 22 ++++++++++++-------
 .../channels/ECDHRendezvousChannel.kt         | 16 ++++----------
 .../SimpleHttpRendezvousTransport.kt          | 11 +++-------
 3 files changed, 21 insertions(+), 28 deletions(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/Rendezvous.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/Rendezvous.kt
index 6403d17031..b43b122cba 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/Rendezvous.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/Rendezvous.kt
@@ -36,9 +36,14 @@ import org.matrix.android.sdk.api.util.MatrixJsonParser
 import timber.log.Timber
 
 internal enum class PayloadType(val value: String) {
-    @Json(name = "m.login.start") Start("m.login.start"),
-    @Json(name = "m.login.finish") Finish("m.login.finish"),
-    @Json(name = "m.login.progress") Progress("m.login.progress")
+    @Json(name = "m.login.start")
+    Start("m.login.start"),
+
+    @Json(name = "m.login.finish")
+    Finish("m.login.finish"),
+
+    @Json(name = "m.login.progress")
+    Progress("m.login.progress")
 }
 
 @JsonClass(generateAdapter = true)
@@ -150,12 +155,12 @@ class Rendezvous(
         }
 
         val homeserver = loginToken?.homeserver ?: throw RuntimeException("No homeserver returned")
-        val login_token = loginToken.loginToken ?: throw RuntimeException("No login token returned")
+        val token = loginToken.loginToken ?: throw RuntimeException("No login token returned")
 
-        Timber.tag(TAG).i("Got login_token: $login_token for $homeserver")
+        Timber.tag(TAG).i("Got login_token now attempting to sign in with $homeserver")
 
         val hsConfig = HomeServerConnectionConfig(homeServerUri = Uri.parse(homeserver))
-        return authenticationService.loginUsingQrLoginToken(hsConfig, login_token)
+        return authenticationService.loginUsingQrLoginToken(hsConfig, token)
     }
 
     suspend fun completeVerificationOnNewDevice(session: Session) {
@@ -171,8 +176,8 @@ class Rendezvous(
         val verifyingDeviceId = verificationResponse?.verifyingDeviceId ?: throw RuntimeException("No verifying device id returned")
         val verifyingDeviceFromServer = crypto.getCryptoDeviceInfo(userId, verifyingDeviceId)
         if (verifyingDeviceFromServer?.fingerprint() != verificationResponse.verifyingDeviceKey) {
-            Timber.tag(TAG).w("Verifying device $verifyingDeviceId doesn't match: $verifyingDeviceFromServer")
-            return
+            Timber.tag(TAG).w("Verifying device $verifyingDeviceId key doesn't match: ${verifyingDeviceFromServer?.fingerprint()} vs ${verificationResponse.verifyingDeviceKey})")
+            throw RuntimeException("Key from verifying device doesn't match")
         }
 
         // set other device as verified
@@ -187,6 +192,7 @@ class Rendezvous(
                     crypto.crossSigningService().markMyMasterKeyAsTrusted()
                 } else {
                     Timber.tag(TAG).w("Master key from verifying device doesn't match: $masterKeyFromVerifyingDevice vs $localMasterKey")
+                    throw RuntimeException("Master key from verifying device doesn't match")
                 }
             } ?: Timber.tag(TAG).i("No local master key")
         } ?: Timber.tag(TAG).i("No master key given by verifying device")
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt
index 1c8bca5d1c..4d5ed30ac5 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt
@@ -33,6 +33,7 @@ import org.matrix.android.sdk.api.rendezvous.transports.SimpleHttpRendezvousTran
 import org.matrix.android.sdk.api.util.MatrixJsonParser
 import org.matrix.android.sdk.internal.extensions.toUnsignedInt
 import org.matrix.olm.OlmSAS
+import timber.log.Timber
 import java.security.SecureRandom
 import java.util.LinkedList
 import javax.crypto.Cipher
@@ -93,7 +94,7 @@ class ECDHRendezvousChannel(override var transport: RendezvousTransport, theirPu
         val isInitiator = theirPublicKey == null
 
         if (isInitiator) {
-//            Timber.tag(TAG).i("Waiting for other device to send their public key")
+            Timber.tag(TAG).i("Waiting for other device to send their public key")
             val res = this.receiveAsPayload() ?: throw RuntimeException("No reply from other device")
 
             if (res.key == null) {
@@ -105,7 +106,7 @@ class ECDHRendezvousChannel(override var transport: RendezvousTransport, theirPu
             theirPublicKey = Base64.decode(res.key, Base64.NO_WRAP)
         } else {
             // send our public key unencrypted
-//            Timber.tag(TAG).i("Sending public key")
+            Timber.tag(TAG).i("Sending public key")
             send(
                     ECDHPayload(
                     algorithm = SecureRendezvousChannelAlgorithm.ECDH_V1,
@@ -122,11 +123,6 @@ class ECDHRendezvousChannel(override var transport: RendezvousTransport, theirPu
 
         aesKey = olmSAS!!.generateShortCode(aesInfo, 32)
 
-//        Timber.tag(TAG).i("Our public key: ${Base64.encodeToString(ourPublicKey, Base64.NO_WRAP)}")
-//        Timber.tag(TAG).i("Their public key: ${Base64.encodeToString(theirPublicKey, Base64.NO_WRAP)}")
-//        Timber.tag(TAG).i("AES info: $aesInfo")
-//        Timber.tag(TAG).i("AES key: ${Base64.encodeToString(aesKey, Base64.NO_WRAP)}")
-
         val rawChecksum = olmSAS!!.generateShortCode(aesInfo, 5)
         return getDecimalCodeRepresentation(rawChecksum)
     }
@@ -181,7 +177,6 @@ class ECDHRendezvousChannel(override var transport: RendezvousTransport, theirPu
     }
 
     private fun encrypt(plainText: ByteArray): ECDHPayload {
-//        Timber.tag(TAG).d("Encrypting: ${plainText.toString(Charsets.UTF_8)}")
         val iv = ByteArray(16)
         SecureRandom().nextBytes(iv)
 
@@ -211,9 +206,6 @@ class ECDHRendezvousChannel(override var transport: RendezvousTransport, theirPu
         plainText.addAll(encryptCipher.update(Base64.decode(payload.ciphertext, Base64.NO_WRAP)).toList())
         plainText.addAll(encryptCipher.doFinal().toList())
 
-        val plainTextBytes = plainText.toByteArray()
-
-//        Timber.tag(TAG).d("Decrypted: ${plainTextBytes.toString(Charsets.UTF_8)}")
-        return plainTextBytes
+        return plainText.toByteArray()
     }
 }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt
index 475a4fbe6c..004cf38e24 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt
@@ -67,8 +67,6 @@ class SimpleHttpRendezvousTransport(override var onCancelled: ((reason: Rendezvo
         // TODO: properly determine endpoint
         val uri = if (uri != null) uri!! else "https://rendezvous.lab.element.dev"
 
-//        Timber.tag(TAG).i("Sending data: ${data.toString(Charsets.UTF_8)} to $uri")
-
         val httpClient = okhttp3.OkHttpClient.Builder().build()
 
         val request = Request.Builder()
@@ -123,8 +121,6 @@ class SimpleHttpRendezvousTransport(override var onCancelled: ((reason: Rendezvo
             val response = httpClient.newCall(request.build()).execute()
 
             try {
-//                Timber.tag(TAG).d("Received polling response: ${response.code} from $uri")
-
                 if (response.code == 404) {
                     cancel(RendezvousFailureReason.Unknown)
                     return null
@@ -140,9 +136,7 @@ class SimpleHttpRendezvousTransport(override var onCancelled: ((reason: Rendezvo
                     response.header("etag")?.let {
                         etag = it
                     }
-                    val data = response.body?.bytes()
-//                    Timber.tag(TAG).d("Received data: ${data?.toString(Charsets.UTF_8)} from $uri with etag $etag")
-                    return data
+                    return response.body?.bytes()
                 }
 
                 done = false
@@ -159,7 +153,8 @@ class SimpleHttpRendezvousTransport(override var onCancelled: ((reason: Rendezvo
         var mappedReason = reason
         Timber.tag(TAG).i("$expiresAt")
         if (mappedReason == RendezvousFailureReason.Unknown &&
-                expiresAt != null && Date() > expiresAt) {
+                expiresAt != null && Date() > expiresAt
+        ) {
             mappedReason = RendezvousFailureReason.Expired
         }
 

From ed6bc01bef48b4266dbe34e600f8038b0dcc6a3a Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Mon, 17 Oct 2022 10:54:28 +0100
Subject: [PATCH 53/93] Resolve TODO

---
 .../api/rendezvous/transports/SimpleHttpRendezvousTransport.kt | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt
index 004cf38e24..dde5edcb93 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt
@@ -64,8 +64,7 @@ class SimpleHttpRendezvousTransport(override var onCancelled: ((reason: Rendezvo
         }
 
         val method = if (uri != null) "PUT" else "POST"
-        // TODO: properly determine endpoint
-        val uri = if (uri != null) uri!! else "https://rendezvous.lab.element.dev"
+        val uri = this.uri ?: throw RuntimeException("No rendezvous URI")
 
         val httpClient = okhttp3.OkHttpClient.Builder().build()
 

From 33be5c257dcaa8c674486a77c5851d1a6a626f7e Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Mon, 17 Oct 2022 11:24:48 +0100
Subject: [PATCH 54/93] Refactor into dedicated files and companion objects

---
 .../android/sdk/api/rendezvous/Rendezvous.kt  | 39 +++---------
 .../channels/ECDHRendezvousChannel.kt         | 59 ++++++++++---------
 .../api/rendezvous/model/ECDHRendezvous.kt    |  7 ---
 .../rendezvous/model/ECDHRendezvousCode.kt    | 26 ++++++++
 .../sdk/api/rendezvous/model/Payload.kt       | 36 +++++++++++
 .../sdk/api/rendezvous/model/PayloadType.kt   | 30 ++++++++++
 .../SimpleHttpRendezvousTransportDetails.kt   | 25 ++++++++
 .../SimpleHttpRendezvousTransport.kt          | 13 ++--
 8 files changed, 159 insertions(+), 76 deletions(-)
 create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/ECDHRendezvousCode.kt
 create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Payload.kt
 create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/PayloadType.kt
 create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/SimpleHttpRendezvousTransportDetails.kt

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/Rendezvous.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/Rendezvous.kt
index b43b122cba..4270d4a09c 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/Rendezvous.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/Rendezvous.kt
@@ -17,13 +17,13 @@
 package org.matrix.android.sdk.api.rendezvous
 
 import android.net.Uri
-import com.squareup.moshi.Json
-import com.squareup.moshi.JsonClass
 import org.matrix.android.sdk.api.auth.AuthenticationService
 import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig
 import org.matrix.android.sdk.api.logger.LoggerTag
 import org.matrix.android.sdk.api.rendezvous.channels.ECDHRendezvousChannel
 import org.matrix.android.sdk.api.rendezvous.model.ECDHRendezvousCode
+import org.matrix.android.sdk.api.rendezvous.model.Payload
+import org.matrix.android.sdk.api.rendezvous.model.PayloadType
 import org.matrix.android.sdk.api.rendezvous.model.RendezvousIntent
 import org.matrix.android.sdk.api.rendezvous.transports.SimpleHttpRendezvousTransport
 import org.matrix.android.sdk.api.session.Session
@@ -35,35 +35,6 @@ import org.matrix.android.sdk.api.session.crypto.crosssigning.USER_SIGNING_KEY_S
 import org.matrix.android.sdk.api.util.MatrixJsonParser
 import timber.log.Timber
 
-internal enum class PayloadType(val value: String) {
-    @Json(name = "m.login.start")
-    Start("m.login.start"),
-
-    @Json(name = "m.login.finish")
-    Finish("m.login.finish"),
-
-    @Json(name = "m.login.progress")
-    Progress("m.login.progress")
-}
-
-@JsonClass(generateAdapter = true)
-internal data class Payload(
-        @Json val type: PayloadType,
-        @Json val intent: RendezvousIntent? = null,
-        @Json val outcome: String? = null,
-        @Json val protocols: List<String>? = null,
-        @Json val protocol: String? = null,
-        @Json val homeserver: String? = null,
-        @Json(name = "login_token") val loginToken: String? = null,
-        @Json(name = "device_id") val deviceId: String? = null,
-        @Json(name = "device_key") val deviceKey: String? = null,
-        @Json(name = "verifying_device_id") val verifyingDeviceId: String? = null,
-        @Json(name = "verifying_device_key") val verifyingDeviceKey: String? = null,
-        @Json(name = "master_key") val masterKey: String? = null
-)
-
-private val TAG = LoggerTag(Rendezvous::class.java.simpleName, LoggerTag.RENDEZVOUS).value
-
 /**
  * Implementation of MSC3906 to sign in + E2EE set up using a QR code.
  */
@@ -72,6 +43,8 @@ class Rendezvous(
         val theirIntent: RendezvousIntent,
 ) {
     companion object {
+        private val TAG = LoggerTag(Rendezvous::class.java.simpleName, LoggerTag.RENDEZVOUS).value
+
         fun buildChannelFromCode(code: String, onCancelled: (reason: RendezvousFailureReason) -> Unit): Rendezvous {
             val parsed = MatrixJsonParser.getMoshi().adapter(ECDHRendezvousCode::class.java).fromJson(code) ?: throw RuntimeException("Invalid code")
 
@@ -176,7 +149,9 @@ class Rendezvous(
         val verifyingDeviceId = verificationResponse?.verifyingDeviceId ?: throw RuntimeException("No verifying device id returned")
         val verifyingDeviceFromServer = crypto.getCryptoDeviceInfo(userId, verifyingDeviceId)
         if (verifyingDeviceFromServer?.fingerprint() != verificationResponse.verifyingDeviceKey) {
-            Timber.tag(TAG).w("Verifying device $verifyingDeviceId key doesn't match: ${verifyingDeviceFromServer?.fingerprint()} vs ${verificationResponse.verifyingDeviceKey})")
+            Timber.tag(TAG).w("Verifying device $verifyingDeviceId key doesn't match: ${
+                verifyingDeviceFromServer?.fingerprint()} vs ${verificationResponse.verifyingDeviceKey})"
+            )
             throw RuntimeException("Key from verifying device doesn't match")
         }
 
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt
index 4d5ed30ac5..5dbac894ae 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt
@@ -29,7 +29,7 @@ import org.matrix.android.sdk.api.rendezvous.model.ECDHRendezvousCode
 import org.matrix.android.sdk.api.rendezvous.model.RendezvousError
 import org.matrix.android.sdk.api.rendezvous.model.RendezvousIntent
 import org.matrix.android.sdk.api.rendezvous.model.SecureRendezvousChannelAlgorithm
-import org.matrix.android.sdk.api.rendezvous.transports.SimpleHttpRendezvousTransportDetails
+import org.matrix.android.sdk.api.rendezvous.model.SimpleHttpRendezvousTransportDetails
 import org.matrix.android.sdk.api.util.MatrixJsonParser
 import org.matrix.android.sdk.internal.extensions.toUnsignedInt
 import org.matrix.olm.OlmSAS
@@ -40,39 +40,40 @@ import javax.crypto.Cipher
 import javax.crypto.spec.IvParameterSpec
 import javax.crypto.spec.SecretKeySpec
 
-@JsonClass(generateAdapter = true)
-data class ECDHPayload(
-        @Json val algorithm: SecureRendezvousChannelAlgorithm? = null,
-        @Json val key: String? = null,
-        @Json val ciphertext: String? = null,
-        @Json val iv: String? = null
-)
-
-private val TAG = LoggerTag(ECDHRendezvousChannel::class.java.simpleName, LoggerTag.RENDEZVOUS).value
-
-fun getDecimalCodeRepresentation(byteArray: ByteArray): String {
-    val b0 = byteArray[0].toUnsignedInt() // need unsigned byte
-    val b1 = byteArray[1].toUnsignedInt() // need unsigned byte
-    val b2 = byteArray[2].toUnsignedInt() // need unsigned byte
-    val b3 = byteArray[3].toUnsignedInt() // need unsigned byte
-    val b4 = byteArray[4].toUnsignedInt() // need unsigned byte
-    // (B0 << 5 | B1 >> 3) + 1000
-    val first = (b0.shl(5) or b1.shr(3)) + 1000
-    // ((B1 & 0x7) << 10 | B2 << 2 | B3 >> 6) + 1000
-    val second = ((b1 and 0x7).shl(10) or b2.shl(2) or b3.shr(6)) + 1000
-    // ((B3 & 0x3f) << 7 | B4 >> 1) + 1000
-    val third = ((b3 and 0x3f).shl(7) or b4.shr(1)) + 1000
-    return "$first-$second-$third"
-}
-
-const val ALGORITHM_SPEC = "AES/GCM/NoPadding"
-const val KEY_SPEC = "AES"
-
 /**
  *  Implements X25519 ECDH key agreement and AES-256-GCM encryption channel as per MSC3903:
  *  https://github.com/matrix-org/matrix-spec-proposals/pull/3903
  */
 class ECDHRendezvousChannel(override var transport: RendezvousTransport, theirPublicKeyBase64: String?) : RendezvousChannel {
+    companion object {
+        private const val ALGORITHM_SPEC = "AES/GCM/NoPadding"
+        private const val KEY_SPEC = "AES"
+        private val TAG = LoggerTag(ECDHRendezvousChannel::class.java.simpleName, LoggerTag.RENDEZVOUS).value
+
+        private fun getDecimalCodeRepresentation(byteArray: ByteArray): String {
+            val b0 = byteArray[0].toUnsignedInt() // need unsigned byte
+            val b1 = byteArray[1].toUnsignedInt() // need unsigned byte
+            val b2 = byteArray[2].toUnsignedInt() // need unsigned byte
+            val b3 = byteArray[3].toUnsignedInt() // need unsigned byte
+            val b4 = byteArray[4].toUnsignedInt() // need unsigned byte
+            // (B0 << 5 | B1 >> 3) + 1000
+            val first = (b0.shl(5) or b1.shr(3)) + 1000
+            // ((B1 & 0x7) << 10 | B2 << 2 | B3 >> 6) + 1000
+            val second = ((b1 and 0x7).shl(10) or b2.shl(2) or b3.shr(6)) + 1000
+            // ((B3 & 0x3f) << 7 | B4 >> 1) + 1000
+            val third = ((b3 and 0x3f).shl(7) or b4.shr(1)) + 1000
+            return "$first-$second-$third"
+        }
+    }
+
+    @JsonClass(generateAdapter = true)
+    internal data class ECDHPayload(
+            @Json val algorithm: SecureRendezvousChannelAlgorithm? = null,
+            @Json val key: String? = null,
+            @Json val ciphertext: String? = null,
+            @Json val iv: String? = null
+    )
+
     private var olmSAS: OlmSAS?
     private val ourPublicKey: ByteArray
     private val ecdhAdapter = MatrixJsonParser.getMoshi().adapter(ECDHPayload::class.java)
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/ECDHRendezvous.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/ECDHRendezvous.kt
index b203101b66..0840e1ca2e 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/ECDHRendezvous.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/ECDHRendezvous.kt
@@ -18,7 +18,6 @@ package org.matrix.android.sdk.api.rendezvous.model
 
 import com.squareup.moshi.Json
 import com.squareup.moshi.JsonClass
-import org.matrix.android.sdk.api.rendezvous.transports.SimpleHttpRendezvousTransportDetails
 
 @JsonClass(generateAdapter = true)
 data class ECDHRendezvous(
@@ -26,9 +25,3 @@ data class ECDHRendezvous(
         @Json val algorithm: SecureRendezvousChannelAlgorithm,
         @Json val key: String
 )
-
-@JsonClass(generateAdapter = true)
-data class ECDHRendezvousCode(
-        @Json val intent: RendezvousIntent,
-        @Json val rendezvous: ECDHRendezvous
-)
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/ECDHRendezvousCode.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/ECDHRendezvousCode.kt
new file mode 100644
index 0000000000..410c5c1036
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/ECDHRendezvousCode.kt
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2022 The Matrix.org Foundation C.I.C.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.matrix.android.sdk.api.rendezvous.model
+
+import com.squareup.moshi.Json
+import com.squareup.moshi.JsonClass
+
+@JsonClass(generateAdapter = true)
+data class ECDHRendezvousCode(
+        @Json val intent: RendezvousIntent,
+        @Json val rendezvous: ECDHRendezvous
+)
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Payload.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Payload.kt
new file mode 100644
index 0000000000..5627452232
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Payload.kt
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2022 The Matrix.org Foundation C.I.C.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.matrix.android.sdk.api.rendezvous.model
+
+import com.squareup.moshi.Json
+import com.squareup.moshi.JsonClass
+
+@JsonClass(generateAdapter = true)
+internal data class Payload(
+        @Json val type: PayloadType,
+        @Json val intent: RendezvousIntent? = null,
+        @Json val outcome: String? = null,
+        @Json val protocols: List<String>? = null,
+        @Json val protocol: String? = null,
+        @Json val homeserver: String? = null,
+        @Json(name = "login_token") val loginToken: String? = null,
+        @Json(name = "device_id") val deviceId: String? = null,
+        @Json(name = "device_key") val deviceKey: String? = null,
+        @Json(name = "verifying_device_id") val verifyingDeviceId: String? = null,
+        @Json(name = "verifying_device_key") val verifyingDeviceKey: String? = null,
+        @Json(name = "master_key") val masterKey: String? = null
+)
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/PayloadType.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/PayloadType.kt
new file mode 100644
index 0000000000..9854e7c070
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/PayloadType.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2022 The Matrix.org Foundation C.I.C.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.matrix.android.sdk.api.rendezvous.model
+
+import com.squareup.moshi.Json
+
+internal enum class PayloadType(val value: String) {
+    @Json(name = "m.login.start")
+    Start("m.login.start"),
+
+    @Json(name = "m.login.finish")
+    Finish("m.login.finish"),
+
+    @Json(name = "m.login.progress")
+    Progress("m.login.progress")
+}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/SimpleHttpRendezvousTransportDetails.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/SimpleHttpRendezvousTransportDetails.kt
new file mode 100644
index 0000000000..70a441d760
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/SimpleHttpRendezvousTransportDetails.kt
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2022 The Matrix.org Foundation C.I.C.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.matrix.android.sdk.api.rendezvous.model
+
+import com.squareup.moshi.Json
+import com.squareup.moshi.JsonClass
+
+@JsonClass(generateAdapter = true)
+data class SimpleHttpRendezvousTransportDetails(
+        @Json val uri: String
+) : RendezvousTransportDetails(type = RendezvousTransportType.MSC3886_SIMPLE_HTTP_V1)
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt
index dde5edcb93..03e8b0cda7 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt
@@ -26,22 +26,19 @@ import org.matrix.android.sdk.api.logger.LoggerTag
 import org.matrix.android.sdk.api.rendezvous.RendezvousFailureReason
 import org.matrix.android.sdk.api.rendezvous.RendezvousTransport
 import org.matrix.android.sdk.api.rendezvous.model.RendezvousTransportDetails
-import org.matrix.android.sdk.api.rendezvous.model.RendezvousTransportType
+import org.matrix.android.sdk.api.rendezvous.model.SimpleHttpRendezvousTransportDetails
 import timber.log.Timber
 import java.text.SimpleDateFormat
 import java.util.Date
 
-private val TAG = LoggerTag(SimpleHttpRendezvousTransport::class.java.simpleName, LoggerTag.RENDEZVOUS).value
-
-@JsonClass(generateAdapter = true)
-data class SimpleHttpRendezvousTransportDetails(
-        @Json val uri: String
-) : RendezvousTransportDetails(type = RendezvousTransportType.MSC3886_SIMPLE_HTTP_V1)
-
 /**
  * Implementation of the Simple HTTP transport MSC3886: https://github.com/matrix-org/matrix-spec-proposals/pull/3886
  */
 class SimpleHttpRendezvousTransport(override var onCancelled: ((reason: RendezvousFailureReason) -> Unit)?, rendezvousUri: String?) : RendezvousTransport {
+    companion object {
+        private val TAG = LoggerTag(SimpleHttpRendezvousTransport::class.java.simpleName, LoggerTag.RENDEZVOUS).value
+    }
+
     override var ready = false
     private var cancelled = false
     private var uri: String?

From 3be4a0ca2137c339b66b9b5aa12bb0617c15d935 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Mon, 17 Oct 2022 11:25:09 +0100
Subject: [PATCH 55/93] Remove unused val

---
 .../java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt | 1 -
 1 file changed, 1 deletion(-)

diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
index 81a00cf548..8b70f61e80 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
@@ -35,7 +35,6 @@ import timber.log.Timber
 
 class QrCodeLoginViewModel @AssistedInject constructor(
         @Assisted private val initialState: QrCodeLoginViewState,
-        private val applicationContext: Context,
         private val authenticationService: AuthenticationService,
         private val activeSessionHolder: ActiveSessionHolder,
         private val configureAndStartSessionUseCase: ConfigureAndStartSessionUseCase

From 48de8f4e345105668298ec238bbad5600c057ab9 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Mon, 17 Oct 2022 11:48:35 +0100
Subject: [PATCH 56/93] Fix bad merge

---
 .../session/homeserver/GetHomeServerCapabilitiesTask.kt         | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/GetHomeServerCapabilitiesTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/GetHomeServerCapabilitiesTask.kt
index d65e629b71..42bba5fe88 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/GetHomeServerCapabilitiesTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/GetHomeServerCapabilitiesTask.kt
@@ -133,6 +133,8 @@ internal class DefaultGetHomeServerCapabilitiesTask @Inject constructor(
                 homeServerCapabilitiesEntity.roomVersionsJson = capabilities?.roomVersions?.let {
                     MoshiProvider.providesMoshi().adapter(RoomVersions::class.java).toJson(it)
                 }
+                homeServerCapabilitiesEntity.canUseThreading = /* capabilities?.threads?.enabled.orFalse() || */
+                        getVersionResult?.doesServerSupportThreads().orFalse()
             }
 
             if (getMediaConfigResult != null) {

From 506fa729ea8f9d77357cc0f1347d3227e6b51702 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Mon, 17 Oct 2022 11:50:56 +0100
Subject: [PATCH 57/93] Cleanup

---
 .../android/sdk/api/rendezvous/Rendezvous.kt  | 29 ++++++++++--------
 .../channels/ECDHRendezvousChannel.kt         |  1 +
 .../sdk/api/rendezvous/model/Outcome.kt       | 30 +++++++++++++++++++
 .../sdk/api/rendezvous/model/Payload.kt       |  6 ++--
 .../sdk/api/rendezvous/model/PayloadType.kt   |  6 ++--
 .../sdk/api/rendezvous/model/Protocol.kt      | 24 +++++++++++++++
 6 files changed, 78 insertions(+), 18 deletions(-)
 create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Outcome.kt
 create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Protocol.kt

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/Rendezvous.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/Rendezvous.kt
index 4270d4a09c..e467ff06e3 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/Rendezvous.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/Rendezvous.kt
@@ -22,8 +22,10 @@ import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig
 import org.matrix.android.sdk.api.logger.LoggerTag
 import org.matrix.android.sdk.api.rendezvous.channels.ECDHRendezvousChannel
 import org.matrix.android.sdk.api.rendezvous.model.ECDHRendezvousCode
+import org.matrix.android.sdk.api.rendezvous.model.Outcome
 import org.matrix.android.sdk.api.rendezvous.model.Payload
 import org.matrix.android.sdk.api.rendezvous.model.PayloadType
+import org.matrix.android.sdk.api.rendezvous.model.Protocol
 import org.matrix.android.sdk.api.rendezvous.model.RendezvousIntent
 import org.matrix.android.sdk.api.rendezvous.transports.SimpleHttpRendezvousTransport
 import org.matrix.android.sdk.api.session.Session
@@ -68,7 +70,7 @@ class Rendezvous(
         Timber.tag(TAG).d("ourIntent: $ourIntent, theirIntent: $theirIntent, incompatible: $incompatible")
 
         if (incompatible) {
-            send(Payload(PayloadType.Finish, intent = ourIntent))
+            send(Payload(PayloadType.FINISH, intent = ourIntent))
             val reason = if (ourIntent == RendezvousIntent.LOGIN_ON_NEW_DEVICE) {
                 RendezvousFailureReason.OtherDeviceNotSignedIn
             } else {
@@ -93,14 +95,14 @@ class Rendezvous(
         Timber.tag(TAG).i("Waiting for protocols")
         val protocolsResponse = receive()
 
-        if (protocolsResponse?.protocols == null || !protocolsResponse.protocols.contains("login_token")) {
-            send(Payload(PayloadType.Finish, outcome = "unsupported"))
+        if (protocolsResponse?.protocols == null || !protocolsResponse.protocols.contains(Protocol.LOGIN_TOKEN)) {
+            send(Payload(PayloadType.FINISH, outcome = Outcome.UNSUPPORTED))
             Timber.tag(TAG).i("No supported protocol")
             cancel(RendezvousFailureReason.Unknown)
             return null
         }
 
-        send(Payload(PayloadType.Progress, protocol = "login_token"))
+        send(Payload(PayloadType.PROGRESS, protocol = Protocol.LOGIN_TOKEN))
 
         return checksum
     }
@@ -110,21 +112,23 @@ class Rendezvous(
 
         val loginToken = receive()
 
-        if (loginToken?.type == PayloadType.Finish) {
+        if (loginToken?.type == PayloadType.FINISH) {
             when (loginToken.outcome) {
-                "declined" -> {
+                Outcome.DECLINED -> {
                     Timber.tag(TAG).i("Login declined by other device")
                     channel.cancel(RendezvousFailureReason.UserDeclined)
                     return null
                 }
-                "unsupported" -> {
+                Outcome.UNSUPPORTED -> {
                     Timber.tag(TAG).i("Not supported")
                     channel.cancel(RendezvousFailureReason.HomeserverLacksSupport)
                     return null
                 }
+                else -> {
+                    channel.cancel(RendezvousFailureReason.Unknown)
+                    return null
+                }
             }
-            channel.cancel(RendezvousFailureReason.Unknown)
-            return null
         }
 
         val homeserver = loginToken?.homeserver ?: throw RuntimeException("No homeserver returned")
@@ -141,7 +145,7 @@ class Rendezvous(
         val crypto = session.cryptoService()
         val deviceId = crypto.getMyDevice().deviceId
         val deviceKey = crypto.getMyDevice().fingerprint()
-        send(Payload(PayloadType.Progress, outcome = "success", deviceId = deviceId, deviceKey = deviceKey))
+        send(Payload(PayloadType.PROGRESS, outcome = Outcome.SUCCESS, deviceId = deviceId, deviceKey = deviceKey))
 
         // await confirmation of verification
 
@@ -149,8 +153,9 @@ class Rendezvous(
         val verifyingDeviceId = verificationResponse?.verifyingDeviceId ?: throw RuntimeException("No verifying device id returned")
         val verifyingDeviceFromServer = crypto.getCryptoDeviceInfo(userId, verifyingDeviceId)
         if (verifyingDeviceFromServer?.fingerprint() != verificationResponse.verifyingDeviceKey) {
-            Timber.tag(TAG).w("Verifying device $verifyingDeviceId key doesn't match: ${
-                verifyingDeviceFromServer?.fingerprint()} vs ${verificationResponse.verifyingDeviceKey})"
+            Timber.tag(TAG).w(
+                    "Verifying device $verifyingDeviceId key doesn't match: ${
+                        verifyingDeviceFromServer?.fingerprint()} vs ${verificationResponse.verifyingDeviceKey})"
             )
             throw RuntimeException("Key from verifying device doesn't match")
         }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt
index 5dbac894ae..489d20e588 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt
@@ -50,6 +50,7 @@ class ECDHRendezvousChannel(override var transport: RendezvousTransport, theirPu
         private const val KEY_SPEC = "AES"
         private val TAG = LoggerTag(ECDHRendezvousChannel::class.java.simpleName, LoggerTag.RENDEZVOUS).value
 
+        // n.b. we are only aver processing byte array that we have generated, so we can make assumptions about the length
         private fun getDecimalCodeRepresentation(byteArray: ByteArray): String {
             val b0 = byteArray[0].toUnsignedInt() // need unsigned byte
             val b1 = byteArray[1].toUnsignedInt() // need unsigned byte
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Outcome.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Outcome.kt
new file mode 100644
index 0000000000..2dd6e7be28
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Outcome.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2022 The Matrix.org Foundation C.I.C.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.matrix.android.sdk.api.rendezvous.model
+
+import com.squareup.moshi.Json
+
+enum class Outcome(val value: String) {
+    @Json(name = "success")
+    SUCCESS("success"),
+
+    @Json(name = "declined")
+    DECLINED("declined"),
+
+    @Json(name = "unsupported")
+    UNSUPPORTED("unsupported")
+}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Payload.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Payload.kt
index 5627452232..593177e625 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Payload.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Payload.kt
@@ -23,9 +23,9 @@ import com.squareup.moshi.JsonClass
 internal data class Payload(
         @Json val type: PayloadType,
         @Json val intent: RendezvousIntent? = null,
-        @Json val outcome: String? = null,
-        @Json val protocols: List<String>? = null,
-        @Json val protocol: String? = null,
+        @Json val outcome: Outcome? = null,
+        @Json val protocols: List<Protocol>? = null,
+        @Json val protocol: Protocol? = null,
         @Json val homeserver: String? = null,
         @Json(name = "login_token") val loginToken: String? = null,
         @Json(name = "device_id") val deviceId: String? = null,
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/PayloadType.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/PayloadType.kt
index 9854e7c070..5ff4cd7cfa 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/PayloadType.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/PayloadType.kt
@@ -20,11 +20,11 @@ import com.squareup.moshi.Json
 
 internal enum class PayloadType(val value: String) {
     @Json(name = "m.login.start")
-    Start("m.login.start"),
+    START("m.login.start"),
 
     @Json(name = "m.login.finish")
-    Finish("m.login.finish"),
+    FINISH("m.login.finish"),
 
     @Json(name = "m.login.progress")
-    Progress("m.login.progress")
+    PROGRESS("m.login.progress")
 }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Protocol.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Protocol.kt
new file mode 100644
index 0000000000..18381984a5
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Protocol.kt
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2022 The Matrix.org Foundation C.I.C.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.matrix.android.sdk.api.rendezvous.model
+
+import com.squareup.moshi.Json
+
+enum class Protocol(val value: String) {
+    @Json(name = "login_token")
+    LOGIN_TOKEN("login_token")
+}

From 4306c57236dccd50531bf20e8732031a84d6bb0e Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Mon, 17 Oct 2022 12:01:12 +0100
Subject: [PATCH 58/93] Thread safe use of OlmSAS

---
 .../channels/ECDHRendezvousChannel.kt         | 69 ++++++++++---------
 1 file changed, 38 insertions(+), 31 deletions(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt
index 489d20e588..ca7083b297 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt
@@ -90,43 +90,45 @@ class ECDHRendezvousChannel(override var transport: RendezvousTransport, theirPu
     }
 
     override suspend fun connect(): String {
-        if (olmSAS == null) {
-            throw RuntimeException("Channel closed")
-        }
-        val isInitiator = theirPublicKey == null
+        olmSAS ?.let { olmSAS ->
+            val isInitiator = theirPublicKey == null
 
-        if (isInitiator) {
-            Timber.tag(TAG).i("Waiting for other device to send their public key")
-            val res = this.receiveAsPayload() ?: throw RuntimeException("No reply from other device")
+            if (isInitiator) {
+                Timber.tag(TAG).i("Waiting for other device to send their public key")
+                val res = this.receiveAsPayload() ?: throw RuntimeException("No reply from other device")
 
-            if (res.key == null) {
-                throw RendezvousError(
-                        "Unsupported algorithm: ${res.algorithm}",
-                        RendezvousFailureReason.UnsupportedAlgorithm,
+                if (res.key == null) {
+                    throw RendezvousError(
+                            "Unsupported algorithm: ${res.algorithm}",
+                            RendezvousFailureReason.UnsupportedAlgorithm,
+                    )
+                }
+                theirPublicKey = Base64.decode(res.key, Base64.NO_WRAP)
+            } else {
+                // send our public key unencrypted
+                Timber.tag(TAG).i("Sending public key")
+                send(
+                        ECDHPayload(
+                                algorithm = SecureRendezvousChannelAlgorithm.ECDH_V1,
+                                key = Base64.encodeToString(ourPublicKey, Base64.NO_WRAP)
+                        )
                 )
             }
-            theirPublicKey = Base64.decode(res.key, Base64.NO_WRAP)
-        } else {
-            // send our public key unencrypted
-            Timber.tag(TAG).i("Sending public key")
-            send(
-                    ECDHPayload(
-                    algorithm = SecureRendezvousChannelAlgorithm.ECDH_V1,
-                    key = Base64.encodeToString(ourPublicKey, Base64.NO_WRAP)
-            )
-            )
-        }
 
-        olmSAS!!.setTheirPublicKey(Base64.encodeToString(theirPublicKey, Base64.NO_WRAP))
+            synchronized(olmSAS) {
+                olmSAS.setTheirPublicKey(Base64.encodeToString(theirPublicKey, Base64.NO_WRAP))
+                olmSAS.setTheirPublicKey(Base64.encodeToString(theirPublicKey, Base64.NO_WRAP))
 
-        val initiatorKey = Base64.encodeToString(if (isInitiator) ourPublicKey else theirPublicKey, Base64.NO_WRAP)
-        val recipientKey = Base64.encodeToString(if (isInitiator) theirPublicKey else ourPublicKey, Base64.NO_WRAP)
-        val aesInfo = "${SecureRendezvousChannelAlgorithm.ECDH_V1.value}|$initiatorKey|$recipientKey"
+                val initiatorKey = Base64.encodeToString(if (isInitiator) ourPublicKey else theirPublicKey, Base64.NO_WRAP)
+                val recipientKey = Base64.encodeToString(if (isInitiator) theirPublicKey else ourPublicKey, Base64.NO_WRAP)
+                val aesInfo = "${SecureRendezvousChannelAlgorithm.ECDH_V1.value}|$initiatorKey|$recipientKey"
 
-        aesKey = olmSAS!!.generateShortCode(aesInfo, 32)
+                aesKey = olmSAS.generateShortCode(aesInfo, 32)
 
-        val rawChecksum = olmSAS!!.generateShortCode(aesInfo, 5)
-        return getDecimalCodeRepresentation(rawChecksum)
+                val rawChecksum = olmSAS.generateShortCode(aesInfo, 5)
+                return getDecimalCodeRepresentation(rawChecksum)
+            }
+        } ?: throw RuntimeException("Channel closed")
     }
 
     private suspend fun send(payload: ECDHPayload) {
@@ -174,8 +176,13 @@ class ECDHRendezvousChannel(override var transport: RendezvousTransport, theirPu
     }
 
     override suspend fun close() {
-        olmSAS?.releaseSas()
-        olmSAS = null
+        olmSAS ?.let {
+            synchronized(it) {
+                // this does a double release check already so we don't re-check ourselves
+                it.releaseSas()
+                olmSAS = null
+            }
+        }
     }
 
     private fun encrypt(plainText: ByteArray): ECDHPayload {

From fb86ab70a270e124b15b3e7af888381f32b78dd8 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Mon, 17 Oct 2022 12:05:32 +0100
Subject: [PATCH 59/93] Comments and error mapping

---
 .../transports/SimpleHttpRendezvousTransport.kt          | 9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt
index 03e8b0cda7..e899a64e99 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt
@@ -99,9 +99,8 @@ class SimpleHttpRendezvousTransport(override var onCancelled: ((reason: Rendezvo
 
     override suspend fun receive(): ByteArray? {
         val uri = uri ?: throw IllegalStateException("Rendezvous not set up")
-        var done = false
         val httpClient = okhttp3.OkHttpClient.Builder().build()
-        while (!done) {
+        while (true) {
             if (cancelled) {
                 return null
             }
@@ -117,8 +116,9 @@ class SimpleHttpRendezvousTransport(override var onCancelled: ((reason: Rendezvo
             val response = httpClient.newCall(request.build()).execute()
 
             try {
+                // expired
                 if (response.code == 404) {
-                    cancel(RendezvousFailureReason.Unknown)
+                    cancel(RendezvousFailureReason.Expired)
                     return null
                 }
 
@@ -135,7 +135,8 @@ class SimpleHttpRendezvousTransport(override var onCancelled: ((reason: Rendezvo
                     return response.body?.bytes()
                 }
 
-                done = false
+                // sleep for a second before polling again
+                // we rely on the server expiring the channel rather than checking it ourselves
                 delay(1000)
             } finally {
                 response.close()

From 1976451c8164ee15b88186ae1fc6291513d158ec Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Mon, 17 Oct 2022 12:23:14 +0100
Subject: [PATCH 60/93] Lint

---
 .../matrix/android/sdk/api/rendezvous/RendezvousChannel.kt    | 2 +-
 .../rendezvous/transports/SimpleHttpRendezvousTransport.kt    | 4 ----
 .../im/vector/app/features/login/qr/QrCodeLoginViewModel.kt   | 1 -
 3 files changed, 1 insertion(+), 6 deletions(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousChannel.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousChannel.kt
index 9d2843ce66..31014e392d 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousChannel.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousChannel.kt
@@ -20,7 +20,7 @@ import org.matrix.android.sdk.api.rendezvous.model.ECDHRendezvousCode
 import org.matrix.android.sdk.api.rendezvous.model.RendezvousIntent
 
 /**
- * Representation of a rendezvous channel such as that described by MSC3903
+ * Representation of a rendezvous channel such as that described by MSC3903.
  */
 interface RendezvousChannel {
     var transport: RendezvousTransport
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt
index e899a64e99..3e9b11a68b 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt
@@ -16,8 +16,6 @@
 
 package org.matrix.android.sdk.api.rendezvous.transports
 
-import com.squareup.moshi.Json
-import com.squareup.moshi.JsonClass
 import kotlinx.coroutines.delay
 import okhttp3.MediaType
 import okhttp3.Request
@@ -142,8 +140,6 @@ class SimpleHttpRendezvousTransport(override var onCancelled: ((reason: Rendezvo
                 response.close()
             }
         }
-
-        return null
     }
 
     override suspend fun cancel(reason: RendezvousFailureReason) {
diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
index 8b70f61e80..36e88b284c 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
@@ -16,7 +16,6 @@
 
 package im.vector.app.features.login.qr
 
-import android.content.Context
 import com.airbnb.mvrx.MavericksViewModelFactory
 import dagger.assisted.Assisted
 import dagger.assisted.AssistedFactory

From eb30ef166acff6fca015b321c71ce1b10a811c49 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Mon, 17 Oct 2022 12:32:40 +0100
Subject: [PATCH 61/93] Improve 404 handling

---
 .../rendezvous/transports/SimpleHttpRendezvousTransport.kt   | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt
index 3e9b11a68b..ca2a3425cd 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt
@@ -75,7 +75,9 @@ class SimpleHttpRendezvousTransport(override var onCancelled: ((reason: Rendezvo
         val response = httpClient.newCall(request.build()).execute()
 
         if (response.code == 404) {
+            // we set to unknown and the cancel method will rewrite the reason to expired if applicable
             cancel(RendezvousFailureReason.Unknown)
+            return
         }
         etag = response.header("etag")
 
@@ -116,7 +118,8 @@ class SimpleHttpRendezvousTransport(override var onCancelled: ((reason: Rendezvo
             try {
                 // expired
                 if (response.code == 404) {
-                    cancel(RendezvousFailureReason.Expired)
+                    // we set to unknown and the cancel method will rewrite the reason to expired if applicable
+                    cancel(RendezvousFailureReason.Unknown)
                     return null
                 }
 

From d616251f260515548f8b699514247b6e0a8ee9bc Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Mon, 17 Oct 2022 13:41:27 +0100
Subject: [PATCH 62/93] Fix merge

---
 .../session/homeserver/GetHomeServerCapabilitiesTask.kt         | 2 --
 1 file changed, 2 deletions(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/GetHomeServerCapabilitiesTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/GetHomeServerCapabilitiesTask.kt
index 9147c4e1cd..2c3cb440b6 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/GetHomeServerCapabilitiesTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/GetHomeServerCapabilitiesTask.kt
@@ -132,8 +132,6 @@ internal class DefaultGetHomeServerCapabilitiesTask @Inject constructor(
                 homeServerCapabilitiesEntity.roomVersionsJson = capabilities?.roomVersions?.let {
                     MoshiProvider.providesMoshi().adapter(RoomVersions::class.java).toJson(it)
                 }
-                homeServerCapabilitiesEntity.canUseThreading = /* capabilities?.threads?.enabled.orFalse() || */
-                        getVersionResult?.doesServerSupportThreads().orFalse()
             }
 
             if (getMediaConfigResult != null) {

From e01ee619d3bb59d5bec0c1d0ee1bef9829584f40 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Mon, 17 Oct 2022 16:02:25 +0100
Subject: [PATCH 63/93] Refactor error handling and report E2EE errors

---
 .../src/main/res/values/strings.xml           |   6 +
 .../android/sdk/api/rendezvous/Rendezvous.kt  | 140 ++++++++++--------
 .../sdk/api/rendezvous/RendezvousChannel.kt   |  12 +-
 .../api/rendezvous/RendezvousFailureReason.kt |  25 ++--
 .../sdk/api/rendezvous/RendezvousTransport.kt |   7 +-
 .../channels/ECDHRendezvousChannel.kt         |  27 +---
 .../sdk/api/rendezvous/model/Outcome.kt       |   8 +-
 .../api/rendezvous/model/RendezvousError.kt   |   2 +-
 .../SimpleHttpRendezvousTransport.kt          |  57 ++++---
 .../login/qr/QrCodeLoginStatusFragment.kt     |   6 +
 .../features/login/qr/QrCodeLoginViewModel.kt |  27 +++-
 11 files changed, 171 insertions(+), 146 deletions(-)

diff --git a/library/ui-strings/src/main/res/values/strings.xml b/library/ui-strings/src/main/res/values/strings.xml
index 7d0173f341..e28ebb31c6 100644
--- a/library/ui-strings/src/main/res/values/strings.xml
+++ b/library/ui-strings/src/main/res/values/strings.xml
@@ -3383,6 +3383,12 @@
     <string name="qr_code_login_header_failed_timeout_description">The linking wasn’t completed in the required time.</string>
     <string name="qr_code_login_header_failed_denied_description">The request was denied on the other device.</string>
     <string name="qr_code_login_header_failed_other_description">The request failed.</string>
+    <string name="qr_code_login_header_failed_e2ee_security_issue_description">A security issue was encountered setting up secure messaging. One of the following may be compromised: Your homeserver; Your intent connection(s); Your device(s);</string>
+    <string name="qr_code_login_header_failed_other_device_already_signed_in_description">The other device is already signed in.</string>
+    <string name="qr_code_login_header_failed_other_device_not_signed_in_description">The other device must be signed in.</string>
+    <string name="qr_code_login_header_failed_invalid_qr_code_description">The QR code scanned is invalid.</string>
+    <string name="qr_code_login_header_failed_user_cancelled_description">The sign in was cancelled on the other device.</string>
+    <string name="qr_code_login_header_failed_homeserver_is_not_supported_description">The homeserver doesn\'t support sign in with QR code.</string>
     <string name="qr_code_login_new_device_instruction_1">Open ${app_name} on your other device</string>
     <string name="qr_code_login_new_device_instruction_2">Go to Settings -> Security &amp; Privacy -> Show All Sessions</string>
     <string name="qr_code_login_new_device_instruction_3">Select \'Show QR code in this device\'</string>
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/Rendezvous.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/Rendezvous.kt
index e467ff06e3..9ad889fca0 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/Rendezvous.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/Rendezvous.kt
@@ -26,6 +26,7 @@ import org.matrix.android.sdk.api.rendezvous.model.Outcome
 import org.matrix.android.sdk.api.rendezvous.model.Payload
 import org.matrix.android.sdk.api.rendezvous.model.PayloadType
 import org.matrix.android.sdk.api.rendezvous.model.Protocol
+import org.matrix.android.sdk.api.rendezvous.model.RendezvousError
 import org.matrix.android.sdk.api.rendezvous.model.RendezvousIntent
 import org.matrix.android.sdk.api.rendezvous.transports.SimpleHttpRendezvousTransport
 import org.matrix.android.sdk.api.session.Session
@@ -47,10 +48,16 @@ class Rendezvous(
     companion object {
         private val TAG = LoggerTag(Rendezvous::class.java.simpleName, LoggerTag.RENDEZVOUS).value
 
-        fun buildChannelFromCode(code: String, onCancelled: (reason: RendezvousFailureReason) -> Unit): Rendezvous {
-            val parsed = MatrixJsonParser.getMoshi().adapter(ECDHRendezvousCode::class.java).fromJson(code) ?: throw RuntimeException("Invalid code")
+        @Throws(RendezvousError::class)
+        fun buildChannelFromCode(code: String): Rendezvous {
+            val parsed = try {
+                // we rely on moshi validating the code and throwing exception if invalid JSON or doesn't
+                MatrixJsonParser.getMoshi().adapter(ECDHRendezvousCode::class.java).fromJson(code)
+            } catch (a: Throwable) {
+                throw RendezvousError("Invalid code", RendezvousFailureReason.InvalidCode)
+            } ?: throw RendezvousError("Invalid code", RendezvousFailureReason.InvalidCode)
 
-            val transport = SimpleHttpRendezvousTransport(onCancelled, parsed.rendezvous.transport.uri)
+            val transport = SimpleHttpRendezvousTransport(parsed.rendezvous.transport.uri)
 
             return Rendezvous(
                     ECDHRendezvousChannel(transport, parsed.rendezvous.key),
@@ -64,32 +71,30 @@ class Rendezvous(
     // not yet implemented: RendezvousIntent.RECIPROCATE_LOGIN_ON_EXISTING_DEVICE
     val ourIntent: RendezvousIntent = RendezvousIntent.LOGIN_ON_NEW_DEVICE
 
-    private suspend fun areIntentsIncompatible(): Boolean {
+    @Throws(RendezvousError::class)
+    private suspend fun checkCompatibility() {
         val incompatible = theirIntent == ourIntent
 
         Timber.tag(TAG).d("ourIntent: $ourIntent, theirIntent: $theirIntent, incompatible: $incompatible")
 
         if (incompatible) {
+            // inform the other side
             send(Payload(PayloadType.FINISH, intent = ourIntent))
-            val reason = if (ourIntent == RendezvousIntent.LOGIN_ON_NEW_DEVICE) {
-                RendezvousFailureReason.OtherDeviceNotSignedIn
+            if (ourIntent == RendezvousIntent.LOGIN_ON_NEW_DEVICE) {
+                throw RendezvousError("The other device isn't signed in", RendezvousFailureReason.OtherDeviceNotSignedIn)
             } else {
-                RendezvousFailureReason.OtherDeviceAlreadySignedIn
+                throw RendezvousError("The other device is already signed in", RendezvousFailureReason.OtherDeviceAlreadySignedIn)
             }
-            channel.cancel(reason)
         }
-
-        return incompatible
     }
 
+    @Throws(RendezvousError::class)
     suspend fun startAfterScanningCode(): String? {
         val checksum = channel.connect()
 
         Timber.tag(TAG).i("Connected to secure channel with checksum: $checksum")
 
-        if (areIntentsIncompatible()) {
-            return null
-        }
+        checkCompatibility()
 
         // get protocols
         Timber.tag(TAG).i("Waiting for protocols")
@@ -97,9 +102,7 @@ class Rendezvous(
 
         if (protocolsResponse?.protocols == null || !protocolsResponse.protocols.contains(Protocol.LOGIN_TOKEN)) {
             send(Payload(PayloadType.FINISH, outcome = Outcome.UNSUPPORTED))
-            Timber.tag(TAG).i("No supported protocol")
-            cancel(RendezvousFailureReason.Unknown)
-            return null
+            throw RendezvousError("Unsupported protocols", RendezvousFailureReason.UnsupportedHomeserver)
         }
 
         send(Payload(PayloadType.PROGRESS, protocol = Protocol.LOGIN_TOKEN))
@@ -107,6 +110,7 @@ class Rendezvous(
         return checksum
     }
 
+    @Throws(RendezvousError::class)
     suspend fun waitForLoginOnNewDevice(authenticationService: AuthenticationService): Session? {
         Timber.tag(TAG).i("Waiting for login_token")
 
@@ -115,24 +119,19 @@ class Rendezvous(
         if (loginToken?.type == PayloadType.FINISH) {
             when (loginToken.outcome) {
                 Outcome.DECLINED -> {
-                    Timber.tag(TAG).i("Login declined by other device")
-                    channel.cancel(RendezvousFailureReason.UserDeclined)
-                    return null
+                    throw RendezvousError("Login declined by other device", RendezvousFailureReason.UserDeclined)
                 }
                 Outcome.UNSUPPORTED -> {
-                    Timber.tag(TAG).i("Not supported")
-                    channel.cancel(RendezvousFailureReason.HomeserverLacksSupport)
-                    return null
+                    throw RendezvousError("Homeserver lacks support", RendezvousFailureReason.UnsupportedHomeserver)
                 }
                 else -> {
-                    channel.cancel(RendezvousFailureReason.Unknown)
-                    return null
+                    throw RendezvousError("Unknown error", RendezvousFailureReason.Unknown)
                 }
             }
         }
 
-        val homeserver = loginToken?.homeserver ?: throw RuntimeException("No homeserver returned")
-        val token = loginToken.loginToken ?: throw RuntimeException("No login token returned")
+        val homeserver = loginToken?.homeserver ?: throw RendezvousError("No homeserver returned", RendezvousFailureReason.ProtocolError)
+        val token = loginToken.loginToken ?: throw RendezvousError("No login token returned", RendezvousFailureReason.ProtocolError)
 
         Timber.tag(TAG).i("Got login_token now attempting to sign in with $homeserver")
 
@@ -140,6 +139,7 @@ class Rendezvous(
         return authenticationService.loginUsingQrLoginToken(hsConfig, token)
     }
 
+    @Throws(RendezvousError::class)
     suspend fun completeVerificationOnNewDevice(session: Session) {
         val userId = session.myUserId
         val crypto = session.cryptoService()
@@ -148,59 +148,77 @@ class Rendezvous(
         send(Payload(PayloadType.PROGRESS, outcome = Outcome.SUCCESS, deviceId = deviceId, deviceKey = deviceKey))
 
         // await confirmation of verification
-
         val verificationResponse = receive()
-        val verifyingDeviceId = verificationResponse?.verifyingDeviceId ?: throw RuntimeException("No verifying device id returned")
-        val verifyingDeviceFromServer = crypto.getCryptoDeviceInfo(userId, verifyingDeviceId)
-        if (verifyingDeviceFromServer?.fingerprint() != verificationResponse.verifyingDeviceKey) {
-            Timber.tag(TAG).w(
-                    "Verifying device $verifyingDeviceId key doesn't match: ${
-                        verifyingDeviceFromServer?.fingerprint()} vs ${verificationResponse.verifyingDeviceKey})"
-            )
-            throw RuntimeException("Key from verifying device doesn't match")
-        }
+        if (verificationResponse?.outcome == Outcome.VERIFIED) {
+            val verifyingDeviceId = verificationResponse.verifyingDeviceId
+                    ?: throw RendezvousError("No verifying device id returned", RendezvousFailureReason.ProtocolError)
+            val verifyingDeviceFromServer = crypto.getCryptoDeviceInfo(userId, verifyingDeviceId)
+            if (verifyingDeviceFromServer?.fingerprint() != verificationResponse.verifyingDeviceKey) {
+                Timber.tag(TAG).w(
+                        "Verifying device $verifyingDeviceId key doesn't match: ${
+                            verifyingDeviceFromServer?.fingerprint()
+                        } vs ${verificationResponse.verifyingDeviceKey})"
+                )
+                // inform the other side
+                send(Payload(PayloadType.FINISH, outcome = Outcome.E2EE_SECURITY_ERROR))
+                throw RendezvousError("Key from verifying device doesn't match", RendezvousFailureReason.E2EESecurityIssue)
+            }
 
-        // set other device as verified
-        Timber.tag(TAG).i("Setting device $verifyingDeviceId as verified")
-        crypto.setDeviceVerification(DeviceTrustLevel(locallyVerified = true, crossSigningVerified = false), userId, verifyingDeviceId)
+            verificationResponse.masterKey?.let { masterKeyFromVerifyingDevice ->
+                // check master key againt what the homeserver told us
+                crypto.crossSigningService().getMyCrossSigningKeys()?.masterKey()?.let { localMasterKey ->
+                    if (localMasterKey.unpaddedBase64PublicKey != masterKeyFromVerifyingDevice) {
+                        Timber.tag(TAG).w("Master key from verifying device doesn't match: $masterKeyFromVerifyingDevice vs $localMasterKey")
+                        // inform the other side
+                        send(Payload(PayloadType.FINISH, outcome = Outcome.E2EE_SECURITY_ERROR))
+                        throw RendezvousError("Master key from verifying device doesn't match", RendezvousFailureReason.E2EESecurityIssue)
+                    }
+                    // set other device as verified
+                    Timber.tag(TAG).i("Setting device $verifyingDeviceId as verified")
+                    crypto.setDeviceVerification(DeviceTrustLevel(locallyVerified = true, crossSigningVerified = false), userId, verifyingDeviceId)
 
-        verificationResponse.masterKey ?.let { masterKeyFromVerifyingDevice ->
-            // set master key as trusted
-            crypto.crossSigningService().getMyCrossSigningKeys()?.masterKey()?.let { localMasterKey ->
-                if (localMasterKey.unpaddedBase64PublicKey == masterKeyFromVerifyingDevice) {
                     Timber.tag(TAG).i("Setting master key as trusted")
                     crypto.crossSigningService().markMyMasterKeyAsTrusted()
-                } else {
-                    Timber.tag(TAG).w("Master key from verifying device doesn't match: $masterKeyFromVerifyingDevice vs $localMasterKey")
-                    throw RuntimeException("Master key from verifying device doesn't match")
-                }
-            } ?: Timber.tag(TAG).i("No local master key")
-        } ?: Timber.tag(TAG).i("No master key given by verifying device")
+                } ?: Timber.tag(TAG).w("No local master key so not verifying")
+            } ?: run {
+                // set other device as verified anyway
+                Timber.tag(TAG).i("Setting device $verifyingDeviceId as verified")
+                crypto.setDeviceVerification(DeviceTrustLevel(locallyVerified = true, crossSigningVerified = false), userId, verifyingDeviceId)
 
-        // request secrets from the verifying device
-        Timber.tag(TAG).i("Requesting secrets from $verifyingDeviceId")
+                Timber.tag(TAG).i("No master key given by verifying device")
+            }
 
-        session.sharedSecretStorageService().let {
-            it.requestSecret(MASTER_KEY_SSSS_NAME, verifyingDeviceId)
-            it.requestSecret(SELF_SIGNING_KEY_SSSS_NAME, verifyingDeviceId)
-            it.requestSecret(USER_SIGNING_KEY_SSSS_NAME, verifyingDeviceId)
-            it.requestSecret(KEYBACKUP_SECRET_SSSS_NAME, verifyingDeviceId)
+            // request secrets from the verifying device
+            Timber.tag(TAG).i("Requesting secrets from $verifyingDeviceId")
+
+            session.sharedSecretStorageService().let {
+                it.requestSecret(MASTER_KEY_SSSS_NAME, verifyingDeviceId)
+                it.requestSecret(SELF_SIGNING_KEY_SSSS_NAME, verifyingDeviceId)
+                it.requestSecret(USER_SIGNING_KEY_SSSS_NAME, verifyingDeviceId)
+                it.requestSecret(KEYBACKUP_SECRET_SSSS_NAME, verifyingDeviceId)
+            }
+        } else {
+            Timber.tag(TAG).i("Not doing verification")
         }
     }
 
+    @Throws(RendezvousError::class)
     private suspend fun receive(): Payload? {
         val data = channel.receive() ?: return null
-        return adapter.fromJson(data.toString(Charsets.UTF_8))
+        val payload = try {
+            adapter.fromJson(data.toString(Charsets.UTF_8))
+        } catch (e: Exception) {
+            Timber.tag(TAG).w(e, "Failed to parse payload")
+            throw RendezvousError("Invalid payload received", RendezvousFailureReason.Unknown)
+        }
+
+        return payload
     }
 
     private suspend fun send(payload: Payload) {
         channel.send(adapter.toJson(payload).toByteArray(Charsets.UTF_8))
     }
 
-    suspend fun cancel(reason: RendezvousFailureReason) {
-        channel.cancel(reason)
-    }
-
     suspend fun close() {
         channel.close()
     }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousChannel.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousChannel.kt
index 31014e392d..be79569164 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousChannel.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousChannel.kt
@@ -16,8 +16,7 @@
 
 package org.matrix.android.sdk.api.rendezvous
 
-import org.matrix.android.sdk.api.rendezvous.model.ECDHRendezvousCode
-import org.matrix.android.sdk.api.rendezvous.model.RendezvousIntent
+import org.matrix.android.sdk.api.rendezvous.model.RendezvousError
 
 /**
  * Representation of a rendezvous channel such as that described by MSC3903.
@@ -28,26 +27,25 @@ interface RendezvousChannel {
     /**
      * @returns the checksum/confirmation digits to be shown to the user
      */
+    @Throws(RendezvousError::class)
     suspend fun connect(): String
 
     /**
      * Send a payload via the channel.
      * @param data payload to send
      */
+    @Throws(RendezvousError::class)
     suspend fun send(data: ByteArray)
 
     /**
      * Receive a payload from the channel.
      * @returns the received payload
      */
+    @Throws(RendezvousError::class)
     suspend fun receive(): ByteArray?
 
     /**
-     * @returns a representation of the channel that can be encoded in a QR or similar
+     * @returns closes the channel and cleans up
      */
     suspend fun close()
-
-    // In future we probably want this to be a more generic RendezvousCode but it is suffice for now
-    suspend fun generateCode(intent: RendezvousIntent): ECDHRendezvousCode
-    suspend fun cancel(reason: RendezvousFailureReason)
 }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousFailureReason.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousFailureReason.kt
index a607dc7f38..18e625d825 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousFailureReason.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousFailureReason.kt
@@ -16,16 +16,17 @@
 
 package org.matrix.android.sdk.api.rendezvous
 
-enum class RendezvousFailureReason(val value: String, val canRetry: Boolean = true) {
-    UserDeclined("user_declined"),
-    OtherDeviceNotSignedIn("other_device_not_signed_in"),
-    OtherDeviceAlreadySignedIn("other_device_already_signed_in"),
-    Unknown("unknown"),
-    Expired("expired"),
-    UserCancelled("user_cancelled"),
-    InvalidCode("invalid_code"),
-    UnsupportedAlgorithm("unsupported_algorithm", false),
-    DataMismatch("data_mismatch"),
-    UnsupportedTransport("unsupported_transport", false),
-    HomeserverLacksSupport("homeserver_lacks_support", false)
+enum class RendezvousFailureReason(val canRetry: Boolean = true) {
+    UserDeclined,
+    OtherDeviceNotSignedIn,
+    OtherDeviceAlreadySignedIn,
+    Unknown,
+    Expired,
+    UserCancelled,
+    InvalidCode,
+    UnsupportedAlgorithm(false),
+    UnsupportedTransport(false),
+    UnsupportedHomeserver(false),
+    ProtocolError,
+    E2EESecurityIssue(false)
 }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousTransport.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousTransport.kt
index de0aed7efc..5daf906930 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousTransport.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousTransport.kt
@@ -17,13 +17,16 @@
 package org.matrix.android.sdk.api.rendezvous
 
 import okhttp3.MediaType
+import org.matrix.android.sdk.api.rendezvous.model.RendezvousError
 import org.matrix.android.sdk.api.rendezvous.model.RendezvousTransportDetails
 
 interface RendezvousTransport {
     var ready: Boolean
-    var onCancelled: ((reason: RendezvousFailureReason) -> Unit)?
+    @Throws(RendezvousError::class)
     suspend fun details(): RendezvousTransportDetails
+    @Throws(RendezvousError::class)
     suspend fun send(contentType: MediaType, data: ByteArray)
+    @Throws(RendezvousError::class)
     suspend fun receive(): ByteArray?
-    suspend fun cancel(reason: RendezvousFailureReason)
+    suspend fun close()
 }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt
index ca7083b297..9a5c5e865a 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt
@@ -89,13 +89,14 @@ class ECDHRendezvousChannel(override var transport: RendezvousTransport, theirPu
         ourPublicKey = Base64.decode(olmSAS!!.publicKey, Base64.NO_WRAP)
     }
 
+    @Throws(RendezvousError::class)
     override suspend fun connect(): String {
         olmSAS ?.let { olmSAS ->
             val isInitiator = theirPublicKey == null
 
             if (isInitiator) {
                 Timber.tag(TAG).i("Waiting for other device to send their public key")
-                val res = this.receiveAsPayload() ?: throw RuntimeException("No reply from other device")
+                val res = this.receiveAsPayload() ?: throw RendezvousError("No reply from other device", RendezvousFailureReason.ProtocolError)
 
                 if (res.key == null) {
                     throw RendezvousError(
@@ -137,7 +138,7 @@ class ECDHRendezvousChannel(override var transport: RendezvousTransport, theirPu
 
     override suspend fun send(data: ByteArray) {
         if (aesKey == null) {
-            throw RuntimeException("Shared secret not established")
+            throw IllegalStateException("Shared secret not established")
         }
         send(encrypt(data))
     }
@@ -150,31 +151,12 @@ class ECDHRendezvousChannel(override var transport: RendezvousTransport, theirPu
 
     override suspend fun receive(): ByteArray? {
         if (aesKey == null) {
-            throw RuntimeException("Shared secret not established")
+            throw IllegalStateException("Shared secret not established")
         }
         val payload = receiveAsPayload() ?: return null
         return decrypt(payload)
     }
 
-    override suspend fun generateCode(intent: RendezvousIntent): ECDHRendezvousCode {
-        return ECDHRendezvousCode(
-                intent,
-                rendezvous = ECDHRendezvous(
-                        transport.details() as SimpleHttpRendezvousTransportDetails,
-                        SecureRendezvousChannelAlgorithm.ECDH_V1,
-                        key = Base64.encodeToString(ourPublicKey, Base64.NO_WRAP)
-                )
-        )
-    }
-
-    override suspend fun cancel(reason: RendezvousFailureReason) {
-        try {
-            transport.cancel(reason)
-        } finally {
-            close()
-        }
-    }
-
     override suspend fun close() {
         olmSAS ?.let {
             synchronized(it) {
@@ -183,6 +165,7 @@ class ECDHRendezvousChannel(override var transport: RendezvousTransport, theirPu
                 olmSAS = null
             }
         }
+        transport.close()
     }
 
     private fun encrypt(plainText: ByteArray): ECDHPayload {
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Outcome.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Outcome.kt
index 2dd6e7be28..2ef24e9cb7 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Outcome.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Outcome.kt
@@ -26,5 +26,11 @@ enum class Outcome(val value: String) {
     DECLINED("declined"),
 
     @Json(name = "unsupported")
-    UNSUPPORTED("unsupported")
+    UNSUPPORTED("unsupported"),
+
+    @Json(name = "verified")
+    VERIFIED("verified"),
+
+    @Json(name = "e2ee_security_error")
+    E2EE_SECURITY_ERROR("e2ee_security_error")
 }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousError.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousError.kt
index fec55ffb67..c52b11a322 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousError.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousError.kt
@@ -18,4 +18,4 @@ package org.matrix.android.sdk.api.rendezvous.model
 
 import org.matrix.android.sdk.api.rendezvous.RendezvousFailureReason
 
-class RendezvousError(val description: String, val reason: RendezvousFailureReason) : RuntimeException(description)
+class RendezvousError(val description: String, val reason: RendezvousFailureReason) : Exception(description)
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt
index ca2a3425cd..fa0e5d8e2a 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt
@@ -23,6 +23,7 @@ import okhttp3.RequestBody.Companion.toRequestBody
 import org.matrix.android.sdk.api.logger.LoggerTag
 import org.matrix.android.sdk.api.rendezvous.RendezvousFailureReason
 import org.matrix.android.sdk.api.rendezvous.RendezvousTransport
+import org.matrix.android.sdk.api.rendezvous.model.RendezvousError
 import org.matrix.android.sdk.api.rendezvous.model.RendezvousTransportDetails
 import org.matrix.android.sdk.api.rendezvous.model.SimpleHttpRendezvousTransportDetails
 import timber.log.Timber
@@ -32,7 +33,7 @@ import java.util.Date
 /**
  * Implementation of the Simple HTTP transport MSC3886: https://github.com/matrix-org/matrix-spec-proposals/pull/3886
  */
-class SimpleHttpRendezvousTransport(override var onCancelled: ((reason: RendezvousFailureReason) -> Unit)?, rendezvousUri: String?) : RendezvousTransport {
+class SimpleHttpRendezvousTransport(rendezvousUri: String?) : RendezvousTransport {
     companion object {
         private val TAG = LoggerTag(SimpleHttpRendezvousTransport::class.java.simpleName, LoggerTag.RENDEZVOUS).value
     }
@@ -55,7 +56,7 @@ class SimpleHttpRendezvousTransport(override var onCancelled: ((reason: Rendezvo
 
     override suspend fun send(contentType: MediaType, data: ByteArray) {
         if (cancelled) {
-            return
+            throw IllegalStateException("Rendezvous cancelled")
         }
 
         val method = if (uri != null) "PUT" else "POST"
@@ -75,9 +76,7 @@ class SimpleHttpRendezvousTransport(override var onCancelled: ((reason: Rendezvo
         val response = httpClient.newCall(request.build()).execute()
 
         if (response.code == 404) {
-            // we set to unknown and the cancel method will rewrite the reason to expired if applicable
-            cancel(RendezvousFailureReason.Unknown)
-            return
+            throw get404Error()
         }
         etag = response.header("etag")
 
@@ -98,12 +97,12 @@ class SimpleHttpRendezvousTransport(override var onCancelled: ((reason: Rendezvo
     }
 
     override suspend fun receive(): ByteArray? {
+        if (cancelled) {
+            throw IllegalStateException("Rendezvous cancelled")
+        }
         val uri = uri ?: throw IllegalStateException("Rendezvous not set up")
         val httpClient = okhttp3.OkHttpClient.Builder().build()
         while (true) {
-            if (cancelled) {
-                return null
-            }
             Timber.tag(TAG).i("Polling: $uri after etag $etag")
             val request = Request.Builder()
                     .url(uri)
@@ -118,9 +117,7 @@ class SimpleHttpRendezvousTransport(override var onCancelled: ((reason: Rendezvo
             try {
                 // expired
                 if (response.code == 404) {
-                    // we set to unknown and the cancel method will rewrite the reason to expired if applicable
-                    cancel(RendezvousFailureReason.Unknown)
-                    return null
+                    throw get404Error()
                 }
 
                 // rely on server expiring the channel rather than checking ourselves
@@ -145,31 +142,27 @@ class SimpleHttpRendezvousTransport(override var onCancelled: ((reason: Rendezvo
         }
     }
 
-    override suspend fun cancel(reason: RendezvousFailureReason) {
-        var mappedReason = reason
-        Timber.tag(TAG).i("$expiresAt")
-        if (mappedReason == RendezvousFailureReason.Unknown &&
-                expiresAt != null && Date() > expiresAt
-        ) {
-            mappedReason = RendezvousFailureReason.Expired
-        }
+    private fun get404Error(): RendezvousError {
+        return if (expiresAt != null && Date() > expiresAt)
+            RendezvousError("Expired", RendezvousFailureReason.Expired)
+        else
+            RendezvousError("Received unexpected 404", RendezvousFailureReason.Unknown)
+    }
 
+    override suspend fun close() {
         cancelled = true
         ready = false
-        onCancelled ?.let { it(mappedReason) }
 
-        if (mappedReason == RendezvousFailureReason.UserDeclined) {
-            uri ?.let {
-                try {
-                    val httpClient = okhttp3.OkHttpClient.Builder().build()
-                    val request = Request.Builder()
-                            .url(it)
-                            .delete()
-                            .build()
-                    httpClient.newCall(request).execute()
-                } catch (e: Exception) {
-                    Timber.tag(TAG).w(e, "Failed to delete channel")
-                }
+        uri ?.let {
+            try {
+                val httpClient = okhttp3.OkHttpClient.Builder().build()
+                val request = Request.Builder()
+                        .url(it)
+                        .delete()
+                        .build()
+                httpClient.newCall(request).execute()
+            } catch (e: Throwable) {
+                Timber.tag(TAG).w(e, "Failed to delete channel")
             }
         }
     }
diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt
index 0691e2367e..617d620c27 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt
@@ -70,8 +70,14 @@ class QrCodeLoginStatusFragment : VectorBaseFragment<FragmentQrCodeLoginStatusBi
         return when (reason) {
             RendezvousFailureReason.UnsupportedAlgorithm,
             RendezvousFailureReason.UnsupportedTransport -> getString(R.string.qr_code_login_header_failed_device_is_not_supported_description)
+            RendezvousFailureReason.UnsupportedHomeserver -> getString(R.string.qr_code_login_header_failed_homeserver_is_not_supported_description)
             RendezvousFailureReason.Expired -> getString(R.string.qr_code_login_header_failed_timeout_description)
             RendezvousFailureReason.UserDeclined -> getString(R.string.qr_code_login_header_failed_denied_description)
+            RendezvousFailureReason.E2EESecurityIssue -> getString(R.string.qr_code_login_header_failed_e2ee_security_issue_description)
+            RendezvousFailureReason.OtherDeviceAlreadySignedIn -> getString(R.string.qr_code_login_header_failed_other_device_already_signed_in_description)
+            RendezvousFailureReason.OtherDeviceNotSignedIn -> getString(R.string.qr_code_login_header_failed_other_device_not_signed_in_description)
+            RendezvousFailureReason.InvalidCode -> getString(R.string.qr_code_login_header_failed_invalid_qr_code_description)
+            RendezvousFailureReason.UserCancelled -> getString(R.string.qr_code_login_header_failed_user_cancelled_description)
             else -> getString(R.string.qr_code_login_header_failed_other_description)
         }
     }
diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
index 9d4e5e9870..7f95fad485 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
@@ -30,6 +30,7 @@ import kotlinx.coroutines.launch
 import org.matrix.android.sdk.api.auth.AuthenticationService
 import org.matrix.android.sdk.api.rendezvous.Rendezvous
 import org.matrix.android.sdk.api.rendezvous.RendezvousFailureReason
+import org.matrix.android.sdk.api.rendezvous.model.RendezvousError
 import timber.log.Timber
 
 class QrCodeLoginViewModel @AssistedInject constructor(
@@ -38,14 +39,15 @@ class QrCodeLoginViewModel @AssistedInject constructor(
         private val activeSessionHolder: ActiveSessionHolder,
         private val configureAndStartSessionUseCase: ConfigureAndStartSessionUseCase
 ) : VectorViewModel<QrCodeLoginViewState, QrCodeLoginAction, QrCodeLoginViewEvents>(initialState) {
-    val TAG: String = QrCodeLoginViewModel::class.java.simpleName
 
     @AssistedFactory
     interface Factory : MavericksAssistedViewModelFactory<QrCodeLoginViewModel, QrCodeLoginViewState> {
         override fun create(initialState: QrCodeLoginViewState): QrCodeLoginViewModel
     }
 
-    companion object : MavericksViewModelFactory<QrCodeLoginViewModel, QrCodeLoginViewState> by hiltMavericksViewModelFactory()
+    companion object : MavericksViewModelFactory<QrCodeLoginViewModel, QrCodeLoginViewState> by hiltMavericksViewModelFactory() {
+        val TAG: String = QrCodeLoginViewModel::class.java.simpleName
+    }
 
     override fun handle(action: QrCodeLoginAction) {
         when (action) {
@@ -71,9 +73,14 @@ class QrCodeLoginViewModel @AssistedInject constructor(
     private fun handleOnQrCodeScanned(action: QrCodeLoginAction.OnQrCodeScanned) {
         Timber.tag(TAG).d("Scanned code: ${action.qrCode}")
 
-        val rendezvous = Rendezvous.buildChannelFromCode(action.qrCode) { reason ->
-            Timber.tag(TAG).d("Rendezvous cancelled: $reason")
-            onFailed(reason)
+        val rendezvous = try { Rendezvous.buildChannelFromCode(action.qrCode) } catch (t: Throwable) {
+            Timber.tag(TAG).e(t, "Error occurred during sign in")
+            if (t is RendezvousError) {
+                onFailed(t.reason)
+            } else {
+                onFailed(RendezvousFailureReason.Unknown)
+            }
+            return
         }
 
         setState {
@@ -103,9 +110,13 @@ class QrCodeLoginViewModel @AssistedInject constructor(
                         _viewEvents.post(QrCodeLoginViewEvents.NavigateToHomeScreen)
                     }
                 }
-            } catch (failure: Throwable) {
-                Timber.tag(TAG).e(failure, "Error occurred during sign in")
-                onFailed(RendezvousFailureReason.Unknown)
+            } catch (t: Throwable) {
+                Timber.tag(TAG).e(t, "Error occurred during sign in")
+                if (t is RendezvousError) {
+                    onFailed(t.reason)
+                } else {
+                    onFailed(RendezvousFailureReason.Unknown)
+                }
             }
         }
     }

From 29065b819f6be245a9e78ca3ba7666766d0ce28a Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Mon, 17 Oct 2022 16:03:47 +0100
Subject: [PATCH 64/93] Remove unused class

---
 .../rendezvous/model/EmbeddedRendezvous.kt    | 26 -------------------
 1 file changed, 26 deletions(-)
 delete mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/EmbeddedRendezvous.kt

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/EmbeddedRendezvous.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/EmbeddedRendezvous.kt
deleted file mode 100644
index 785ce1fed7..0000000000
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/EmbeddedRendezvous.kt
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Copyright 2022 The Matrix.org Foundation C.I.C.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.matrix.android.sdk.api.rendezvous.model
-
-import com.squareup.moshi.Json
-import com.squareup.moshi.JsonClass
-
-@JsonClass(generateAdapter = true)
-open class EmbeddedRendezvous(
-        @Json(name = "transport") val transport: RendezvousTransportDetails,
-        @Json(name = "algorithm") val algorithm: SecureRendezvousChannelAlgorithm
-)

From e877feed6eedc22792a24188e3fa37099d5f1f93 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Mon, 17 Oct 2022 16:06:08 +0100
Subject: [PATCH 65/93] Add @JsonClass to all enums

---
 .../java/org/matrix/android/sdk/api/rendezvous/model/Outcome.kt | 2 ++
 .../org/matrix/android/sdk/api/rendezvous/model/PayloadType.kt  | 2 ++
 .../org/matrix/android/sdk/api/rendezvous/model/Protocol.kt     | 2 ++
 .../matrix/android/sdk/api/rendezvous/model/RendezvousIntent.kt | 2 ++
 .../android/sdk/api/rendezvous/model/RendezvousTransportType.kt | 2 ++
 .../api/rendezvous/model/SecureRendezvousChannelAlgorithm.kt    | 2 ++
 6 files changed, 12 insertions(+)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Outcome.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Outcome.kt
index 2ef24e9cb7..a0b2cb31b4 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Outcome.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Outcome.kt
@@ -17,7 +17,9 @@
 package org.matrix.android.sdk.api.rendezvous.model
 
 import com.squareup.moshi.Json
+import com.squareup.moshi.JsonClass
 
+@JsonClass(generateAdapter = true)
 enum class Outcome(val value: String) {
     @Json(name = "success")
     SUCCESS("success"),
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/PayloadType.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/PayloadType.kt
index 5ff4cd7cfa..9e0dbd9562 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/PayloadType.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/PayloadType.kt
@@ -17,7 +17,9 @@
 package org.matrix.android.sdk.api.rendezvous.model
 
 import com.squareup.moshi.Json
+import com.squareup.moshi.JsonClass
 
+@JsonClass(generateAdapter = true)
 internal enum class PayloadType(val value: String) {
     @Json(name = "m.login.start")
     START("m.login.start"),
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Protocol.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Protocol.kt
index 18381984a5..3442d4abf4 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Protocol.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Protocol.kt
@@ -17,7 +17,9 @@
 package org.matrix.android.sdk.api.rendezvous.model
 
 import com.squareup.moshi.Json
+import com.squareup.moshi.JsonClass
 
+@JsonClass(generateAdapter = true)
 enum class Protocol(val value: String) {
     @Json(name = "login_token")
     LOGIN_TOKEN("login_token")
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousIntent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousIntent.kt
index 1c070599b0..8ce6430212 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousIntent.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousIntent.kt
@@ -17,7 +17,9 @@
 package org.matrix.android.sdk.api.rendezvous.model
 
 import com.squareup.moshi.Json
+import com.squareup.moshi.JsonClass
 
+@JsonClass(generateAdapter = true)
 enum class RendezvousIntent {
     @Json(name = "login.start") LOGIN_ON_NEW_DEVICE,
     @Json(name = "login.reciprocate") RECIPROCATE_LOGIN_ON_EXISTING_DEVICE
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousTransportType.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousTransportType.kt
index 9c3e44f25b..2814cad77c 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousTransportType.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousTransportType.kt
@@ -17,7 +17,9 @@
 package org.matrix.android.sdk.api.rendezvous.model
 
 import com.squareup.moshi.Json
+import com.squareup.moshi.JsonClass
 
+@JsonClass(generateAdapter = true)
 enum class RendezvousTransportType(val value: String) {
     @Json(name = "http.v1") MSC3886_SIMPLE_HTTP_V1("http.v1")
 }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/SecureRendezvousChannelAlgorithm.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/SecureRendezvousChannelAlgorithm.kt
index 9a9db58a41..d9f73a0b99 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/SecureRendezvousChannelAlgorithm.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/SecureRendezvousChannelAlgorithm.kt
@@ -17,7 +17,9 @@
 package org.matrix.android.sdk.api.rendezvous.model
 
 import com.squareup.moshi.Json
+import com.squareup.moshi.JsonClass
 
+@JsonClass(generateAdapter = true)
 enum class SecureRendezvousChannelAlgorithm(val value: String) {
     @Json(name = "m.rendezvous.v1.curve25519-aes-sha256") ECDH_V1("m.rendezvous.v1.curve25519-aes-sha256")
 }

From 623277e31ff94a4deac05b3bc297b95f52c85441 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Mon, 17 Oct 2022 16:13:49 +0100
Subject: [PATCH 66/93] Lint

---
 .../sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt | 4 ----
 .../transports/SimpleHttpRendezvousTransport.kt          | 9 +++++----
 2 files changed, 5 insertions(+), 8 deletions(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt
index 9a5c5e865a..2f368d6520 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt
@@ -24,12 +24,8 @@ import org.matrix.android.sdk.api.logger.LoggerTag
 import org.matrix.android.sdk.api.rendezvous.RendezvousChannel
 import org.matrix.android.sdk.api.rendezvous.RendezvousFailureReason
 import org.matrix.android.sdk.api.rendezvous.RendezvousTransport
-import org.matrix.android.sdk.api.rendezvous.model.ECDHRendezvous
-import org.matrix.android.sdk.api.rendezvous.model.ECDHRendezvousCode
 import org.matrix.android.sdk.api.rendezvous.model.RendezvousError
-import org.matrix.android.sdk.api.rendezvous.model.RendezvousIntent
 import org.matrix.android.sdk.api.rendezvous.model.SecureRendezvousChannelAlgorithm
-import org.matrix.android.sdk.api.rendezvous.model.SimpleHttpRendezvousTransportDetails
 import org.matrix.android.sdk.api.util.MatrixJsonParser
 import org.matrix.android.sdk.internal.extensions.toUnsignedInt
 import org.matrix.olm.OlmSAS
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt
index fa0e5d8e2a..50cebae12d 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt
@@ -143,10 +143,11 @@ class SimpleHttpRendezvousTransport(rendezvousUri: String?) : RendezvousTranspor
     }
 
     private fun get404Error(): RendezvousError {
-        return if (expiresAt != null && Date() > expiresAt)
-            RendezvousError("Expired", RendezvousFailureReason.Expired)
-        else
-            RendezvousError("Received unexpected 404", RendezvousFailureReason.Unknown)
+        if (expiresAt != null && Date() > expiresAt) {
+            return RendezvousError("Expired", RendezvousFailureReason.Expired)
+        }
+
+        return RendezvousError("Received unexpected 404", RendezvousFailureReason.Unknown)
     }
 
     override suspend fun close() {

From 3d37e0b2a557cf121efe01935a5a2980c9177ff2 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Mon, 17 Oct 2022 16:19:03 +0100
Subject: [PATCH 67/93] Fix enum JsonClass generateAdapter = false

---
 .../java/org/matrix/android/sdk/api/rendezvous/model/Outcome.kt | 2 +-
 .../org/matrix/android/sdk/api/rendezvous/model/PayloadType.kt  | 2 +-
 .../org/matrix/android/sdk/api/rendezvous/model/Protocol.kt     | 2 +-
 .../matrix/android/sdk/api/rendezvous/model/RendezvousIntent.kt | 2 +-
 .../android/sdk/api/rendezvous/model/RendezvousTransportType.kt | 2 +-
 5 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Outcome.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Outcome.kt
index a0b2cb31b4..0ebd1f88b3 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Outcome.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Outcome.kt
@@ -19,7 +19,7 @@ package org.matrix.android.sdk.api.rendezvous.model
 import com.squareup.moshi.Json
 import com.squareup.moshi.JsonClass
 
-@JsonClass(generateAdapter = true)
+@JsonClass(generateAdapter = false)
 enum class Outcome(val value: String) {
     @Json(name = "success")
     SUCCESS("success"),
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/PayloadType.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/PayloadType.kt
index 9e0dbd9562..33beb1f525 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/PayloadType.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/PayloadType.kt
@@ -19,7 +19,7 @@ package org.matrix.android.sdk.api.rendezvous.model
 import com.squareup.moshi.Json
 import com.squareup.moshi.JsonClass
 
-@JsonClass(generateAdapter = true)
+@JsonClass(generateAdapter = false)
 internal enum class PayloadType(val value: String) {
     @Json(name = "m.login.start")
     START("m.login.start"),
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Protocol.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Protocol.kt
index 3442d4abf4..7c020da641 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Protocol.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Protocol.kt
@@ -19,7 +19,7 @@ package org.matrix.android.sdk.api.rendezvous.model
 import com.squareup.moshi.Json
 import com.squareup.moshi.JsonClass
 
-@JsonClass(generateAdapter = true)
+@JsonClass(generateAdapter = false)
 enum class Protocol(val value: String) {
     @Json(name = "login_token")
     LOGIN_TOKEN("login_token")
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousIntent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousIntent.kt
index 8ce6430212..65037e1252 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousIntent.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousIntent.kt
@@ -19,7 +19,7 @@ package org.matrix.android.sdk.api.rendezvous.model
 import com.squareup.moshi.Json
 import com.squareup.moshi.JsonClass
 
-@JsonClass(generateAdapter = true)
+@JsonClass(generateAdapter = false)
 enum class RendezvousIntent {
     @Json(name = "login.start") LOGIN_ON_NEW_DEVICE,
     @Json(name = "login.reciprocate") RECIPROCATE_LOGIN_ON_EXISTING_DEVICE
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousTransportType.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousTransportType.kt
index 2814cad77c..aa578e3660 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousTransportType.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousTransportType.kt
@@ -19,7 +19,7 @@ package org.matrix.android.sdk.api.rendezvous.model
 import com.squareup.moshi.Json
 import com.squareup.moshi.JsonClass
 
-@JsonClass(generateAdapter = true)
+@JsonClass(generateAdapter = false)
 enum class RendezvousTransportType(val value: String) {
     @Json(name = "http.v1") MSC3886_SIMPLE_HTTP_V1("http.v1")
 }

From 552fb9de9a30d8295256329f0c63fc525d1031cb Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Mon, 17 Oct 2022 16:24:01 +0100
Subject: [PATCH 68/93] Improved comment around QR generation

---
 .../im/vector/app/features/login/qr/QrCodeLoginViewModel.kt  | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
index 7f95fad485..69deb44c70 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
@@ -147,9 +147,10 @@ class QrCodeLoginViewModel @AssistedInject constructor(
     }
 
     /**
-     * TODO. UI test purpose. Fixme accordingly.
+     * QR code generation is not currently supported and this is a placeholder for future
+     * functionality.
      */
     private fun generateQrCodeData(): String {
-        return "TODO"
+        return "NOT SUPPORTED"
     }
 }

From b2dc0b33b5fb07bf47a5cc697589479d138e5515 Mon Sep 17 00:00:00 2001
From: Onuray Sahin <onurays@element.io>
Date: Mon, 17 Oct 2022 18:32:35 +0300
Subject: [PATCH 69/93] Implement try again button action.

---
 .../GetHomeServerCapabilitiesTask.kt          |  1 +
 .../features/login/qr/QrCodeLoginAction.kt    |  1 +
 .../features/login/qr/QrCodeLoginActivity.kt  | 44 +++++++++++--------
 .../login/qr/QrCodeLoginStatusFragment.kt     |  7 +++
 .../login/qr/QrCodeLoginViewEvents.kt         |  1 +
 .../features/login/qr/QrCodeLoginViewModel.kt |  5 +++
 6 files changed, 41 insertions(+), 18 deletions(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/GetHomeServerCapabilitiesTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/GetHomeServerCapabilitiesTask.kt
index 2c3cb440b6..4b56d8e756 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/GetHomeServerCapabilitiesTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/GetHomeServerCapabilitiesTask.kt
@@ -20,6 +20,7 @@ import com.zhuinden.monarchy.Monarchy
 import org.matrix.android.sdk.api.MatrixPatterns.getServerName
 import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig
 import org.matrix.android.sdk.api.auth.wellknown.WellknownResult
+import org.matrix.android.sdk.api.extensions.orFalse
 import org.matrix.android.sdk.api.extensions.orTrue
 import org.matrix.android.sdk.api.session.homeserver.HomeServerCapabilities
 import org.matrix.android.sdk.internal.auth.version.Versions
diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginAction.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginAction.kt
index 8854d0720f..5ea46d3dcd 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginAction.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginAction.kt
@@ -22,4 +22,5 @@ sealed class QrCodeLoginAction : VectorViewModelAction {
     data class OnQrCodeScanned(val qrCode: String) : QrCodeLoginAction()
     object GenerateQrCode : QrCodeLoginAction()
     object ShowQrCode : QrCodeLoginAction()
+    object TryAgain : QrCodeLoginAction()
 }
diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginActivity.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginActivity.kt
index 2f30261890..c9b8ae0080 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginActivity.kt
@@ -38,30 +38,33 @@ class QrCodeLoginActivity : SimpleFragmentActivity() {
         super.onCreate(savedInstanceState)
         views.toolbar.visibility = View.GONE
 
-        val qrCodeLoginArgs: QrCodeLoginArgs? = intent?.extras?.getParcelableCompat(Mavericks.KEY_ARG)
         if (isFirstCreation()) {
-            when (qrCodeLoginArgs?.loginType) {
-                QrCodeLoginType.LOGIN -> {
-                    showInstructionsFragment(qrCodeLoginArgs)
-                }
-                QrCodeLoginType.LINK_A_DEVICE -> {
-                    if (qrCodeLoginArgs.showQrCodeImmediately) {
-                        handleNavigateToShowQrCodeScreen()
-                    } else {
-                        showInstructionsFragment(qrCodeLoginArgs)
-                    }
-                }
-                null -> {
-                    Timber.i("QrCodeLoginArgs is null. This is not expected.")
-                    finish()
-                    return
-                }
-            }
+           navigateToInitialFragment()
         }
 
         observeViewEvents()
     }
 
+    private fun navigateToInitialFragment() {
+        val qrCodeLoginArgs: QrCodeLoginArgs? = intent?.extras?.getParcelableCompat(Mavericks.KEY_ARG)
+        when (qrCodeLoginArgs?.loginType) {
+            QrCodeLoginType.LOGIN -> {
+                showInstructionsFragment(qrCodeLoginArgs)
+            }
+            QrCodeLoginType.LINK_A_DEVICE -> {
+                if (qrCodeLoginArgs.showQrCodeImmediately) {
+                    handleNavigateToShowQrCodeScreen()
+                } else {
+                    showInstructionsFragment(qrCodeLoginArgs)
+                }
+            }
+            null -> {
+                Timber.i("QrCodeLoginArgs is null. This is not expected.")
+                finish()
+            }
+        }
+    }
+
     private fun showInstructionsFragment(qrCodeLoginArgs: QrCodeLoginArgs) {
         addFragment(
                 views.container,
@@ -77,10 +80,15 @@ class QrCodeLoginActivity : SimpleFragmentActivity() {
                 QrCodeLoginViewEvents.NavigateToStatusScreen -> handleNavigateToStatusScreen()
                 QrCodeLoginViewEvents.NavigateToShowQrCodeScreen -> handleNavigateToShowQrCodeScreen()
                 QrCodeLoginViewEvents.NavigateToHomeScreen -> handleNavigateToHomeScreen()
+                QrCodeLoginViewEvents.NavigateToInitialScreen -> handleNavigateToInitialScreen()
             }
         }
     }
 
+    private fun handleNavigateToInitialScreen() {
+        navigateToInitialFragment()
+    }
+
     private fun handleNavigateToShowQrCodeScreen() {
         addFragment(
                 views.container,
diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt
index 617d620c27..097c956f2c 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt
@@ -42,6 +42,13 @@ class QrCodeLoginStatusFragment : VectorBaseFragment<FragmentQrCodeLoginStatusBi
     override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
         super.onViewCreated(view, savedInstanceState)
         initCancelButton()
+        initTryAgainButton()
+    }
+
+    private fun initTryAgainButton() {
+        views.qrCodeLoginStatusTryAgainButton.debouncedClicks {
+            viewModel.handle(QrCodeLoginAction.TryAgain)
+        }
     }
 
     private fun initCancelButton() {
diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewEvents.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewEvents.kt
index 0f282fee38..e20ea6b2e8 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewEvents.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewEvents.kt
@@ -22,4 +22,5 @@ sealed class QrCodeLoginViewEvents : VectorViewEvents {
     object NavigateToStatusScreen : QrCodeLoginViewEvents()
     object NavigateToShowQrCodeScreen : QrCodeLoginViewEvents()
     object NavigateToHomeScreen : QrCodeLoginViewEvents()
+    object NavigateToInitialScreen : QrCodeLoginViewEvents()
 }
diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
index 69deb44c70..ed2fb6e0f5 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
@@ -54,9 +54,14 @@ class QrCodeLoginViewModel @AssistedInject constructor(
             is QrCodeLoginAction.OnQrCodeScanned -> handleOnQrCodeScanned(action)
             QrCodeLoginAction.GenerateQrCode -> handleQrCodeViewStarted()
             QrCodeLoginAction.ShowQrCode -> handleShowQrCode()
+            QrCodeLoginAction.TryAgain -> handleTryAgain()
         }
     }
 
+    private fun handleTryAgain() {
+        _viewEvents.post(QrCodeLoginViewEvents.NavigateToInitialScreen)
+    }
+
     private fun handleShowQrCode() {
         _viewEvents.post(QrCodeLoginViewEvents.NavigateToShowQrCodeScreen)
     }

From 1863e4c3efe867faa0e30d001fadf33885fbe08d Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Mon, 17 Oct 2022 16:34:51 +0100
Subject: [PATCH 70/93] Use unstable prefixes

---
 .../org/matrix/android/sdk/api/rendezvous/model/Protocol.kt   | 4 ++--
 .../sdk/api/rendezvous/model/RendezvousTransportType.kt       | 3 ++-
 .../api/rendezvous/model/SecureRendezvousChannelAlgorithm.kt  | 3 ++-
 3 files changed, 6 insertions(+), 4 deletions(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Protocol.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Protocol.kt
index 7c020da641..6fce2fa11c 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Protocol.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Protocol.kt
@@ -21,6 +21,6 @@ import com.squareup.moshi.JsonClass
 
 @JsonClass(generateAdapter = false)
 enum class Protocol(val value: String) {
-    @Json(name = "login_token")
-    LOGIN_TOKEN("login_token")
+    @Json(name = "org.matrix.msc3906.login_token")
+    LOGIN_TOKEN("org.matrix.msc3906.login_token")
 }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousTransportType.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousTransportType.kt
index aa578e3660..6fca7efa71 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousTransportType.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousTransportType.kt
@@ -21,5 +21,6 @@ import com.squareup.moshi.JsonClass
 
 @JsonClass(generateAdapter = false)
 enum class RendezvousTransportType(val value: String) {
-    @Json(name = "http.v1") MSC3886_SIMPLE_HTTP_V1("http.v1")
+    @Json(name = "org.matrix.msc3886.http.v1")
+    MSC3886_SIMPLE_HTTP_V1("org.matrix.msc3886.http.v1")
 }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/SecureRendezvousChannelAlgorithm.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/SecureRendezvousChannelAlgorithm.kt
index d9f73a0b99..ab58179f5a 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/SecureRendezvousChannelAlgorithm.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/SecureRendezvousChannelAlgorithm.kt
@@ -21,5 +21,6 @@ import com.squareup.moshi.JsonClass
 
 @JsonClass(generateAdapter = true)
 enum class SecureRendezvousChannelAlgorithm(val value: String) {
-    @Json(name = "m.rendezvous.v1.curve25519-aes-sha256") ECDH_V1("m.rendezvous.v1.curve25519-aes-sha256")
+    @Json(name = "org.matrix.msc3903.rendezvous.v1.curve25519-aes-sha256")
+    ECDH_V1("org.matrix.msc3903.rendezvous.v1.curve25519-aes-sha256")
 }

From d3e61a23a9a23ea75fe57f5b4c01cf25ea642558 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Mon, 17 Oct 2022 17:29:45 +0100
Subject: [PATCH 71/93] Fix generator

---
 .../api/rendezvous/model/SecureRendezvousChannelAlgorithm.kt    | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/SecureRendezvousChannelAlgorithm.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/SecureRendezvousChannelAlgorithm.kt
index ab58179f5a..75f0024fda 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/SecureRendezvousChannelAlgorithm.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/SecureRendezvousChannelAlgorithm.kt
@@ -19,7 +19,7 @@ package org.matrix.android.sdk.api.rendezvous.model
 import com.squareup.moshi.Json
 import com.squareup.moshi.JsonClass
 
-@JsonClass(generateAdapter = true)
+@JsonClass(generateAdapter = false)
 enum class SecureRendezvousChannelAlgorithm(val value: String) {
     @Json(name = "org.matrix.msc3903.rendezvous.v1.curve25519-aes-sha256")
     ECDH_V1("org.matrix.msc3903.rendezvous.v1.curve25519-aes-sha256")

From 41dbdbcd7bc5c492620612f4e7f3bcd05c89e7e1 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Mon, 17 Oct 2022 17:30:54 +0100
Subject: [PATCH 72/93] Lint

---
 .../im/vector/app/features/login/qr/QrCodeLoginViewModel.kt     | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
index ed2fb6e0f5..2647c5974d 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
@@ -37,7 +37,7 @@ class QrCodeLoginViewModel @AssistedInject constructor(
         @Assisted private val initialState: QrCodeLoginViewState,
         private val authenticationService: AuthenticationService,
         private val activeSessionHolder: ActiveSessionHolder,
-        private val configureAndStartSessionUseCase: ConfigureAndStartSessionUseCase
+        private val configureAndStartSessionUseCase: ConfigureAndStartSessionUseCase,
 ) : VectorViewModel<QrCodeLoginViewState, QrCodeLoginAction, QrCodeLoginViewEvents>(initialState) {
 
     @AssistedFactory

From 8c8190202f9792b708d623ef5944016220edc69c Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Mon, 17 Oct 2022 17:31:14 +0100
Subject: [PATCH 73/93] Better function name

---
 .../vector/app/features/login/qr/QrCodeLoginStatusFragment.kt | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt
index 097c956f2c..6ef261e6d9 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt
@@ -66,14 +66,14 @@ class QrCodeLoginStatusFragment : VectorBaseFragment<FragmentQrCodeLoginStatusBi
         views.qrCodeLoginStatusCancelButton.isVisible = true
         views.qrCodeLoginStatusTryAgainButton.isVisible = connectionStatus.canTryAgain
         views.qrCodeLoginStatusHeaderView.setTitle(getString(R.string.qr_code_login_header_failed_title))
-        views.qrCodeLoginStatusHeaderView.setDescription(getErrorCode(connectionStatus.errorType))
+        views.qrCodeLoginStatusHeaderView.setDescription(getErrorDescription(connectionStatus.errorType))
         views.qrCodeLoginStatusHeaderView.setImage(
                 imageResource = R.drawable.ic_qr_code_login_failed,
                 backgroundTintColor = ThemeUtils.getColor(requireContext(), R.attr.colorError)
         )
     }
 
-    private fun getErrorCode(reason: RendezvousFailureReason): String {
+    private fun getErrorDescription(reason: RendezvousFailureReason): String {
         return when (reason) {
             RendezvousFailureReason.UnsupportedAlgorithm,
             RendezvousFailureReason.UnsupportedTransport -> getString(R.string.qr_code_login_header_failed_device_is_not_supported_description)

From 8f4d998362d4a89ea0aed78d4abe7c7e2738e9f2 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Mon, 17 Oct 2022 17:33:43 +0100
Subject: [PATCH 74/93] Lint

---
 .../internal/session/homeserver/GetHomeServerCapabilitiesTask.kt | 1 -
 1 file changed, 1 deletion(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/GetHomeServerCapabilitiesTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/GetHomeServerCapabilitiesTask.kt
index 4b56d8e756..2c3cb440b6 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/GetHomeServerCapabilitiesTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/GetHomeServerCapabilitiesTask.kt
@@ -20,7 +20,6 @@ import com.zhuinden.monarchy.Monarchy
 import org.matrix.android.sdk.api.MatrixPatterns.getServerName
 import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig
 import org.matrix.android.sdk.api.auth.wellknown.WellknownResult
-import org.matrix.android.sdk.api.extensions.orFalse
 import org.matrix.android.sdk.api.extensions.orTrue
 import org.matrix.android.sdk.api.session.homeserver.HomeServerCapabilities
 import org.matrix.android.sdk.internal.auth.version.Versions

From 6d17d51fe9e7a5483288f833106f4e572fafcb46 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Mon, 17 Oct 2022 17:36:35 +0100
Subject: [PATCH 75/93] remove nullability

---
 .../android/sdk/api/rendezvous/Rendezvous.kt  |  4 ++--
 .../features/login/qr/QrCodeLoginViewModel.kt | 22 +++++++++----------
 2 files changed, 12 insertions(+), 14 deletions(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/Rendezvous.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/Rendezvous.kt
index 9ad889fca0..e41a72db94 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/Rendezvous.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/Rendezvous.kt
@@ -89,7 +89,7 @@ class Rendezvous(
     }
 
     @Throws(RendezvousError::class)
-    suspend fun startAfterScanningCode(): String? {
+    suspend fun startAfterScanningCode(): String {
         val checksum = channel.connect()
 
         Timber.tag(TAG).i("Connected to secure channel with checksum: $checksum")
@@ -111,7 +111,7 @@ class Rendezvous(
     }
 
     @Throws(RendezvousError::class)
-    suspend fun waitForLoginOnNewDevice(authenticationService: AuthenticationService): Session? {
+    suspend fun waitForLoginOnNewDevice(authenticationService: AuthenticationService): Session {
         Timber.tag(TAG).i("Waiting for login_token")
 
         val loginToken = receive()
diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
index 2647c5974d..4134dc8ab2 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
@@ -100,21 +100,19 @@ class QrCodeLoginViewModel @AssistedInject constructor(
             try {
                 val confirmationCode = rendezvous.startAfterScanningCode()
                 Timber.tag(TAG).i("Established secure channel with checksum: $confirmationCode")
-                confirmationCode?.let {
-                    onConnectionEstablished(it)
-                    val session = rendezvous.waitForLoginOnNewDevice(authenticationService)
-                    onSigningIn()
-                    session?.let {
-                        activeSessionHolder.setActiveSession(session)
-                        authenticationService.reset()
 
-                        configureAndStartSessionUseCase.execute(session)
+                onConnectionEstablished(confirmationCode)
 
-                        rendezvous.completeVerificationOnNewDevice(session)
+                val session = rendezvous.waitForLoginOnNewDevice(authenticationService)
+                onSigningIn()
 
-                        _viewEvents.post(QrCodeLoginViewEvents.NavigateToHomeScreen)
-                    }
-                }
+                activeSessionHolder.setActiveSession(session)
+                authenticationService.reset()
+                configureAndStartSessionUseCase.execute(session)
+
+                rendezvous.completeVerificationOnNewDevice(session)
+
+                _viewEvents.post(QrCodeLoginViewEvents.NavigateToHomeScreen)
             } catch (t: Throwable) {
                 Timber.tag(TAG).e(t, "Error occurred during sign in")
                 if (t is RendezvousError) {

From 8cfe6b13639158011e63b40e2592aa770316e5a0 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Mon, 17 Oct 2022 23:46:28 +0100
Subject: [PATCH 76/93] Wording updates

---
 library/ui-strings/src/main/res/values/strings.xml | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/library/ui-strings/src/main/res/values/strings.xml b/library/ui-strings/src/main/res/values/strings.xml
index e28ebb31c6..7fe8dd8d08 100644
--- a/library/ui-strings/src/main/res/values/strings.xml
+++ b/library/ui-strings/src/main/res/values/strings.xml
@@ -3386,12 +3386,12 @@
     <string name="qr_code_login_header_failed_e2ee_security_issue_description">A security issue was encountered setting up secure messaging. One of the following may be compromised: Your homeserver; Your intent connection(s); Your device(s);</string>
     <string name="qr_code_login_header_failed_other_device_already_signed_in_description">The other device is already signed in.</string>
     <string name="qr_code_login_header_failed_other_device_not_signed_in_description">The other device must be signed in.</string>
-    <string name="qr_code_login_header_failed_invalid_qr_code_description">The QR code scanned is invalid.</string>
+    <string name="qr_code_login_header_failed_invalid_qr_code_description">That QR code is invalid.</string>
     <string name="qr_code_login_header_failed_user_cancelled_description">The sign in was cancelled on the other device.</string>
     <string name="qr_code_login_header_failed_homeserver_is_not_supported_description">The homeserver doesn\'t support sign in with QR code.</string>
-    <string name="qr_code_login_new_device_instruction_1">Open ${app_name} on your other device</string>
-    <string name="qr_code_login_new_device_instruction_2">Go to Settings -> Security &amp; Privacy -> Show All Sessions</string>
-    <string name="qr_code_login_new_device_instruction_3">Select \'Show QR code in this device\'</string>
+    <string name="qr_code_login_new_device_instruction_1">Open the app on your other device</string>
+    <string name="qr_code_login_new_device_instruction_2">Go to Settings -> Security &amp; Privacy</string>
+    <string name="qr_code_login_new_device_instruction_3">Select \'Show QR code\'</string>
     <string name="qr_code_login_link_a_device_scan_qr_code_instruction_1">Start at the sign in screen</string>
     <string name="qr_code_login_link_a_device_scan_qr_code_instruction_2">Select \'Sign in with QR code\'</string>
     <string name="qr_code_login_link_a_device_show_qr_code_instruction_1">Start at the sign in screen</string>

From cf1c7515fb87283915412c7718917a0d9902221f Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Tue, 18 Oct 2022 00:35:13 +0100
Subject: [PATCH 77/93] Automatically try again on partial failed QR scan

---
 .../features/login/qr/QrCodeLoginInstructionsFragment.kt   | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginInstructionsFragment.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginInstructionsFragment.kt
index efd23f2530..40fcbbbb85 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginInstructionsFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginInstructionsFragment.kt
@@ -63,6 +63,7 @@ class QrCodeLoginInstructionsFragment : VectorBaseFragment<FragmentQrCodeLoginIn
             val scannedQrCode = QrCodeScannerActivity.getResultText(activityResult.data)
             val wasQrCode = QrCodeScannerActivity.getResultIsQrCode(activityResult.data)
 
+            Timber.d("Scanned QR code: $scannedQrCode, was QR code: $wasQrCode")
             if (wasQrCode && !scannedQrCode.isNullOrBlank()) {
                 onQrCodeScanned(scannedQrCode)
             } else {
@@ -76,7 +77,11 @@ class QrCodeLoginInstructionsFragment : VectorBaseFragment<FragmentQrCodeLoginIn
     }
 
     private fun onQrCodeScannerFailed() {
-        Timber.d("QrCodeLoginInstructionsFragment.onQrCodeScannerFailed")
+        // The user scanned something unexpected, so we try scanning again.
+        // This seems to happen particularly with the large QRs needed for rendezvous
+        // especially when the QR is partially off the screen
+        Timber.d("QrCodeLoginInstructionsFragment.onQrCodeScannerFailed - showing scanner again")
+        QrCodeScannerActivity.startForResult(requireActivity(), scanActivityResultLauncher)
     }
 
     override fun invalidate() = withState(viewModel) { state ->

From a3126b0026dc70ad6c9e2cf12a11fe3ba0944179 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Tue, 18 Oct 2022 00:35:35 +0100
Subject: [PATCH 78/93] Progress to status screen on failure

---
 .../im/vector/app/features/login/qr/QrCodeLoginViewModel.kt     | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
index 4134dc8ab2..1a8ed945c0 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
@@ -125,6 +125,8 @@ class QrCodeLoginViewModel @AssistedInject constructor(
     }
 
     private fun onFailed(reason: RendezvousFailureReason) {
+        _viewEvents.post(QrCodeLoginViewEvents.NavigateToStatusScreen)
+
         setState {
             copy(
                     connectionStatus = QrCodeLoginConnectionStatus.Failed(reason, reason.canRetry)

From 8a62dfb34aca1c3ab52d05d240ea74cc2b5a6d54 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Tue, 18 Oct 2022 00:35:43 +0100
Subject: [PATCH 79/93] Lint

---
 .../matrix/android/sdk/api/rendezvous/RendezvousTransport.kt  | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousTransport.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousTransport.kt
index 5daf906930..81632e951a 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousTransport.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousTransport.kt
@@ -22,11 +22,15 @@ import org.matrix.android.sdk.api.rendezvous.model.RendezvousTransportDetails
 
 interface RendezvousTransport {
     var ready: Boolean
+
     @Throws(RendezvousError::class)
     suspend fun details(): RendezvousTransportDetails
+
     @Throws(RendezvousError::class)
     suspend fun send(contentType: MediaType, data: ByteArray)
+
     @Throws(RendezvousError::class)
     suspend fun receive(): ByteArray?
+
     suspend fun close()
 }

From f297117df2fe66c61e8589e73bc88cf7a99586bb Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Tue, 18 Oct 2022 08:48:28 +0100
Subject: [PATCH 80/93] Use mutex

---
 .../channels/ECDHRendezvousChannel.kt         | 75 ++++++++++---------
 1 file changed, 38 insertions(+), 37 deletions(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt
index 2f368d6520..19c46db34d 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt
@@ -19,6 +19,8 @@ package org.matrix.android.sdk.api.rendezvous.channels
 import android.util.Base64
 import com.squareup.moshi.Json
 import com.squareup.moshi.JsonClass
+import kotlinx.coroutines.sync.Mutex
+import kotlinx.coroutines.sync.withLock
 import okhttp3.MediaType.Companion.toMediaType
 import org.matrix.android.sdk.api.logger.LoggerTag
 import org.matrix.android.sdk.api.rendezvous.RendezvousChannel
@@ -71,6 +73,7 @@ class ECDHRendezvousChannel(override var transport: RendezvousTransport, theirPu
             @Json val iv: String? = null
     )
 
+    private var olmSASMutex = Mutex()
     private var olmSAS: OlmSAS?
     private val ourPublicKey: ByteArray
     private val ecdhAdapter = MatrixJsonParser.getMoshi().adapter(ECDHPayload::class.java)
@@ -87,45 +90,44 @@ class ECDHRendezvousChannel(override var transport: RendezvousTransport, theirPu
 
     @Throws(RendezvousError::class)
     override suspend fun connect(): String {
-        olmSAS ?.let { olmSAS ->
-            val isInitiator = theirPublicKey == null
+        val sas = olmSAS ?: throw RendezvousError("Channel closed", RendezvousFailureReason.Unknown)
+        val isInitiator = theirPublicKey == null
 
-            if (isInitiator) {
-                Timber.tag(TAG).i("Waiting for other device to send their public key")
-                val res = this.receiveAsPayload() ?: throw RendezvousError("No reply from other device", RendezvousFailureReason.ProtocolError)
+        if (isInitiator) {
+            Timber.tag(TAG).i("Waiting for other device to send their public key")
+            val res = this.receiveAsPayload() ?: throw RendezvousError("No reply from other device", RendezvousFailureReason.ProtocolError)
 
-                if (res.key == null) {
-                    throw RendezvousError(
-                            "Unsupported algorithm: ${res.algorithm}",
-                            RendezvousFailureReason.UnsupportedAlgorithm,
-                    )
-                }
-                theirPublicKey = Base64.decode(res.key, Base64.NO_WRAP)
-            } else {
-                // send our public key unencrypted
-                Timber.tag(TAG).i("Sending public key")
-                send(
-                        ECDHPayload(
-                                algorithm = SecureRendezvousChannelAlgorithm.ECDH_V1,
-                                key = Base64.encodeToString(ourPublicKey, Base64.NO_WRAP)
-                        )
+            if (res.key == null) {
+                throw RendezvousError(
+                        "Unsupported algorithm: ${res.algorithm}",
+                        RendezvousFailureReason.UnsupportedAlgorithm,
                 )
             }
+            theirPublicKey = Base64.decode(res.key, Base64.NO_WRAP)
+        } else {
+            // send our public key unencrypted
+            Timber.tag(TAG).i("Sending public key")
+            send(
+                    ECDHPayload(
+                            algorithm = SecureRendezvousChannelAlgorithm.ECDH_V1,
+                            key = Base64.encodeToString(ourPublicKey, Base64.NO_WRAP)
+                    )
+            )
+        }
 
-            synchronized(olmSAS) {
-                olmSAS.setTheirPublicKey(Base64.encodeToString(theirPublicKey, Base64.NO_WRAP))
-                olmSAS.setTheirPublicKey(Base64.encodeToString(theirPublicKey, Base64.NO_WRAP))
+        olmSASMutex.withLock {
+            sas.setTheirPublicKey(Base64.encodeToString(theirPublicKey, Base64.NO_WRAP))
+            sas.setTheirPublicKey(Base64.encodeToString(theirPublicKey, Base64.NO_WRAP))
 
-                val initiatorKey = Base64.encodeToString(if (isInitiator) ourPublicKey else theirPublicKey, Base64.NO_WRAP)
-                val recipientKey = Base64.encodeToString(if (isInitiator) theirPublicKey else ourPublicKey, Base64.NO_WRAP)
-                val aesInfo = "${SecureRendezvousChannelAlgorithm.ECDH_V1.value}|$initiatorKey|$recipientKey"
+            val initiatorKey = Base64.encodeToString(if (isInitiator) ourPublicKey else theirPublicKey, Base64.NO_WRAP)
+            val recipientKey = Base64.encodeToString(if (isInitiator) theirPublicKey else ourPublicKey, Base64.NO_WRAP)
+            val aesInfo = "${SecureRendezvousChannelAlgorithm.ECDH_V1.value}|$initiatorKey|$recipientKey"
 
-                aesKey = olmSAS.generateShortCode(aesInfo, 32)
+            aesKey = sas.generateShortCode(aesInfo, 32)
 
-                val rawChecksum = olmSAS.generateShortCode(aesInfo, 5)
-                return getDecimalCodeRepresentation(rawChecksum)
-            }
-        } ?: throw RuntimeException("Channel closed")
+            val rawChecksum = sas.generateShortCode(aesInfo, 5)
+            return getDecimalCodeRepresentation(rawChecksum)
+        }
     }
 
     private suspend fun send(payload: ECDHPayload) {
@@ -154,12 +156,11 @@ class ECDHRendezvousChannel(override var transport: RendezvousTransport, theirPu
     }
 
     override suspend fun close() {
-        olmSAS ?.let {
-            synchronized(it) {
-                // this does a double release check already so we don't re-check ourselves
-                it.releaseSas()
-                olmSAS = null
-            }
+        val sas = olmSAS ?: throw IllegalStateException("Channel already closed")
+        olmSASMutex.withLock {
+            // this does a double release check already so we don't re-check ourselves
+            sas.releaseSas()
+            olmSAS = null
         }
         transport.close()
     }

From a1d2944c324c37bdcf9c92e0ec02864ce6781d71 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Tue, 18 Oct 2022 09:08:40 +0100
Subject: [PATCH 81/93] Always check master key when provided by verifying
 device

---
 .../android/sdk/api/rendezvous/Rendezvous.kt  | 32 +++++++++++--------
 1 file changed, 18 insertions(+), 14 deletions(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/Rendezvous.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/Rendezvous.kt
index e41a72db94..f724ac4b62 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/Rendezvous.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/Rendezvous.kt
@@ -165,21 +165,25 @@ class Rendezvous(
             }
 
             verificationResponse.masterKey?.let { masterKeyFromVerifyingDevice ->
-                // check master key againt what the homeserver told us
-                crypto.crossSigningService().getMyCrossSigningKeys()?.masterKey()?.let { localMasterKey ->
-                    if (localMasterKey.unpaddedBase64PublicKey != masterKeyFromVerifyingDevice) {
-                        Timber.tag(TAG).w("Master key from verifying device doesn't match: $masterKeyFromVerifyingDevice vs $localMasterKey")
-                        // inform the other side
-                        send(Payload(PayloadType.FINISH, outcome = Outcome.E2EE_SECURITY_ERROR))
-                        throw RendezvousError("Master key from verifying device doesn't match", RendezvousFailureReason.E2EESecurityIssue)
-                    }
-                    // set other device as verified
-                    Timber.tag(TAG).i("Setting device $verifyingDeviceId as verified")
-                    crypto.setDeviceVerification(DeviceTrustLevel(locallyVerified = true, crossSigningVerified = false), userId, verifyingDeviceId)
+                // verifying device provided us with a master key, so use it to check integrity
 
-                    Timber.tag(TAG).i("Setting master key as trusted")
-                    crypto.crossSigningService().markMyMasterKeyAsTrusted()
-                } ?: Timber.tag(TAG).w("No local master key so not verifying")
+                // see what the homeserver told us
+                val localMasterKey = crypto.crossSigningService().getMyCrossSigningKeys()?.masterKey()
+
+                // n.b. if no local master key this is a problem, as well as it not matching
+                if (localMasterKey?.unpaddedBase64PublicKey != masterKeyFromVerifyingDevice) {
+                    Timber.tag(TAG).w("Master key from verifying device doesn't match: $masterKeyFromVerifyingDevice vs $localMasterKey")
+                    // inform the other side
+                    send(Payload(PayloadType.FINISH, outcome = Outcome.E2EE_SECURITY_ERROR))
+                    throw RendezvousError("Master key from verifying device doesn't match", RendezvousFailureReason.E2EESecurityIssue)
+                }
+
+                // set other device as verified
+                Timber.tag(TAG).i("Setting device $verifyingDeviceId as verified")
+                crypto.setDeviceVerification(DeviceTrustLevel(locallyVerified = true, crossSigningVerified = false), userId, verifyingDeviceId)
+
+                Timber.tag(TAG).i("Setting master key as trusted")
+                crypto.crossSigningService().markMyMasterKeyAsTrusted()
             } ?: run {
                 // set other device as verified anyway
                 Timber.tag(TAG).i("Setting device $verifyingDeviceId as verified")

From 57a8dd4a1f61b50e180830d96264d5c3b1f1fe72 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Tue, 18 Oct 2022 09:31:13 +0100
Subject: [PATCH 82/93] Whitespce

---
 .../sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt  | 4 ++--
 .../transports/SimpleHttpRendezvousTransport.kt           | 8 ++++----
 2 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt
index 19c46db34d..f7da0efa92 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt
@@ -81,7 +81,7 @@ class ECDHRendezvousChannel(override var transport: RendezvousTransport, theirPu
     private var aesKey: ByteArray? = null
 
     init {
-        theirPublicKeyBase64 ?.let {
+        theirPublicKeyBase64?.let {
             theirPublicKey = Base64.decode(it, Base64.NO_WRAP)
         }
         olmSAS = OlmSAS()
@@ -142,7 +142,7 @@ class ECDHRendezvousChannel(override var transport: RendezvousTransport, theirPu
     }
 
     private suspend fun receiveAsPayload(): ECDHPayload? {
-        transport.receive()?.toString(Charsets.UTF_8) ?.let {
+        transport.receive()?.toString(Charsets.UTF_8)?.let {
             return ecdhAdapter.fromJson(it)
         } ?: return null
     }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt
index 50cebae12d..3f7cfe786e 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt
@@ -69,7 +69,7 @@ class SimpleHttpRendezvousTransport(rendezvousUri: String?) : RendezvousTranspor
                 .method(method, data.toRequestBody())
                 .header("content-type", contentType.toString())
 
-        etag ?.let {
+        etag?.let {
             request.header("if-match", it)
         }
 
@@ -85,7 +85,7 @@ class SimpleHttpRendezvousTransport(rendezvousUri: String?) : RendezvousTranspor
         if (method == "POST") {
             val location = response.header("location") ?: throw RuntimeException("No rendezvous URI found in response")
 
-            response.header("expires") ?.let {
+            response.header("expires")?.let {
                 val format = SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz")
                 expiresAt = format.parse(it)
             }
@@ -108,7 +108,7 @@ class SimpleHttpRendezvousTransport(rendezvousUri: String?) : RendezvousTranspor
                     .url(uri)
                     .get()
 
-            etag ?.let {
+            etag?.let {
                 request.header("if-none-match", it)
             }
 
@@ -154,7 +154,7 @@ class SimpleHttpRendezvousTransport(rendezvousUri: String?) : RendezvousTranspor
         cancelled = true
         ready = false
 
-        uri ?.let {
+        uri?.let {
             try {
                 val httpClient = okhttp3.OkHttpClient.Builder().build()
                 val request = Request.Builder()

From 376cd1cb367c8fa3d3b9f9f2da898a56e0f3636e Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Tue, 18 Oct 2022 09:34:28 +0100
Subject: [PATCH 83/93] Missing throws

---
 .../api/rendezvous/transports/SimpleHttpRendezvousTransport.kt  | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt
index 3f7cfe786e..7a5cbe5424 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt
@@ -54,6 +54,7 @@ class SimpleHttpRendezvousTransport(rendezvousUri: String?) : RendezvousTranspor
         return SimpleHttpRendezvousTransportDetails(uri)
     }
 
+    @Throws(RendezvousError::class)
     override suspend fun send(contentType: MediaType, data: ByteArray) {
         if (cancelled) {
             throw IllegalStateException("Rendezvous cancelled")
@@ -96,6 +97,7 @@ class SimpleHttpRendezvousTransport(rendezvousUri: String?) : RendezvousTranspor
         }
     }
 
+    @Throws(RendezvousError::class)
     override suspend fun receive(): ByteArray? {
         if (cancelled) {
             throw IllegalStateException("Rendezvous cancelled")

From 9fb0db3129ff6521ac86aba4bee29c12e6d45d5b Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@users.noreply.github.com>
Date: Tue, 18 Oct 2022 12:07:12 +0100
Subject: [PATCH 84/93] Update
 library/ui-strings/src/main/res/values/strings.xml

Co-authored-by: Benoit Marty <benoitm@matrix.org>
---
 library/ui-strings/src/main/res/values/strings.xml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/library/ui-strings/src/main/res/values/strings.xml b/library/ui-strings/src/main/res/values/strings.xml
index 7fe8dd8d08..7b6b9b914d 100644
--- a/library/ui-strings/src/main/res/values/strings.xml
+++ b/library/ui-strings/src/main/res/values/strings.xml
@@ -3383,7 +3383,7 @@
     <string name="qr_code_login_header_failed_timeout_description">The linking wasn’t completed in the required time.</string>
     <string name="qr_code_login_header_failed_denied_description">The request was denied on the other device.</string>
     <string name="qr_code_login_header_failed_other_description">The request failed.</string>
-    <string name="qr_code_login_header_failed_e2ee_security_issue_description">A security issue was encountered setting up secure messaging. One of the following may be compromised: Your homeserver; Your intent connection(s); Your device(s);</string>
+    <string name="qr_code_login_header_failed_e2ee_security_issue_description">A security issue was encountered setting up secure messaging. One of the following may be compromised: Your homeserver; Your internet connection(s); Your device(s);</string>
     <string name="qr_code_login_header_failed_other_device_already_signed_in_description">The other device is already signed in.</string>
     <string name="qr_code_login_header_failed_other_device_not_signed_in_description">The other device must be signed in.</string>
     <string name="qr_code_login_header_failed_invalid_qr_code_description">That QR code is invalid.</string>

From 0d1df3f66e5587ba299b909183fa464673694489 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@users.noreply.github.com>
Date: Tue, 18 Oct 2022 12:08:09 +0100
Subject: [PATCH 85/93] Update
 matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousChannel.kt

Co-authored-by: Benoit Marty <benoitm@matrix.org>
---
 .../org/matrix/android/sdk/api/rendezvous/RendezvousChannel.kt  | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousChannel.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousChannel.kt
index be79569164..9976c7a8e4 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousChannel.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousChannel.kt
@@ -45,7 +45,7 @@ interface RendezvousChannel {
     suspend fun receive(): ByteArray?
 
     /**
-     * @returns closes the channel and cleans up
+     * Closes the channel and cleans up.
      */
     suspend fun close()
 }

From 8530f8f28049dae1be38c2cf0503e8cfac04f2f6 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@users.noreply.github.com>
Date: Tue, 18 Oct 2022 12:09:06 +0100
Subject: [PATCH 86/93] Update
 matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt

Co-authored-by: Benoit Marty <benoitm@matrix.org>
---
 .../sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt        | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt
index f7da0efa92..b21d89059b 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt
@@ -73,7 +73,7 @@ class ECDHRendezvousChannel(override var transport: RendezvousTransport, theirPu
             @Json val iv: String? = null
     )
 
-    private var olmSASMutex = Mutex()
+    private val olmSASMutex = Mutex()
     private var olmSAS: OlmSAS?
     private val ourPublicKey: ByteArray
     private val ecdhAdapter = MatrixJsonParser.getMoshi().adapter(ECDHPayload::class.java)

From a83fb8bf83c10759c1e25e126e1fae5319cc4dad Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@users.noreply.github.com>
Date: Tue, 18 Oct 2022 12:09:17 +0100
Subject: [PATCH 87/93] Update
 matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousChannel.kt

Co-authored-by: Benoit Marty <benoitm@matrix.org>
---
 .../org/matrix/android/sdk/api/rendezvous/RendezvousChannel.kt  | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousChannel.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousChannel.kt
index 9976c7a8e4..0956a5b0a0 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousChannel.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousChannel.kt
@@ -22,7 +22,7 @@ import org.matrix.android.sdk.api.rendezvous.model.RendezvousError
  * Representation of a rendezvous channel such as that described by MSC3903.
  */
 interface RendezvousChannel {
-    var transport: RendezvousTransport
+    val transport: RendezvousTransport
 
     /**
      * @returns the checksum/confirmation digits to be shown to the user

From 916ae654e790bc001a1f5f86ef30d32657da952d Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Tue, 18 Oct 2022 12:11:41 +0100
Subject: [PATCH 88/93] Don't log whole QR code

---
 .../im/vector/app/features/login/qr/QrCodeLoginViewModel.kt     | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
index 1a8ed945c0..9bf3e955d3 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
@@ -76,7 +76,7 @@ class QrCodeLoginViewModel @AssistedInject constructor(
     }
 
     private fun handleOnQrCodeScanned(action: QrCodeLoginAction.OnQrCodeScanned) {
-        Timber.tag(TAG).d("Scanned code: ${action.qrCode}")
+        Timber.tag(TAG).d("Scanned code of length ${action.qrCode.length}")
 
         val rendezvous = try { Rendezvous.buildChannelFromCode(action.qrCode) } catch (t: Throwable) {
             Timber.tag(TAG).e(t, "Error occurred during sign in")

From 811d6d87ae50595726494abe772a8931d27da0c2 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Tue, 18 Oct 2022 12:23:21 +0100
Subject: [PATCH 89/93] Reuse getDecimalCodeRepresentation from SAS instead of
 duplicating code

---
 .../channels/ECDHRendezvousChannel.kt         | 17 ++----
 .../SASDefaultVerificationTransaction.kt      | 54 +++++++++----------
 2 files changed, 30 insertions(+), 41 deletions(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt
index b21d89059b..6c45aacfaa 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt
@@ -29,7 +29,7 @@ import org.matrix.android.sdk.api.rendezvous.RendezvousTransport
 import org.matrix.android.sdk.api.rendezvous.model.RendezvousError
 import org.matrix.android.sdk.api.rendezvous.model.SecureRendezvousChannelAlgorithm
 import org.matrix.android.sdk.api.util.MatrixJsonParser
-import org.matrix.android.sdk.internal.extensions.toUnsignedInt
+import org.matrix.android.sdk.internal.crypto.verification.SASDefaultVerificationTransaction
 import org.matrix.olm.OlmSAS
 import timber.log.Timber
 import java.security.SecureRandom
@@ -48,20 +48,9 @@ class ECDHRendezvousChannel(override var transport: RendezvousTransport, theirPu
         private const val KEY_SPEC = "AES"
         private val TAG = LoggerTag(ECDHRendezvousChannel::class.java.simpleName, LoggerTag.RENDEZVOUS).value
 
-        // n.b. we are only aver processing byte array that we have generated, so we can make assumptions about the length
+        // this is the same representation as for SAS but we delimit by dashes instead of spaces for readability
         private fun getDecimalCodeRepresentation(byteArray: ByteArray): String {
-            val b0 = byteArray[0].toUnsignedInt() // need unsigned byte
-            val b1 = byteArray[1].toUnsignedInt() // need unsigned byte
-            val b2 = byteArray[2].toUnsignedInt() // need unsigned byte
-            val b3 = byteArray[3].toUnsignedInt() // need unsigned byte
-            val b4 = byteArray[4].toUnsignedInt() // need unsigned byte
-            // (B0 << 5 | B1 >> 3) + 1000
-            val first = (b0.shl(5) or b1.shr(3)) + 1000
-            // ((B1 & 0x7) << 10 | B2 << 2 | B3 >> 6) + 1000
-            val second = ((b1 and 0x7).shl(10) or b2.shl(2) or b3.shr(6)) + 1000
-            // ((B3 & 0x3f) << 7 | B4 >> 1) + 1000
-            val third = ((b3 and 0x3f).shl(7) or b4.shr(1)) + 1000
-            return "$first-$second-$third"
+            return SASDefaultVerificationTransaction.getDecimalCodeRepresentation(byteArray).replace(" ", "-")
         }
     }
 
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/SASDefaultVerificationTransaction.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/SASDefaultVerificationTransaction.kt
index 1cbaff059a..b306288c5e 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/SASDefaultVerificationTransaction.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/SASDefaultVerificationTransaction.kt
@@ -82,6 +82,33 @@ internal abstract class SASDefaultVerificationTransaction(
         // older devices have limited support of emoji but SDK offers images for the 64 verification emojis
         // so always send that we support EMOJI
         val KNOWN_SHORT_CODES = listOf(SasMode.EMOJI, SasMode.DECIMAL)
+
+        /**
+         * decimal: generate five bytes by using HKDF.
+         * Take the first 13 bits and convert it to a decimal number (which will be a number between 0 and 8191 inclusive),
+         * and add 1000 (resulting in a number between 1000 and 9191 inclusive).
+         * Do the same with the second 13 bits, and the third 13 bits, giving three 4-digit numbers.
+         * In other words, if the five bytes are B0, B1, B2, B3, and B4, then the first number is (B0 << 5 | B1 >> 3) + 1000,
+         * the second number is ((B1 & 0x7) << 10 | B2 << 2 | B3 >> 6) + 1000, and the third number is ((B3 & 0x3f) << 7 | B4 >> 1) + 1000.
+         * (This method of converting 13 bits at a time is used to avoid requiring 32-bit clients to do big-number arithmetic,
+         * and adding 1000 to the number avoids having clients to worry about properly zero-padding the number when displaying to the user.)
+         * The three 4-digit numbers are displayed to the user either with dashes (or another appropriate separator) separating the three numbers,
+         * or with the three numbers on separate lines.
+         */
+        fun getDecimalCodeRepresentation(byteArray: ByteArray): String {
+            val b0 = byteArray[0].toUnsignedInt() // need unsigned byte
+            val b1 = byteArray[1].toUnsignedInt() // need unsigned byte
+            val b2 = byteArray[2].toUnsignedInt() // need unsigned byte
+            val b3 = byteArray[3].toUnsignedInt() // need unsigned byte
+            val b4 = byteArray[4].toUnsignedInt() // need unsigned byte
+            // (B0 << 5 | B1 >> 3) + 1000
+            val first = (b0.shl(5) or b1.shr(3)) + 1000
+            // ((B1 & 0x7) << 10 | B2 << 2 | B3 >> 6) + 1000
+            val second = ((b1 and 0x7).shl(10) or b2.shl(2) or b3.shr(6)) + 1000
+            // ((B3 & 0x3f) << 7 | B4 >> 1) + 1000
+            val third = ((b3 and 0x3f).shl(7) or b4.shr(1)) + 1000
+            return "$first $second $third"
+        }
     }
 
     override var state: VerificationTxState = VerificationTxState.None
@@ -371,33 +398,6 @@ internal abstract class SASDefaultVerificationTransaction(
         return getDecimalCodeRepresentation(shortCodeBytes!!)
     }
 
-    /**
-     * decimal: generate five bytes by using HKDF.
-     * Take the first 13 bits and convert it to a decimal number (which will be a number between 0 and 8191 inclusive),
-     * and add 1000 (resulting in a number between 1000 and 9191 inclusive).
-     * Do the same with the second 13 bits, and the third 13 bits, giving three 4-digit numbers.
-     * In other words, if the five bytes are B0, B1, B2, B3, and B4, then the first number is (B0 << 5 | B1 >> 3) + 1000,
-     * the second number is ((B1 & 0x7) << 10 | B2 << 2 | B3 >> 6) + 1000, and the third number is ((B3 & 0x3f) << 7 | B4 >> 1) + 1000.
-     * (This method of converting 13 bits at a time is used to avoid requiring 32-bit clients to do big-number arithmetic,
-     * and adding 1000 to the number avoids having clients to worry about properly zero-padding the number when displaying to the user.)
-     * The three 4-digit numbers are displayed to the user either with dashes (or another appropriate separator) separating the three numbers,
-     * or with the three numbers on separate lines.
-     */
-    fun getDecimalCodeRepresentation(byteArray: ByteArray): String {
-        val b0 = byteArray[0].toUnsignedInt() // need unsigned byte
-        val b1 = byteArray[1].toUnsignedInt() // need unsigned byte
-        val b2 = byteArray[2].toUnsignedInt() // need unsigned byte
-        val b3 = byteArray[3].toUnsignedInt() // need unsigned byte
-        val b4 = byteArray[4].toUnsignedInt() // need unsigned byte
-        // (B0 << 5 | B1 >> 3) + 1000
-        val first = (b0.shl(5) or b1.shr(3)) + 1000
-        // ((B1 & 0x7) << 10 | B2 << 2 | B3 >> 6) + 1000
-        val second = ((b1 and 0x7).shl(10) or b2.shl(2) or b3.shr(6)) + 1000
-        // ((B3 & 0x3f) << 7 | B4 >> 1) + 1000
-        val third = ((b3 and 0x3f).shl(7) or b4.shr(1)) + 1000
-        return "$first $second $third"
-    }
-
     override fun getEmojiCodeRepresentation(): List<EmojiRepresentation> {
         return getEmojiCodeRepresentation(shortCodeBytes!!)
     }

From f7e0a198335b9830af68404d522c834319adb784 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Tue, 18 Oct 2022 12:29:48 +0100
Subject: [PATCH 90/93] Remove redundant annotations

---
 .../api/rendezvous/channels/ECDHRendezvousChannel.kt |  8 ++++----
 .../sdk/api/rendezvous/model/ECDHRendezvous.kt       |  7 +++----
 .../sdk/api/rendezvous/model/ECDHRendezvousCode.kt   |  5 ++---
 .../android/sdk/api/rendezvous/model/Payload.kt      | 12 ++++++------
 .../rendezvous/model/RendezvousTransportDetails.kt   |  3 +--
 .../model/SimpleHttpRendezvousTransportDetails.kt    |  3 +--
 6 files changed, 17 insertions(+), 21 deletions(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt
index 6c45aacfaa..4bbf34280c 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt
@@ -56,10 +56,10 @@ class ECDHRendezvousChannel(override var transport: RendezvousTransport, theirPu
 
     @JsonClass(generateAdapter = true)
     internal data class ECDHPayload(
-            @Json val algorithm: SecureRendezvousChannelAlgorithm? = null,
-            @Json val key: String? = null,
-            @Json val ciphertext: String? = null,
-            @Json val iv: String? = null
+            val algorithm: SecureRendezvousChannelAlgorithm? = null,
+            val key: String? = null,
+            val ciphertext: String? = null,
+            val iv: String? = null
     )
 
     private val olmSASMutex = Mutex()
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/ECDHRendezvous.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/ECDHRendezvous.kt
index 0840e1ca2e..55bac6397e 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/ECDHRendezvous.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/ECDHRendezvous.kt
@@ -16,12 +16,11 @@
 
 package org.matrix.android.sdk.api.rendezvous.model
 
-import com.squareup.moshi.Json
 import com.squareup.moshi.JsonClass
 
 @JsonClass(generateAdapter = true)
 data class ECDHRendezvous(
-        @Json val transport: SimpleHttpRendezvousTransportDetails,
-        @Json val algorithm: SecureRendezvousChannelAlgorithm,
-        @Json val key: String
+        val transport: SimpleHttpRendezvousTransportDetails,
+        val algorithm: SecureRendezvousChannelAlgorithm,
+        val key: String
 )
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/ECDHRendezvousCode.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/ECDHRendezvousCode.kt
index 410c5c1036..575b5d4bfd 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/ECDHRendezvousCode.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/ECDHRendezvousCode.kt
@@ -16,11 +16,10 @@
 
 package org.matrix.android.sdk.api.rendezvous.model
 
-import com.squareup.moshi.Json
 import com.squareup.moshi.JsonClass
 
 @JsonClass(generateAdapter = true)
 data class ECDHRendezvousCode(
-        @Json val intent: RendezvousIntent,
-        @Json val rendezvous: ECDHRendezvous
+        val intent: RendezvousIntent,
+        val rendezvous: ECDHRendezvous
 )
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Payload.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Payload.kt
index 593177e625..04631ce959 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Payload.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Payload.kt
@@ -21,12 +21,12 @@ import com.squareup.moshi.JsonClass
 
 @JsonClass(generateAdapter = true)
 internal data class Payload(
-        @Json val type: PayloadType,
-        @Json val intent: RendezvousIntent? = null,
-        @Json val outcome: Outcome? = null,
-        @Json val protocols: List<Protocol>? = null,
-        @Json val protocol: Protocol? = null,
-        @Json val homeserver: String? = null,
+        val type: PayloadType,
+        val intent: RendezvousIntent? = null,
+        val outcome: Outcome? = null,
+        val protocols: List<Protocol>? = null,
+        val protocol: Protocol? = null,
+        val homeserver: String? = null,
         @Json(name = "login_token") val loginToken: String? = null,
         @Json(name = "device_id") val deviceId: String? = null,
         @Json(name = "device_key") val deviceKey: String? = null,
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousTransportDetails.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousTransportDetails.kt
index 55b3bbb5d9..1bde43ab7e 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousTransportDetails.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousTransportDetails.kt
@@ -16,10 +16,9 @@
 
 package org.matrix.android.sdk.api.rendezvous.model
 
-import com.squareup.moshi.Json
 import com.squareup.moshi.JsonClass
 
 @JsonClass(generateAdapter = true)
 open class RendezvousTransportDetails(
-    @Json val type: RendezvousTransportType
+    val type: RendezvousTransportType
 )
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/SimpleHttpRendezvousTransportDetails.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/SimpleHttpRendezvousTransportDetails.kt
index 70a441d760..049aa8b756 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/SimpleHttpRendezvousTransportDetails.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/SimpleHttpRendezvousTransportDetails.kt
@@ -16,10 +16,9 @@
 
 package org.matrix.android.sdk.api.rendezvous.model
 
-import com.squareup.moshi.Json
 import com.squareup.moshi.JsonClass
 
 @JsonClass(generateAdapter = true)
 data class SimpleHttpRendezvousTransportDetails(
-        @Json val uri: String
+        val uri: String
 ) : RendezvousTransportDetails(type = RendezvousTransportType.MSC3886_SIMPLE_HTTP_V1)

From 67be8c3c40f06a88345436abaf129ce3c4e52344 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Tue, 18 Oct 2022 12:44:05 +0100
Subject: [PATCH 91/93] The one that got away

---
 .../android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt | 1 -
 1 file changed, 1 deletion(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt
index 4bbf34280c..e844143889 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt
@@ -17,7 +17,6 @@
 package org.matrix.android.sdk.api.rendezvous.channels
 
 import android.util.Base64
-import com.squareup.moshi.Json
 import com.squareup.moshi.JsonClass
 import kotlinx.coroutines.sync.Mutex
 import kotlinx.coroutines.sync.withLock

From 0c52a7ed040c5637d81c408e2ec41197de4b2563 Mon Sep 17 00:00:00 2001
From: Onuray Sahin <onurays@element.io>
Date: Tue, 18 Oct 2022 15:45:39 +0300
Subject: [PATCH 92/93] Fix layout after try again button is clicked.

---
 .../im/vector/app/features/login/qr/QrCodeLoginActivity.kt   | 3 ++-
 .../im/vector/app/features/login/qr/QrCodeLoginViewModel.kt  | 5 +++++
 2 files changed, 7 insertions(+), 1 deletion(-)

diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginActivity.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginActivity.kt
index c9b8ae0080..a0c113224d 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginActivity.kt
@@ -24,6 +24,7 @@ import com.airbnb.mvrx.Mavericks
 import com.airbnb.mvrx.viewModel
 import dagger.hilt.android.AndroidEntryPoint
 import im.vector.app.core.extensions.addFragment
+import im.vector.app.core.extensions.replaceFragment
 import im.vector.app.core.platform.SimpleFragmentActivity
 import im.vector.app.features.home.HomeActivity
 import im.vector.lib.core.utils.compat.getParcelableCompat
@@ -66,7 +67,7 @@ class QrCodeLoginActivity : SimpleFragmentActivity() {
     }
 
     private fun showInstructionsFragment(qrCodeLoginArgs: QrCodeLoginArgs) {
-        addFragment(
+        replaceFragment(
                 views.container,
                 QrCodeLoginInstructionsFragment::class.java,
                 qrCodeLoginArgs,
diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
index 9bf3e955d3..97cca9d791 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
@@ -59,6 +59,11 @@ class QrCodeLoginViewModel @AssistedInject constructor(
     }
 
     private fun handleTryAgain() {
+        setState {
+            copy(
+                    connectionStatus = null
+            )
+        }
         _viewEvents.post(QrCodeLoginViewEvents.NavigateToInitialScreen)
     }
 

From 4f652f102676e8bd2364149d00009f9d0f1ea656 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Wed, 19 Oct 2022 09:12:09 +0100
Subject: [PATCH 93/93] Request changes from review

---
 .../sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt   | 7 +------
 .../rendezvous/transports/SimpleHttpRendezvousTransport.kt | 3 ++-
 .../verification/SASDefaultVerificationTransaction.kt      | 4 ++--
 3 files changed, 5 insertions(+), 9 deletions(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt
index e844143889..c1d6b1b70e 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt
@@ -46,11 +46,6 @@ class ECDHRendezvousChannel(override var transport: RendezvousTransport, theirPu
         private const val ALGORITHM_SPEC = "AES/GCM/NoPadding"
         private const val KEY_SPEC = "AES"
         private val TAG = LoggerTag(ECDHRendezvousChannel::class.java.simpleName, LoggerTag.RENDEZVOUS).value
-
-        // this is the same representation as for SAS but we delimit by dashes instead of spaces for readability
-        private fun getDecimalCodeRepresentation(byteArray: ByteArray): String {
-            return SASDefaultVerificationTransaction.getDecimalCodeRepresentation(byteArray).replace(" ", "-")
-        }
     }
 
     @JsonClass(generateAdapter = true)
@@ -114,7 +109,7 @@ class ECDHRendezvousChannel(override var transport: RendezvousTransport, theirPu
             aesKey = sas.generateShortCode(aesInfo, 32)
 
             val rawChecksum = sas.generateShortCode(aesInfo, 5)
-            return getDecimalCodeRepresentation(rawChecksum)
+            return SASDefaultVerificationTransaction.getDecimalCodeRepresentation(rawChecksum, separator = "-")
         }
     }
 
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt
index 7a5cbe5424..620b599e3d 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt
@@ -29,6 +29,7 @@ import org.matrix.android.sdk.api.rendezvous.model.SimpleHttpRendezvousTransport
 import timber.log.Timber
 import java.text.SimpleDateFormat
 import java.util.Date
+import java.util.Locale
 
 /**
  * Implementation of the Simple HTTP transport MSC3886: https://github.com/matrix-org/matrix-spec-proposals/pull/3886
@@ -87,7 +88,7 @@ class SimpleHttpRendezvousTransport(rendezvousUri: String?) : RendezvousTranspor
             val location = response.header("location") ?: throw RuntimeException("No rendezvous URI found in response")
 
             response.header("expires")?.let {
-                val format = SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz")
+                val format = SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US)
                 expiresAt = format.parse(it)
             }
 
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/SASDefaultVerificationTransaction.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/SASDefaultVerificationTransaction.kt
index b306288c5e..29b416bb82 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/SASDefaultVerificationTransaction.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/SASDefaultVerificationTransaction.kt
@@ -95,7 +95,7 @@ internal abstract class SASDefaultVerificationTransaction(
          * The three 4-digit numbers are displayed to the user either with dashes (or another appropriate separator) separating the three numbers,
          * or with the three numbers on separate lines.
          */
-        fun getDecimalCodeRepresentation(byteArray: ByteArray): String {
+        fun getDecimalCodeRepresentation(byteArray: ByteArray, separator: String = " "): String {
             val b0 = byteArray[0].toUnsignedInt() // need unsigned byte
             val b1 = byteArray[1].toUnsignedInt() // need unsigned byte
             val b2 = byteArray[2].toUnsignedInt() // need unsigned byte
@@ -107,7 +107,7 @@ internal abstract class SASDefaultVerificationTransaction(
             val second = ((b1 and 0x7).shl(10) or b2.shl(2) or b3.shr(6)) + 1000
             // ((B3 & 0x3f) << 7 | B4 >> 1) + 1000
             val third = ((b3 and 0x3f).shl(7) or b4.shr(1)) + 1000
-            return "$first $second $third"
+            return "$first$separator$second$separator$third"
         }
     }