Merge branch 'main' of github.com:vector-im/element-android into jonny/feat/rich-text-mentions
This commit is contained in:
commit
36b97b1647
73
CHANGES.md
73
CHANGES.md
|
@ -1,3 +1,76 @@
|
||||||
|
Changes in Element v1.6.2 (2023-06-02)
|
||||||
|
======================================
|
||||||
|
|
||||||
|
Features ✨
|
||||||
|
----------
|
||||||
|
- **Element Android is now using the Crypto Rust SDK**. Migration of user's data should be done at first launch after application upgrade. ([#8390](https://github.com/vector-im/element-android/issues/8390))
|
||||||
|
- Marks WebP files as Animated and allows them to play ([#8120](https://github.com/vector-im/element-android/issues/8120))
|
||||||
|
- Updates to protocol used for Sign in with QR code ([#8299](https://github.com/vector-im/element-android/issues/8299))
|
||||||
|
- Updated rust crypto SDK to version 0.3.9 ([#8488](https://github.com/vector-im/element-android/issues/8488))
|
||||||
|
|
||||||
|
Bugfixes 🐛
|
||||||
|
----------
|
||||||
|
- Fix: Allow users to sign out even if the sign out request fails. ([#4855](https://github.com/vector-im/element-android/issues/4855))
|
||||||
|
- fix: Make some crypto calls suspendable to avoid reported ANR ([#8482](https://github.com/vector-im/element-android/issues/8482))
|
||||||
|
|
||||||
|
Other changes
|
||||||
|
-------------
|
||||||
|
- Refactoring: Extract a new interface for common access to crypto store between kotlin and rust crypto ([#8470](https://github.com/vector-im/element-android/issues/8470))
|
||||||
|
|
||||||
|
|
||||||
|
Changes in Element v1.6.1 (2023-05-25)
|
||||||
|
======================================
|
||||||
|
|
||||||
|
Corrective release for 1.6.0
|
||||||
|
|
||||||
|
Bugfixes 🐛
|
||||||
|
----------
|
||||||
|
- Allow stateloss on verification dialogfragment ([#8439](https://github.com/vector-im/element-android/issues/8439))
|
||||||
|
- Fix: Update verification popup text when a re-verification is needed after rust migration (read only sessions) ([#8445](https://github.com/vector-im/element-android/issues/8445))
|
||||||
|
- Fix several performance issues causing app non responsive issues. ([#8454](https://github.com/vector-im/element-android/issues/8454))
|
||||||
|
- Fix: The device list screen from the member profile page was always showing the current user devices (rust crypto). ([#8457](https://github.com/vector-im/element-android/issues/8457))
|
||||||
|
|
||||||
|
Other changes
|
||||||
|
-------------
|
||||||
|
- Remove UI option to manually verify a specific device of another user (deprecated behaviour) ([#8458](https://github.com/vector-im/element-android/issues/8458))
|
||||||
|
|
||||||
|
|
||||||
|
Changes in Element v1.6.0 (2023-05-17)
|
||||||
|
======================================
|
||||||
|
|
||||||
|
Features ✨
|
||||||
|
----------
|
||||||
|
- **Element Android is now using the Crypto Rust SDK**. Migration of user's data should be done at first launch after application upgrade. ([#8390](https://github.com/vector-im/element-android/issues/8390))
|
||||||
|
- Enable free style cropping for camera and gallery images ([#8325](https://github.com/vector-im/element-android/issues/8325))
|
||||||
|
|
||||||
|
Bugfixes 🐛
|
||||||
|
----------
|
||||||
|
- User pills get lost at message editing ([#748](https://github.com/vector-im/element-android/issues/748))
|
||||||
|
- Upgrade Jitsi SDK from 6.2.2 to 8.1.1. This fixes video call on some Android devices. ([#7619](https://github.com/vector-im/element-android/issues/7619))
|
||||||
|
- Fix duplicate reactions when using full emoji picker. Contributed by @tulir @ Beeper. ([#8327](https://github.com/vector-im/element-android/issues/8327))
|
||||||
|
- Fix: RustCrossSigning service API confusion (identity trusted vs own device trusted by identity) ([#8352](https://github.com/vector-im/element-android/issues/8352))
|
||||||
|
- Allow custom push gateway to use non-default port ([#8376](https://github.com/vector-im/element-android/issues/8376))
|
||||||
|
- Fix crash when opening "Protect access" screen, and various other issue with `repeatOnLifecycle` ([#8410](https://github.com/vector-im/element-android/issues/8410))
|
||||||
|
- RustCrypto: Verification UX not refreshed after scanning a QR code ([#8418](https://github.com/vector-im/element-android/issues/8418))
|
||||||
|
|
||||||
|
SDK API changes ⚠️
|
||||||
|
------------------
|
||||||
|
- First integration of rust crypto module. See documentation for details `docs/rust_crypto_integration.md` ([#7628](https://github.com/vector-im/element-android/issues/7628))
|
||||||
|
- Add crypto database migration 22, that extract account and olm session to the new rust DB format ([#8405](https://github.com/vector-im/element-android/issues/8405))
|
||||||
|
|
||||||
|
Other changes
|
||||||
|
-------------
|
||||||
|
- Add an audio alert when the voice broadcast recording is automatically paused ([#8339](https://github.com/vector-im/element-android/issues/8339))
|
||||||
|
- Analytics: add crypto module to E2E events ([#8340](https://github.com/vector-im/element-android/issues/8340))
|
||||||
|
- Bump rust crypto crate to 0.3.5 ([#8354](https://github.com/vector-im/element-android/issues/8354))
|
||||||
|
- Expose Rust SDK Version in Help & About page and in Bug Reports ([#8364](https://github.com/vector-im/element-android/issues/8364))
|
||||||
|
- Matrix-Ids are sometimes shown in notice events instead of display names ([#8365](https://github.com/vector-im/element-android/issues/8365))
|
||||||
|
- CI: Add workflow to run test with crypto flavor ([#8366](https://github.com/vector-im/element-android/issues/8366))
|
||||||
|
- Remove ability to migrate session from Riot to Element. ([#8402](https://github.com/vector-im/element-android/issues/8402))
|
||||||
|
- Improve keyboard navigation and accessibility when using a screen reader. ([#8426](https://github.com/vector-im/element-android/issues/8426))
|
||||||
|
- Updated posthog url (cosmetic, target same server) and added a new sentry env. ([#8436](https://github.com/vector-im/element-android/issues/8436))
|
||||||
|
|
||||||
|
|
||||||
Changes in Element v1.5.32 (2023-04-19)
|
Changes in Element v1.5.32 (2023-04-19)
|
||||||
=======================================
|
=======================================
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,7 @@ buildscript {
|
||||||
classpath 'com.google.gms:google-services:4.3.15'
|
classpath 'com.google.gms:google-services:4.3.15'
|
||||||
classpath 'org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:4.0.0.2929'
|
classpath 'org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:4.0.0.2929'
|
||||||
classpath 'com.google.android.gms:oss-licenses-plugin:0.10.6'
|
classpath 'com.google.android.gms:oss-licenses-plugin:0.10.6'
|
||||||
classpath "com.likethesalad.android:stem-plugin:2.4.0"
|
classpath "com.likethesalad.android:stem-plugin:2.4.1"
|
||||||
classpath 'org.owasp:dependency-check-gradle:8.2.1'
|
classpath 'org.owasp:dependency-check-gradle:8.2.1'
|
||||||
classpath "org.jetbrains.dokka:dokka-gradle-plugin:1.8.10"
|
classpath "org.jetbrains.dokka:dokka-gradle-plugin:1.8.10"
|
||||||
classpath "org.jetbrains.kotlinx:kotlinx-knit:0.4.0"
|
classpath "org.jetbrains.kotlinx:kotlinx-knit:0.4.0"
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
User pills get lost at message editing
|
|
|
@ -1 +0,0 @@
|
||||||
Upgrade Jitsi SDK from 6.2.2 to 8.1.1. This fixes video call on some Android devices.
|
|
|
@ -1 +0,0 @@
|
||||||
First integration of rust crypto module. See documentation for details `docs/rust_crypto_integration.md`
|
|
|
@ -1 +0,0 @@
|
||||||
Enable free style cropping for camera and gallery images
|
|
|
@ -1 +0,0 @@
|
||||||
Fix duplicate reactions when using full emoji picker. Contributed by @tulir @ Beeper.
|
|
|
@ -1 +0,0 @@
|
||||||
Add an audio alert when the voice broadcast recording is automatically paused
|
|
|
@ -1 +0,0 @@
|
||||||
Analytics: add crypto module to E2E events
|
|
|
@ -1 +0,0 @@
|
||||||
Fix: RustCrossSigning service API confusion (identity trusted vs own device trusted by identity)
|
|
|
@ -1 +0,0 @@
|
||||||
Bump rust crypto crate to 0.3.5
|
|
|
@ -1 +0,0 @@
|
||||||
Expose Rust SDK Version in Help & About page and in Bug Reports
|
|
|
@ -1 +0,0 @@
|
||||||
Matrix-Ids are sometimes shown in notice events instead of display names
|
|
|
@ -1 +0,0 @@
|
||||||
CI: Add workflow to run test with crypto flavor
|
|
|
@ -1 +0,0 @@
|
||||||
Allow custom push gateway to use non-default port
|
|
|
@ -1 +0,0 @@
|
||||||
Element Android is now using the Crypto Rust SDK. Migration of user's data should be done at first launch after application upgrade.
|
|
|
@ -1 +0,0 @@
|
||||||
Remove ability to migrate session from Riot to Element.
|
|
|
@ -1 +0,0 @@
|
||||||
Add crypto database migration 22, that extract account and olm session to the new rust DB format
|
|
|
@ -1 +0,0 @@
|
||||||
Fix crash when opening "Protect access" screen, and various other issue with `repeatOnLifecycle`
|
|
|
@ -1 +0,0 @@
|
||||||
RustCrypto: Verification UX not refreshed after scanning a QR code
|
|
|
@ -1 +0,0 @@
|
||||||
Improve keyboard navigation and accessibility when using a screen reader.
|
|
|
@ -1 +0,0 @@
|
||||||
Updated posthog url (cosmetic, target same server) and added a new sentry env.
|
|
|
@ -47,7 +47,7 @@ ext.libs = [
|
||||||
'coroutinesTest' : "org.jetbrains.kotlinx:kotlinx-coroutines-test:$kotlinCoroutines"
|
'coroutinesTest' : "org.jetbrains.kotlinx:kotlinx-coroutines-test:$kotlinCoroutines"
|
||||||
],
|
],
|
||||||
androidx : [
|
androidx : [
|
||||||
'activity' : "androidx.activity:activity-ktx:1.7.1",
|
'activity' : "androidx.activity:activity-ktx:1.7.2",
|
||||||
'appCompat' : "androidx.appcompat:appcompat:1.6.1",
|
'appCompat' : "androidx.appcompat:appcompat:1.6.1",
|
||||||
'biometric' : "androidx.biometric:biometric:1.1.0",
|
'biometric' : "androidx.biometric:biometric:1.1.0",
|
||||||
'core' : "androidx.core:core-ktx:1.10.1",
|
'core' : "androidx.core:core-ktx:1.10.1",
|
||||||
|
@ -101,7 +101,7 @@ ext.libs = [
|
||||||
],
|
],
|
||||||
element : [
|
element : [
|
||||||
'opusencoder' : "io.element.android:opusencoder:1.1.0",
|
'opusencoder' : "io.element.android:opusencoder:1.1.0",
|
||||||
'wysiwyg' : "io.element.android:wysiwyg:2.2.0"
|
'wysiwyg' : "io.element.android:wysiwyg:2.2.1"
|
||||||
],
|
],
|
||||||
squareup : [
|
squareup : [
|
||||||
'moshi' : "com.squareup.moshi:moshi:$moshi",
|
'moshi' : "com.squareup.moshi:moshi:$moshi",
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
Main changes in this version: Element Android is now using the Crypto Rust SDK.
|
||||||
|
Full changelog: https://github.com/vector-im/element-android/releases
|
|
@ -0,0 +1,2 @@
|
||||||
|
Main changes in this version: Element Android is now using the Crypto Rust SDK.
|
||||||
|
Full changelog: https://github.com/vector-im/element-android/releases
|
|
@ -0,0 +1,2 @@
|
||||||
|
Main changes in this version: Element Android is now using the Crypto Rust SDK.
|
||||||
|
Full changelog: https://github.com/vector-im/element-android/releases
|
|
@ -328,6 +328,9 @@
|
||||||
<string name="backup">Back up</string>
|
<string name="backup">Back up</string>
|
||||||
<string name="sign_out_bottom_sheet_will_lose_secure_messages">You’ll lose access to your encrypted messages unless you back up your keys before signing out.</string>
|
<string name="sign_out_bottom_sheet_will_lose_secure_messages">You’ll lose access to your encrypted messages unless you back up your keys before signing out.</string>
|
||||||
|
|
||||||
|
<string name="sign_out_failed_dialog_message">Cannot reach the homeserver. If you sign out anyway, this device will not be erased from your device list, you may want to remove it using another client.</string>
|
||||||
|
<string name="sign_out_anyway">Sign out anyway</string>
|
||||||
|
|
||||||
<!-- splash screen accessibility -->
|
<!-- splash screen accessibility -->
|
||||||
<string name="loading">Loading…</string>
|
<string name="loading">Loading…</string>
|
||||||
|
|
||||||
|
@ -2446,6 +2449,7 @@
|
||||||
</plurals>
|
</plurals>
|
||||||
|
|
||||||
<string name="crosssigning_verify_this_session">Verify this device</string>
|
<string name="crosssigning_verify_this_session">Verify this device</string>
|
||||||
|
<string name="crosssigning_verify_after_update">App updated</string>
|
||||||
<string name="crosssigning_cannot_verify_this_session">Unable to verify this device</string>
|
<string name="crosssigning_cannot_verify_this_session">Unable to verify this device</string>
|
||||||
<string name="crosssigning_cannot_verify_this_session_desc">You won’t be able to access encrypted message history. Reset your Secure Message Backup and verification keys to start fresh.</string>
|
<string name="crosssigning_cannot_verify_this_session_desc">You won’t be able to access encrypted message history. Reset your Secure Message Backup and verification keys to start fresh.</string>
|
||||||
|
|
||||||
|
@ -2465,7 +2469,9 @@
|
||||||
|
|
||||||
<string name="verification_profile_device_verified_because">This session is trusted for secure messaging because %1$s (%2$s) verified it:</string>
|
<string name="verification_profile_device_verified_because">This session is trusted for secure messaging because %1$s (%2$s) verified it:</string>
|
||||||
<string name="verification_profile_device_new_signing">%1$s (%2$s) signed in using a new session:</string>
|
<string name="verification_profile_device_new_signing">%1$s (%2$s) signed in using a new session:</string>
|
||||||
<string name="verification_profile_device_untrust_info">Until this user trusts this session, messages sent to and from it are labeled with warnings. Alternatively, you can manually verify it.</string>
|
<!-- TODO TO BE REMOVED -->
|
||||||
|
<string tools:ignore="UnusedResources" name="verification_profile_device_untrust_info">Until this user trusts this session, messages sent to and from it are labeled with warnings. Alternatively, you can manually verify it.</string>
|
||||||
|
<string name="verification_profile_other_device_untrust_info">Until this user trusts this session, messages sent to and from it are labeled with warnings.</string>
|
||||||
|
|
||||||
|
|
||||||
<string name="initialize_cross_signing">Initialize CrossSigning</string>
|
<string name="initialize_cross_signing">Initialize CrossSigning</string>
|
||||||
|
@ -2707,6 +2713,7 @@
|
||||||
<string tools:ignore="UnusedResources" name="crosssigning_verify_session">Verify login</string>
|
<string tools:ignore="UnusedResources" name="crosssigning_verify_session">Verify login</string>
|
||||||
<string name="cross_signing_verify_by_emoji">Interactively Verify by Emoji</string>
|
<string name="cross_signing_verify_by_emoji">Interactively Verify by Emoji</string>
|
||||||
<string name="confirm_your_identity">Confirm your identity by verifying this login from one of your other sessions, granting it access to encrypted messages.</string>
|
<string name="confirm_your_identity">Confirm your identity by verifying this login from one of your other sessions, granting it access to encrypted messages.</string>
|
||||||
|
<string name="confirm_your_identity_after_update">Secure messaging has been improved with the latest update. Please re-verify your device.</string>
|
||||||
<string name="confirm_your_identity_quad_s">Confirm your identity by verifying this login, granting it access to encrypted messages.</string>
|
<string name="confirm_your_identity_quad_s">Confirm your identity by verifying this login, granting it access to encrypted messages.</string>
|
||||||
|
|
||||||
<string name="failed_to_initialize_cross_signing">Failed to set up Cross Signing</string>
|
<string name="failed_to_initialize_cross_signing">Failed to set up Cross Signing</string>
|
||||||
|
|
|
@ -63,7 +63,7 @@ android {
|
||||||
// that the app's state is completely cleared between tests.
|
// that the app's state is completely cleared between tests.
|
||||||
testInstrumentationRunnerArguments clearPackageData: 'true'
|
testInstrumentationRunnerArguments clearPackageData: 'true'
|
||||||
|
|
||||||
buildConfigField "String", "SDK_VERSION", "\"1.5.34\""
|
buildConfigField "String", "SDK_VERSION", "\"1.6.2\""
|
||||||
|
|
||||||
buildConfigField "String", "GIT_SDK_REVISION", "\"${gitRevision()}\""
|
buildConfigField "String", "GIT_SDK_REVISION", "\"${gitRevision()}\""
|
||||||
buildConfigField "String", "GIT_SDK_REVISION_UNIX_DATE", "\"${gitRevisionUnixDate()}\""
|
buildConfigField "String", "GIT_SDK_REVISION_UNIX_DATE", "\"${gitRevisionUnixDate()}\""
|
||||||
|
@ -216,8 +216,8 @@ dependencies {
|
||||||
|
|
||||||
implementation libs.google.phonenumber
|
implementation libs.google.phonenumber
|
||||||
|
|
||||||
rustCryptoImplementation("org.matrix.rustcomponents:crypto-android:0.3.7")
|
rustCryptoImplementation("org.matrix.rustcomponents:crypto-android:0.3.9")
|
||||||
// rustCryptoApi project(":library:rustCrypto")
|
// rustCryptoApi project(":library:rustCrypto")
|
||||||
|
|
||||||
testImplementation libs.tests.junit
|
testImplementation libs.tests.junit
|
||||||
// Note: version sticks to 1.9.2 due to https://github.com/mockk/mockk/issues/281
|
// Note: version sticks to 1.9.2 due to https://github.com/mockk/mockk/issues/281
|
||||||
|
|
|
@ -59,6 +59,7 @@ import org.matrix.android.sdk.internal.crypto.keysbackup.tasks.StoreRoomSessionD
|
||||||
import org.matrix.android.sdk.internal.crypto.keysbackup.tasks.StoreRoomSessionsDataTask
|
import org.matrix.android.sdk.internal.crypto.keysbackup.tasks.StoreRoomSessionsDataTask
|
||||||
import org.matrix.android.sdk.internal.crypto.keysbackup.tasks.StoreSessionsDataTask
|
import org.matrix.android.sdk.internal.crypto.keysbackup.tasks.StoreSessionsDataTask
|
||||||
import org.matrix.android.sdk.internal.crypto.keysbackup.tasks.UpdateKeysBackupVersionTask
|
import org.matrix.android.sdk.internal.crypto.keysbackup.tasks.UpdateKeysBackupVersionTask
|
||||||
|
import org.matrix.android.sdk.internal.crypto.store.IMXCommonCryptoStore
|
||||||
import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore
|
import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.RealmCryptoStore
|
import org.matrix.android.sdk.internal.crypto.store.db.RealmCryptoStore
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.RealmCryptoStoreMigration
|
import org.matrix.android.sdk.internal.crypto.store.db.RealmCryptoStoreMigration
|
||||||
|
@ -254,6 +255,9 @@ internal abstract class CryptoModule {
|
||||||
@Binds
|
@Binds
|
||||||
abstract fun bindCryptoStore(store: RealmCryptoStore): IMXCryptoStore
|
abstract fun bindCryptoStore(store: RealmCryptoStore): IMXCryptoStore
|
||||||
|
|
||||||
|
@Binds
|
||||||
|
abstract fun bindCommonCryptoStore(store: RealmCryptoStore): IMXCommonCryptoStore
|
||||||
|
|
||||||
@Binds
|
@Binds
|
||||||
abstract fun bindSendEventTask(task: DefaultSendEventTask): SendEventTask
|
abstract fun bindSendEventTask(task: DefaultSendEventTask): SendEventTask
|
||||||
|
|
||||||
|
|
|
@ -256,7 +256,7 @@ internal class DefaultCryptoService @Inject constructor(
|
||||||
return if (longFormat) olmManager.getDetailedVersion(context) else olmManager.version
|
return if (longFormat) olmManager.getDetailedVersion(context) else olmManager.version
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getMyCryptoDevice(): CryptoDeviceInfo {
|
override suspend fun getMyCryptoDevice(): CryptoDeviceInfo {
|
||||||
return myDeviceInfoHolder.get().myDevice
|
return myDeviceInfoHolder.get().myDevice
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -506,10 +506,7 @@ internal class DefaultCryptoService @Inject constructor(
|
||||||
null
|
null
|
||||||
} else {
|
} else {
|
||||||
withContext(coroutineDispatchers.io) {
|
withContext(coroutineDispatchers.io) {
|
||||||
cryptoStore.deviceWithIdentityKey(senderKey).takeIf {
|
cryptoStore.deviceWithIdentityKey(userId, senderKey)
|
||||||
// check that the claimed user id matches
|
|
||||||
it?.userId == userId
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -539,7 +536,7 @@ internal class DefaultCryptoService @Inject constructor(
|
||||||
// .executeBy(taskExecutor)
|
// .executeBy(taskExecutor)
|
||||||
// }
|
// }
|
||||||
|
|
||||||
override fun getCryptoDeviceInfo(userId: String): List<CryptoDeviceInfo> {
|
override suspend fun getCryptoDeviceInfo(userId: String): List<CryptoDeviceInfo> {
|
||||||
return cryptoStore.getUserDeviceList(userId).orEmpty()
|
return cryptoStore.getUserDeviceList(userId).orEmpty()
|
||||||
}
|
}
|
||||||
//
|
//
|
||||||
|
|
|
@ -28,20 +28,16 @@ import org.matrix.android.sdk.api.session.crypto.crosssigning.UserIdentity
|
||||||
import org.matrix.android.sdk.api.session.crypto.keysbackup.SavedKeyBackupKeyInfo
|
import org.matrix.android.sdk.api.session.crypto.keysbackup.SavedKeyBackupKeyInfo
|
||||||
import org.matrix.android.sdk.api.session.crypto.model.AuditTrail
|
import org.matrix.android.sdk.api.session.crypto.model.AuditTrail
|
||||||
import org.matrix.android.sdk.api.session.crypto.model.CryptoDeviceInfo
|
import org.matrix.android.sdk.api.session.crypto.model.CryptoDeviceInfo
|
||||||
import org.matrix.android.sdk.api.session.crypto.model.CryptoRoomInfo
|
|
||||||
import org.matrix.android.sdk.api.session.crypto.model.DeviceInfo
|
|
||||||
import org.matrix.android.sdk.api.session.crypto.model.MXUsersDevicesMap
|
import org.matrix.android.sdk.api.session.crypto.model.MXUsersDevicesMap
|
||||||
import org.matrix.android.sdk.api.session.crypto.model.RoomKeyRequestBody
|
import org.matrix.android.sdk.api.session.crypto.model.RoomKeyRequestBody
|
||||||
import org.matrix.android.sdk.api.session.crypto.model.TrailType
|
import org.matrix.android.sdk.api.session.crypto.model.TrailType
|
||||||
import org.matrix.android.sdk.api.session.events.model.Event
|
import org.matrix.android.sdk.api.session.events.model.Event
|
||||||
import org.matrix.android.sdk.api.session.events.model.content.EncryptionEventContent
|
|
||||||
import org.matrix.android.sdk.api.session.events.model.content.RoomKeyWithHeldContent
|
import org.matrix.android.sdk.api.session.events.model.content.RoomKeyWithHeldContent
|
||||||
import org.matrix.android.sdk.api.session.events.model.content.WithHeldCode
|
import org.matrix.android.sdk.api.session.events.model.content.WithHeldCode
|
||||||
import org.matrix.android.sdk.api.util.Optional
|
import org.matrix.android.sdk.api.util.Optional
|
||||||
import org.matrix.android.sdk.internal.crypto.model.MXInboundMegolmSessionWrapper
|
import org.matrix.android.sdk.internal.crypto.model.MXInboundMegolmSessionWrapper
|
||||||
import org.matrix.android.sdk.internal.crypto.model.OlmSessionWrapper
|
import org.matrix.android.sdk.internal.crypto.model.OlmSessionWrapper
|
||||||
import org.matrix.android.sdk.internal.crypto.model.OutboundGroupSessionWrapper
|
import org.matrix.android.sdk.internal.crypto.model.OutboundGroupSessionWrapper
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.CryptoStoreAggregator
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.model.KeysBackupDataEntity
|
import org.matrix.android.sdk.internal.crypto.store.db.model.KeysBackupDataEntity
|
||||||
import org.matrix.olm.OlmAccount
|
import org.matrix.olm.OlmAccount
|
||||||
import org.matrix.olm.OlmOutboundGroupSession
|
import org.matrix.olm.OlmOutboundGroupSession
|
||||||
|
@ -49,7 +45,7 @@ import org.matrix.olm.OlmOutboundGroupSession
|
||||||
/**
|
/**
|
||||||
* The crypto data store.
|
* The crypto data store.
|
||||||
*/
|
*/
|
||||||
internal interface IMXCryptoStore {
|
internal interface IMXCryptoStore : IMXCommonCryptoStore {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the device id
|
* @return the device id
|
||||||
|
@ -78,21 +74,6 @@ internal interface IMXCryptoStore {
|
||||||
*/
|
*/
|
||||||
fun getInboundGroupSessions(roomId: String): List<MXInboundMegolmSessionWrapper>
|
fun getInboundGroupSessions(roomId: String): List<MXInboundMegolmSessionWrapper>
|
||||||
|
|
||||||
/**
|
|
||||||
* @return true to unilaterally blacklist all unverified devices.
|
|
||||||
*/
|
|
||||||
fun getGlobalBlacklistUnverifiedDevices(): Boolean
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the global override for whether the client should ever send encrypted
|
|
||||||
* messages to unverified devices.
|
|
||||||
* If false, it can still be overridden per-room.
|
|
||||||
* If true, it overrides the per-room settings.
|
|
||||||
*
|
|
||||||
* @param block true to unilaterally blacklist all
|
|
||||||
*/
|
|
||||||
fun setGlobalBlacklistUnverifiedDevices(block: Boolean)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enable or disable key gossiping.
|
* Enable or disable key gossiping.
|
||||||
* Default is true.
|
* Default is true.
|
||||||
|
@ -123,28 +104,6 @@ internal interface IMXCryptoStore {
|
||||||
*/
|
*/
|
||||||
fun getRoomsListBlacklistUnverifiedDevices(): List<String>
|
fun getRoomsListBlacklistUnverifiedDevices(): List<String>
|
||||||
|
|
||||||
/**
|
|
||||||
* A live status regarding sharing keys for unverified devices in this room.
|
|
||||||
*
|
|
||||||
* @return Live status
|
|
||||||
*/
|
|
||||||
fun getLiveBlockUnverifiedDevices(roomId: String): LiveData<Boolean>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tell if unverified devices should be blacklisted when sending keys.
|
|
||||||
*
|
|
||||||
* @return true if should not send keys to unverified devices
|
|
||||||
*/
|
|
||||||
fun getBlockUnverifiedDevices(roomId: String): Boolean
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Define if encryption keys should be sent to unverified devices in this room.
|
|
||||||
*
|
|
||||||
* @param roomId the roomId
|
|
||||||
* @param block if true will not send keys to unverified devices
|
|
||||||
*/
|
|
||||||
fun blockUnverifiedDevicesInRoom(roomId: String, block: Boolean)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the current keys backup version.
|
* Get the current keys backup version.
|
||||||
*/
|
*/
|
||||||
|
@ -186,16 +145,6 @@ internal interface IMXCryptoStore {
|
||||||
*/
|
*/
|
||||||
fun deleteStore()
|
fun deleteStore()
|
||||||
|
|
||||||
/**
|
|
||||||
* open any existing crypto store.
|
|
||||||
*/
|
|
||||||
fun open()
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Close the store.
|
|
||||||
*/
|
|
||||||
fun close()
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Store the device id.
|
* Store the device id.
|
||||||
*
|
*
|
||||||
|
@ -262,14 +211,6 @@ internal interface IMXCryptoStore {
|
||||||
|
|
||||||
fun getLiveDeviceWithId(deviceId: String): LiveData<Optional<CryptoDeviceInfo>>
|
fun getLiveDeviceWithId(deviceId: String): LiveData<Optional<CryptoDeviceInfo>>
|
||||||
|
|
||||||
fun getMyDevicesInfo(): List<DeviceInfo>
|
|
||||||
|
|
||||||
fun getLiveMyDevicesInfo(): LiveData<List<DeviceInfo>>
|
|
||||||
|
|
||||||
fun getLiveMyDevicesInfo(deviceId: String): LiveData<Optional<DeviceInfo>>
|
|
||||||
|
|
||||||
fun saveMyDevicesInfo(info: List<DeviceInfo>)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Store the crypto algorithm for a room.
|
* Store the crypto algorithm for a room.
|
||||||
*
|
*
|
||||||
|
@ -278,47 +219,8 @@ internal interface IMXCryptoStore {
|
||||||
*/
|
*/
|
||||||
fun storeRoomAlgorithm(roomId: String, algorithm: String?)
|
fun storeRoomAlgorithm(roomId: String, algorithm: String?)
|
||||||
|
|
||||||
/**
|
|
||||||
* Provides the algorithm used in a dedicated room.
|
|
||||||
*
|
|
||||||
* @param roomId the room id
|
|
||||||
* @return the algorithm, null is the room is not encrypted
|
|
||||||
*/
|
|
||||||
fun getRoomAlgorithm(roomId: String): String?
|
|
||||||
|
|
||||||
fun getRoomCryptoInfo(roomId: String): CryptoRoomInfo?
|
|
||||||
fun setAlgorithmInfo(roomId: String, encryption: EncryptionEventContent?)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This is a bit different than isRoomEncrypted.
|
|
||||||
* A room is encrypted when there is a m.room.encryption state event in the room (malformed/invalid or not).
|
|
||||||
* But the crypto layer has additional guaranty to ensure that encryption would never been reverted.
|
|
||||||
* It's defensive coding out of precaution (if ever state is reset).
|
|
||||||
*/
|
|
||||||
fun roomWasOnceEncrypted(roomId: String): Boolean
|
|
||||||
|
|
||||||
fun shouldEncryptForInvitedMembers(roomId: String): Boolean
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets a boolean flag that will determine whether or not this device should encrypt Events for
|
|
||||||
* invited members.
|
|
||||||
*
|
|
||||||
* @param roomId the room id
|
|
||||||
* @param shouldEncryptForInvitedMembers The boolean flag
|
|
||||||
*/
|
|
||||||
fun setShouldEncryptForInvitedMembers(roomId: String, shouldEncryptForInvitedMembers: Boolean)
|
|
||||||
|
|
||||||
fun shouldShareHistory(roomId: String): Boolean
|
fun shouldShareHistory(roomId: String): Boolean
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets a boolean flag that will determine whether or not room history (existing inbound sessions)
|
|
||||||
* will be shared to new user invites.
|
|
||||||
*
|
|
||||||
* @param roomId the room id
|
|
||||||
* @param shouldShareHistory The boolean flag
|
|
||||||
*/
|
|
||||||
fun setShouldShareHistory(roomId: String, shouldShareHistory: Boolean)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Store a session between the logged-in user and another device.
|
* Store a session between the logged-in user and another device.
|
||||||
*
|
*
|
||||||
|
@ -361,15 +263,6 @@ internal interface IMXCryptoStore {
|
||||||
*/
|
*/
|
||||||
fun storeInboundGroupSessions(sessions: List<MXInboundMegolmSessionWrapper>)
|
fun storeInboundGroupSessions(sessions: List<MXInboundMegolmSessionWrapper>)
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve an inbound group session.
|
|
||||||
*
|
|
||||||
* @param sessionId the session identifier.
|
|
||||||
* @param senderKey the base64-encoded curve25519 key of the sender.
|
|
||||||
* @return an inbound group session.
|
|
||||||
*/
|
|
||||||
fun getInboundGroupSession(sessionId: String, senderKey: String): MXInboundMegolmSessionWrapper?
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve an inbound group session, filtering shared history.
|
* Retrieve an inbound group session, filtering shared history.
|
||||||
*
|
*
|
||||||
|
@ -552,7 +445,6 @@ internal interface IMXCryptoStore {
|
||||||
// fun getCrossSigningPrivateKeysFlow(): Flow<Optional<PrivateKeysInfo>>
|
// fun getCrossSigningPrivateKeysFlow(): Flow<Optional<PrivateKeysInfo>>
|
||||||
|
|
||||||
fun getGlobalCryptoConfig(): GlobalCryptoConfig
|
fun getGlobalCryptoConfig(): GlobalCryptoConfig
|
||||||
fun getLiveGlobalCryptoConfig(): LiveData<GlobalCryptoConfig>
|
|
||||||
|
|
||||||
fun saveBackupRecoveryKey(recoveryKey: String?, version: String?)
|
fun saveBackupRecoveryKey(recoveryKey: String?, version: String?)
|
||||||
fun getKeyBackupRecoveryKeyInfo(): SavedKeyBackupKeyInfo?
|
fun getKeyBackupRecoveryKeyInfo(): SavedKeyBackupKeyInfo?
|
||||||
|
@ -597,14 +489,8 @@ internal interface IMXCryptoStore {
|
||||||
|
|
||||||
fun setDeviceKeysUploaded(uploaded: Boolean)
|
fun setDeviceKeysUploaded(uploaded: Boolean)
|
||||||
fun areDeviceKeysUploaded(): Boolean
|
fun areDeviceKeysUploaded(): Boolean
|
||||||
fun tidyUpDataBase()
|
|
||||||
fun getOutgoingRoomKeyRequests(inStates: Set<OutgoingRoomKeyRequestState>): List<OutgoingKeyRequest>
|
fun getOutgoingRoomKeyRequests(inStates: Set<OutgoingRoomKeyRequestState>): List<OutgoingKeyRequest>
|
||||||
|
|
||||||
/**
|
|
||||||
* Store a bunch of data collected during a sync response treatment. @See [CryptoStoreAggregator].
|
|
||||||
*/
|
|
||||||
fun storeData(cryptoStoreAggregator: CryptoStoreAggregator)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Store a bunch of data related to the users. @See [UserDataToStore].
|
* Store a bunch of data related to the users. @See [UserDataToStore].
|
||||||
*/
|
*/
|
|
@ -280,6 +280,19 @@ internal class RealmCryptoStore @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun deviceWithIdentityKey(userId: String, identityKey: String): CryptoDeviceInfo? {
|
||||||
|
return doWithRealm(realmConfiguration) { realm ->
|
||||||
|
realm.where<DeviceInfoEntity>()
|
||||||
|
.equalTo(DeviceInfoEntityFields.USER_ID, userId)
|
||||||
|
.contains(DeviceInfoEntityFields.KEYS_MAP_JSON, identityKey)
|
||||||
|
.findAll()
|
||||||
|
.mapNotNull { CryptoMapper.mapToModel(it) }
|
||||||
|
.firstOrNull {
|
||||||
|
it.identityKey() == identityKey
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun storeUserDevices(userId: String, devices: Map<String, CryptoDeviceInfo>?) {
|
override fun storeUserDevices(userId: String, devices: Map<String, CryptoDeviceInfo>?) {
|
||||||
doRealmTransaction("storeUserDevices", realmConfiguration) { realm ->
|
doRealmTransaction("storeUserDevices", realmConfiguration) { realm ->
|
||||||
storeUserDevices(realm, userId, devices)
|
storeUserDevices(realm, userId, devices)
|
|
@ -73,7 +73,7 @@ interface CryptoService {
|
||||||
|
|
||||||
suspend fun getUserDevices(userId: String): List<CryptoDeviceInfo>
|
suspend fun getUserDevices(userId: String): List<CryptoDeviceInfo>
|
||||||
|
|
||||||
fun getMyCryptoDevice(): CryptoDeviceInfo
|
suspend fun getMyCryptoDevice(): CryptoDeviceInfo
|
||||||
|
|
||||||
fun getGlobalBlacklistUnverifiedDevices(): Boolean
|
fun getGlobalBlacklistUnverifiedDevices(): Boolean
|
||||||
|
|
||||||
|
@ -130,7 +130,7 @@ interface CryptoService {
|
||||||
|
|
||||||
suspend fun getCryptoDeviceInfo(userId: String, deviceId: String?): CryptoDeviceInfo?
|
suspend fun getCryptoDeviceInfo(userId: String, deviceId: String?): CryptoDeviceInfo?
|
||||||
|
|
||||||
fun getCryptoDeviceInfo(userId: String): List<CryptoDeviceInfo>
|
suspend fun getCryptoDeviceInfo(userId: String): List<CryptoDeviceInfo>
|
||||||
|
|
||||||
// fun getCryptoDeviceInfoFlow(userId: String): Flow<List<CryptoDeviceInfo>>
|
// fun getCryptoDeviceInfoFlow(userId: String): Flow<List<CryptoDeviceInfo>>
|
||||||
|
|
||||||
|
|
|
@ -37,6 +37,7 @@ interface SignOutService {
|
||||||
/**
|
/**
|
||||||
* Sign out, and release the session, clear all the session data, including crypto data.
|
* Sign out, and release the session, clear all the session data, including crypto data.
|
||||||
* @param signOutFromHomeserver true if the sign out request has to be done
|
* @param signOutFromHomeserver true if the sign out request has to be done
|
||||||
|
* @param ignoreServerRequestError true to ignore server error if any
|
||||||
*/
|
*/
|
||||||
suspend fun signOut(signOutFromHomeserver: Boolean)
|
suspend fun signOut(signOutFromHomeserver: Boolean, ignoreServerRequestError: Boolean = false)
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,6 +30,7 @@ object MimeTypes {
|
||||||
const val BadJpg = "image/jpg"
|
const val BadJpg = "image/jpg"
|
||||||
const val Jpeg = "image/jpeg"
|
const val Jpeg = "image/jpeg"
|
||||||
const val Gif = "image/gif"
|
const val Gif = "image/gif"
|
||||||
|
const val Webp = "image/webp"
|
||||||
|
|
||||||
const val Ogg = "audio/ogg"
|
const val Ogg = "audio/ogg"
|
||||||
|
|
||||||
|
|
|
@ -301,6 +301,9 @@ internal class DefaultAuthenticationService @Inject constructor(
|
||||||
val oidcCompatibilityFlow = loginFlowResponse.flows.orEmpty().firstOrNull { it.type == "m.login.sso" && it.delegatedOidcCompatibilty == true }
|
val oidcCompatibilityFlow = loginFlowResponse.flows.orEmpty().firstOrNull { it.type == "m.login.sso" && it.delegatedOidcCompatibilty == true }
|
||||||
val flows = if (oidcCompatibilityFlow != null) listOf(oidcCompatibilityFlow) else loginFlowResponse.flows
|
val flows = if (oidcCompatibilityFlow != null) listOf(oidcCompatibilityFlow) else loginFlowResponse.flows
|
||||||
|
|
||||||
|
val supportsGetLoginTokenFlow = loginFlowResponse.flows.orEmpty().firstOrNull { it.type == "m.login.token" && it.getLoginToken == true } != null
|
||||||
|
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
return LoginFlowResult(
|
return LoginFlowResult(
|
||||||
supportedLoginTypes = flows.orEmpty().mapNotNull { it.type },
|
supportedLoginTypes = flows.orEmpty().mapNotNull { it.type },
|
||||||
ssoIdentityProviders = flows.orEmpty().firstOrNull { it.type == LoginFlowTypes.SSO }?.ssoIdentityProvider,
|
ssoIdentityProviders = flows.orEmpty().firstOrNull { it.type == LoginFlowTypes.SSO }?.ssoIdentityProvider,
|
||||||
|
@ -309,7 +312,7 @@ internal class DefaultAuthenticationService @Inject constructor(
|
||||||
isOutdatedHomeserver = !versions.isSupportedBySdk(),
|
isOutdatedHomeserver = !versions.isSupportedBySdk(),
|
||||||
hasOidcCompatibilityFlow = oidcCompatibilityFlow != null,
|
hasOidcCompatibilityFlow = oidcCompatibilityFlow != null,
|
||||||
isLogoutDevicesSupported = versions.doesServerSupportLogoutDevices(),
|
isLogoutDevicesSupported = versions.doesServerSupportLogoutDevices(),
|
||||||
isLoginWithQrSupported = versions.doesServerSupportQrCodeLogin(),
|
isLoginWithQrSupported = supportsGetLoginTokenFlow || versions.doesServerSupportQrCodeLogin(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -51,5 +51,13 @@ internal data class LoginFlow(
|
||||||
* See [MSC3824](https://github.com/matrix-org/matrix-spec-proposals/pull/3824)
|
* See [MSC3824](https://github.com/matrix-org/matrix-spec-proposals/pull/3824)
|
||||||
*/
|
*/
|
||||||
@Json(name = "org.matrix.msc3824.delegated_oidc_compatibility")
|
@Json(name = "org.matrix.msc3824.delegated_oidc_compatibility")
|
||||||
val delegatedOidcCompatibilty: Boolean? = null
|
val delegatedOidcCompatibilty: Boolean? = null,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether a login flow of type m.login.token could accept a token issued using /login/get_token.
|
||||||
|
*
|
||||||
|
* See https://spec.matrix.org/v1.7/client-server-api/#post_matrixclientv1loginget_token
|
||||||
|
*/
|
||||||
|
@Json(name = "get_login_token")
|
||||||
|
val getLoginToken: Boolean? = null
|
||||||
)
|
)
|
||||||
|
|
|
@ -54,6 +54,7 @@ private const val FEATURE_ID_ACCESS_TOKEN = "m.id_access_token"
|
||||||
private const val FEATURE_SEPARATE_ADD_AND_BIND = "m.separate_add_and_bind"
|
private const val FEATURE_SEPARATE_ADD_AND_BIND = "m.separate_add_and_bind"
|
||||||
private const val FEATURE_THREADS_MSC3440 = "org.matrix.msc3440"
|
private const val FEATURE_THREADS_MSC3440 = "org.matrix.msc3440"
|
||||||
private const val FEATURE_THREADS_MSC3440_STABLE = "org.matrix.msc3440.stable"
|
private const val FEATURE_THREADS_MSC3440_STABLE = "org.matrix.msc3440.stable"
|
||||||
|
@Deprecated("The availability of stable get_login_token is now exposed as a capability and part of login flow")
|
||||||
private const val FEATURE_QR_CODE_LOGIN = "org.matrix.msc3882"
|
private const val FEATURE_QR_CODE_LOGIN = "org.matrix.msc3882"
|
||||||
private const val FEATURE_THREADS_MSC3771 = "org.matrix.msc3771"
|
private const val FEATURE_THREADS_MSC3771 = "org.matrix.msc3771"
|
||||||
private const val FEATURE_THREADS_MSC3773 = "org.matrix.msc3773"
|
private const val FEATURE_THREADS_MSC3773 = "org.matrix.msc3773"
|
||||||
|
@ -94,7 +95,9 @@ internal fun Versions.doesServerSupportThreadUnreadNotifications(): Boolean {
|
||||||
return getMaxVersion() >= HomeServerVersion.v1_4_0 || (msc3771 && msc3773)
|
return getMaxVersion() >= HomeServerVersion.v1_4_0 || (msc3771 && msc3773)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Deprecated("The availability of stable get_login_token is now exposed as a capability and part of login flow")
|
||||||
internal fun Versions.doesServerSupportQrCodeLogin(): Boolean {
|
internal fun Versions.doesServerSupportQrCodeLogin(): Boolean {
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
return unstableFeatures?.get(FEATURE_QR_CODE_LOGIN) ?: false
|
return unstableFeatures?.get(FEATURE_QR_CODE_LOGIN) ?: false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,11 +17,11 @@
|
||||||
package org.matrix.android.sdk.internal.crypto
|
package org.matrix.android.sdk.internal.crypto
|
||||||
|
|
||||||
import org.matrix.android.sdk.api.crypto.MXCryptoConfig
|
import org.matrix.android.sdk.api.crypto.MXCryptoConfig
|
||||||
import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore
|
import org.matrix.android.sdk.internal.crypto.store.IMXCommonCryptoStore
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
internal class ShouldEncryptForInvitedMembersUseCase @Inject constructor(private val cryptoConfig: MXCryptoConfig,
|
internal class ShouldEncryptForInvitedMembersUseCase @Inject constructor(private val cryptoConfig: MXCryptoConfig,
|
||||||
private val cryptoStore: IMXCryptoStore) {
|
private val cryptoStore: IMXCommonCryptoStore) {
|
||||||
|
|
||||||
operator fun invoke(roomId: String): Boolean {
|
operator fun invoke(roomId: String): Boolean {
|
||||||
return cryptoConfig.enableEncryptionForInvitedMembers && cryptoStore.shouldEncryptForInvitedMembers(roomId)
|
return cryptoConfig.enableEncryptionForInvitedMembers && cryptoStore.shouldEncryptForInvitedMembers(roomId)
|
||||||
|
|
|
@ -0,0 +1,156 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2023 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.crypto.store
|
||||||
|
|
||||||
|
import androidx.lifecycle.LiveData
|
||||||
|
import org.matrix.android.sdk.api.session.crypto.GlobalCryptoConfig
|
||||||
|
import org.matrix.android.sdk.api.session.crypto.model.CryptoDeviceInfo
|
||||||
|
import org.matrix.android.sdk.api.session.crypto.model.CryptoRoomInfo
|
||||||
|
import org.matrix.android.sdk.api.session.crypto.model.DeviceInfo
|
||||||
|
import org.matrix.android.sdk.api.session.events.model.content.EncryptionEventContent
|
||||||
|
import org.matrix.android.sdk.api.util.Optional
|
||||||
|
import org.matrix.android.sdk.internal.crypto.model.MXInboundMegolmSessionWrapper
|
||||||
|
import org.matrix.android.sdk.internal.crypto.store.db.CryptoStoreAggregator
|
||||||
|
|
||||||
|
/**
|
||||||
|
* As a temporary measure rust and kotlin flavor are still using realm to store some crypto
|
||||||
|
* related information. In the near future rust flavor will complitly stop using realm, as soon
|
||||||
|
* as the missing bits are store in rust side (like room encryption settings, ..)
|
||||||
|
* This interface defines what's now used by both flavors.
|
||||||
|
* The actual implementation are moved in each flavors
|
||||||
|
*/
|
||||||
|
interface IMXCommonCryptoStore {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides the algorithm used in a dedicated room.
|
||||||
|
*
|
||||||
|
* @param roomId the room id
|
||||||
|
* @return the algorithm, null is the room is not encrypted
|
||||||
|
*/
|
||||||
|
fun getRoomAlgorithm(roomId: String): String?
|
||||||
|
|
||||||
|
fun getRoomCryptoInfo(roomId: String): CryptoRoomInfo?
|
||||||
|
|
||||||
|
fun setAlgorithmInfo(roomId: String, encryption: EncryptionEventContent?)
|
||||||
|
|
||||||
|
fun roomWasOnceEncrypted(roomId: String): Boolean
|
||||||
|
|
||||||
|
fun saveMyDevicesInfo(info: List<DeviceInfo>)
|
||||||
|
|
||||||
|
// questionable that it's stored in crypto store
|
||||||
|
fun getMyDevicesInfo(): List<DeviceInfo>
|
||||||
|
|
||||||
|
// questionable that it's stored in crypto store
|
||||||
|
fun getLiveMyDevicesInfo(): LiveData<List<DeviceInfo>>
|
||||||
|
|
||||||
|
// questionable that it's stored in crypto store
|
||||||
|
fun getLiveMyDevicesInfo(deviceId: String): LiveData<Optional<DeviceInfo>>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* open any existing crypto store.
|
||||||
|
*/
|
||||||
|
fun open()
|
||||||
|
fun tidyUpDataBase()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Close the store.
|
||||||
|
*/
|
||||||
|
fun close()
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Store a bunch of data collected during a sync response treatment. @See [CryptoStoreAggregator].
|
||||||
|
*/
|
||||||
|
fun storeData(cryptoStoreAggregator: CryptoStoreAggregator)
|
||||||
|
|
||||||
|
fun shouldEncryptForInvitedMembers(roomId: String): Boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets a boolean flag that will determine whether or not room history (existing inbound sessions)
|
||||||
|
* will be shared to new user invites.
|
||||||
|
*
|
||||||
|
* @param roomId the room id
|
||||||
|
* @param shouldShareHistory The boolean flag
|
||||||
|
*/
|
||||||
|
fun setShouldShareHistory(roomId: String, shouldShareHistory: Boolean)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets a boolean flag that will determine whether or not this device should encrypt Events for
|
||||||
|
* invited members.
|
||||||
|
*
|
||||||
|
* @param roomId the room id
|
||||||
|
* @param shouldEncryptForInvitedMembers The boolean flag
|
||||||
|
*/
|
||||||
|
fun setShouldEncryptForInvitedMembers(roomId: String, shouldEncryptForInvitedMembers: Boolean)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Define if encryption keys should be sent to unverified devices in this room.
|
||||||
|
*
|
||||||
|
* @param roomId the roomId
|
||||||
|
* @param block if true will not send keys to unverified devices
|
||||||
|
*/
|
||||||
|
fun blockUnverifiedDevicesInRoom(roomId: String, block: Boolean)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the global override for whether the client should ever send encrypted
|
||||||
|
* messages to unverified devices.
|
||||||
|
* If false, it can still be overridden per-room.
|
||||||
|
* If true, it overrides the per-room settings.
|
||||||
|
*
|
||||||
|
* @param block true to unilaterally blacklist all
|
||||||
|
*/
|
||||||
|
fun setGlobalBlacklistUnverifiedDevices(block: Boolean)
|
||||||
|
|
||||||
|
fun getLiveGlobalCryptoConfig(): LiveData<GlobalCryptoConfig>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return true to unilaterally blacklist all unverified devices.
|
||||||
|
*/
|
||||||
|
fun getGlobalBlacklistUnverifiedDevices(): Boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A live status regarding sharing keys for unverified devices in this room.
|
||||||
|
*
|
||||||
|
* @return Live status
|
||||||
|
*/
|
||||||
|
fun getLiveBlockUnverifiedDevices(roomId: String): LiveData<Boolean>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tell if unverified devices should be blacklisted when sending keys.
|
||||||
|
*
|
||||||
|
* @return true if should not send keys to unverified devices
|
||||||
|
*/
|
||||||
|
fun getBlockUnverifiedDevices(roomId: String): Boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve a device by its identity key.
|
||||||
|
*
|
||||||
|
* @param userId the device owner
|
||||||
|
* @param identityKey the device identity key (`MXDeviceInfo.identityKey`)
|
||||||
|
* @return the device or null if not found
|
||||||
|
*/
|
||||||
|
fun deviceWithIdentityKey(userId: String, identityKey: String): CryptoDeviceInfo?
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve an inbound group session.
|
||||||
|
* Used in rust for lazy migration
|
||||||
|
*
|
||||||
|
* @param sessionId the session identifier.
|
||||||
|
* @param senderKey the base64-encoded curve25519 key of the sender.
|
||||||
|
* @return an inbound group session.
|
||||||
|
*/
|
||||||
|
fun getInboundGroupSession(sessionId: String, senderKey: String): MXInboundMegolmSessionWrapper?
|
||||||
|
}
|
|
@ -71,7 +71,14 @@ internal data class Capabilities(
|
||||||
* True if the user can use m.thread relation, false otherwise.
|
* True if the user can use m.thread relation, false otherwise.
|
||||||
*/
|
*/
|
||||||
@Json(name = "m.thread")
|
@Json(name = "m.thread")
|
||||||
val threads: BooleanCapability? = null
|
val threads: BooleanCapability? = null,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Capability to indicate if the server supports login token issuance for signing in another device.
|
||||||
|
* True if the user can use /login/get_token, false otherwise.
|
||||||
|
*/
|
||||||
|
@Json(name = "m.get_login_token")
|
||||||
|
val getLoginToken: BooleanCapability? = null
|
||||||
)
|
)
|
||||||
|
|
||||||
@JsonClass(generateAdapter = true)
|
@JsonClass(generateAdapter = true)
|
||||||
|
|
|
@ -151,8 +151,6 @@ internal class DefaultGetHomeServerCapabilitiesTask @Inject constructor(
|
||||||
getVersionResult.doesServerSupportThreads()
|
getVersionResult.doesServerSupportThreads()
|
||||||
homeServerCapabilitiesEntity.canUseThreadReadReceiptsAndNotifications =
|
homeServerCapabilitiesEntity.canUseThreadReadReceiptsAndNotifications =
|
||||||
getVersionResult.doesServerSupportThreadUnreadNotifications()
|
getVersionResult.doesServerSupportThreadUnreadNotifications()
|
||||||
homeServerCapabilitiesEntity.canLoginWithQrCode =
|
|
||||||
getVersionResult.doesServerSupportQrCodeLogin()
|
|
||||||
homeServerCapabilitiesEntity.canRemotelyTogglePushNotificationsOfDevices =
|
homeServerCapabilitiesEntity.canRemotelyTogglePushNotificationsOfDevices =
|
||||||
getVersionResult.doesServerSupportRemoteToggleOfPushNotifications()
|
getVersionResult.doesServerSupportRemoteToggleOfPushNotifications()
|
||||||
homeServerCapabilitiesEntity.canRedactEventWithRelations =
|
homeServerCapabilitiesEntity.canRedactEventWithRelations =
|
||||||
|
@ -169,10 +167,25 @@ internal class DefaultGetHomeServerCapabilitiesTask @Inject constructor(
|
||||||
}
|
}
|
||||||
homeServerCapabilitiesEntity.externalAccountManagementUrl = getWellknownResult.wellKnown.unstableDelegatedAuthConfig?.accountManagementUrl
|
homeServerCapabilitiesEntity.externalAccountManagementUrl = getWellknownResult.wellKnown.unstableDelegatedAuthConfig?.accountManagementUrl
|
||||||
}
|
}
|
||||||
|
|
||||||
|
homeServerCapabilitiesEntity.canLoginWithQrCode = canLoginWithQrCode(getCapabilitiesResult, getVersionResult)
|
||||||
|
|
||||||
homeServerCapabilitiesEntity.lastUpdatedTimestamp = Date().time
|
homeServerCapabilitiesEntity.lastUpdatedTimestamp = Date().time
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun canLoginWithQrCode(getCapabilitiesResult: GetCapabilitiesResult?, getVersionResult: Versions?): Boolean {
|
||||||
|
// in r0 of MSC3882 an unstable feature was exposed. In stable it is done via /capabilities and /login
|
||||||
|
|
||||||
|
// in stable 1.7 a capability is exposed for the authenticated user
|
||||||
|
if (getCapabilitiesResult?.capabilities?.getLoginToken != null) {
|
||||||
|
return getCapabilitiesResult.capabilities.getLoginToken.enabled == true
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
|
return getVersionResult?.doesServerSupportQrCodeLogin() == true
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
// 8 hours like on Element Web
|
// 8 hours like on Element Web
|
||||||
private const val MIN_DELAY_BETWEEN_TWO_REQUEST_MILLIS = 8 * 60 * 60 * 1000
|
private const val MIN_DELAY_BETWEEN_TWO_REQUEST_MILLIS = 8 * 60 * 60 * 1000
|
||||||
|
|
|
@ -26,11 +26,10 @@ import org.matrix.android.sdk.api.session.events.model.toModel
|
||||||
import org.matrix.android.sdk.api.session.events.model.toValidDecryptedEvent
|
import org.matrix.android.sdk.api.session.events.model.toValidDecryptedEvent
|
||||||
import org.matrix.android.sdk.api.session.room.model.message.MessageContent
|
import org.matrix.android.sdk.api.session.room.model.message.MessageContent
|
||||||
import org.matrix.android.sdk.api.session.room.model.message.MessagePollContent
|
import org.matrix.android.sdk.api.session.room.model.message.MessagePollContent
|
||||||
import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore
|
import org.matrix.android.sdk.internal.crypto.store.IMXCommonCryptoStore
|
||||||
import timber.log.Timber
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
internal class EventEditValidator @Inject constructor(val cryptoStore: IMXCryptoStore) {
|
internal class EventEditValidator @Inject constructor(val cryptoStore: IMXCommonCryptoStore) {
|
||||||
|
|
||||||
sealed class EditValidity {
|
sealed class EditValidity {
|
||||||
object Valid : EditValidity()
|
object Valid : EditValidity()
|
||||||
|
@ -53,7 +52,6 @@ internal class EventEditValidator @Inject constructor(val cryptoStore: IMXCrypto
|
||||||
* If the original event was encrypted, the replacement should be too.
|
* If the original event was encrypted, the replacement should be too.
|
||||||
*/
|
*/
|
||||||
fun validateEdit(originalEvent: Event?, replaceEvent: Event): EditValidity {
|
fun validateEdit(originalEvent: Event?, replaceEvent: Event): EditValidity {
|
||||||
Timber.v("###REPLACE valide event $originalEvent replaced $replaceEvent")
|
|
||||||
// we might not know the original event at that time. In this case we can't perform the validation
|
// we might not know the original event at that time. In this case we can't perform the validation
|
||||||
// Edits should be revalidated when the original event is received
|
// Edits should be revalidated when the original event is received
|
||||||
if (originalEvent == null) {
|
if (originalEvent == null) {
|
||||||
|
@ -80,25 +78,21 @@ internal class EventEditValidator @Inject constructor(val cryptoStore: IMXCrypto
|
||||||
val replaceDecrypted = replaceEvent.toValidDecryptedEvent()
|
val replaceDecrypted = replaceEvent.toValidDecryptedEvent()
|
||||||
?: return EditValidity.Unknown // UTD can't decide
|
?: return EditValidity.Unknown // UTD can't decide
|
||||||
|
|
||||||
val originalCryptoSenderId = cryptoStore.deviceWithIdentityKey(originalDecrypted.cryptoSenderKey)?.userId
|
if (originalEvent.senderId != replaceEvent.senderId) {
|
||||||
val editCryptoSenderId = cryptoStore.deviceWithIdentityKey(replaceDecrypted.cryptoSenderKey)?.userId
|
return EditValidity.Invalid("original event and replacement event must have the same sender")
|
||||||
|
}
|
||||||
|
|
||||||
|
val originalSendingDevice = originalEvent.senderId?.let { cryptoStore.deviceWithIdentityKey(it, originalDecrypted.cryptoSenderKey) }
|
||||||
|
val editSendingDevice = originalEvent.senderId?.let { cryptoStore.deviceWithIdentityKey(it, replaceDecrypted.cryptoSenderKey) }
|
||||||
|
|
||||||
if (originalDecrypted.getRelationContent()?.type == RelationType.REPLACE) {
|
if (originalDecrypted.getRelationContent()?.type == RelationType.REPLACE) {
|
||||||
return EditValidity.Invalid("The original event must not, itself, have a rel_type of m.replace ")
|
return EditValidity.Invalid("The original event must not, itself, have a rel_type of m.replace ")
|
||||||
}
|
}
|
||||||
|
|
||||||
if (originalCryptoSenderId == null || editCryptoSenderId == null) {
|
if (originalSendingDevice == null || editSendingDevice == null) {
|
||||||
// mm what can we do? we don't know if it's cryptographically from same user?
|
// mm what can we do? we don't know if it's cryptographically from same user?
|
||||||
// let valid and UI should display send by deleted device warning?
|
// maybe it's a deleted device or a not yet downloaded one?
|
||||||
val bestEffortOriginal = originalCryptoSenderId ?: originalEvent.senderId
|
return EditValidity.Unknown
|
||||||
val bestEffortEdit = editCryptoSenderId ?: replaceEvent.senderId
|
|
||||||
if (bestEffortOriginal != bestEffortEdit) {
|
|
||||||
return EditValidity.Invalid("original event and replacement event must have the same sender")
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (originalCryptoSenderId != editCryptoSenderId) {
|
|
||||||
return EditValidity.Invalid("Crypto: original event and replacement event must have the same sender")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (originalDecrypted.type != replaceDecrypted.type) {
|
if (originalDecrypted.type != replaceDecrypted.type) {
|
||||||
|
|
|
@ -22,6 +22,8 @@ import com.zhuinden.monarchy.Monarchy
|
||||||
import dagger.assisted.Assisted
|
import dagger.assisted.Assisted
|
||||||
import dagger.assisted.AssistedFactory
|
import dagger.assisted.AssistedFactory
|
||||||
import dagger.assisted.AssistedInject
|
import dagger.assisted.AssistedInject
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
import org.matrix.android.sdk.api.MatrixCoroutineDispatchers
|
||||||
import org.matrix.android.sdk.api.session.room.model.ReadReceipt
|
import org.matrix.android.sdk.api.session.room.model.ReadReceipt
|
||||||
import org.matrix.android.sdk.api.session.room.read.ReadService
|
import org.matrix.android.sdk.api.session.room.read.ReadService
|
||||||
import org.matrix.android.sdk.api.util.Optional
|
import org.matrix.android.sdk.api.util.Optional
|
||||||
|
@ -43,7 +45,8 @@ internal class DefaultReadService @AssistedInject constructor(
|
||||||
private val setReadMarkersTask: SetReadMarkersTask,
|
private val setReadMarkersTask: SetReadMarkersTask,
|
||||||
private val readReceiptsSummaryMapper: ReadReceiptsSummaryMapper,
|
private val readReceiptsSummaryMapper: ReadReceiptsSummaryMapper,
|
||||||
@UserId private val userId: String,
|
@UserId private val userId: String,
|
||||||
private val homeServerCapabilitiesDataSource: HomeServerCapabilitiesDataSource
|
private val homeServerCapabilitiesDataSource: HomeServerCapabilitiesDataSource,
|
||||||
|
private val matrixCoroutineDispatchers: MatrixCoroutineDispatchers,
|
||||||
) : ReadService {
|
) : ReadService {
|
||||||
|
|
||||||
@AssistedFactory
|
@AssistedFactory
|
||||||
|
@ -66,7 +69,7 @@ internal class DefaultReadService @AssistedInject constructor(
|
||||||
setReadMarkersTask.execute(taskParams)
|
setReadMarkersTask.execute(taskParams)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun setReadReceipt(eventId: String, threadId: String) {
|
override suspend fun setReadReceipt(eventId: String, threadId: String) = withContext(matrixCoroutineDispatchers.io) {
|
||||||
val readReceiptThreadId = if (homeServerCapabilitiesDataSource.getHomeServerCapabilities()?.canUseThreadReadReceiptsAndNotifications == true) {
|
val readReceiptThreadId = if (homeServerCapabilitiesDataSource.getHomeServerCapabilities()?.canUseThreadReadReceiptsAndNotifications == true) {
|
||||||
threadId
|
threadId
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -18,6 +18,8 @@ package org.matrix.android.sdk.internal.session.room.read
|
||||||
|
|
||||||
import com.zhuinden.monarchy.Monarchy
|
import com.zhuinden.monarchy.Monarchy
|
||||||
import io.realm.Realm
|
import io.realm.Realm
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
import org.matrix.android.sdk.api.MatrixCoroutineDispatchers
|
||||||
import org.matrix.android.sdk.api.session.events.model.LocalEcho
|
import org.matrix.android.sdk.api.session.events.model.LocalEcho
|
||||||
import org.matrix.android.sdk.api.session.homeserver.HomeServerCapabilitiesService
|
import org.matrix.android.sdk.api.session.homeserver.HomeServerCapabilitiesService
|
||||||
import org.matrix.android.sdk.internal.database.model.RoomSummaryEntity
|
import org.matrix.android.sdk.internal.database.model.RoomSummaryEntity
|
||||||
|
@ -64,9 +66,10 @@ internal class DefaultSetReadMarkersTask @Inject constructor(
|
||||||
private val globalErrorReceiver: GlobalErrorReceiver,
|
private val globalErrorReceiver: GlobalErrorReceiver,
|
||||||
private val clock: Clock,
|
private val clock: Clock,
|
||||||
private val homeServerCapabilitiesService: HomeServerCapabilitiesService,
|
private val homeServerCapabilitiesService: HomeServerCapabilitiesService,
|
||||||
|
private val coroutineDispatchers: MatrixCoroutineDispatchers,
|
||||||
) : SetReadMarkersTask {
|
) : SetReadMarkersTask {
|
||||||
|
|
||||||
override suspend fun execute(params: SetReadMarkersTask.Params) {
|
override suspend fun execute(params: SetReadMarkersTask.Params) = withContext(coroutineDispatchers.io) {
|
||||||
val markers = mutableMapOf<String, String>()
|
val markers = mutableMapOf<String, String>()
|
||||||
Timber.v("Execute set read marker with params: $params")
|
Timber.v("Execute set read marker with params: $params")
|
||||||
val latestSyncedEventId = latestSyncedEventId(params.roomId)
|
val latestSyncedEventId = latestSyncedEventId(params.roomId)
|
||||||
|
|
|
@ -49,7 +49,7 @@ import org.matrix.android.sdk.api.util.CancelableBag
|
||||||
import org.matrix.android.sdk.api.util.JsonDict
|
import org.matrix.android.sdk.api.util.JsonDict
|
||||||
import org.matrix.android.sdk.api.util.NoOpCancellable
|
import org.matrix.android.sdk.api.util.NoOpCancellable
|
||||||
import org.matrix.android.sdk.api.util.TextContent
|
import org.matrix.android.sdk.api.util.TextContent
|
||||||
import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore
|
import org.matrix.android.sdk.internal.crypto.store.IMXCommonCryptoStore
|
||||||
import org.matrix.android.sdk.internal.di.SessionId
|
import org.matrix.android.sdk.internal.di.SessionId
|
||||||
import org.matrix.android.sdk.internal.di.WorkManagerProvider
|
import org.matrix.android.sdk.internal.di.WorkManagerProvider
|
||||||
import org.matrix.android.sdk.internal.session.content.UploadContentWorker
|
import org.matrix.android.sdk.internal.session.content.UploadContentWorker
|
||||||
|
@ -69,7 +69,7 @@ internal class DefaultSendService @AssistedInject constructor(
|
||||||
private val workManagerProvider: WorkManagerProvider,
|
private val workManagerProvider: WorkManagerProvider,
|
||||||
@SessionId private val sessionId: String,
|
@SessionId private val sessionId: String,
|
||||||
private val localEchoEventFactory: LocalEchoEventFactory,
|
private val localEchoEventFactory: LocalEchoEventFactory,
|
||||||
private val cryptoStore: IMXCryptoStore,
|
private val cryptoStore: IMXCommonCryptoStore,
|
||||||
private val taskExecutor: TaskExecutor,
|
private val taskExecutor: TaskExecutor,
|
||||||
private val localEchoRepository: LocalEchoRepository,
|
private val localEchoRepository: LocalEchoRepository,
|
||||||
private val eventSenderProcessor: EventSenderProcessor,
|
private val eventSenderProcessor: EventSenderProcessor,
|
||||||
|
|
|
@ -28,7 +28,7 @@ import org.matrix.android.sdk.api.failure.isLimitExceededError
|
||||||
import org.matrix.android.sdk.api.session.Session
|
import org.matrix.android.sdk.api.session.Session
|
||||||
import org.matrix.android.sdk.api.session.events.model.Event
|
import org.matrix.android.sdk.api.session.events.model.Event
|
||||||
import org.matrix.android.sdk.api.util.Cancelable
|
import org.matrix.android.sdk.api.util.Cancelable
|
||||||
import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore
|
import org.matrix.android.sdk.internal.crypto.store.IMXCommonCryptoStore
|
||||||
import org.matrix.android.sdk.internal.session.SessionScope
|
import org.matrix.android.sdk.internal.session.SessionScope
|
||||||
import org.matrix.android.sdk.internal.task.CoroutineSequencer
|
import org.matrix.android.sdk.internal.task.CoroutineSequencer
|
||||||
import org.matrix.android.sdk.internal.task.SemaphoreCoroutineSequencer
|
import org.matrix.android.sdk.internal.task.SemaphoreCoroutineSequencer
|
||||||
|
@ -54,7 +54,7 @@ private const val MAX_RETRY_COUNT = 3
|
||||||
*/
|
*/
|
||||||
@SessionScope
|
@SessionScope
|
||||||
internal class EventSenderProcessorCoroutine @Inject constructor(
|
internal class EventSenderProcessorCoroutine @Inject constructor(
|
||||||
private val cryptoStore: IMXCryptoStore,
|
private val cryptoStore: IMXCommonCryptoStore,
|
||||||
private val sessionParams: SessionParams,
|
private val sessionParams: SessionParams,
|
||||||
private val queuedTaskFactory: QueuedTaskFactory,
|
private val queuedTaskFactory: QueuedTaskFactory,
|
||||||
private val taskExecutor: TaskExecutor,
|
private val taskExecutor: TaskExecutor,
|
||||||
|
|
|
@ -35,7 +35,12 @@ internal class DefaultSignOutService @Inject constructor(
|
||||||
sessionParamsStore.updateCredentials(credentials)
|
sessionParamsStore.updateCredentials(credentials)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun signOut(signOutFromHomeserver: Boolean) {
|
override suspend fun signOut(signOutFromHomeserver: Boolean, ignoreServerRequestError: Boolean) {
|
||||||
return signOutTask.execute(SignOutTask.Params(signOutFromHomeserver))
|
return signOutTask.execute(
|
||||||
|
SignOutTask.Params(
|
||||||
|
signOutFromHomeserver = signOutFromHomeserver,
|
||||||
|
ignoreServerRequestError = ignoreServerRequestError
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,7 +30,8 @@ import javax.inject.Inject
|
||||||
|
|
||||||
internal interface SignOutTask : Task<SignOutTask.Params, Unit> {
|
internal interface SignOutTask : Task<SignOutTask.Params, Unit> {
|
||||||
data class Params(
|
data class Params(
|
||||||
val signOutFromHomeserver: Boolean
|
val signOutFromHomeserver: Boolean,
|
||||||
|
val ignoreServerRequestError: Boolean,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,7 +60,9 @@ internal class DefaultSignOutTask @Inject constructor(
|
||||||
// Ignore
|
// Ignore
|
||||||
Timber.w("Ignore error due to https://github.com/matrix-org/synapse/issues/5755")
|
Timber.w("Ignore error due to https://github.com/matrix-org/synapse/issues/5755")
|
||||||
} else {
|
} else {
|
||||||
throw throwable
|
if (!params.ignoreServerRequestError) {
|
||||||
|
throw throwable
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,8 +58,8 @@ import org.matrix.android.sdk.internal.crypto.keysbackup.tasks.StoreRoomSessionD
|
||||||
import org.matrix.android.sdk.internal.crypto.keysbackup.tasks.StoreRoomSessionsDataTask
|
import org.matrix.android.sdk.internal.crypto.keysbackup.tasks.StoreRoomSessionsDataTask
|
||||||
import org.matrix.android.sdk.internal.crypto.keysbackup.tasks.StoreSessionsDataTask
|
import org.matrix.android.sdk.internal.crypto.keysbackup.tasks.StoreSessionsDataTask
|
||||||
import org.matrix.android.sdk.internal.crypto.keysbackup.tasks.UpdateKeysBackupVersionTask
|
import org.matrix.android.sdk.internal.crypto.keysbackup.tasks.UpdateKeysBackupVersionTask
|
||||||
import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore
|
import org.matrix.android.sdk.internal.crypto.store.IMXCommonCryptoStore
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.RealmCryptoStore
|
import org.matrix.android.sdk.internal.crypto.store.RustCryptoStore
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.RealmCryptoStoreMigration
|
import org.matrix.android.sdk.internal.crypto.store.db.RealmCryptoStoreMigration
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.RealmCryptoStoreModule
|
import org.matrix.android.sdk.internal.crypto.store.db.RealmCryptoStoreModule
|
||||||
import org.matrix.android.sdk.internal.crypto.tasks.ClaimOneTimeKeysForUsersDeviceTask
|
import org.matrix.android.sdk.internal.crypto.tasks.ClaimOneTimeKeysForUsersDeviceTask
|
||||||
|
@ -246,7 +246,7 @@ internal abstract class CryptoModule {
|
||||||
abstract fun bindVerificationService(service: RustVerificationService): VerificationService
|
abstract fun bindVerificationService(service: RustVerificationService): VerificationService
|
||||||
|
|
||||||
@Binds
|
@Binds
|
||||||
abstract fun bindCryptoStore(store: RealmCryptoStore): IMXCryptoStore
|
abstract fun bindCryptoStore(store: RustCryptoStore): IMXCommonCryptoStore
|
||||||
|
|
||||||
@Binds
|
@Binds
|
||||||
abstract fun bindSendEventTask(task: DefaultSendEventTask): SendEventTask
|
abstract fun bindSendEventTask(task: DefaultSendEventTask): SendEventTask
|
||||||
|
|
|
@ -54,6 +54,7 @@ import org.matrix.android.sdk.api.util.toOptional
|
||||||
import org.matrix.android.sdk.internal.coroutines.builder.safeInvokeOnClose
|
import org.matrix.android.sdk.internal.coroutines.builder.safeInvokeOnClose
|
||||||
import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.DefaultKeysAlgorithmAndData
|
import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.DefaultKeysAlgorithmAndData
|
||||||
import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.KeysAlgorithmAndData
|
import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.KeysAlgorithmAndData
|
||||||
|
import org.matrix.android.sdk.internal.crypto.model.MXInboundMegolmSessionWrapper
|
||||||
import org.matrix.android.sdk.internal.crypto.network.RequestSender
|
import org.matrix.android.sdk.internal.crypto.network.RequestSender
|
||||||
import org.matrix.android.sdk.internal.crypto.verification.SasVerification
|
import org.matrix.android.sdk.internal.crypto.verification.SasVerification
|
||||||
import org.matrix.android.sdk.internal.crypto.verification.VerificationRequest
|
import org.matrix.android.sdk.internal.crypto.verification.VerificationRequest
|
||||||
|
@ -312,10 +313,10 @@ internal class OlmMachine @Inject constructor(
|
||||||
/**
|
/**
|
||||||
* Used for lazy migration of inboundGroupSession from EA to ER
|
* Used for lazy migration of inboundGroupSession from EA to ER
|
||||||
*/
|
*/
|
||||||
suspend fun importRoomKey(inbound: InboundGroupSessionHolder): Result<Unit> {
|
suspend fun importRoomKey(inbound: MXInboundMegolmSessionWrapper): Result<Unit> {
|
||||||
Timber.v("Migration:: Tentative lazy migration")
|
Timber.v("Migration:: Tentative lazy migration")
|
||||||
return withContext(coroutineDispatchers.io) {
|
return withContext(coroutineDispatchers.io) {
|
||||||
val export = inbound.wrapper.exportKeys()
|
val export = inbound.exportKeys()
|
||||||
?: return@withContext Result.failure(Exception("Failed to export key"))
|
?: return@withContext Result.failure(Exception("Failed to export key"))
|
||||||
val result = importDecryptedKeys(listOf(export), null).also {
|
val result = importDecryptedKeys(listOf(export), null).also {
|
||||||
Timber.v("Migration:: Tentative lazy migration result: ${it.totalNumberOfKeys}")
|
Timber.v("Migration:: Tentative lazy migration result: ${it.totalNumberOfKeys}")
|
||||||
|
|
|
@ -28,7 +28,7 @@ import org.matrix.android.sdk.api.logger.LoggerTag
|
||||||
import org.matrix.android.sdk.api.session.crypto.MXCryptoError
|
import org.matrix.android.sdk.api.session.crypto.MXCryptoError
|
||||||
import org.matrix.android.sdk.internal.crypto.keysbackup.RustKeyBackupService
|
import org.matrix.android.sdk.internal.crypto.keysbackup.RustKeyBackupService
|
||||||
import org.matrix.android.sdk.internal.crypto.network.RequestSender
|
import org.matrix.android.sdk.internal.crypto.network.RequestSender
|
||||||
import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore
|
import org.matrix.android.sdk.internal.crypto.store.IMXCommonCryptoStore
|
||||||
import org.matrix.android.sdk.internal.session.SessionScope
|
import org.matrix.android.sdk.internal.session.SessionScope
|
||||||
import org.matrix.android.sdk.internal.session.room.membership.LoadRoomMembersTask
|
import org.matrix.android.sdk.internal.session.room.membership.LoadRoomMembersTask
|
||||||
import org.matrix.rustcomponents.sdk.crypto.EncryptionSettings
|
import org.matrix.rustcomponents.sdk.crypto.EncryptionSettings
|
||||||
|
@ -47,7 +47,7 @@ private val loggerTag = LoggerTag("PrepareToEncryptUseCase", LoggerTag.CRYPTO)
|
||||||
internal class PrepareToEncryptUseCase @Inject constructor(
|
internal class PrepareToEncryptUseCase @Inject constructor(
|
||||||
private val olmMachine: OlmMachine,
|
private val olmMachine: OlmMachine,
|
||||||
private val coroutineDispatchers: MatrixCoroutineDispatchers,
|
private val coroutineDispatchers: MatrixCoroutineDispatchers,
|
||||||
private val cryptoStore: IMXCryptoStore,
|
private val cryptoStore: IMXCommonCryptoStore,
|
||||||
private val getRoomUserIds: GetRoomUserIdsUseCase,
|
private val getRoomUserIds: GetRoomUserIdsUseCase,
|
||||||
private val requestSender: RequestSender,
|
private val requestSender: RequestSender,
|
||||||
private val loadRoomMembersTask: LoadRoomMembersTask,
|
private val loadRoomMembersTask: LoadRoomMembersTask,
|
||||||
|
|
|
@ -17,7 +17,6 @@
|
||||||
package org.matrix.android.sdk.internal.crypto
|
package org.matrix.android.sdk.internal.crypto
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import androidx.annotation.VisibleForTesting
|
|
||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
import androidx.lifecycle.map
|
import androidx.lifecycle.map
|
||||||
import androidx.paging.PagedList
|
import androidx.paging.PagedList
|
||||||
|
@ -26,7 +25,6 @@ import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.NonCancellable
|
import kotlinx.coroutines.NonCancellable
|
||||||
import kotlinx.coroutines.cancelChildren
|
import kotlinx.coroutines.cancelChildren
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.runBlocking
|
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import org.matrix.android.sdk.api.MatrixConfiguration
|
import org.matrix.android.sdk.api.MatrixConfiguration
|
||||||
import org.matrix.android.sdk.api.MatrixCoroutineDispatchers
|
import org.matrix.android.sdk.api.MatrixCoroutineDispatchers
|
||||||
|
@ -76,7 +74,7 @@ import org.matrix.android.sdk.internal.crypto.keysbackup.RustKeyBackupService
|
||||||
import org.matrix.android.sdk.internal.crypto.model.SessionInfo
|
import org.matrix.android.sdk.internal.crypto.model.SessionInfo
|
||||||
import org.matrix.android.sdk.internal.crypto.network.OutgoingRequestsProcessor
|
import org.matrix.android.sdk.internal.crypto.network.OutgoingRequestsProcessor
|
||||||
import org.matrix.android.sdk.internal.crypto.repository.WarnOnUnknownDeviceRepository
|
import org.matrix.android.sdk.internal.crypto.repository.WarnOnUnknownDeviceRepository
|
||||||
import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore
|
import org.matrix.android.sdk.internal.crypto.store.IMXCommonCryptoStore
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.CryptoStoreAggregator
|
import org.matrix.android.sdk.internal.crypto.store.db.CryptoStoreAggregator
|
||||||
import org.matrix.android.sdk.internal.crypto.tasks.DeleteDeviceTask
|
import org.matrix.android.sdk.internal.crypto.tasks.DeleteDeviceTask
|
||||||
import org.matrix.android.sdk.internal.crypto.tasks.GetDeviceInfoTask
|
import org.matrix.android.sdk.internal.crypto.tasks.GetDeviceInfoTask
|
||||||
|
@ -111,7 +109,7 @@ internal class RustCryptoService @Inject constructor(
|
||||||
@UserId private val myUserId: String,
|
@UserId private val myUserId: String,
|
||||||
@DeviceId private val deviceId: String,
|
@DeviceId private val deviceId: String,
|
||||||
// the crypto store
|
// the crypto store
|
||||||
private val cryptoStore: IMXCryptoStore,
|
private val cryptoStore: IMXCommonCryptoStore,
|
||||||
// Set of parameters used to configure/customize the end-to-end crypto.
|
// Set of parameters used to configure/customize the end-to-end crypto.
|
||||||
private val mxCryptoConfig: MXCryptoConfig,
|
private val mxCryptoConfig: MXCryptoConfig,
|
||||||
// Actions
|
// Actions
|
||||||
|
@ -185,12 +183,13 @@ internal class RustCryptoService @Inject constructor(
|
||||||
|
|
||||||
override fun getCryptoVersion(context: Context, longFormat: Boolean): String {
|
override fun getCryptoVersion(context: Context, longFormat: Boolean): String {
|
||||||
val version = org.matrix.rustcomponents.sdk.crypto.version()
|
val version = org.matrix.rustcomponents.sdk.crypto.version()
|
||||||
|
val gitHash = org.matrix.rustcomponents.sdk.crypto.versionInfo().gitSha
|
||||||
val vodozemac = org.matrix.rustcomponents.sdk.crypto.vodozemacVersion()
|
val vodozemac = org.matrix.rustcomponents.sdk.crypto.vodozemacVersion()
|
||||||
return if (longFormat) "Rust SDK $version, Vodozemac $vodozemac" else version
|
return if (longFormat) "Rust SDK $version ($gitHash), Vodozemac $vodozemac" else version
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getMyCryptoDevice(): CryptoDeviceInfo {
|
override suspend fun getMyCryptoDevice(): CryptoDeviceInfo = withContext(coroutineDispatchers.io) {
|
||||||
return runBlocking { olmMachine.ownDevice() }
|
olmMachine.ownDevice()
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun fetchDevicesList(): List<DeviceInfo> {
|
override suspend fun fetchDevicesList(): List<DeviceInfo> {
|
||||||
|
@ -342,11 +341,11 @@ internal class RustCryptoService @Inject constructor(
|
||||||
*/
|
*/
|
||||||
override suspend fun getCryptoDeviceInfo(userId: String, deviceId: String?): CryptoDeviceInfo? {
|
override suspend fun getCryptoDeviceInfo(userId: String, deviceId: String?): CryptoDeviceInfo? {
|
||||||
if (userId.isEmpty() || deviceId.isNullOrEmpty()) return null
|
if (userId.isEmpty() || deviceId.isNullOrEmpty()) return null
|
||||||
return olmMachine.getCryptoDeviceInfo(userId, deviceId)
|
return withContext(coroutineDispatchers.io) { olmMachine.getCryptoDeviceInfo(userId, deviceId) }
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getCryptoDeviceInfo(userId: String): List<CryptoDeviceInfo> {
|
override suspend fun getCryptoDeviceInfo(userId: String): List<CryptoDeviceInfo> {
|
||||||
return runBlocking {
|
return withContext(coroutineDispatchers.io) {
|
||||||
olmMachine.getCryptoDeviceInfo(userId)
|
olmMachine.getCryptoDeviceInfo(userId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -360,9 +359,7 @@ internal class RustCryptoService @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getLiveCryptoDeviceInfo(userIds: List<String>): LiveData<List<CryptoDeviceInfo>> {
|
override fun getLiveCryptoDeviceInfo(userIds: List<String>): LiveData<List<CryptoDeviceInfo>> {
|
||||||
return olmMachine.getLiveDevices(listOf(myUserId)).map {
|
return olmMachine.getLiveDevices(userIds)
|
||||||
it.filter { it.userId == myUserId }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getLiveCryptoDeviceInfoWithId(deviceId: String): LiveData<Optional<CryptoDeviceInfo>> {
|
override fun getLiveCryptoDeviceInfoWithId(deviceId: String): LiveData<Optional<CryptoDeviceInfo>> {
|
||||||
|
@ -905,13 +902,6 @@ internal class RustCryptoService @Inject constructor(
|
||||||
// TODO("Not yet implemented")
|
// TODO("Not yet implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ==========================================================================================
|
|
||||||
* For test only
|
|
||||||
* ========================================================================================== */
|
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
val cryptoStoreForTesting = cryptoStore
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val CRYPTO_MIN_FORCE_SESSION_PERIOD_MILLIS = 3_600_000 // one hour
|
const val CRYPTO_MIN_FORCE_SESSION_PERIOD_MILLIS = 3_600_000 // one hour
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@ import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.SupervisorJob
|
import kotlinx.coroutines.SupervisorJob
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor
|
import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor
|
||||||
|
import org.matrix.android.sdk.api.extensions.tryOrNull
|
||||||
import org.matrix.android.sdk.api.failure.Failure
|
import org.matrix.android.sdk.api.failure.Failure
|
||||||
import org.matrix.android.sdk.api.failure.MatrixError
|
import org.matrix.android.sdk.api.failure.MatrixError
|
||||||
import org.matrix.android.sdk.api.session.crypto.keysbackup.KeysBackupLastVersionResult
|
import org.matrix.android.sdk.api.session.crypto.keysbackup.KeysBackupLastVersionResult
|
||||||
|
@ -36,7 +37,6 @@ import org.matrix.android.sdk.api.session.events.model.Event
|
||||||
import org.matrix.android.sdk.api.session.events.model.EventType
|
import org.matrix.android.sdk.api.session.events.model.EventType
|
||||||
import org.matrix.android.sdk.api.session.uia.UiaResult
|
import org.matrix.android.sdk.api.session.uia.UiaResult
|
||||||
import org.matrix.android.sdk.internal.auth.registration.handleUIA
|
import org.matrix.android.sdk.internal.auth.registration.handleUIA
|
||||||
import org.matrix.android.sdk.internal.crypto.InboundGroupSessionStore
|
|
||||||
import org.matrix.android.sdk.internal.crypto.OlmMachine
|
import org.matrix.android.sdk.internal.crypto.OlmMachine
|
||||||
import org.matrix.android.sdk.internal.crypto.PerSessionBackupQueryRateLimiter
|
import org.matrix.android.sdk.internal.crypto.PerSessionBackupQueryRateLimiter
|
||||||
import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.BackupKeysResult
|
import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.BackupKeysResult
|
||||||
|
@ -59,6 +59,7 @@ import org.matrix.android.sdk.internal.crypto.model.rest.KeysUploadBody
|
||||||
import org.matrix.android.sdk.internal.crypto.model.rest.KeysUploadResponse
|
import org.matrix.android.sdk.internal.crypto.model.rest.KeysUploadResponse
|
||||||
import org.matrix.android.sdk.internal.crypto.model.rest.RestKeyInfo
|
import org.matrix.android.sdk.internal.crypto.model.rest.RestKeyInfo
|
||||||
import org.matrix.android.sdk.internal.crypto.model.rest.SignatureUploadResponse
|
import org.matrix.android.sdk.internal.crypto.model.rest.SignatureUploadResponse
|
||||||
|
import org.matrix.android.sdk.internal.crypto.store.IMXCommonCryptoStore
|
||||||
import org.matrix.android.sdk.internal.crypto.tasks.ClaimOneTimeKeysForUsersDeviceTask
|
import org.matrix.android.sdk.internal.crypto.tasks.ClaimOneTimeKeysForUsersDeviceTask
|
||||||
import org.matrix.android.sdk.internal.crypto.tasks.DefaultSendVerificationMessageTask
|
import org.matrix.android.sdk.internal.crypto.tasks.DefaultSendVerificationMessageTask
|
||||||
import org.matrix.android.sdk.internal.crypto.tasks.DownloadKeysForUsersTask
|
import org.matrix.android.sdk.internal.crypto.tasks.DownloadKeysForUsersTask
|
||||||
|
@ -102,7 +103,7 @@ internal class RequestSender @Inject constructor(
|
||||||
private val moshi: Moshi,
|
private val moshi: Moshi,
|
||||||
cryptoCoroutineScope: CoroutineScope,
|
cryptoCoroutineScope: CoroutineScope,
|
||||||
private val rateLimiter: PerSessionBackupQueryRateLimiter,
|
private val rateLimiter: PerSessionBackupQueryRateLimiter,
|
||||||
private val inboundGroupSessionStore: InboundGroupSessionStore,
|
private val cryptoStore: IMXCommonCryptoStore,
|
||||||
private val localEchoRepository: LocalEchoRepository,
|
private val localEchoRepository: LocalEchoRepository,
|
||||||
private val olmMachine: Lazy<OlmMachine>,
|
private val olmMachine: Lazy<OlmMachine>,
|
||||||
) {
|
) {
|
||||||
|
@ -266,7 +267,9 @@ internal class RequestSender @Inject constructor(
|
||||||
val senderKey = requestBody?.get("sender_key") as? String
|
val senderKey = requestBody?.get("sender_key") as? String
|
||||||
if (roomId != null && sessionId != null) {
|
if (roomId != null && sessionId != null) {
|
||||||
// try to perform a lazy migration from legacy store
|
// try to perform a lazy migration from legacy store
|
||||||
val legacy = inboundGroupSessionStore.getInboundGroupSession(sessionId, senderKey.orEmpty())
|
val legacy = tryOrNull("Failed to access legacy crypto store") {
|
||||||
|
cryptoStore.getInboundGroupSession(sessionId, senderKey.orEmpty())
|
||||||
|
}
|
||||||
if (legacy == null || olmMachine.get().importRoomKey(legacy).isFailure) {
|
if (legacy == null || olmMachine.get().importRoomKey(legacy).isFailure) {
|
||||||
rateLimiter.tryFromBackupIfPossible(sessionId, roomId)
|
rateLimiter.tryFromBackupIfPossible(sessionId, roomId)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,387 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2023 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.crypto.store
|
||||||
|
|
||||||
|
import androidx.lifecycle.LiveData
|
||||||
|
import androidx.lifecycle.Transformations
|
||||||
|
import com.zhuinden.monarchy.Monarchy
|
||||||
|
import io.realm.Realm
|
||||||
|
import io.realm.RealmConfiguration
|
||||||
|
import io.realm.kotlin.where
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import org.matrix.android.sdk.api.MatrixCoroutineDispatchers
|
||||||
|
import org.matrix.android.sdk.api.crypto.MXCRYPTO_ALGORITHM_MEGOLM
|
||||||
|
import org.matrix.android.sdk.api.extensions.tryOrNull
|
||||||
|
import org.matrix.android.sdk.api.logger.LoggerTag
|
||||||
|
import org.matrix.android.sdk.api.session.crypto.GlobalCryptoConfig
|
||||||
|
import org.matrix.android.sdk.api.session.crypto.model.CryptoDeviceInfo
|
||||||
|
import org.matrix.android.sdk.api.session.crypto.model.CryptoRoomInfo
|
||||||
|
import org.matrix.android.sdk.api.session.crypto.model.DeviceInfo
|
||||||
|
import org.matrix.android.sdk.api.session.events.model.content.EncryptionEventContent
|
||||||
|
import org.matrix.android.sdk.api.util.Optional
|
||||||
|
import org.matrix.android.sdk.api.util.toOptional
|
||||||
|
import org.matrix.android.sdk.internal.crypto.OlmMachine
|
||||||
|
import org.matrix.android.sdk.internal.crypto.model.MXInboundMegolmSessionWrapper
|
||||||
|
import org.matrix.android.sdk.internal.crypto.store.db.CryptoStoreAggregator
|
||||||
|
import org.matrix.android.sdk.internal.crypto.store.db.doRealmTransaction
|
||||||
|
import org.matrix.android.sdk.internal.crypto.store.db.doRealmTransactionAsync
|
||||||
|
import org.matrix.android.sdk.internal.crypto.store.db.doWithRealm
|
||||||
|
import org.matrix.android.sdk.internal.crypto.store.db.mapper.CryptoRoomInfoMapper
|
||||||
|
import org.matrix.android.sdk.internal.crypto.store.db.mapper.MyDeviceLastSeenInfoEntityMapper
|
||||||
|
import org.matrix.android.sdk.internal.crypto.store.db.model.AuditTrailEntity
|
||||||
|
import org.matrix.android.sdk.internal.crypto.store.db.model.AuditTrailEntityFields
|
||||||
|
import org.matrix.android.sdk.internal.crypto.store.db.model.CryptoMetadataEntity
|
||||||
|
import org.matrix.android.sdk.internal.crypto.store.db.model.CryptoRoomEntity
|
||||||
|
import org.matrix.android.sdk.internal.crypto.store.db.model.CryptoRoomEntityFields
|
||||||
|
import org.matrix.android.sdk.internal.crypto.store.db.model.MyDeviceLastSeenInfoEntity
|
||||||
|
import org.matrix.android.sdk.internal.crypto.store.db.model.MyDeviceLastSeenInfoEntityFields
|
||||||
|
import org.matrix.android.sdk.internal.crypto.store.db.model.OlmInboundGroupSessionEntity
|
||||||
|
import org.matrix.android.sdk.internal.crypto.store.db.model.OlmInboundGroupSessionEntityFields
|
||||||
|
import org.matrix.android.sdk.internal.crypto.store.db.model.OutgoingKeyRequestEntity
|
||||||
|
import org.matrix.android.sdk.internal.crypto.store.db.model.OutgoingKeyRequestEntityFields
|
||||||
|
import org.matrix.android.sdk.internal.crypto.store.db.model.createPrimaryKey
|
||||||
|
import org.matrix.android.sdk.internal.crypto.store.db.query.getById
|
||||||
|
import org.matrix.android.sdk.internal.crypto.store.db.query.getOrCreate
|
||||||
|
import org.matrix.android.sdk.internal.di.CryptoDatabase
|
||||||
|
import org.matrix.android.sdk.internal.di.DeviceId
|
||||||
|
import org.matrix.android.sdk.internal.di.UserId
|
||||||
|
import org.matrix.android.sdk.internal.session.SessionScope
|
||||||
|
import org.matrix.android.sdk.internal.util.time.Clock
|
||||||
|
import timber.log.Timber
|
||||||
|
import java.util.concurrent.Executors
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
private val loggerTag = LoggerTag("RealmCryptoStore", LoggerTag.CRYPTO)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* In the transition phase, the rust SDK is still using parts to the realm crypto store,
|
||||||
|
* this should be removed after full migration
|
||||||
|
*/
|
||||||
|
@SessionScope
|
||||||
|
internal class RustCryptoStore @Inject constructor(
|
||||||
|
@CryptoDatabase private val realmConfiguration: RealmConfiguration,
|
||||||
|
private val clock: Clock,
|
||||||
|
@UserId private val userId: String,
|
||||||
|
@DeviceId private val deviceId: String,
|
||||||
|
private val myDeviceLastSeenInfoEntityMapper: MyDeviceLastSeenInfoEntityMapper,
|
||||||
|
private val olmMachine: dagger.Lazy<OlmMachine>,
|
||||||
|
private val matrixCoroutineDispatchers: MatrixCoroutineDispatchers,
|
||||||
|
) : IMXCommonCryptoStore {
|
||||||
|
|
||||||
|
// still needed on rust due to the global crypto settings
|
||||||
|
init {
|
||||||
|
// Ensure CryptoMetadataEntity is inserted in DB
|
||||||
|
doRealmTransaction("init", realmConfiguration) { realm ->
|
||||||
|
var currentMetadata = realm.where<CryptoMetadataEntity>().findFirst()
|
||||||
|
|
||||||
|
var deleteAll = false
|
||||||
|
|
||||||
|
if (currentMetadata != null) {
|
||||||
|
// Check credentials
|
||||||
|
// The device id may not have been provided in credentials.
|
||||||
|
// Check it only if provided, else trust the stored one.
|
||||||
|
if (currentMetadata.userId != userId || deviceId != currentMetadata.deviceId) {
|
||||||
|
Timber.w("## open() : Credentials do not match, close this store and delete data")
|
||||||
|
deleteAll = true
|
||||||
|
currentMetadata = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentMetadata == null) {
|
||||||
|
if (deleteAll) {
|
||||||
|
realm.deleteAll()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Metadata not found, or database cleaned, create it
|
||||||
|
realm.createObject(CryptoMetadataEntity::class.java, userId).apply {
|
||||||
|
deviceId = this@RustCryptoStore.deviceId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve a device by its identity key.
|
||||||
|
*
|
||||||
|
* @param identityKey the device identity key (`MXDeviceInfo.identityKey`)
|
||||||
|
* @return the device or null if not found
|
||||||
|
*/
|
||||||
|
override fun deviceWithIdentityKey(userId: String, identityKey: String): CryptoDeviceInfo? {
|
||||||
|
// XXX make this suspendable?
|
||||||
|
val knownDevices = runBlocking(matrixCoroutineDispatchers.io) {
|
||||||
|
olmMachine.get().getUserDevices(userId)
|
||||||
|
}
|
||||||
|
return knownDevices
|
||||||
|
.map { it.toCryptoDeviceInfo() }
|
||||||
|
.firstOrNull {
|
||||||
|
it.identityKey() == identityKey
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Needed for lazy migration of sessions from the legacy store
|
||||||
|
*/
|
||||||
|
override fun getInboundGroupSession(sessionId: String, senderKey: String): MXInboundMegolmSessionWrapper? {
|
||||||
|
val key = OlmInboundGroupSessionEntity.createPrimaryKey(sessionId, senderKey)
|
||||||
|
|
||||||
|
return doWithRealm(realmConfiguration) { realm ->
|
||||||
|
realm.where<OlmInboundGroupSessionEntity>()
|
||||||
|
.equalTo(OlmInboundGroupSessionEntityFields.PRIMARY_KEY, key)
|
||||||
|
.findFirst()
|
||||||
|
?.toModel()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ================================================
|
||||||
|
// Things that should be migrated to another store than realm
|
||||||
|
// ================================================
|
||||||
|
|
||||||
|
private val monarchyWriteAsyncExecutor = Executors.newSingleThreadExecutor()
|
||||||
|
|
||||||
|
private val monarchy = Monarchy.Builder()
|
||||||
|
.setRealmConfiguration(realmConfiguration)
|
||||||
|
.setWriteAsyncExecutor(monarchyWriteAsyncExecutor)
|
||||||
|
.build()
|
||||||
|
|
||||||
|
override fun open() {
|
||||||
|
// nop
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun tidyUpDataBase() {
|
||||||
|
// These entities are not used in rust actually, but as they are not yet cleaned up, this will do it with time
|
||||||
|
val prevWeekTs = clock.epochMillis() - 7 * 24 * 60 * 60 * 1_000
|
||||||
|
doRealmTransaction("tidyUpDataBase", realmConfiguration) { realm ->
|
||||||
|
|
||||||
|
// Clean the old ones?
|
||||||
|
realm.where<OutgoingKeyRequestEntity>()
|
||||||
|
.lessThan(OutgoingKeyRequestEntityFields.CREATION_TIME_STAMP, prevWeekTs)
|
||||||
|
.findAll()
|
||||||
|
.also { Timber.i("## Crypto Clean up ${it.size} OutgoingKeyRequestEntity") }
|
||||||
|
.deleteAllFromRealm()
|
||||||
|
|
||||||
|
// Only keep one month history
|
||||||
|
|
||||||
|
val prevMonthTs = clock.epochMillis() - 4 * 7 * 24 * 60 * 60 * 1_000L
|
||||||
|
realm.where<AuditTrailEntity>()
|
||||||
|
.lessThan(AuditTrailEntityFields.AGE_LOCAL_TS, prevMonthTs)
|
||||||
|
.findAll()
|
||||||
|
.also { Timber.i("## Crypto Clean up ${it.size} AuditTrailEntity") }
|
||||||
|
.deleteAllFromRealm()
|
||||||
|
|
||||||
|
// Can we do something for WithHeldSessionEntity?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun close() {
|
||||||
|
val tasks = monarchyWriteAsyncExecutor.shutdownNow()
|
||||||
|
Timber.w("Closing RealmCryptoStore, ${tasks.size} async task(s) cancelled")
|
||||||
|
tryOrNull("Interrupted") {
|
||||||
|
// Wait 1 minute max
|
||||||
|
monarchyWriteAsyncExecutor.awaitTermination(1, TimeUnit.MINUTES)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getRoomAlgorithm(roomId: String): String? {
|
||||||
|
return doWithRealm(realmConfiguration) {
|
||||||
|
CryptoRoomEntity.getById(it, roomId)?.algorithm
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getRoomCryptoInfo(roomId: String): CryptoRoomInfo? {
|
||||||
|
return doWithRealm(realmConfiguration) { realm ->
|
||||||
|
CryptoRoomEntity.getById(realm, roomId)?.let {
|
||||||
|
CryptoRoomInfoMapper.map(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a bit different than isRoomEncrypted.
|
||||||
|
* A room is encrypted when there is a m.room.encryption state event in the room (malformed/invalid or not).
|
||||||
|
* But the crypto layer has additional guaranty to ensure that encryption would never been reverted.
|
||||||
|
* It's defensive coding out of precaution (if ever state is reset).
|
||||||
|
*/
|
||||||
|
override fun roomWasOnceEncrypted(roomId: String): Boolean {
|
||||||
|
return doWithRealm(realmConfiguration) {
|
||||||
|
CryptoRoomEntity.getById(it, roomId)?.wasEncryptedOnce ?: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setAlgorithmInfo(roomId: String, encryption: EncryptionEventContent?) {
|
||||||
|
doRealmTransaction("setAlgorithmInfo", realmConfiguration) {
|
||||||
|
CryptoRoomEntity.getOrCreate(it, roomId).let { entity ->
|
||||||
|
entity.algorithm = encryption?.algorithm
|
||||||
|
// store anyway the new algorithm, but mark the room
|
||||||
|
// as having been encrypted once whatever, this can never
|
||||||
|
// go back to false
|
||||||
|
if (encryption?.algorithm == MXCRYPTO_ALGORITHM_MEGOLM) {
|
||||||
|
entity.wasEncryptedOnce = true
|
||||||
|
entity.rotationPeriodMs = encryption.rotationPeriodMs
|
||||||
|
entity.rotationPeriodMsgs = encryption.rotationPeriodMsgs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun saveMyDevicesInfo(info: List<DeviceInfo>) {
|
||||||
|
val entities = info.map { myDeviceLastSeenInfoEntityMapper.map(it) }
|
||||||
|
doRealmTransactionAsync(realmConfiguration) { realm ->
|
||||||
|
realm.where<MyDeviceLastSeenInfoEntity>().findAll().deleteAllFromRealm()
|
||||||
|
entities.forEach {
|
||||||
|
realm.insertOrUpdate(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getMyDevicesInfo(): List<DeviceInfo> {
|
||||||
|
return monarchy.fetchAllCopiedSync {
|
||||||
|
it.where<MyDeviceLastSeenInfoEntity>()
|
||||||
|
}.map {
|
||||||
|
DeviceInfo(
|
||||||
|
deviceId = it.deviceId,
|
||||||
|
lastSeenIp = it.lastSeenIp,
|
||||||
|
lastSeenTs = it.lastSeenTs,
|
||||||
|
displayName = it.displayName
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getLiveMyDevicesInfo(): LiveData<List<DeviceInfo>> {
|
||||||
|
return monarchy.findAllMappedWithChanges(
|
||||||
|
{ realm: Realm ->
|
||||||
|
realm.where<MyDeviceLastSeenInfoEntity>()
|
||||||
|
},
|
||||||
|
{ entity -> myDeviceLastSeenInfoEntityMapper.map(entity) }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getLiveMyDevicesInfo(deviceId: String): LiveData<Optional<DeviceInfo>> {
|
||||||
|
val liveData = monarchy.findAllMappedWithChanges(
|
||||||
|
{ realm: Realm ->
|
||||||
|
realm.where<MyDeviceLastSeenInfoEntity>()
|
||||||
|
.equalTo(MyDeviceLastSeenInfoEntityFields.DEVICE_ID, deviceId)
|
||||||
|
},
|
||||||
|
{ entity -> myDeviceLastSeenInfoEntityMapper.map(entity) }
|
||||||
|
)
|
||||||
|
|
||||||
|
return Transformations.map(liveData) {
|
||||||
|
it.firstOrNull().toOptional()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun storeData(cryptoStoreAggregator: CryptoStoreAggregator) {
|
||||||
|
if (cryptoStoreAggregator.isEmpty()) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
doRealmTransaction("storeData - CryptoStoreAggregator", realmConfiguration) { realm ->
|
||||||
|
// setShouldShareHistory
|
||||||
|
cryptoStoreAggregator.setShouldShareHistoryData.forEach {
|
||||||
|
Timber.tag(loggerTag.value)
|
||||||
|
.v("setShouldShareHistory for room ${it.key} is ${it.value}")
|
||||||
|
CryptoRoomEntity.getOrCreate(realm, it.key).shouldShareHistory = it.value
|
||||||
|
}
|
||||||
|
// setShouldEncryptForInvitedMembers
|
||||||
|
cryptoStoreAggregator.setShouldEncryptForInvitedMembersData.forEach {
|
||||||
|
CryptoRoomEntity.getOrCreate(realm, it.key).shouldEncryptForInvitedMembers = it.value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun shouldEncryptForInvitedMembers(roomId: String): Boolean {
|
||||||
|
return doWithRealm(realmConfiguration) {
|
||||||
|
CryptoRoomEntity.getById(it, roomId)?.shouldEncryptForInvitedMembers
|
||||||
|
}
|
||||||
|
?: false
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setShouldShareHistory(roomId: String, shouldShareHistory: Boolean) {
|
||||||
|
Timber.tag(loggerTag.value)
|
||||||
|
.v("setShouldShareHistory for room $roomId is $shouldShareHistory")
|
||||||
|
doRealmTransaction("setShouldShareHistory", realmConfiguration) {
|
||||||
|
CryptoRoomEntity.getOrCreate(it, roomId).shouldShareHistory = shouldShareHistory
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setShouldEncryptForInvitedMembers(roomId: String, shouldEncryptForInvitedMembers: Boolean) {
|
||||||
|
doRealmTransaction("setShouldEncryptForInvitedMembers", realmConfiguration) {
|
||||||
|
CryptoRoomEntity.getOrCreate(it, roomId).shouldEncryptForInvitedMembers = shouldEncryptForInvitedMembers
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun blockUnverifiedDevicesInRoom(roomId: String, block: Boolean) {
|
||||||
|
doRealmTransaction("blockUnverifiedDevicesInRoom", realmConfiguration) { realm ->
|
||||||
|
CryptoRoomEntity.getById(realm, roomId)
|
||||||
|
?.blacklistUnverifiedDevices = block
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setGlobalBlacklistUnverifiedDevices(block: Boolean) {
|
||||||
|
doRealmTransaction("setGlobalBlacklistUnverifiedDevices", realmConfiguration) {
|
||||||
|
it.where<CryptoMetadataEntity>().findFirst()?.globalBlacklistUnverifiedDevices = block
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getLiveGlobalCryptoConfig(): LiveData<GlobalCryptoConfig> {
|
||||||
|
val liveData = monarchy.findAllMappedWithChanges(
|
||||||
|
{ realm: Realm ->
|
||||||
|
realm
|
||||||
|
.where<CryptoMetadataEntity>()
|
||||||
|
},
|
||||||
|
{
|
||||||
|
GlobalCryptoConfig(
|
||||||
|
globalBlockUnverifiedDevices = it.globalBlacklistUnverifiedDevices,
|
||||||
|
globalEnableKeyGossiping = it.globalEnableKeyGossiping,
|
||||||
|
enableKeyForwardingOnInvite = it.enableKeyForwardingOnInvite
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
return Transformations.map(liveData) {
|
||||||
|
it.firstOrNull() ?: GlobalCryptoConfig(false, false, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getGlobalBlacklistUnverifiedDevices(): Boolean {
|
||||||
|
return doWithRealm(realmConfiguration) {
|
||||||
|
it.where<CryptoMetadataEntity>().findFirst()?.globalBlacklistUnverifiedDevices
|
||||||
|
} ?: false
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getLiveBlockUnverifiedDevices(roomId: String): LiveData<Boolean> {
|
||||||
|
val liveData = monarchy.findAllMappedWithChanges(
|
||||||
|
{ realm: Realm ->
|
||||||
|
realm.where<CryptoRoomEntity>()
|
||||||
|
.equalTo(CryptoRoomEntityFields.ROOM_ID, roomId)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
it.blacklistUnverifiedDevices
|
||||||
|
}
|
||||||
|
)
|
||||||
|
return Transformations.map(liveData) {
|
||||||
|
it.firstOrNull() ?: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getBlockUnverifiedDevices(roomId: String): Boolean {
|
||||||
|
return doWithRealm(realmConfiguration) { realm ->
|
||||||
|
realm.where<CryptoRoomEntity>()
|
||||||
|
.equalTo(CryptoRoomEntityFields.ROOM_ID, roomId)
|
||||||
|
.findFirst()
|
||||||
|
?.blacklistUnverifiedDevices ?: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -71,6 +71,10 @@ fun RealmToMigrate.getPickledAccount(pickleKey: ByteArray): MigrationData {
|
||||||
val userKey = metadataEntity.xSignUserPrivateKey
|
val userKey = metadataEntity.xSignUserPrivateKey
|
||||||
val selfSignedKey = metadataEntity.xSignSelfSignedPrivateKey
|
val selfSignedKey = metadataEntity.xSignSelfSignedPrivateKey
|
||||||
|
|
||||||
|
Timber.i("## Migration: has private MSK ${masterKey.isNullOrBlank().not()}")
|
||||||
|
Timber.i("## Migration: has private USK ${userKey.isNullOrBlank().not()}")
|
||||||
|
Timber.i("## Migration: has private SSK ${selfSignedKey.isNullOrBlank().not()}")
|
||||||
|
|
||||||
val userId = metadataEntity.userId
|
val userId = metadataEntity.userId
|
||||||
?: throw java.lang.IllegalArgumentException("Rust db migration: userId is null")
|
?: throw java.lang.IllegalArgumentException("Rust db migration: userId is null")
|
||||||
val deviceId = metadataEntity.deviceId
|
val deviceId = metadataEntity.deviceId
|
||||||
|
@ -79,6 +83,8 @@ fun RealmToMigrate.getPickledAccount(pickleKey: ByteArray): MigrationData {
|
||||||
val backupVersion = metadataEntity.backupVersion
|
val backupVersion = metadataEntity.backupVersion
|
||||||
val backupRecoveryKey = metadataEntity.keyBackupRecoveryKey
|
val backupRecoveryKey = metadataEntity.keyBackupRecoveryKey
|
||||||
|
|
||||||
|
Timber.i("## Migration: has private backup key ${backupRecoveryKey != null} for version $backupVersion")
|
||||||
|
|
||||||
val isOlmAccountShared = metadataEntity.deviceKeysSentToServer
|
val isOlmAccountShared = metadataEntity.deviceKeysSentToServer
|
||||||
|
|
||||||
val olmAccount = metadataEntity.getOlmAccount()
|
val olmAccount = metadataEntity.getOlmAccount()
|
||||||
|
|
|
@ -36,6 +36,7 @@ import org.matrix.android.sdk.internal.crypto.model.rest.VERIFICATION_METHOD_QR_
|
||||||
import org.matrix.android.sdk.internal.crypto.model.rest.VERIFICATION_METHOD_RECIPROCATE
|
import org.matrix.android.sdk.internal.crypto.model.rest.VERIFICATION_METHOD_RECIPROCATE
|
||||||
import org.matrix.android.sdk.internal.crypto.model.rest.toValue
|
import org.matrix.android.sdk.internal.crypto.model.rest.toValue
|
||||||
import org.matrix.android.sdk.internal.session.SessionScope
|
import org.matrix.android.sdk.internal.session.SessionScope
|
||||||
|
import org.matrix.rustcomponents.sdk.crypto.VerificationRequestState
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@ -165,7 +166,7 @@ internal class RustVerificationService @Inject constructor(
|
||||||
// If this is a SAS verification originating from a `m.key.verification.request`
|
// If this is a SAS verification originating from a `m.key.verification.request`
|
||||||
// event, we auto-accept here considering that we either initiated the request or
|
// event, we auto-accept here considering that we either initiated the request or
|
||||||
// accepted the request. If it's a QR code verification, just dispatch an update.
|
// accepted the request. If it's a QR code verification, just dispatch an update.
|
||||||
if (request.isReady() && transaction is SasVerification) {
|
if (request.innerState() is VerificationRequestState.Ready && transaction is SasVerification) {
|
||||||
// accept() will dispatch an update, no need to do it twice.
|
// accept() will dispatch an update, no need to do it twice.
|
||||||
Timber.d("## Verification: Auto accepting SAS verification with $sender")
|
Timber.d("## Verification: Auto accepting SAS verification with $sender")
|
||||||
transaction.accept()
|
transaction.accept()
|
||||||
|
@ -308,7 +309,7 @@ internal class RustVerificationService @Inject constructor(
|
||||||
return if (request != null) {
|
return if (request != null) {
|
||||||
request.acceptWithMethods(methods)
|
request.acceptWithMethods(methods)
|
||||||
request.startQrCode()
|
request.startQrCode()
|
||||||
request.isReady()
|
request.innerState() is VerificationRequestState.Ready
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
|
@ -136,9 +136,9 @@ internal class VerificationRequest @AssistedInject constructor(
|
||||||
* concrete verification flow, i.e. we can show/scan a QR code or start emoji
|
* concrete verification flow, i.e. we can show/scan a QR code or start emoji
|
||||||
* verification.
|
* verification.
|
||||||
*/
|
*/
|
||||||
internal fun isReady(): Boolean {
|
// internal fun isReady(): Boolean {
|
||||||
return innerVerificationRequest.isReady()
|
// return innerVerificationRequest.isReady()
|
||||||
}
|
// }
|
||||||
|
|
||||||
/** Did we advertise that we're able to scan QR codes */
|
/** Did we advertise that we're able to scan QR codes */
|
||||||
internal fun canScanQrCodes(): Boolean {
|
internal fun canScanQrCodes(): Boolean {
|
||||||
|
|
|
@ -24,7 +24,7 @@ import org.matrix.android.sdk.api.session.crypto.model.CryptoDeviceInfo
|
||||||
import org.matrix.android.sdk.api.session.crypto.model.OlmDecryptionResult
|
import org.matrix.android.sdk.api.session.crypto.model.OlmDecryptionResult
|
||||||
import org.matrix.android.sdk.api.session.events.model.Event
|
import org.matrix.android.sdk.api.session.events.model.Event
|
||||||
import org.matrix.android.sdk.api.session.events.model.EventType
|
import org.matrix.android.sdk.api.session.events.model.EventType
|
||||||
import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore
|
import org.matrix.android.sdk.internal.crypto.store.IMXCommonCryptoStore
|
||||||
|
|
||||||
class EventEditValidatorTest {
|
class EventEditValidatorTest {
|
||||||
|
|
||||||
|
@ -62,7 +62,7 @@ class EventEditValidatorTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `edit should be valid`() {
|
fun `edit should be valid`() {
|
||||||
val mockCryptoStore = mockk<IMXCryptoStore>()
|
val mockCryptoStore = mockk<IMXCommonCryptoStore>()
|
||||||
val validator = EventEditValidator(mockCryptoStore)
|
val validator = EventEditValidator(mockCryptoStore)
|
||||||
|
|
||||||
validator
|
validator
|
||||||
|
@ -71,7 +71,7 @@ class EventEditValidatorTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `original event and replacement event must have the same sender`() {
|
fun `original event and replacement event must have the same sender`() {
|
||||||
val mockCryptoStore = mockk<IMXCryptoStore>()
|
val mockCryptoStore = mockk<IMXCommonCryptoStore>()
|
||||||
val validator = EventEditValidator(mockCryptoStore)
|
val validator = EventEditValidator(mockCryptoStore)
|
||||||
|
|
||||||
validator
|
validator
|
||||||
|
@ -83,7 +83,7 @@ class EventEditValidatorTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `original event and replacement event must have the same room_id`() {
|
fun `original event and replacement event must have the same room_id`() {
|
||||||
val mockCryptoStore = mockk<IMXCryptoStore>()
|
val mockCryptoStore = mockk<IMXCommonCryptoStore>()
|
||||||
val validator = EventEditValidator(mockCryptoStore)
|
val validator = EventEditValidator(mockCryptoStore)
|
||||||
|
|
||||||
validator
|
validator
|
||||||
|
@ -101,7 +101,7 @@ class EventEditValidatorTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `replacement and original events must not have a state_key property`() {
|
fun `replacement and original events must not have a state_key property`() {
|
||||||
val mockCryptoStore = mockk<IMXCryptoStore>()
|
val mockCryptoStore = mockk<IMXCommonCryptoStore>()
|
||||||
val validator = EventEditValidator(mockCryptoStore)
|
val validator = EventEditValidator(mockCryptoStore)
|
||||||
|
|
||||||
validator
|
validator
|
||||||
|
@ -119,8 +119,8 @@ class EventEditValidatorTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `replacement event must have an new_content property`() {
|
fun `replacement event must have an new_content property`() {
|
||||||
val mockCryptoStore = mockk<IMXCryptoStore> {
|
val mockCryptoStore = mockk<IMXCommonCryptoStore> {
|
||||||
every { deviceWithIdentityKey("R0s/7Aindgg/RNWqUGJyJOXtCz5H7Gx7fInFuroq1xo") } returns
|
every { deviceWithIdentityKey("@alice:example.com", "R0s/7Aindgg/RNWqUGJyJOXtCz5H7Gx7fInFuroq1xo") } returns
|
||||||
mockk<CryptoDeviceInfo> {
|
mockk<CryptoDeviceInfo> {
|
||||||
every { userId } returns "@alice:example.com"
|
every { userId } returns "@alice:example.com"
|
||||||
}
|
}
|
||||||
|
@ -157,8 +157,8 @@ class EventEditValidatorTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `The original event must not itself have a rel_type of m_replace`() {
|
fun `The original event must not itself have a rel_type of m_replace`() {
|
||||||
val mockCryptoStore = mockk<IMXCryptoStore> {
|
val mockCryptoStore = mockk<IMXCommonCryptoStore> {
|
||||||
every { deviceWithIdentityKey("R0s/7Aindgg/RNWqUGJyJOXtCz5H7Gx7fInFuroq1xo") } returns
|
every { deviceWithIdentityKey("@alice:example.com", "R0s/7Aindgg/RNWqUGJyJOXtCz5H7Gx7fInFuroq1xo") } returns
|
||||||
mockk<CryptoDeviceInfo> {
|
mockk<CryptoDeviceInfo> {
|
||||||
every { userId } returns "@alice:example.com"
|
every { userId } returns "@alice:example.com"
|
||||||
}
|
}
|
||||||
|
@ -207,8 +207,8 @@ class EventEditValidatorTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `valid e2ee edit`() {
|
fun `valid e2ee edit`() {
|
||||||
val mockCryptoStore = mockk<IMXCryptoStore> {
|
val mockCryptoStore = mockk<IMXCommonCryptoStore> {
|
||||||
every { deviceWithIdentityKey("R0s/7Aindgg/RNWqUGJyJOXtCz5H7Gx7fInFuroq1xo") } returns
|
every { deviceWithIdentityKey("@alice:example.com", "R0s/7Aindgg/RNWqUGJyJOXtCz5H7Gx7fInFuroq1xo") } returns
|
||||||
mockk<CryptoDeviceInfo> {
|
mockk<CryptoDeviceInfo> {
|
||||||
every { userId } returns "@alice:example.com"
|
every { userId } returns "@alice:example.com"
|
||||||
}
|
}
|
||||||
|
@ -224,8 +224,8 @@ class EventEditValidatorTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `If the original event was encrypted, the replacement should be too`() {
|
fun `If the original event was encrypted, the replacement should be too`() {
|
||||||
val mockCryptoStore = mockk<IMXCryptoStore> {
|
val mockCryptoStore = mockk<IMXCommonCryptoStore> {
|
||||||
every { deviceWithIdentityKey("R0s/7Aindgg/RNWqUGJyJOXtCz5H7Gx7fInFuroq1xo") } returns
|
every { deviceWithIdentityKey("@alice:example.com", "R0s/7Aindgg/RNWqUGJyJOXtCz5H7Gx7fInFuroq1xo") } returns
|
||||||
mockk<CryptoDeviceInfo> {
|
mockk<CryptoDeviceInfo> {
|
||||||
every { userId } returns "@alice:example.com"
|
every { userId } returns "@alice:example.com"
|
||||||
}
|
}
|
||||||
|
@ -241,12 +241,12 @@ class EventEditValidatorTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `encrypted, original event and replacement event must have the same sender`() {
|
fun `encrypted, original event and replacement event must have the same sender`() {
|
||||||
val mockCryptoStore = mockk<IMXCryptoStore> {
|
val mockCryptoStore = mockk<IMXCommonCryptoStore> {
|
||||||
every { deviceWithIdentityKey("R0s/7Aindgg/RNWqUGJyJOXtCz5H7Gx7fInFuroq1xo") } returns
|
every { deviceWithIdentityKey("@alice:example.com", "R0s/7Aindgg/RNWqUGJyJOXtCz5H7Gx7fInFuroq1xo") } returns
|
||||||
mockk {
|
mockk {
|
||||||
every { userId } returns "@alice:example.com"
|
every { userId } returns "@alice:example.com"
|
||||||
}
|
}
|
||||||
every { deviceWithIdentityKey("7V5e/2O93mf4GeW7Mtq4YWcRNpYS9NhQbdJMgdnIPUI") } returns
|
every { deviceWithIdentityKey("@bob:example.com", "7V5e/2O93mf4GeW7Mtq4YWcRNpYS9NhQbdJMgdnIPUI") } returns
|
||||||
mockk {
|
mockk {
|
||||||
every { userId } returns "@bob:example.com"
|
every { userId } returns "@bob:example.com"
|
||||||
}
|
}
|
||||||
|
@ -256,7 +256,9 @@ class EventEditValidatorTest {
|
||||||
validator
|
validator
|
||||||
.validateEdit(
|
.validateEdit(
|
||||||
encryptedEvent,
|
encryptedEvent,
|
||||||
encryptedEditEvent.copy().apply {
|
encryptedEditEvent.copy(
|
||||||
|
senderId = "@bob:example.com"
|
||||||
|
).apply {
|
||||||
mxDecryptionResult = encryptedEditEvent.mxDecryptionResult!!.copy(
|
mxDecryptionResult = encryptedEditEvent.mxDecryptionResult!!.copy(
|
||||||
senderKey = "7V5e/2O93mf4GeW7Mtq4YWcRNpYS9NhQbdJMgdnIPUI"
|
senderKey = "7V5e/2O93mf4GeW7Mtq4YWcRNpYS9NhQbdJMgdnIPUI"
|
||||||
)
|
)
|
||||||
|
@ -269,12 +271,12 @@ class EventEditValidatorTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `encrypted, sent fom a deleted device, original event and replacement event must have the same sender`() {
|
fun `encrypted, sent fom a deleted device, original event and replacement event must have the same sender`() {
|
||||||
val mockCryptoStore = mockk<IMXCryptoStore> {
|
val mockCryptoStore = mockk<IMXCommonCryptoStore> {
|
||||||
every { deviceWithIdentityKey("R0s/7Aindgg/RNWqUGJyJOXtCz5H7Gx7fInFuroq1xo") } returns
|
every { deviceWithIdentityKey("@alice:example.com", "R0s/7Aindgg/RNWqUGJyJOXtCz5H7Gx7fInFuroq1xo") } returns
|
||||||
mockk {
|
mockk {
|
||||||
every { userId } returns "@alice:example.com"
|
every { userId } returns "@alice:example.com"
|
||||||
}
|
}
|
||||||
every { deviceWithIdentityKey("7V5e/2O93mf4GeW7Mtq4YWcRNpYS9NhQbdJMgdnIPUI") } returns
|
every { deviceWithIdentityKey(any(), "7V5e/2O93mf4GeW7Mtq4YWcRNpYS9NhQbdJMgdnIPUI") } returns
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
val validator = EventEditValidator(mockCryptoStore)
|
val validator = EventEditValidator(mockCryptoStore)
|
||||||
|
@ -288,7 +290,7 @@ class EventEditValidatorTest {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
) shouldBeInstanceOf EventEditValidator.EditValidity.Valid::class
|
) shouldBeInstanceOf EventEditValidator.EditValidity.Unknown::class
|
||||||
|
|
||||||
validator
|
validator
|
||||||
.validateEdit(
|
.validateEdit(
|
||||||
|
|
|
@ -160,11 +160,11 @@ adb -e uninstall im.vector.app.debug.test
|
||||||
|
|
||||||
printf "\n================================================================================\n"
|
printf "\n================================================================================\n"
|
||||||
printf "Running the integration test UiAllScreensSanityTest.allScreensTest()...\n"
|
printf "Running the integration test UiAllScreensSanityTest.allScreensTest()...\n"
|
||||||
./gradlew connectedGplayDebugAndroidTest -Pandroid.testInstrumentationRunnerArguments.class=im.vector.app.ui.UiAllScreensSanityTest
|
./gradlew connectedGplayRustCryptoDebugAndroidTest -Pandroid.testInstrumentationRunnerArguments.class=im.vector.app.ui.UiAllScreensSanityTest
|
||||||
|
|
||||||
printf "\n================================================================================\n"
|
printf "\n================================================================================\n"
|
||||||
printf "Building the app...\n"
|
printf "Building the app...\n"
|
||||||
./gradlew assembleGplayDebug
|
./gradlew assembleGplayRustCryptoDebug
|
||||||
|
|
||||||
printf "\n================================================================================\n"
|
printf "\n================================================================================\n"
|
||||||
printf "Uninstalling previous debug app if any...\n"
|
printf "Uninstalling previous debug app if any...\n"
|
||||||
|
@ -172,7 +172,7 @@ adb -e uninstall im.vector.app.debug
|
||||||
|
|
||||||
printf "\n================================================================================\n"
|
printf "\n================================================================================\n"
|
||||||
printf "Installing the app...\n"
|
printf "Installing the app...\n"
|
||||||
adb -e install ./vector-app/build/outputs/apk/gplay/debug/vector-gplay-arm64-v8a-debug.apk
|
adb -e install ./vector-app/build/outputs/apk/gplayRustCrypto/debug/vector-gplay-rustCrypto-arm64-v8a-debug.apk
|
||||||
|
|
||||||
printf "\n================================================================================\n"
|
printf "\n================================================================================\n"
|
||||||
printf "Running the app...\n"
|
printf "Running the app...\n"
|
||||||
|
@ -293,67 +293,67 @@ printf "Unzipping the artifact...\n"
|
||||||
unzip ${targetPath}/vector-gplay-release-unsigned.zip -d ${targetPath}
|
unzip ${targetPath}/vector-gplay-release-unsigned.zip -d ${targetPath}
|
||||||
|
|
||||||
# Flatten folder hierarchy
|
# Flatten folder hierarchy
|
||||||
mv ${targetPath}/gplay/release/* ${targetPath}
|
mv ${targetPath}/gplayRustCrypto/release/* ${targetPath}
|
||||||
rm -rf ${targetPath}/gplay
|
rm -rf ${targetPath}/gplay
|
||||||
|
|
||||||
printf "\n================================================================================\n"
|
printf "\n================================================================================\n"
|
||||||
printf "Signing the APKs...\n"
|
printf "Signing the APKs...\n"
|
||||||
|
|
||||||
cp ${targetPath}/vector-gplay-arm64-v8a-release-unsigned.apk \
|
cp ${targetPath}/vector-gplay-rustCrypto-arm64-v8a-release-unsigned.apk \
|
||||||
${targetPath}/vector-gplay-arm64-v8a-release-signed.apk
|
${targetPath}/vector-gplay-rustCrypto-arm64-v8a-release-signed.apk
|
||||||
./tools/release/sign_apk_unsafe.sh \
|
./tools/release/sign_apk_unsafe.sh \
|
||||||
${keyStorePath} \
|
${keyStorePath} \
|
||||||
${targetPath}/vector-gplay-arm64-v8a-release-signed.apk \
|
${targetPath}/vector-gplay-rustCrypto-arm64-v8a-release-signed.apk \
|
||||||
${keyStorePassword} \
|
${keyStorePassword} \
|
||||||
${keyPassword}
|
${keyPassword}
|
||||||
|
|
||||||
cp ${targetPath}/vector-gplay-armeabi-v7a-release-unsigned.apk \
|
cp ${targetPath}/vector-gplay-rustCrypto-armeabi-v7a-release-unsigned.apk \
|
||||||
${targetPath}/vector-gplay-armeabi-v7a-release-signed.apk
|
${targetPath}/vector-gplay-rustCrypto-armeabi-v7a-release-signed.apk
|
||||||
./tools/release/sign_apk_unsafe.sh \
|
./tools/release/sign_apk_unsafe.sh \
|
||||||
${keyStorePath} \
|
${keyStorePath} \
|
||||||
${targetPath}/vector-gplay-armeabi-v7a-release-signed.apk \
|
${targetPath}/vector-gplay-rustCrypto-armeabi-v7a-release-signed.apk \
|
||||||
${keyStorePassword} \
|
${keyStorePassword} \
|
||||||
${keyPassword}
|
${keyPassword}
|
||||||
|
|
||||||
cp ${targetPath}/vector-gplay-x86-release-unsigned.apk \
|
cp ${targetPath}/vector-gplay-rustCrypto-x86-release-unsigned.apk \
|
||||||
${targetPath}/vector-gplay-x86-release-signed.apk
|
${targetPath}/vector-gplay-rustCrypto-x86-release-signed.apk
|
||||||
./tools/release/sign_apk_unsafe.sh \
|
./tools/release/sign_apk_unsafe.sh \
|
||||||
${keyStorePath} \
|
${keyStorePath} \
|
||||||
${targetPath}/vector-gplay-x86-release-signed.apk \
|
${targetPath}/vector-gplay-rustCrypto-x86-release-signed.apk \
|
||||||
${keyStorePassword} \
|
${keyStorePassword} \
|
||||||
${keyPassword}
|
${keyPassword}
|
||||||
|
|
||||||
cp ${targetPath}/vector-gplay-x86_64-release-unsigned.apk \
|
cp ${targetPath}/vector-gplay-rustCrypto-x86_64-release-unsigned.apk \
|
||||||
${targetPath}/vector-gplay-x86_64-release-signed.apk
|
${targetPath}/vector-gplay-rustCrypto-x86_64-release-signed.apk
|
||||||
./tools/release/sign_apk_unsafe.sh \
|
./tools/release/sign_apk_unsafe.sh \
|
||||||
${keyStorePath} \
|
${keyStorePath} \
|
||||||
${targetPath}/vector-gplay-x86_64-release-signed.apk \
|
${targetPath}/vector-gplay-rustCrypto-x86_64-release-signed.apk \
|
||||||
${keyStorePassword} \
|
${keyStorePassword} \
|
||||||
${keyPassword}
|
${keyPassword}
|
||||||
|
|
||||||
# Ref: https://docs.fastlane.tools/getting-started/android/beta-deployment/#uploading-your-app
|
# Ref: https://docs.fastlane.tools/getting-started/android/beta-deployment/#uploading-your-app
|
||||||
# set SUPPLY_APK_PATHS="${targetPath}/vector-gplay-arm64-v8a-release-unsigned.apk,${targetPath}/vector-gplay-armeabi-v7a-release-unsigned.apk,${targetPath}/vector-gplay-x86-release-unsigned.apk,${targetPath}/vector-gplay-x86_64-release-unsigned.apk"
|
# set SUPPLY_APK_PATHS="${targetPath}/vector-gplay-rustCrypto-arm64-v8a-release-unsigned.apk,${targetPath}/vector-gplay-rustCrypto-armeabi-v7a-release-unsigned.apk,${targetPath}/vector-gplay-rustCrypto-x86-release-unsigned.apk,${targetPath}/vector-gplay-rustCrypto-x86_64-release-unsigned.apk"
|
||||||
#
|
#
|
||||||
# ./fastlane beta
|
# ./fastlane beta
|
||||||
|
|
||||||
printf "\n================================================================================\n"
|
printf "\n================================================================================\n"
|
||||||
printf "Please check the information below:\n"
|
printf "Please check the information below:\n"
|
||||||
|
|
||||||
printf "File vector-gplay-arm64-v8a-release-signed.apk:\n"
|
printf "File vector-gplay-rustCrypto-arm64-v8a-release-signed.apk:\n"
|
||||||
${buildToolsPath}/aapt dump badging ${targetPath}/vector-gplay-arm64-v8a-release-signed.apk | grep package
|
${buildToolsPath}/aapt dump badging ${targetPath}/vector-gplay-rustCrypto-arm64-v8a-release-signed.apk | grep package
|
||||||
printf "File vector-gplay-armeabi-v7a-release-signed.apk:\n"
|
printf "File vector-gplay-rustCrypto-armeabi-v7a-release-signed.apk:\n"
|
||||||
${buildToolsPath}/aapt dump badging ${targetPath}/vector-gplay-armeabi-v7a-release-signed.apk | grep package
|
${buildToolsPath}/aapt dump badging ${targetPath}/vector-gplay-rustCrypto-armeabi-v7a-release-signed.apk | grep package
|
||||||
printf "File vector-gplay-x86-release-signed.apk:\n"
|
printf "File vector-gplay-rustCrypto-x86-release-signed.apk:\n"
|
||||||
${buildToolsPath}/aapt dump badging ${targetPath}/vector-gplay-x86-release-signed.apk | grep package
|
${buildToolsPath}/aapt dump badging ${targetPath}/vector-gplay-rustCrypto-x86-release-signed.apk | grep package
|
||||||
printf "File vector-gplay-x86_64-release-signed.apk:\n"
|
printf "File vector-gplay-rustCrypto-x86_64-release-signed.apk:\n"
|
||||||
${buildToolsPath}/aapt dump badging ${targetPath}/vector-gplay-x86_64-release-signed.apk | grep package
|
${buildToolsPath}/aapt dump badging ${targetPath}/vector-gplay-rustCrypto-x86_64-release-signed.apk | grep package
|
||||||
|
|
||||||
printf "\n"
|
printf "\n"
|
||||||
read -p "Does it look correct? Press enter when it's done."
|
read -p "Does it look correct? Press enter when it's done."
|
||||||
|
|
||||||
printf "\n================================================================================\n"
|
printf "\n================================================================================\n"
|
||||||
read -p "Installing apk on a real device, press enter when a real device is connected. "
|
read -p "Installing apk on a real device, press enter when a real device is connected. "
|
||||||
apkPath="${targetPath}/vector-gplay-arm64-v8a-release-signed.apk"
|
apkPath="${targetPath}/vector-gplay-rustCrypto-arm64-v8a-release-signed.apk"
|
||||||
adb -d install ${apkPath}
|
adb -d install ${apkPath}
|
||||||
|
|
||||||
read -p "Please run the APK on your phone to check that the upgrade went well (no init sync, etc.). Press enter when it's done."
|
read -p "Please run the APK on your phone to check that the upgrade went well (no init sync, etc.). Press enter when it's done."
|
||||||
|
|
|
@ -33,11 +33,11 @@ knit {
|
||||||
|
|
||||||
// Note: 2 digits max for each value
|
// Note: 2 digits max for each value
|
||||||
ext.versionMajor = 1
|
ext.versionMajor = 1
|
||||||
ext.versionMinor = 5
|
ext.versionMinor = 6
|
||||||
// Note: even values are reserved for regular release, odd values for hotfix release.
|
// Note: even values are reserved for regular release, odd values for hotfix release.
|
||||||
// When creating a hotfix, you should decrease the value, since the current value
|
// When creating a hotfix, you should decrease the value, since the current value
|
||||||
// is the value for the next regular release.
|
// is the value for the next regular release.
|
||||||
ext.versionPatch = 34
|
ext.versionPatch = 2
|
||||||
|
|
||||||
static def getGitTimestamp() {
|
static def getGitTimestamp() {
|
||||||
def cmd = 'git show -s --format=%ct'
|
def cmd = 'git show -s --format=%ct'
|
||||||
|
|
|
@ -17,6 +17,7 @@ package im.vector.app.gplay.features.settings.troubleshoot
|
||||||
|
|
||||||
import androidx.fragment.app.FragmentActivity
|
import androidx.fragment.app.FragmentActivity
|
||||||
import androidx.lifecycle.Observer
|
import androidx.lifecycle.Observer
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.work.WorkInfo
|
import androidx.work.WorkInfo
|
||||||
import androidx.work.WorkManager
|
import androidx.work.WorkManager
|
||||||
import im.vector.app.R
|
import im.vector.app.R
|
||||||
|
@ -25,6 +26,8 @@ import im.vector.app.core.pushers.FcmHelper
|
||||||
import im.vector.app.core.pushers.PushersManager
|
import im.vector.app.core.pushers.PushersManager
|
||||||
import im.vector.app.core.resources.StringProvider
|
import im.vector.app.core.resources.StringProvider
|
||||||
import im.vector.app.features.settings.troubleshoot.TroubleshootTest
|
import im.vector.app.features.settings.troubleshoot.TroubleshootTest
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import org.matrix.android.sdk.api.session.pushers.PusherState
|
import org.matrix.android.sdk.api.session.pushers.PusherState
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@ -60,16 +63,18 @@ class TestTokenRegistration @Inject constructor(
|
||||||
)
|
)
|
||||||
quickFix = object : TroubleshootQuickFix(R.string.settings_troubleshoot_test_token_registration_quick_fix) {
|
quickFix = object : TroubleshootQuickFix(R.string.settings_troubleshoot_test_token_registration_quick_fix) {
|
||||||
override fun doFix() {
|
override fun doFix() {
|
||||||
val workId = pushersManager.enqueueRegisterPusherWithFcmKey(fcmToken)
|
context.lifecycleScope.launch(Dispatchers.IO) {
|
||||||
WorkManager.getInstance(context).getWorkInfoByIdLiveData(workId).observe(context, Observer { workInfo ->
|
val workId = pushersManager.enqueueRegisterPusherWithFcmKey(fcmToken)
|
||||||
if (workInfo != null) {
|
WorkManager.getInstance(context).getWorkInfoByIdLiveData(workId).observe(context, Observer { workInfo ->
|
||||||
if (workInfo.state == WorkInfo.State.SUCCEEDED) {
|
if (workInfo != null) {
|
||||||
manager?.retry(testParameters)
|
if (workInfo.state == WorkInfo.State.SUCCEEDED) {
|
||||||
} else if (workInfo.state == WorkInfo.State.FAILED) {
|
manager?.retry(testParameters)
|
||||||
manager?.retry(testParameters)
|
} else if (workInfo.state == WorkInfo.State.FAILED) {
|
||||||
|
manager?.retry(testParameters)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,8 +26,11 @@ import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
import im.vector.app.R
|
import im.vector.app.R
|
||||||
import im.vector.app.core.di.ActiveSessionHolder
|
import im.vector.app.core.di.ActiveSessionHolder
|
||||||
import im.vector.app.core.di.DefaultPreferences
|
import im.vector.app.core.di.DefaultPreferences
|
||||||
|
import im.vector.app.core.dispatchers.CoroutineDispatchers
|
||||||
import im.vector.app.core.pushers.FcmHelper
|
import im.vector.app.core.pushers.FcmHelper
|
||||||
import im.vector.app.core.pushers.PushersManager
|
import im.vector.app.core.pushers.PushersManager
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@ -38,7 +41,12 @@ import javax.inject.Inject
|
||||||
class GoogleFcmHelper @Inject constructor(
|
class GoogleFcmHelper @Inject constructor(
|
||||||
@ApplicationContext private val context: Context,
|
@ApplicationContext private val context: Context,
|
||||||
@DefaultPreferences private val sharedPrefs: SharedPreferences,
|
@DefaultPreferences private val sharedPrefs: SharedPreferences,
|
||||||
|
appScope: CoroutineScope,
|
||||||
|
private val coroutineDispatchers: CoroutineDispatchers
|
||||||
) : FcmHelper {
|
) : FcmHelper {
|
||||||
|
|
||||||
|
private val scope = CoroutineScope(appScope.coroutineContext + coroutineDispatchers.io)
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val PREFS_KEY_FCM_TOKEN = "FCM_TOKEN"
|
private const val PREFS_KEY_FCM_TOKEN = "FCM_TOKEN"
|
||||||
}
|
}
|
||||||
|
@ -64,7 +72,9 @@ class GoogleFcmHelper @Inject constructor(
|
||||||
.addOnSuccessListener { token ->
|
.addOnSuccessListener { token ->
|
||||||
storeFcmToken(token)
|
storeFcmToken(token)
|
||||||
if (registerPusher) {
|
if (registerPusher) {
|
||||||
pushersManager.enqueueRegisterPusherWithFcmKey(token)
|
scope.launch {
|
||||||
|
pushersManager.enqueueRegisterPusherWithFcmKey(token)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.addOnFailureListener { e ->
|
.addOnFailureListener { e ->
|
||||||
|
|
|
@ -27,6 +27,10 @@ import im.vector.app.core.pushers.PushersManager
|
||||||
import im.vector.app.core.pushers.UnifiedPushHelper
|
import im.vector.app.core.pushers.UnifiedPushHelper
|
||||||
import im.vector.app.core.pushers.VectorPushHandler
|
import im.vector.app.core.pushers.VectorPushHandler
|
||||||
import im.vector.app.features.settings.VectorPreferences
|
import im.vector.app.features.settings.VectorPreferences
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.SupervisorJob
|
||||||
|
import kotlinx.coroutines.cancel
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import org.matrix.android.sdk.api.logger.LoggerTag
|
import org.matrix.android.sdk.api.logger.LoggerTag
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
@ -43,6 +47,12 @@ class VectorFirebaseMessagingService : FirebaseMessagingService() {
|
||||||
@Inject lateinit var vectorPushHandler: VectorPushHandler
|
@Inject lateinit var vectorPushHandler: VectorPushHandler
|
||||||
@Inject lateinit var unifiedPushHelper: UnifiedPushHelper
|
@Inject lateinit var unifiedPushHelper: UnifiedPushHelper
|
||||||
|
|
||||||
|
private val scope = CoroutineScope(SupervisorJob())
|
||||||
|
|
||||||
|
override fun onDestroy() {
|
||||||
|
scope.cancel()
|
||||||
|
super.onDestroy()
|
||||||
|
}
|
||||||
override fun onNewToken(token: String) {
|
override fun onNewToken(token: String) {
|
||||||
Timber.tag(loggerTag.value).d("New Firebase token")
|
Timber.tag(loggerTag.value).d("New Firebase token")
|
||||||
fcmHelper.storeFcmToken(token)
|
fcmHelper.storeFcmToken(token)
|
||||||
|
@ -51,7 +61,9 @@ class VectorFirebaseMessagingService : FirebaseMessagingService() {
|
||||||
activeSessionHolder.hasActiveSession() &&
|
activeSessionHolder.hasActiveSession() &&
|
||||||
unifiedPushHelper.isEmbeddedDistributor()
|
unifiedPushHelper.isEmbeddedDistributor()
|
||||||
) {
|
) {
|
||||||
pushersManager.enqueueRegisterPusher(token, getString(R.string.pusher_http_url))
|
scope.launch {
|
||||||
|
pushersManager.enqueueRegisterPusher(token, getString(R.string.pusher_http_url))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -311,7 +311,7 @@ dependencies {
|
||||||
// Fix issue with Jitsi. Inspired from https://github.com/android/android-test/issues/861#issuecomment-872067868
|
// Fix issue with Jitsi. Inspired from https://github.com/android/android-test/issues/861#issuecomment-872067868
|
||||||
// Error was lots of `Duplicate class org.checkerframework.common.reflection.qual.MethodVal found in modules jetified-checker-3.1 (org.checkerframework:checker:3.1.1) and jetified-checker-qual-3.12.0 (org.checkerframework:checker-qual:3.12.0)
|
// Error was lots of `Duplicate class org.checkerframework.common.reflection.qual.MethodVal found in modules jetified-checker-3.1 (org.checkerframework:checker:3.1.1) and jetified-checker-qual-3.12.0 (org.checkerframework:checker-qual:3.12.0)
|
||||||
//noinspection GradleDependency Cannot use latest 3.15.0 since it required min API 26.
|
//noinspection GradleDependency Cannot use latest 3.15.0 since it required min API 26.
|
||||||
implementation "org.checkerframework:checker:3.34.0"
|
implementation "org.checkerframework:checker:3.35.0"
|
||||||
|
|
||||||
androidTestImplementation libs.androidx.testCore
|
androidTestImplementation libs.androidx.testCore
|
||||||
androidTestImplementation libs.androidx.testRunner
|
androidTestImplementation libs.androidx.testRunner
|
||||||
|
|
|
@ -22,14 +22,14 @@ import javax.inject.Inject
|
||||||
|
|
||||||
interface GetDeviceInfoUseCase {
|
interface GetDeviceInfoUseCase {
|
||||||
|
|
||||||
fun execute(): CryptoDeviceInfo
|
suspend fun execute(): CryptoDeviceInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
class DefaultGetDeviceInfoUseCase @Inject constructor(
|
class DefaultGetDeviceInfoUseCase @Inject constructor(
|
||||||
private val activeSessionHolder: ActiveSessionHolder
|
private val activeSessionHolder: ActiveSessionHolder
|
||||||
) : GetDeviceInfoUseCase {
|
) : GetDeviceInfoUseCase {
|
||||||
|
|
||||||
override fun execute(): CryptoDeviceInfo {
|
override suspend fun execute(): CryptoDeviceInfo {
|
||||||
return activeSessionHolder.getActiveSession().cryptoService().getMyCryptoDevice()
|
return activeSessionHolder.getActiveSession().cryptoService().getMyCryptoDevice()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,11 +49,11 @@ class PushersManager @Inject constructor(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun enqueueRegisterPusherWithFcmKey(pushKey: String): UUID {
|
suspend fun enqueueRegisterPusherWithFcmKey(pushKey: String): UUID {
|
||||||
return enqueueRegisterPusher(pushKey, stringProvider.getString(R.string.pusher_http_url))
|
return enqueueRegisterPusher(pushKey, stringProvider.getString(R.string.pusher_http_url))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun enqueueRegisterPusher(
|
suspend fun enqueueRegisterPusher(
|
||||||
pushKey: String,
|
pushKey: String,
|
||||||
gateway: String
|
gateway: String
|
||||||
): UUID {
|
): UUID {
|
||||||
|
@ -62,7 +62,7 @@ class PushersManager @Inject constructor(
|
||||||
return currentSession.pushersService().enqueueAddHttpPusher(pusher)
|
return currentSession.pushersService().enqueueAddHttpPusher(pusher)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createHttpPusher(
|
private suspend fun createHttpPusher(
|
||||||
pushKey: String,
|
pushKey: String,
|
||||||
gateway: String
|
gateway: String
|
||||||
) = HttpPusher(
|
) = HttpPusher(
|
||||||
|
|
|
@ -76,7 +76,9 @@ class VectorUnifiedPushMessagingReceiver : MessagingReceiver() {
|
||||||
coroutineScope.launch {
|
coroutineScope.launch {
|
||||||
unifiedPushHelper.storeCustomOrDefaultGateway(endpoint) {
|
unifiedPushHelper.storeCustomOrDefaultGateway(endpoint) {
|
||||||
unifiedPushHelper.getPushGateway()?.let {
|
unifiedPushHelper.getPushGateway()?.let {
|
||||||
pushersManager.enqueueRegisterPusher(endpoint, it)
|
coroutineScope.launch {
|
||||||
|
pushersManager.enqueueRegisterPusher(endpoint, it)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
package im.vector.app.core.session.clientinfo
|
package im.vector.app.core.session.clientinfo
|
||||||
|
|
||||||
import im.vector.app.core.di.ActiveSessionHolder
|
import im.vector.app.core.di.ActiveSessionHolder
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
import org.matrix.android.sdk.api.session.crypto.model.DeviceInfo
|
import org.matrix.android.sdk.api.session.crypto.model.DeviceInfo
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@ -27,16 +28,19 @@ class DeleteUnusedClientInformationUseCase @Inject constructor(
|
||||||
suspend fun execute(deviceInfoList: List<DeviceInfo>): Result<Unit> = runCatching {
|
suspend fun execute(deviceInfoList: List<DeviceInfo>): Result<Unit> = runCatching {
|
||||||
// A defensive approach against local storage reports an empty device list (although it is not a seen situation).
|
// A defensive approach against local storage reports an empty device list (although it is not a seen situation).
|
||||||
if (deviceInfoList.isEmpty()) return Result.success(Unit)
|
if (deviceInfoList.isEmpty()) return Result.success(Unit)
|
||||||
|
val dispatcher = activeSessionHolder.getSafeActiveSession()?.coroutineDispatchers?.io
|
||||||
val expectedClientInfoKeyList = deviceInfoList.map { MATRIX_CLIENT_INFO_KEY_PREFIX + it.deviceId }
|
?: return@runCatching
|
||||||
activeSessionHolder
|
withContext(dispatcher) {
|
||||||
.getSafeActiveSession()
|
val expectedClientInfoKeyList = deviceInfoList.map { MATRIX_CLIENT_INFO_KEY_PREFIX + it.deviceId }
|
||||||
?.accountDataService()
|
activeSessionHolder
|
||||||
?.getUserAccountDataEventsStartWith(MATRIX_CLIENT_INFO_KEY_PREFIX)
|
.getSafeActiveSession()
|
||||||
?.map { it.type }
|
?.accountDataService()
|
||||||
?.subtract(expectedClientInfoKeyList.toSet())
|
?.getUserAccountDataEventsStartWith(MATRIX_CLIENT_INFO_KEY_PREFIX)
|
||||||
?.forEach { userAccountDataKeyToDelete ->
|
?.map { it.type }
|
||||||
activeSessionHolder.getSafeActiveSession()?.accountDataService()?.deleteUserAccountData(userAccountDataKeyToDelete)
|
?.subtract(expectedClientInfoKeyList.toSet())
|
||||||
}
|
?.forEach { userAccountDataKeyToDelete ->
|
||||||
|
activeSessionHolder.getSafeActiveSession()?.accountDataService()?.deleteUserAccountData(userAccountDataKeyToDelete)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,6 +29,7 @@ import com.airbnb.mvrx.viewModel
|
||||||
import com.bumptech.glide.Glide
|
import com.bumptech.glide.Glide
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
|
import im.vector.app.BuildConfig
|
||||||
import im.vector.app.R
|
import im.vector.app.R
|
||||||
import im.vector.app.core.extensions.startSyncing
|
import im.vector.app.core.extensions.startSyncing
|
||||||
import im.vector.app.core.extensions.vectorStore
|
import im.vector.app.core.extensions.vectorStore
|
||||||
|
@ -59,6 +60,7 @@ import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import kotlinx.parcelize.Parcelize
|
import kotlinx.parcelize.Parcelize
|
||||||
import org.matrix.android.sdk.api.failure.GlobalError
|
import org.matrix.android.sdk.api.failure.GlobalError
|
||||||
|
import org.matrix.android.sdk.api.session.Session
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@ -170,6 +172,19 @@ class MainActivity : VectorBaseActivity<ActivityMainBinding>(), UnlockedActivity
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleAppStarted() {
|
private fun handleAppStarted() {
|
||||||
|
// On the first run with rust crypto this would be false
|
||||||
|
if (!vectorPreferences.isOnRustCrypto()) {
|
||||||
|
if (activeSessionHolder.hasActiveSession()) {
|
||||||
|
vectorPreferences.setHadExistingLegacyData(activeSessionHolder.getActiveSession().isOpenable)
|
||||||
|
} else {
|
||||||
|
vectorPreferences.setHadExistingLegacyData(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (BuildConfig.FLAVOR == "rustCrypto") {
|
||||||
|
vectorPreferences.setIsOnRustCrypto(true)
|
||||||
|
}
|
||||||
|
|
||||||
if (intent.hasExtra(EXTRA_NEXT_INTENT)) {
|
if (intent.hasExtra(EXTRA_NEXT_INTENT)) {
|
||||||
// Start the next Activity
|
// Start the next Activity
|
||||||
startSyncing()
|
startSyncing()
|
||||||
|
@ -248,18 +263,7 @@ class MainActivity : VectorBaseActivity<ActivityMainBinding>(), UnlockedActivity
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
args.clearCredentials -> {
|
args.clearCredentials -> {
|
||||||
lifecycleScope.launch {
|
signout(session, onboardingStore, ignoreServerError = false)
|
||||||
try {
|
|
||||||
session.signOutService().signOut(!args.isUserLoggedOut)
|
|
||||||
} catch (failure: Throwable) {
|
|
||||||
displayError(failure)
|
|
||||||
return@launch
|
|
||||||
}
|
|
||||||
Timber.w("SIGN_OUT: success, start app")
|
|
||||||
activeSessionHolder.clearActiveSession()
|
|
||||||
doLocalCleanup(clearPreferences = true, onboardingStore)
|
|
||||||
startNextActivityAndFinish()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
args.clearCache -> {
|
args.clearCache -> {
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
|
@ -272,6 +276,26 @@ class MainActivity : VectorBaseActivity<ActivityMainBinding>(), UnlockedActivity
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun signout(
|
||||||
|
session: Session,
|
||||||
|
onboardingStore: VectorSessionStore,
|
||||||
|
ignoreServerError: Boolean,
|
||||||
|
) {
|
||||||
|
lifecycleScope.launch {
|
||||||
|
try {
|
||||||
|
session.signOutService().signOut(!args.isUserLoggedOut, ignoreServerError)
|
||||||
|
} catch (failure: Throwable) {
|
||||||
|
Timber.e(failure, "SIGN_OUT: error, propose to sign out anyway")
|
||||||
|
displaySignOutFailedDialog(session, onboardingStore)
|
||||||
|
return@launch
|
||||||
|
}
|
||||||
|
Timber.w("SIGN_OUT: success, start app")
|
||||||
|
activeSessionHolder.clearActiveSession()
|
||||||
|
doLocalCleanup(clearPreferences = true, onboardingStore)
|
||||||
|
startNextActivityAndFinish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun handleInvalidToken(globalError: GlobalError.InvalidToken) {
|
override fun handleInvalidToken(globalError: GlobalError.InvalidToken) {
|
||||||
// No op here
|
// No op here
|
||||||
Timber.w("Ignoring invalid token global error")
|
Timber.w("Ignoring invalid token global error")
|
||||||
|
@ -299,12 +323,20 @@ class MainActivity : VectorBaseActivity<ActivityMainBinding>(), UnlockedActivity
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun displayError(failure: Throwable) {
|
private fun displaySignOutFailedDialog(
|
||||||
|
session: Session,
|
||||||
|
onboardingStore: VectorSessionStore,
|
||||||
|
) {
|
||||||
if (lifecycle.currentState.isAtLeast(Lifecycle.State.RESUMED)) {
|
if (lifecycle.currentState.isAtLeast(Lifecycle.State.RESUMED)) {
|
||||||
MaterialAlertDialogBuilder(this)
|
MaterialAlertDialogBuilder(this)
|
||||||
.setTitle(R.string.dialog_title_error)
|
.setTitle(R.string.dialog_title_error)
|
||||||
.setMessage(errorFormatter.toHumanReadable(failure))
|
.setMessage(R.string.sign_out_failed_dialog_message)
|
||||||
.setPositiveButton(R.string.global_retry) { _, _ -> doCleanUp() }
|
.setPositiveButton(R.string.sign_out_anyway) { _, _ ->
|
||||||
|
signout(session, onboardingStore, ignoreServerError = true)
|
||||||
|
}
|
||||||
|
.setNeutralButton(R.string.global_retry) { _, _ ->
|
||||||
|
signout(session, onboardingStore, ignoreServerError = false)
|
||||||
|
}
|
||||||
.setNegativeButton(R.string.action_cancel) { _, _ -> startNextActivityAndFinish(ignoreClearCredentials = true) }
|
.setNegativeButton(R.string.action_cancel) { _, _ -> startNextActivityAndFinish(ignoreClearCredentials = true) }
|
||||||
.setCancelable(false)
|
.setCancelable(false)
|
||||||
.show()
|
.show()
|
||||||
|
|
|
@ -52,7 +52,7 @@ class SharedSecuredStorageResetAllFragment :
|
||||||
|
|
||||||
views.ssssResetOtherDevices.debouncedClicks {
|
views.ssssResetOtherDevices.debouncedClicks {
|
||||||
withState(sharedViewModel) {
|
withState(sharedViewModel) {
|
||||||
DeviceListBottomSheet.newInstance(it.userId, false).show(childFragmentManager, "DEV_LIST")
|
DeviceListBottomSheet.newInstance(it.userId).show(childFragmentManager, "DEV_LIST")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -275,7 +275,7 @@ class SelfVerificationController @Inject constructor(
|
||||||
id("notice_div")
|
id("notice_div")
|
||||||
}
|
}
|
||||||
// Option to verify with another device
|
// Option to verify with another device
|
||||||
if (state.hasAnyOtherSession) {
|
if (state.hasAnyOtherSession.invoke() == true) {
|
||||||
bottomSheetVerificationActionItem {
|
bottomSheetVerificationActionItem {
|
||||||
id("start")
|
id("start")
|
||||||
title(host.stringProvider.getString(R.string.verification_verify_with_another_device))
|
title(host.stringProvider.getString(R.string.verification_verify_with_another_device))
|
||||||
|
|
|
@ -83,7 +83,7 @@ data class SelfVerificationViewState(
|
||||||
val transactionId: String? = null,
|
val transactionId: String? = null,
|
||||||
val currentDeviceCanCrossSign: Boolean = false,
|
val currentDeviceCanCrossSign: Boolean = false,
|
||||||
val userWantsToCancel: Boolean = false,
|
val userWantsToCancel: Boolean = false,
|
||||||
val hasAnyOtherSession: Boolean = false,
|
val hasAnyOtherSession: Async<Boolean> = Uninitialized,
|
||||||
val quadSContainsSecrets: Boolean = false,
|
val quadSContainsSecrets: Boolean = false,
|
||||||
val isVerificationRequired: Boolean = false,
|
val isVerificationRequired: Boolean = false,
|
||||||
val isThisSessionVerified: Boolean = false,
|
val isThisSessionVerified: Boolean = false,
|
||||||
|
@ -146,21 +146,28 @@ class SelfVerificationViewModel @AssistedInject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val hasAnyOtherSession = session.cryptoService()
|
setState { copy(hasAnyOtherSession = Loading()) }
|
||||||
.getCryptoDeviceInfo(session.myUserId)
|
viewModelScope.launch {
|
||||||
.any {
|
val hasAnyOtherSession = session.cryptoService()
|
||||||
it.deviceId != session.sessionParams.deviceId
|
.getCryptoDeviceInfo(session.myUserId)
|
||||||
}
|
.any {
|
||||||
|
it.deviceId != session.sessionParams.deviceId
|
||||||
|
}
|
||||||
|
setState {
|
||||||
|
copy(
|
||||||
|
hasAnyOtherSession = Success(hasAnyOtherSession)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
setState {
|
setState {
|
||||||
copy(
|
copy(
|
||||||
currentDeviceCanCrossSign = session.cryptoService().crossSigningService().canCrossSign(),
|
currentDeviceCanCrossSign = session.cryptoService().crossSigningService().canCrossSign(),
|
||||||
quadSContainsSecrets = session.sharedSecretStorageService().isRecoverySetup(),
|
quadSContainsSecrets = session.sharedSecretStorageService().isRecoverySetup(),
|
||||||
hasAnyOtherSession = hasAnyOtherSession
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
viewModelScope.launch {
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
val isThisSessionVerified = session.cryptoService().crossSigningService().isCrossSigningVerified()
|
val isThisSessionVerified = session.cryptoService().crossSigningService().isCrossSigningVerified()
|
||||||
setState {
|
setState {
|
||||||
copy(
|
copy(
|
||||||
|
|
|
@ -469,11 +469,21 @@ class HomeActivity :
|
||||||
|
|
||||||
private fun handleOnNewSession(event: HomeActivityViewEvents.CurrentSessionNotVerified) {
|
private fun handleOnNewSession(event: HomeActivityViewEvents.CurrentSessionNotVerified) {
|
||||||
// We need to ask
|
// We need to ask
|
||||||
|
val titleRes = if (event.afterMigration) {
|
||||||
|
R.string.crosssigning_verify_after_update
|
||||||
|
} else {
|
||||||
|
R.string.crosssigning_verify_this_session
|
||||||
|
}
|
||||||
|
val descRes = if (event.afterMigration) {
|
||||||
|
R.string.confirm_your_identity_after_update
|
||||||
|
} else {
|
||||||
|
R.string.confirm_your_identity
|
||||||
|
}
|
||||||
promptSecurityEvent(
|
promptSecurityEvent(
|
||||||
uid = PopupAlertManager.VERIFY_SESSION_UID,
|
uid = PopupAlertManager.VERIFY_SESSION_UID,
|
||||||
userItem = event.userItem,
|
userItem = event.userItem,
|
||||||
titleRes = R.string.crosssigning_verify_this_session,
|
titleRes = titleRes,
|
||||||
descRes = R.string.confirm_your_identity,
|
descRes = descRes,
|
||||||
) {
|
) {
|
||||||
it.navigator.requestSelfSessionVerification(it)
|
it.navigator.requestSelfSessionVerification(it)
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,7 @@ sealed interface HomeActivityViewEvents : VectorViewEvents {
|
||||||
data class AskPasswordToInitCrossSigning(val userItem: MatrixItem.UserItem) : HomeActivityViewEvents
|
data class AskPasswordToInitCrossSigning(val userItem: MatrixItem.UserItem) : HomeActivityViewEvents
|
||||||
data class CurrentSessionNotVerified(
|
data class CurrentSessionNotVerified(
|
||||||
val userItem: MatrixItem.UserItem,
|
val userItem: MatrixItem.UserItem,
|
||||||
// val waitForIncomingRequest: Boolean = true,
|
val afterMigration: Boolean
|
||||||
) : HomeActivityViewEvents
|
) : HomeActivityViewEvents
|
||||||
|
|
||||||
data class CurrentSessionCannotBeVerified(
|
data class CurrentSessionCannotBeVerified(
|
||||||
|
|
|
@ -453,6 +453,7 @@ class HomeActivityViewModel @AssistedInject constructor(
|
||||||
_viewEvents.post(
|
_viewEvents.post(
|
||||||
HomeActivityViewEvents.CurrentSessionNotVerified(
|
HomeActivityViewEvents.CurrentSessionNotVerified(
|
||||||
session.getUserOrDefault(session.myUserId).toMatrixItem(),
|
session.getUserOrDefault(session.myUserId).toMatrixItem(),
|
||||||
|
vectorPreferences.isOnRustCrypto() && vectorPreferences.hadExistingLegacyData()
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -92,11 +92,6 @@ class UnknownDeviceDetectorSharedViewModel @AssistedInject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
val currentSessionTs = session.cryptoService().getCryptoDeviceInfo(session.myUserId)
|
|
||||||
.firstOrNull { it.deviceId == session.sessionParams.deviceId }
|
|
||||||
?.firstTimeSeenLocalTs
|
|
||||||
?: clock.epochMillis()
|
|
||||||
Timber.v("## Detector - Current Session first time seen $currentSessionTs")
|
|
||||||
|
|
||||||
combine(
|
combine(
|
||||||
session.flow().liveUserCryptoDevices(session.myUserId),
|
session.flow().liveUserCryptoDevices(session.myUserId),
|
||||||
|
@ -108,6 +103,12 @@ class UnknownDeviceDetectorSharedViewModel @AssistedInject constructor(
|
||||||
|
|
||||||
deleteUnusedClientInformation(infoList)
|
deleteUnusedClientInformation(infoList)
|
||||||
|
|
||||||
|
val currentSessionTs = session.cryptoService().getCryptoDeviceInfo(session.myUserId)
|
||||||
|
.firstOrNull { it.deviceId == session.sessionParams.deviceId }
|
||||||
|
?.firstTimeSeenLocalTs
|
||||||
|
?: clock.epochMillis()
|
||||||
|
Timber.v("## Detector - Current Session first time seen $currentSessionTs")
|
||||||
|
|
||||||
infoList
|
infoList
|
||||||
.asSequence()
|
.asSequence()
|
||||||
.filter {
|
.filter {
|
||||||
|
|
|
@ -528,6 +528,8 @@ class MessageItemFactory @Inject constructor(
|
||||||
)
|
)
|
||||||
|
|
||||||
val playable = messageContent.mimeType == MimeTypes.Gif
|
val playable = messageContent.mimeType == MimeTypes.Gif
|
||||||
|
// don't show play button because detecting animated webp isn't possible via mimetype
|
||||||
|
val playableIfAutoplay = playable || messageContent.mimeType == MimeTypes.Webp
|
||||||
|
|
||||||
return MessageImageVideoItem_()
|
return MessageImageVideoItem_()
|
||||||
.attributes(attributes)
|
.attributes(attributes)
|
||||||
|
@ -549,7 +551,7 @@ class MessageItemFactory @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}.apply {
|
}.apply {
|
||||||
if (playable && vectorPreferences.autoplayAnimatedImages()) {
|
if (playableIfAutoplay && vectorPreferences.autoplayAnimatedImages()) {
|
||||||
mode(ImageContentRenderer.Mode.ANIMATED_THUMBNAIL)
|
mode(ImageContentRenderer.Mode.ANIMATED_THUMBNAIL)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -322,11 +322,10 @@ class RoomListViewModel @AssistedInject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleDeleteLocalRooms() {
|
private fun handleDeleteLocalRooms() {
|
||||||
val localRoomIds = session.roomService()
|
viewModelScope.launch(session.coroutineDispatchers.io) {
|
||||||
.getRoomSummaries(roomSummaryQueryParams { roomId = QueryStringValue.Contains(RoomLocalEcho.PREFIX) })
|
val localRoomIds = session.roomService()
|
||||||
.map { it.roomId }
|
.getRoomSummaries(roomSummaryQueryParams { roomId = QueryStringValue.Contains(RoomLocalEcho.PREFIX) })
|
||||||
|
.map { it.roomId }
|
||||||
viewModelScope.launch {
|
|
||||||
localRoomIds.forEach {
|
localRoomIds.forEach {
|
||||||
session.roomService().deleteLocalRoom(it)
|
session.roomService().deleteLocalRoom(it)
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,7 +49,7 @@ class DataAttachmentRoomProvider(
|
||||||
return getItem(position).let {
|
return getItem(position).let {
|
||||||
when (it) {
|
when (it) {
|
||||||
is ImageContentRenderer.Data -> {
|
is ImageContentRenderer.Data -> {
|
||||||
if (it.mimeType == MimeTypes.Gif) {
|
if (it.mimeType == MimeTypes.Gif || it.mimeType == MimeTypes.Webp) {
|
||||||
AttachmentInfo.AnimatedImage(
|
AttachmentInfo.AnimatedImage(
|
||||||
uid = it.eventId,
|
uid = it.eventId,
|
||||||
url = it.url ?: "",
|
url = it.url ?: "",
|
||||||
|
|
|
@ -135,7 +135,7 @@ class ImageContentRenderer @Inject constructor(
|
||||||
if (mode == Mode.ANIMATED_THUMBNAIL) it
|
if (mode == Mode.ANIMATED_THUMBNAIL) it
|
||||||
else it.dontAnimate()
|
else it.dontAnimate()
|
||||||
}
|
}
|
||||||
.transform(cornerTransformation)
|
.optionalTransform(cornerTransformation)
|
||||||
.into(imageView)
|
.into(imageView)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -167,7 +167,7 @@ class ImageContentRenderer @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
req
|
req
|
||||||
.fitCenter()
|
.optionalFitCenter()
|
||||||
.into(target)
|
.into(target)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -211,7 +211,7 @@ class ImageContentRenderer @Inject constructor(
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.fitCenter()
|
.optionalFitCenter()
|
||||||
.into(imageView)
|
.into(imageView)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -71,7 +71,7 @@ class RoomEventsAttachmentProvider(
|
||||||
allowNonMxcUrls = it.root.sendState.isSending()
|
allowNonMxcUrls = it.root.sendState.isSending()
|
||||||
|
|
||||||
)
|
)
|
||||||
if (content.mimeType == MimeTypes.Gif) {
|
if (content.mimeType == MimeTypes.Gif || content.mimeType == MimeTypes.Webp) {
|
||||||
AttachmentInfo.AnimatedImage(
|
AttachmentInfo.AnimatedImage(
|
||||||
uid = it.eventId,
|
uid = it.eventId,
|
||||||
url = content.url ?: "",
|
url = content.url ?: "",
|
||||||
|
|
|
@ -38,6 +38,7 @@ import im.vector.app.config.OnboardingVariant
|
||||||
import im.vector.app.core.debug.DebugNavigator
|
import im.vector.app.core.debug.DebugNavigator
|
||||||
import im.vector.app.core.di.ActiveSessionHolder
|
import im.vector.app.core.di.ActiveSessionHolder
|
||||||
import im.vector.app.core.error.fatalError
|
import im.vector.app.core.error.fatalError
|
||||||
|
import im.vector.app.core.extensions.commitTransaction
|
||||||
import im.vector.app.features.VectorFeatures
|
import im.vector.app.features.VectorFeatures
|
||||||
import im.vector.app.features.analytics.AnalyticsTracker
|
import im.vector.app.features.analytics.AnalyticsTracker
|
||||||
import im.vector.app.features.analytics.extensions.toAnalyticsViewRoom
|
import im.vector.app.features.analytics.extensions.toAnalyticsViewRoom
|
||||||
|
@ -256,8 +257,9 @@ class DefaultNavigator @Inject constructor(
|
||||||
otherSessionId
|
otherSessionId
|
||||||
)
|
)
|
||||||
if (context is AppCompatActivity) {
|
if (context is AppCompatActivity) {
|
||||||
SelfVerificationBottomSheet.forTransaction(request.transactionId)
|
context.supportFragmentManager.commitTransaction(allowStateLoss = true) {
|
||||||
.show(context.supportFragmentManager, "VERIF")
|
add(SelfVerificationBottomSheet.forTransaction(request.transactionId), "VERIF")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -271,8 +273,9 @@ class DefaultNavigator @Inject constructor(
|
||||||
// .filter { it.deviceId != session.sessionParams.deviceId }
|
// .filter { it.deviceId != session.sessionParams.deviceId }
|
||||||
// .map { it.deviceId }
|
// .map { it.deviceId }
|
||||||
if (context is AppCompatActivity) {
|
if (context is AppCompatActivity) {
|
||||||
SelfVerificationBottomSheet.verifyOwnUntrustedDevice()
|
context.supportFragmentManager.commitTransaction(allowStateLoss = true) {
|
||||||
.show(context.supportFragmentManager, "VERIF")
|
add(SelfVerificationBottomSheet.verifyOwnUntrustedDevice(), "VERIF")
|
||||||
|
}
|
||||||
// if (otherSessions.isNotEmpty()) {
|
// if (otherSessions.isNotEmpty()) {
|
||||||
// val pr = session.cryptoService().verificationService().requestSelfKeyVerification(
|
// val pr = session.cryptoService().verificationService().requestSelfKeyVerification(
|
||||||
// supportedVerificationMethodsProvider.provide())
|
// supportedVerificationMethodsProvider.provide())
|
||||||
|
|
|
@ -24,7 +24,9 @@ import im.vector.app.R
|
||||||
import im.vector.app.core.resources.BuildMeta
|
import im.vector.app.core.resources.BuildMeta
|
||||||
import im.vector.app.core.utils.FirstThrottler
|
import im.vector.app.core.utils.FirstThrottler
|
||||||
import im.vector.app.features.displayname.getBestName
|
import im.vector.app.features.displayname.getBestName
|
||||||
|
import im.vector.app.features.session.coroutineScope
|
||||||
import im.vector.app.features.settings.VectorPreferences
|
import im.vector.app.features.settings.VectorPreferences
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import org.matrix.android.sdk.api.session.Session
|
import org.matrix.android.sdk.api.session.Session
|
||||||
import org.matrix.android.sdk.api.session.content.ContentUrlResolver
|
import org.matrix.android.sdk.api.session.content.ContentUrlResolver
|
||||||
import org.matrix.android.sdk.api.session.getUserOrDefault
|
import org.matrix.android.sdk.api.session.getUserOrDefault
|
||||||
|
@ -121,11 +123,15 @@ class NotificationDrawerManager @Inject constructor(
|
||||||
* Used to ignore events related to that room (no need to display notification) and clean any existing notification on this room.
|
* Used to ignore events related to that room (no need to display notification) and clean any existing notification on this room.
|
||||||
*/
|
*/
|
||||||
fun setCurrentRoom(roomId: String?) {
|
fun setCurrentRoom(roomId: String?) {
|
||||||
updateEvents {
|
val dispatcher = currentSession?.coroutineDispatchers?.io ?: return
|
||||||
val hasChanged = roomId != currentRoomId
|
val scope = currentSession?.coroutineScope ?: return
|
||||||
currentRoomId = roomId
|
scope.launch(dispatcher) {
|
||||||
if (hasChanged && roomId != null) {
|
updateEvents {
|
||||||
it.clearMessagesForRoom(roomId)
|
val hasChanged = roomId != currentRoomId
|
||||||
|
currentRoomId = roomId
|
||||||
|
if (hasChanged && roomId != null) {
|
||||||
|
it.clearMessagesForRoom(roomId)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -135,12 +141,16 @@ class NotificationDrawerManager @Inject constructor(
|
||||||
* Used to ignore events related to that thread (no need to display notification) and clean any existing notification on this room.
|
* Used to ignore events related to that thread (no need to display notification) and clean any existing notification on this room.
|
||||||
*/
|
*/
|
||||||
fun setCurrentThread(threadId: String?) {
|
fun setCurrentThread(threadId: String?) {
|
||||||
updateEvents {
|
val dispatcher = currentSession?.coroutineDispatchers?.io ?: return
|
||||||
val hasChanged = threadId != currentThreadId
|
val scope = currentSession?.coroutineScope ?: return
|
||||||
currentThreadId = threadId
|
scope.launch(dispatcher) {
|
||||||
currentRoomId?.let { roomId ->
|
updateEvents {
|
||||||
if (hasChanged && threadId != null) {
|
val hasChanged = threadId != currentThreadId
|
||||||
it.clearMessagesForThread(roomId, threadId)
|
currentThreadId = threadId
|
||||||
|
currentRoomId?.let { roomId ->
|
||||||
|
if (hasChanged && threadId != null) {
|
||||||
|
it.clearMessagesForThread(roomId, threadId)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,4 @@ import org.matrix.android.sdk.api.session.crypto.model.CryptoDeviceInfo
|
||||||
sealed class DeviceListAction : VectorViewModelAction {
|
sealed class DeviceListAction : VectorViewModelAction {
|
||||||
data class SelectDevice(val device: CryptoDeviceInfo) : DeviceListAction()
|
data class SelectDevice(val device: CryptoDeviceInfo) : DeviceListAction()
|
||||||
object DeselectDevice : DeviceListAction()
|
object DeselectDevice : DeviceListAction()
|
||||||
|
|
||||||
data class ManuallyVerify(val deviceId: String) : DeviceListAction()
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,16 +47,7 @@ class DeviceListBottomSheet :
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
viewModel.observeViewEvents {
|
viewModel.observeViewEvents {
|
||||||
when (it) {
|
// nop
|
||||||
is DeviceListBottomSheetViewEvents.Verify -> {
|
|
||||||
// TODO selfverif
|
|
||||||
// VerificationBottomSheet.withArgs(
|
|
||||||
// // roomId = null,
|
|
||||||
// otherUserId = it.userId,
|
|
||||||
// transactionId = it.txID
|
|
||||||
// ).show(requireActivity().supportFragmentManager, "REQPOP")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -109,13 +100,12 @@ class DeviceListBottomSheet :
|
||||||
@Parcelize
|
@Parcelize
|
||||||
data class Args(
|
data class Args(
|
||||||
val userId: String,
|
val userId: String,
|
||||||
val allowDeviceAction: Boolean
|
|
||||||
) : Parcelable
|
) : Parcelable
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun newInstance(userId: String, allowDeviceAction: Boolean = true): DeviceListBottomSheet {
|
fun newInstance(userId: String): DeviceListBottomSheet {
|
||||||
return DeviceListBottomSheet().apply {
|
return DeviceListBottomSheet().apply {
|
||||||
setArguments(Args(userId, allowDeviceAction))
|
setArguments(Args(userId))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,4 @@ import im.vector.app.core.platform.VectorViewEvents
|
||||||
/**
|
/**
|
||||||
* Transient events for device list screen.
|
* Transient events for device list screen.
|
||||||
*/
|
*/
|
||||||
sealed class DeviceListBottomSheetViewEvents : VectorViewEvents {
|
sealed class DeviceListBottomSheetViewEvents : VectorViewEvents
|
||||||
data class Verify(val userId: String, val txID: String) : DeviceListBottomSheetViewEvents()
|
|
||||||
}
|
|
||||||
|
|
|
@ -34,7 +34,6 @@ import org.matrix.android.sdk.api.extensions.tryOrNull
|
||||||
import org.matrix.android.sdk.api.session.Session
|
import org.matrix.android.sdk.api.session.Session
|
||||||
import org.matrix.android.sdk.api.session.crypto.crosssigning.MXCrossSigningInfo
|
import org.matrix.android.sdk.api.session.crypto.crosssigning.MXCrossSigningInfo
|
||||||
import org.matrix.android.sdk.api.session.crypto.model.CryptoDeviceInfo
|
import org.matrix.android.sdk.api.session.crypto.model.CryptoDeviceInfo
|
||||||
import org.matrix.android.sdk.api.session.crypto.verification.VerificationMethod
|
|
||||||
import org.matrix.android.sdk.api.session.getUserOrDefault
|
import org.matrix.android.sdk.api.session.getUserOrDefault
|
||||||
import org.matrix.android.sdk.api.util.MatrixItem
|
import org.matrix.android.sdk.api.util.MatrixItem
|
||||||
import org.matrix.android.sdk.api.util.toMatrixItem
|
import org.matrix.android.sdk.api.util.toMatrixItem
|
||||||
|
@ -42,7 +41,7 @@ import org.matrix.android.sdk.flow.flow
|
||||||
|
|
||||||
data class DeviceListViewState(
|
data class DeviceListViewState(
|
||||||
val userId: String,
|
val userId: String,
|
||||||
val allowDeviceAction: Boolean,
|
val myUserId: String,
|
||||||
val userItem: MatrixItem? = null,
|
val userItem: MatrixItem? = null,
|
||||||
val memberCrossSigningKey: MXCrossSigningInfo? = null,
|
val memberCrossSigningKey: MXCrossSigningInfo? = null,
|
||||||
val myDeviceId: String = "",
|
val myDeviceId: String = "",
|
||||||
|
@ -69,7 +68,7 @@ class DeviceListBottomSheetViewModel @AssistedInject constructor(
|
||||||
val session = EntryPoints.get(viewModelContext.app(), SingletonEntryPoint::class.java).activeSessionHolder().getActiveSession()
|
val session = EntryPoints.get(viewModelContext.app(), SingletonEntryPoint::class.java).activeSessionHolder().getActiveSession()
|
||||||
return DeviceListViewState(
|
return DeviceListViewState(
|
||||||
userId = userId,
|
userId = userId,
|
||||||
allowDeviceAction = args.allowDeviceAction,
|
myUserId = session.myUserId,
|
||||||
userItem = session.getUserOrDefault(userId).toMatrixItem(),
|
userItem = session.getUserOrDefault(userId).toMatrixItem(),
|
||||||
myDeviceId = session.sessionParams.deviceId,
|
myDeviceId = session.sessionParams.deviceId,
|
||||||
)
|
)
|
||||||
|
@ -104,7 +103,6 @@ class DeviceListBottomSheetViewModel @AssistedInject constructor(
|
||||||
when (action) {
|
when (action) {
|
||||||
is DeviceListAction.SelectDevice -> selectDevice(action)
|
is DeviceListAction.SelectDevice -> selectDevice(action)
|
||||||
is DeviceListAction.DeselectDevice -> deselectDevice()
|
is DeviceListAction.DeselectDevice -> deselectDevice()
|
||||||
is DeviceListAction.ManuallyVerify -> manuallyVerify(action)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -121,7 +119,6 @@ class DeviceListBottomSheetViewModel @AssistedInject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun selectDevice(action: DeviceListAction.SelectDevice) {
|
private fun selectDevice(action: DeviceListAction.SelectDevice) {
|
||||||
if (!initialState.allowDeviceAction) return
|
|
||||||
setState {
|
setState {
|
||||||
copy(selectedDevice = action.device)
|
copy(selectedDevice = action.device)
|
||||||
}
|
}
|
||||||
|
@ -132,18 +129,4 @@ class DeviceListBottomSheetViewModel @AssistedInject constructor(
|
||||||
copy(selectedDevice = null)
|
copy(selectedDevice = null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun manuallyVerify(action: DeviceListAction.ManuallyVerify) {
|
|
||||||
if (!initialState.allowDeviceAction) return
|
|
||||||
viewModelScope.launch {
|
|
||||||
session.cryptoService().verificationService().requestDeviceVerification(
|
|
||||||
methods = listOf(VerificationMethod.SAS),
|
|
||||||
otherUserId = initialState.userId,
|
|
||||||
otherDeviceId = action.deviceId,
|
|
||||||
).transactionId
|
|
||||||
.let { txID ->
|
|
||||||
_viewEvents.post(DeviceListBottomSheetViewEvents.Verify(initialState.userId, txID))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,7 +28,6 @@ import im.vector.app.core.extensions.configureWith
|
||||||
import im.vector.app.core.platform.VectorBaseFragment
|
import im.vector.app.core.platform.VectorBaseFragment
|
||||||
import im.vector.app.core.utils.DimensionConverter
|
import im.vector.app.core.utils.DimensionConverter
|
||||||
import im.vector.app.databinding.BottomSheetGenericListBinding
|
import im.vector.app.databinding.BottomSheetGenericListBinding
|
||||||
import org.matrix.android.sdk.api.session.crypto.model.CryptoDeviceInfo
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
|
@ -63,8 +62,4 @@ class DeviceTrustInfoActionFragment :
|
||||||
override fun invalidate() = withState(viewModel) {
|
override fun invalidate() = withState(viewModel) {
|
||||||
epoxyController.setData(it)
|
epoxyController.setData(it)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onVerifyManually(device: CryptoDeviceInfo) {
|
|
||||||
viewModel.handle(DeviceListAction.ManuallyVerify(device.deviceId))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,13 +25,10 @@ import im.vector.app.core.ui.list.genericFooterItem
|
||||||
import im.vector.app.core.ui.list.genericItem
|
import im.vector.app.core.ui.list.genericItem
|
||||||
import im.vector.app.core.ui.list.genericWithValueItem
|
import im.vector.app.core.ui.list.genericWithValueItem
|
||||||
import im.vector.app.core.utils.DimensionConverter
|
import im.vector.app.core.utils.DimensionConverter
|
||||||
import im.vector.app.features.crypto.verification.epoxy.bottomSheetVerificationActionItem
|
|
||||||
import im.vector.app.features.settings.VectorPreferences
|
|
||||||
import im.vector.app.features.settings.devices.TrustUtils
|
import im.vector.app.features.settings.devices.TrustUtils
|
||||||
import im.vector.lib.core.utils.epoxy.charsequence.toEpoxyCharSequence
|
import im.vector.lib.core.utils.epoxy.charsequence.toEpoxyCharSequence
|
||||||
import me.gujun.android.span.span
|
import me.gujun.android.span.span
|
||||||
import org.matrix.android.sdk.api.extensions.orFalse
|
import org.matrix.android.sdk.api.extensions.orFalse
|
||||||
import org.matrix.android.sdk.api.session.crypto.model.CryptoDeviceInfo
|
|
||||||
import org.matrix.android.sdk.api.session.crypto.model.RoomEncryptionTrustLevel
|
import org.matrix.android.sdk.api.session.crypto.model.RoomEncryptionTrustLevel
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@ -39,13 +36,10 @@ class DeviceTrustInfoEpoxyController @Inject constructor(
|
||||||
private val stringProvider: StringProvider,
|
private val stringProvider: StringProvider,
|
||||||
private val colorProvider: ColorProvider,
|
private val colorProvider: ColorProvider,
|
||||||
private val dimensionConverter: DimensionConverter,
|
private val dimensionConverter: DimensionConverter,
|
||||||
private val vectorPreferences: VectorPreferences
|
|
||||||
) :
|
) :
|
||||||
TypedEpoxyController<DeviceListViewState>() {
|
TypedEpoxyController<DeviceListViewState>() {
|
||||||
|
|
||||||
interface InteractionListener {
|
interface InteractionListener
|
||||||
fun onVerifyManually(device: CryptoDeviceInfo)
|
|
||||||
}
|
|
||||||
|
|
||||||
var interactionListener: InteractionListener? = null
|
var interactionListener: InteractionListener? = null
|
||||||
|
|
||||||
|
@ -54,7 +48,7 @@ class DeviceTrustInfoEpoxyController @Inject constructor(
|
||||||
data?.selectedDevice?.let { cryptoDeviceInfo ->
|
data?.selectedDevice?.let { cryptoDeviceInfo ->
|
||||||
val trustMSK = data.memberCrossSigningKey?.isTrusted().orFalse()
|
val trustMSK = data.memberCrossSigningKey?.isTrusted().orFalse()
|
||||||
val legacyMode = data.memberCrossSigningKey == null
|
val legacyMode = data.memberCrossSigningKey == null
|
||||||
val isMyDevice = data.myDeviceId == cryptoDeviceInfo.deviceId
|
val isMyDevice = data.userId == data.myUserId && data.myDeviceId == cryptoDeviceInfo.deviceId
|
||||||
val trustLevel = TrustUtils.shieldForTrust(
|
val trustLevel = TrustUtils.shieldForTrust(
|
||||||
isMyDevice,
|
isMyDevice,
|
||||||
trustMSK,
|
trustMSK,
|
||||||
|
@ -126,18 +120,7 @@ class DeviceTrustInfoEpoxyController @Inject constructor(
|
||||||
id("warn")
|
id("warn")
|
||||||
centered(false)
|
centered(false)
|
||||||
textColor(host.colorProvider.getColorFromAttribute(R.attr.vctr_content_primary))
|
textColor(host.colorProvider.getColorFromAttribute(R.attr.vctr_content_primary))
|
||||||
text(host.stringProvider.getString(R.string.verification_profile_device_untrust_info).toEpoxyCharSequence())
|
text(host.stringProvider.getString(R.string.verification_profile_other_device_untrust_info).toEpoxyCharSequence())
|
||||||
}
|
|
||||||
|
|
||||||
bottomSheetVerificationActionItem {
|
|
||||||
id("verify")
|
|
||||||
title(host.stringProvider.getString(R.string.cross_signing_verify_by_emoji))
|
|
||||||
titleColor(host.colorProvider.getColorFromAttribute(R.attr.colorPrimary))
|
|
||||||
iconRes(R.drawable.ic_arrow_right)
|
|
||||||
iconColor(host.colorProvider.getColorFromAttribute(R.attr.colorPrimary))
|
|
||||||
listener {
|
|
||||||
host.interactionListener?.onVerifyManually(cryptoDeviceInfo)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -254,6 +254,8 @@ class VectorPreferences @Inject constructor(
|
||||||
const val TAKE_PHOTO_VIDEO_MODE_PHOTO = 1
|
const val TAKE_PHOTO_VIDEO_MODE_PHOTO = 1
|
||||||
const val TAKE_PHOTO_VIDEO_MODE_VIDEO = 2
|
const val TAKE_PHOTO_VIDEO_MODE_VIDEO = 2
|
||||||
|
|
||||||
|
const val HAD_EXISTING_LEGACY_DATA = "HAD_EXISTING_LEGACY_DATA"
|
||||||
|
const val IS_ON_RUST_CRYPTO = "IS_ON_RUST_CRYPTO"
|
||||||
// Background sync modes
|
// Background sync modes
|
||||||
|
|
||||||
// some preferences keys must be kept after a logout
|
// some preferences keys must be kept after a logout
|
||||||
|
@ -1278,4 +1280,24 @@ class VectorPreferences @Inject constructor(
|
||||||
putBoolean(SETTINGS_NEW_LOGIN_ALERT_SHOWN_FOR_DEVICE + deviceId, true)
|
putBoolean(SETTINGS_NEW_LOGIN_ALERT_SHOWN_FOR_DEVICE + deviceId, true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun hadExistingLegacyData(): Boolean {
|
||||||
|
return defaultPrefs.getBoolean(HAD_EXISTING_LEGACY_DATA, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setHadExistingLegacyData(had: Boolean) {
|
||||||
|
defaultPrefs.edit {
|
||||||
|
putBoolean(HAD_EXISTING_LEGACY_DATA, had)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun isOnRustCrypto(): Boolean {
|
||||||
|
return defaultPrefs.getBoolean(IS_ON_RUST_CRYPTO, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setIsOnRustCrypto(boolean: Boolean) {
|
||||||
|
defaultPrefs.edit {
|
||||||
|
putBoolean(IS_ON_RUST_CRYPTO, boolean)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,8 @@ class GetEncryptionTrustLevelForDeviceUseCase @Inject constructor(
|
||||||
private val getEncryptionTrustLevelForOtherDeviceUseCase: GetEncryptionTrustLevelForOtherDeviceUseCase,
|
private val getEncryptionTrustLevelForOtherDeviceUseCase: GetEncryptionTrustLevelForOtherDeviceUseCase,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
// XXX why is this using the RoomEncryptionTrustLevel?
|
||||||
|
// should be using a new DeviceTrustShield enum
|
||||||
fun execute(currentSessionCrossSigningInfo: CurrentSessionCrossSigningInfo, cryptoDeviceInfo: CryptoDeviceInfo?): RoomEncryptionTrustLevel? {
|
fun execute(currentSessionCrossSigningInfo: CurrentSessionCrossSigningInfo, cryptoDeviceInfo: CryptoDeviceInfo?): RoomEncryptionTrustLevel? {
|
||||||
if (cryptoDeviceInfo == null) {
|
if (cryptoDeviceInfo == null) {
|
||||||
return null
|
return null
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
package im.vector.app.features.settings.troubleshoot
|
package im.vector.app.features.settings.troubleshoot
|
||||||
|
|
||||||
import androidx.fragment.app.FragmentActivity
|
import androidx.fragment.app.FragmentActivity
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.work.WorkInfo
|
import androidx.work.WorkInfo
|
||||||
import androidx.work.WorkManager
|
import androidx.work.WorkManager
|
||||||
import im.vector.app.R
|
import im.vector.app.R
|
||||||
|
@ -72,13 +73,15 @@ class TestEndpointAsTokenRegistration @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun unregisterThenRegister(testParameters: TestParameters, pushKey: String) {
|
private fun unregisterThenRegister(testParameters: TestParameters, pushKey: String) {
|
||||||
activeSessionHolder.getSafeActiveSession()?.coroutineScope?.launch {
|
val scope = activeSessionHolder.getSafeActiveSession()?.coroutineScope ?: return
|
||||||
|
val io = activeSessionHolder.getActiveSession().coroutineDispatchers.io
|
||||||
|
scope.launch(io) {
|
||||||
unregisterUnifiedPushUseCase.execute(pushersManager)
|
unregisterUnifiedPushUseCase.execute(pushersManager)
|
||||||
registerUnifiedPush(distributor = "", testParameters, pushKey)
|
registerUnifiedPush(distributor = "", testParameters, pushKey)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun registerUnifiedPush(
|
private suspend fun registerUnifiedPush(
|
||||||
distributor: String,
|
distributor: String,
|
||||||
testParameters: TestParameters,
|
testParameters: TestParameters,
|
||||||
pushKey: String,
|
pushKey: String,
|
||||||
|
@ -106,7 +109,9 @@ class TestEndpointAsTokenRegistration @Inject constructor(
|
||||||
pushKey: String,
|
pushKey: String,
|
||||||
) {
|
) {
|
||||||
unifiedPushHelper.showSelectDistributorDialog(context) { selection ->
|
unifiedPushHelper.showSelectDistributorDialog(context) { selection ->
|
||||||
registerUnifiedPush(distributor = selection, testParameters, pushKey)
|
context.lifecycleScope.launch {
|
||||||
|
registerUnifiedPush(distributor = selection, testParameters, pushKey)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@ package im.vector.app.core.device
|
||||||
import im.vector.app.test.fakes.FakeActiveSessionHolder
|
import im.vector.app.test.fakes.FakeActiveSessionHolder
|
||||||
import im.vector.app.test.fakes.FakeCryptoService
|
import im.vector.app.test.fakes.FakeCryptoService
|
||||||
import im.vector.app.test.fakes.FakeSession
|
import im.vector.app.test.fakes.FakeSession
|
||||||
|
import kotlinx.coroutines.test.runTest
|
||||||
import org.amshove.kluent.shouldBeEqualTo
|
import org.amshove.kluent.shouldBeEqualTo
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
|
||||||
|
@ -31,7 +32,7 @@ class DefaultGetDeviceInfoUseCaseTest {
|
||||||
private val getDeviceInfoUseCase = DefaultGetDeviceInfoUseCase(activeSessionHolder.instance)
|
private val getDeviceInfoUseCase = DefaultGetDeviceInfoUseCase(activeSessionHolder.instance)
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `when execute, then get crypto device info`() {
|
fun `when execute, then get crypto device info`() = runTest {
|
||||||
val result = getDeviceInfoUseCase.execute()
|
val result = getDeviceInfoUseCase.execute()
|
||||||
|
|
||||||
result shouldBeEqualTo cryptoService.cryptoDeviceInfo
|
result shouldBeEqualTo cryptoService.cryptoDeviceInfo
|
||||||
|
|
|
@ -29,6 +29,7 @@ import im.vector.app.test.fixtures.CryptoDeviceInfoFixture.aCryptoDeviceInfo
|
||||||
import im.vector.app.test.fixtures.PusherFixture
|
import im.vector.app.test.fixtures.PusherFixture
|
||||||
import im.vector.app.test.fixtures.SessionParamsFixture
|
import im.vector.app.test.fixtures.SessionParamsFixture
|
||||||
import io.mockk.mockk
|
import io.mockk.mockk
|
||||||
|
import kotlinx.coroutines.test.runTest
|
||||||
import org.amshove.kluent.shouldBeEqualTo
|
import org.amshove.kluent.shouldBeEqualTo
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.matrix.android.sdk.api.session.crypto.model.UnsignedDeviceInfo
|
import org.matrix.android.sdk.api.session.crypto.model.UnsignedDeviceInfo
|
||||||
|
@ -56,7 +57,7 @@ class PushersManagerTest {
|
||||||
)
|
)
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `when enqueueRegisterPusher, then HttpPusher created and enqueued`() {
|
fun `when enqueueRegisterPusher, then HttpPusher created and enqueued`() = runTest {
|
||||||
val pushKey = "abc"
|
val pushKey = "abc"
|
||||||
val gateway = "123"
|
val gateway = "123"
|
||||||
val pusherAppId = "app-id"
|
val pusherAppId = "app-id"
|
||||||
|
|
|
@ -84,5 +84,5 @@ class FakeCryptoService(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getMyCryptoDevice() = cryptoDeviceInfo
|
override suspend fun getMyCryptoDevice() = cryptoDeviceInfo
|
||||||
}
|
}
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue