Merge pull request #726 from vector-im/feature/sign_x_stabilization

Registration stabilization
This commit is contained in:
Benoit Marty 2019-12-04 16:26:48 +01:00 committed by GitHub
commit 9fb50dde32
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 160 additions and 108 deletions

View File

@ -16,6 +16,8 @@ Other changes:
Bugfix 🐛: Bugfix 🐛:
- Do not show long click help if only invitation are displayed - Do not show long click help if only invitation are displayed
- Fix emoji filtering not working - Fix emoji filtering not working
- Fix issue of closing Realm in another thread (#725)
- Attempt to properly cancel the crypto module when user signs out (#724)
Translations 🗣: Translations 🗣:
- -

View File

@ -17,6 +17,7 @@
package im.vector.matrix.android.api.session.file package im.vector.matrix.android.api.session.file
import im.vector.matrix.android.api.MatrixCallback import im.vector.matrix.android.api.MatrixCallback
import im.vector.matrix.android.api.util.Cancelable
import im.vector.matrix.android.internal.crypto.attachments.ElementToDecrypt import im.vector.matrix.android.internal.crypto.attachments.ElementToDecrypt
import java.io.File import java.io.File
@ -47,5 +48,5 @@ interface FileService {
fileName: String, fileName: String,
url: String?, url: String?,
elementToDecrypt: ElementToDecrypt?, elementToDecrypt: ElementToDecrypt?,
callback: MatrixCallback<File>) callback: MatrixCallback<File>): Cancelable
} }

View File

@ -22,6 +22,8 @@ import im.vector.matrix.android.internal.database.awaitTransaction
import im.vector.matrix.android.internal.di.AuthDatabase import im.vector.matrix.android.internal.di.AuthDatabase
import io.realm.Realm import io.realm.Realm
import io.realm.RealmConfiguration import io.realm.RealmConfiguration
import io.realm.exceptions.RealmPrimaryKeyConstraintException
import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
internal class RealmSessionParamsStore @Inject constructor(private val mapper: SessionParamsMapper, internal class RealmSessionParamsStore @Inject constructor(private val mapper: SessionParamsMapper,
@ -63,7 +65,12 @@ internal class RealmSessionParamsStore @Inject constructor(private val mapper: S
awaitTransaction(realmConfiguration) { awaitTransaction(realmConfiguration) {
val entity = mapper.map(sessionParams) val entity = mapper.map(sessionParams)
if (entity != null) { if (entity != null) {
it.insert(entity) try {
it.insert(entity)
} catch (e: RealmPrimaryKeyConstraintException) {
Timber.e(e, "Something wrong happened during previous session creation. Override with new credentials")
it.insertOrUpdate(entity)
}
} }
} }
} }

View File

@ -37,6 +37,8 @@ import im.vector.matrix.android.internal.session.SessionScope
import im.vector.matrix.android.internal.session.cache.ClearCacheTask import im.vector.matrix.android.internal.session.cache.ClearCacheTask
import im.vector.matrix.android.internal.session.cache.RealmClearCacheTask import im.vector.matrix.android.internal.session.cache.RealmClearCacheTask
import io.realm.RealmConfiguration import io.realm.RealmConfiguration
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob
import retrofit2.Retrofit import retrofit2.Retrofit
import java.io.File import java.io.File
@ -66,6 +68,13 @@ internal abstract class CryptoModule {
.build() .build()
} }
@JvmStatic
@Provides
@SessionScope
fun providesCryptoCoroutineScope(): CoroutineScope {
return CoroutineScope(SupervisorJob())
}
@JvmStatic @JvmStatic
@Provides @Provides
@CryptoDatabase @CryptoDatabase

View File

@ -132,7 +132,8 @@ internal class DefaultCryptoService @Inject constructor(
private val loadRoomMembersTask: LoadRoomMembersTask, private val loadRoomMembersTask: LoadRoomMembersTask,
private val monarchy: Monarchy, private val monarchy: Monarchy,
private val coroutineDispatchers: MatrixCoroutineDispatchers, private val coroutineDispatchers: MatrixCoroutineDispatchers,
private val taskExecutor: TaskExecutor private val taskExecutor: TaskExecutor,
private val cryptoCoroutineScope: CoroutineScope
) : CryptoService { ) : CryptoService {
private val uiHandler = Handler(Looper.getMainLooper()) private val uiHandler = Handler(Looper.getMainLooper())
@ -243,7 +244,8 @@ internal class DefaultCryptoService @Inject constructor(
return return
} }
isStarting.set(true) isStarting.set(true)
GlobalScope.launch(coroutineDispatchers.crypto) {
cryptoCoroutineScope.launch(coroutineDispatchers.crypto) {
internalStart(isInitialSync) internalStart(isInitialSync)
} }
} }
@ -269,10 +271,9 @@ internal class DefaultCryptoService @Inject constructor(
isStarted.set(true) isStarted.set(true)
}, },
{ {
Timber.e("Start failed: $it")
delay(1000)
isStarting.set(false) isStarting.set(false)
internalStart(isInitialSync) isStarted.set(false)
Timber.e(it, "Start failed")
} }
) )
} }
@ -281,9 +282,12 @@ internal class DefaultCryptoService @Inject constructor(
* Close the crypto * Close the crypto
*/ */
fun close() = runBlocking(coroutineDispatchers.crypto) { fun close() = runBlocking(coroutineDispatchers.crypto) {
cryptoCoroutineScope.coroutineContext.cancelChildren(CancellationException("Closing crypto module"))
outgoingRoomKeyRequestManager.stop()
olmDevice.release() olmDevice.release()
cryptoStore.close() cryptoStore.close()
outgoingRoomKeyRequestManager.stop()
} }
// Aways enabled on RiotX // Aways enabled on RiotX
@ -305,19 +309,21 @@ internal class DefaultCryptoService @Inject constructor(
* @param syncResponse the syncResponse * @param syncResponse the syncResponse
*/ */
fun onSyncCompleted(syncResponse: SyncResponse) { fun onSyncCompleted(syncResponse: SyncResponse) {
GlobalScope.launch(coroutineDispatchers.crypto) { cryptoCoroutineScope.launch(coroutineDispatchers.crypto) {
if (syncResponse.deviceLists != null) { runCatching {
deviceListManager.handleDeviceListsChanges(syncResponse.deviceLists.changed, syncResponse.deviceLists.left) if (syncResponse.deviceLists != null) {
} deviceListManager.handleDeviceListsChanges(syncResponse.deviceLists.changed, syncResponse.deviceLists.left)
if (syncResponse.deviceOneTimeKeysCount != null) { }
val currentCount = syncResponse.deviceOneTimeKeysCount.signedCurve25519 ?: 0 if (syncResponse.deviceOneTimeKeysCount != null) {
oneTimeKeysUploader.updateOneTimeKeyCount(currentCount) val currentCount = syncResponse.deviceOneTimeKeysCount.signedCurve25519 ?: 0
} oneTimeKeysUploader.updateOneTimeKeyCount(currentCount)
if (isStarted()) { }
// Make sure we process to-device messages before generating new one-time-keys #2782 if (isStarted()) {
deviceListManager.refreshOutdatedDeviceLists() // Make sure we process to-device messages before generating new one-time-keys #2782
oneTimeKeysUploader.maybeUploadOneTimeKeys() deviceListManager.refreshOutdatedDeviceLists()
incomingRoomKeyRequestManager.processReceivedRoomKeyRequests() oneTimeKeysUploader.maybeUploadOneTimeKeys()
incomingRoomKeyRequestManager.processReceivedRoomKeyRequests()
}
} }
} }
} }
@ -511,7 +517,7 @@ internal class DefaultCryptoService @Inject constructor(
eventType: String, eventType: String,
roomId: String, roomId: String,
callback: MatrixCallback<MXEncryptEventContentResult>) { callback: MatrixCallback<MXEncryptEventContentResult>) {
GlobalScope.launch(coroutineDispatchers.crypto) { cryptoCoroutineScope.launch(coroutineDispatchers.crypto) {
if (!isStarted()) { if (!isStarted()) {
Timber.v("## encryptEventContent() : wait after e2e init") Timber.v("## encryptEventContent() : wait after e2e init")
internalStart(false) internalStart(false)
@ -571,7 +577,7 @@ internal class DefaultCryptoService @Inject constructor(
* @param callback the callback to return data or null * @param callback the callback to return data or null
*/ */
override fun decryptEventAsync(event: Event, timeline: String, callback: MatrixCallback<MXEventDecryptionResult>) { override fun decryptEventAsync(event: Event, timeline: String, callback: MatrixCallback<MXEventDecryptionResult>) {
GlobalScope.launch { cryptoCoroutineScope.launch {
val result = runCatching { val result = runCatching {
withContext(coroutineDispatchers.crypto) { withContext(coroutineDispatchers.crypto) {
internalDecryptEvent(event, timeline) internalDecryptEvent(event, timeline)
@ -621,7 +627,7 @@ internal class DefaultCryptoService @Inject constructor(
* @param event the event * @param event the event
*/ */
fun onToDeviceEvent(event: Event) { fun onToDeviceEvent(event: Event) {
GlobalScope.launch(coroutineDispatchers.crypto) { cryptoCoroutineScope.launch(coroutineDispatchers.crypto) {
when (event.getClearType()) { when (event.getClearType()) {
EventType.ROOM_KEY, EventType.FORWARDED_ROOM_KEY -> { EventType.ROOM_KEY, EventType.FORWARDED_ROOM_KEY -> {
onRoomKeyEvent(event) onRoomKeyEvent(event)
@ -661,7 +667,7 @@ internal class DefaultCryptoService @Inject constructor(
* @param event the encryption event. * @param event the encryption event.
*/ */
private fun onRoomEncryptionEvent(roomId: String, event: Event) { private fun onRoomEncryptionEvent(roomId: String, event: Event) {
GlobalScope.launch(coroutineDispatchers.crypto) { cryptoCoroutineScope.launch(coroutineDispatchers.crypto) {
val params = LoadRoomMembersTask.Params(roomId) val params = LoadRoomMembersTask.Params(roomId)
try { try {
loadRoomMembersTask.execute(params) loadRoomMembersTask.execute(params)
@ -753,7 +759,7 @@ internal class DefaultCryptoService @Inject constructor(
* @param callback the exported keys * @param callback the exported keys
*/ */
override fun exportRoomKeys(password: String, callback: MatrixCallback<ByteArray>) { override fun exportRoomKeys(password: String, callback: MatrixCallback<ByteArray>) {
GlobalScope.launch(coroutineDispatchers.main) { cryptoCoroutineScope.launch(coroutineDispatchers.main) {
runCatching { runCatching {
exportRoomKeys(password, MXMegolmExportEncryption.DEFAULT_ITERATION_COUNT) exportRoomKeys(password, MXMegolmExportEncryption.DEFAULT_ITERATION_COUNT)
}.foldToCallback(callback) }.foldToCallback(callback)
@ -791,7 +797,7 @@ internal class DefaultCryptoService @Inject constructor(
password: String, password: String,
progressListener: ProgressListener?, progressListener: ProgressListener?,
callback: MatrixCallback<ImportRoomKeysResult>) { callback: MatrixCallback<ImportRoomKeysResult>) {
GlobalScope.launch(coroutineDispatchers.main) { cryptoCoroutineScope.launch(coroutineDispatchers.main) {
runCatching { runCatching {
withContext(coroutineDispatchers.crypto) { withContext(coroutineDispatchers.crypto) {
Timber.v("## importRoomKeys starts") Timber.v("## importRoomKeys starts")
@ -839,7 +845,7 @@ internal class DefaultCryptoService @Inject constructor(
*/ */
fun checkUnknownDevices(userIds: List<String>, callback: MatrixCallback<Unit>) { fun checkUnknownDevices(userIds: List<String>, callback: MatrixCallback<Unit>) {
// force the refresh to ensure that the devices list is up-to-date // force the refresh to ensure that the devices list is up-to-date
GlobalScope.launch(coroutineDispatchers.crypto) { cryptoCoroutineScope.launch(coroutineDispatchers.crypto) {
runCatching { runCatching {
val keys = deviceListManager.downloadKeys(userIds, true) val keys = deviceListManager.downloadKeys(userIds, true)
val unknownDevices = getUnknownDevices(keys) val unknownDevices = getUnknownDevices(keys)
@ -999,7 +1005,7 @@ internal class DefaultCryptoService @Inject constructor(
} }
override fun downloadKeys(userIds: List<String>, forceDownload: Boolean, callback: MatrixCallback<MXUsersDevicesMap<MXDeviceInfo>>) { override fun downloadKeys(userIds: List<String>, forceDownload: Boolean, callback: MatrixCallback<MXUsersDevicesMap<MXDeviceInfo>>) {
GlobalScope.launch(coroutineDispatchers.crypto) { cryptoCoroutineScope.launch(coroutineDispatchers.crypto) {
runCatching { runCatching {
deviceListManager.downloadKeys(userIds, forceDownload) deviceListManager.downloadKeys(userIds, forceDownload)
}.foldToCallback(callback) }.foldToCallback(callback)

View File

@ -25,7 +25,6 @@ import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore
import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.session.SessionScope
import timber.log.Timber import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
import kotlin.collections.ArrayList
@SessionScope @SessionScope
internal class IncomingRoomKeyRequestManager @Inject constructor( internal class IncomingRoomKeyRequestManager @Inject constructor(
@ -51,7 +50,7 @@ internal class IncomingRoomKeyRequestManager @Inject constructor(
* *
* @param event the announcement event. * @param event the announcement event.
*/ */
suspend fun onRoomKeyRequestEvent(event: Event) { fun onRoomKeyRequestEvent(event: Event) {
val roomKeyShare = event.getClearContent().toModel<RoomKeyShare>() val roomKeyShare = event.getClearContent().toModel<RoomKeyShare>()
when (roomKeyShare?.action) { when (roomKeyShare?.action) {
RoomKeyShare.ACTION_SHARE_REQUEST -> receivedRoomKeyRequests.add(IncomingRoomKeyRequest(event)) RoomKeyShare.ACTION_SHARE_REQUEST -> receivedRoomKeyRequests.add(IncomingRoomKeyRequest(event))
@ -78,7 +77,7 @@ internal class IncomingRoomKeyRequestManager @Inject constructor(
Timber.v("m.room_key_request from $userId:$deviceId for $roomId / ${body.sessionId} id ${request.requestId}") Timber.v("m.room_key_request from $userId:$deviceId for $roomId / ${body.sessionId} id ${request.requestId}")
if (userId == null || credentials.userId != userId) { if (userId == null || credentials.userId != userId) {
// TODO: determine if we sent this device the keys already: in // TODO: determine if we sent this device the keys already: in
Timber.e("## processReceivedRoomKeyRequests() : Ignoring room key request from other user for now") Timber.w("## processReceivedRoomKeyRequests() : Ignoring room key request from other user for now")
return return
} }
// TODO: should we queue up requests we don't yet have keys for, in case they turn up later? // TODO: should we queue up requests we don't yet have keys for, in case they turn up later?
@ -86,11 +85,11 @@ internal class IncomingRoomKeyRequestManager @Inject constructor(
// the keys for the requested events, and can drop the requests. // the keys for the requested events, and can drop the requests.
val decryptor = roomDecryptorProvider.getRoomDecryptor(roomId, alg) val decryptor = roomDecryptorProvider.getRoomDecryptor(roomId, alg)
if (null == decryptor) { if (null == decryptor) {
Timber.e("## processReceivedRoomKeyRequests() : room key request for unknown $alg in room $roomId") Timber.w("## processReceivedRoomKeyRequests() : room key request for unknown $alg in room $roomId")
continue continue
} }
if (!decryptor.hasKeysForKeyRequest(request)) { if (!decryptor.hasKeysForKeyRequest(request)) {
Timber.e("## processReceivedRoomKeyRequests() : room key request for unknown session ${body.sessionId!!}") Timber.w("## processReceivedRoomKeyRequests() : room key request for unknown session ${body.sessionId!!}")
cryptoStore.deleteIncomingRoomKeyRequest(request) cryptoStore.deleteIncomingRoomKeyRequest(request)
continue continue
} }
@ -139,7 +138,7 @@ internal class IncomingRoomKeyRequestManager @Inject constructor(
if (null != receivedRoomKeyRequestCancellations) { if (null != receivedRoomKeyRequestCancellations) {
for (request in receivedRoomKeyRequestCancellations!!) { for (request in receivedRoomKeyRequestCancellations!!) {
Timber.v("## ## processReceivedRoomKeyRequests() : m.room_key_request cancellation for " + request.userId Timber.v("## ## processReceivedRoomKeyRequests() : m.room_key_request cancellation for " + request.userId
+ ":" + request.deviceId + " id " + request.requestId) + ":" + request.deviceId + " id " + request.requestId)
// we should probably only notify the app of cancellations we told it // we should probably only notify the app of cancellations we told it
// about, but we don't currently have a record of that, so we just pass // about, but we don't currently have a record of that, so we just pass

View File

@ -764,7 +764,7 @@ internal class MXOlmDevice @Inject constructor(
return session return session
} }
} else { } else {
Timber.e("## getInboundGroupSession() : Cannot retrieve inbound group session $sessionId") Timber.w("## getInboundGroupSession() : Cannot retrieve inbound group session $sessionId")
throw MXCryptoError.Base(MXCryptoError.ErrorType.UNKNOWN_INBOUND_SESSION_ID, MXCryptoError.UNKNOWN_INBOUND_SESSION_ID_REASON) throw MXCryptoError.Base(MXCryptoError.ErrorType.UNKNOWN_INBOUND_SESSION_ID, MXCryptoError.UNKNOWN_INBOUND_SESSION_ID_REASON)
} }
} }

View File

@ -63,6 +63,7 @@ internal class OutgoingRoomKeyRequestManager @Inject constructor(
*/ */
fun stop() { fun stop() {
isClientRunning = false isClientRunning = false
stopTimer()
} }
/** /**
@ -171,6 +172,10 @@ internal class OutgoingRoomKeyRequestManager @Inject constructor(
}, SEND_KEY_REQUESTS_DELAY_MS.toLong()) }, SEND_KEY_REQUESTS_DELAY_MS.toLong())
} }
private fun stopTimer() {
BACKGROUND_HANDLER.removeCallbacksAndMessages(null)
}
// look for and send any queued requests. Runs itself recursively until // look for and send any queued requests. Runs itself recursively until
// there are no more requests, or there is an error (in which case, the // there are no more requests, or there is an error (in which case, the
// timer will be restarted before the promise resolves). // timer will be restarted before the promise resolves).
@ -187,7 +192,7 @@ internal class OutgoingRoomKeyRequestManager @Inject constructor(
OutgoingRoomKeyRequest.RequestState.CANCELLATION_PENDING_AND_WILL_RESEND)) OutgoingRoomKeyRequest.RequestState.CANCELLATION_PENDING_AND_WILL_RESEND))
if (null == outgoingRoomKeyRequest) { if (null == outgoingRoomKeyRequest) {
Timber.e("## sendOutgoingRoomKeyRequests() : No more outgoing room key requests") Timber.v("## sendOutgoingRoomKeyRequests() : No more outgoing room key requests")
sendOutgoingRoomKeyRequestsRunning.set(false) sendOutgoingRoomKeyRequestsRunning.set(false)
return return
} }

View File

@ -34,7 +34,7 @@ import im.vector.matrix.android.internal.crypto.model.rest.RoomKeyRequestBody
import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore
import im.vector.matrix.android.internal.crypto.tasks.SendToDeviceTask import im.vector.matrix.android.internal.crypto.tasks.SendToDeviceTask
import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import timber.log.Timber import timber.log.Timber
@ -46,8 +46,9 @@ internal class MXMegolmDecryption(private val userId: String,
private val ensureOlmSessionsForDevicesAction: EnsureOlmSessionsForDevicesAction, private val ensureOlmSessionsForDevicesAction: EnsureOlmSessionsForDevicesAction,
private val cryptoStore: IMXCryptoStore, private val cryptoStore: IMXCryptoStore,
private val sendToDeviceTask: SendToDeviceTask, private val sendToDeviceTask: SendToDeviceTask,
private val coroutineDispatchers: MatrixCoroutineDispatchers) private val coroutineDispatchers: MatrixCoroutineDispatchers,
: IMXDecrypting { private val cryptoCoroutineScope: CoroutineScope
) : IMXDecrypting {
var newSessionListener: NewSessionListener? = null var newSessionListener: NewSessionListener? = null
@ -61,7 +62,7 @@ internal class MXMegolmDecryption(private val userId: String,
return decryptEvent(event, timeline, true) return decryptEvent(event, timeline, true)
} }
private suspend fun decryptEvent(event: Event, timeline: String, requestKeysOnFail: Boolean): MXEventDecryptionResult { private fun decryptEvent(event: Event, timeline: String, requestKeysOnFail: Boolean): MXEventDecryptionResult {
if (event.roomId.isNullOrBlank()) { if (event.roomId.isNullOrBlank()) {
throw MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_FIELDS, MXCryptoError.MISSING_FIELDS_REASON) throw MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_FIELDS, MXCryptoError.MISSING_FIELDS_REASON)
} }
@ -292,7 +293,7 @@ internal class MXMegolmDecryption(private val userId: String,
return return
} }
val userId = request.userId ?: return val userId = request.userId ?: return
GlobalScope.launch(coroutineDispatchers.crypto) { cryptoCoroutineScope.launch(coroutineDispatchers.crypto) {
runCatching { deviceListManager.downloadKeys(listOf(userId), false) } runCatching { deviceListManager.downloadKeys(listOf(userId), false) }
.mapCatching { .mapCatching {
val deviceId = request.deviceId val deviceId = request.deviceId

View File

@ -25,17 +25,21 @@ import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore
import im.vector.matrix.android.internal.crypto.tasks.SendToDeviceTask import im.vector.matrix.android.internal.crypto.tasks.SendToDeviceTask
import im.vector.matrix.android.internal.di.UserId import im.vector.matrix.android.internal.di.UserId
import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
import kotlinx.coroutines.CoroutineScope
import javax.inject.Inject import javax.inject.Inject
internal class MXMegolmDecryptionFactory @Inject constructor(@UserId private val userId: String, internal class MXMegolmDecryptionFactory @Inject constructor(
private val olmDevice: MXOlmDevice, @UserId private val userId: String,
private val deviceListManager: DeviceListManager, private val olmDevice: MXOlmDevice,
private val outgoingRoomKeyRequestManager: OutgoingRoomKeyRequestManager, private val deviceListManager: DeviceListManager,
private val messageEncrypter: MessageEncrypter, private val outgoingRoomKeyRequestManager: OutgoingRoomKeyRequestManager,
private val ensureOlmSessionsForDevicesAction: EnsureOlmSessionsForDevicesAction, private val messageEncrypter: MessageEncrypter,
private val cryptoStore: IMXCryptoStore, private val ensureOlmSessionsForDevicesAction: EnsureOlmSessionsForDevicesAction,
private val sendToDeviceTask: SendToDeviceTask, private val cryptoStore: IMXCryptoStore,
private val coroutineDispatchers: MatrixCoroutineDispatchers) { private val sendToDeviceTask: SendToDeviceTask,
private val coroutineDispatchers: MatrixCoroutineDispatchers,
private val cryptoCoroutineScope: CoroutineScope
) {
fun create(): MXMegolmDecryption { fun create(): MXMegolmDecryption {
return MXMegolmDecryption( return MXMegolmDecryption(
@ -47,6 +51,7 @@ internal class MXMegolmDecryptionFactory @Inject constructor(@UserId private val
ensureOlmSessionsForDevicesAction, ensureOlmSessionsForDevicesAction,
cryptoStore, cryptoStore,
sendToDeviceTask, sendToDeviceTask,
coroutineDispatchers) coroutineDispatchers,
cryptoCoroutineScope)
} }
} }

View File

@ -59,7 +59,7 @@ import im.vector.matrix.android.internal.task.configureWith
import im.vector.matrix.android.internal.util.JsonCanonicalizer import im.vector.matrix.android.internal.util.JsonCanonicalizer
import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
import im.vector.matrix.android.internal.util.awaitCallback import im.vector.matrix.android.internal.util.awaitCallback
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import org.matrix.olm.OlmException import org.matrix.olm.OlmException
@ -102,7 +102,8 @@ internal class KeysBackup @Inject constructor(
private val updateKeysBackupVersionTask: UpdateKeysBackupVersionTask, private val updateKeysBackupVersionTask: UpdateKeysBackupVersionTask,
// Task executor // Task executor
private val taskExecutor: TaskExecutor, private val taskExecutor: TaskExecutor,
private val coroutineDispatchers: MatrixCoroutineDispatchers private val coroutineDispatchers: MatrixCoroutineDispatchers,
private val cryptoCoroutineScope: CoroutineScope
) : KeysBackupService { ) : KeysBackupService {
private val uiHandler = Handler(Looper.getMainLooper()) private val uiHandler = Handler(Looper.getMainLooper())
@ -143,7 +144,7 @@ internal class KeysBackup @Inject constructor(
override fun prepareKeysBackupVersion(password: String?, override fun prepareKeysBackupVersion(password: String?,
progressListener: ProgressListener?, progressListener: ProgressListener?,
callback: MatrixCallback<MegolmBackupCreationInfo>) { callback: MatrixCallback<MegolmBackupCreationInfo>) {
GlobalScope.launch(coroutineDispatchers.main) { cryptoCoroutineScope.launch(coroutineDispatchers.main) {
runCatching { runCatching {
withContext(coroutineDispatchers.crypto) { withContext(coroutineDispatchers.crypto) {
val olmPkDecryption = OlmPkDecryption() val olmPkDecryption = OlmPkDecryption()
@ -233,7 +234,7 @@ internal class KeysBackup @Inject constructor(
} }
override fun deleteBackup(version: String, callback: MatrixCallback<Unit>?) { override fun deleteBackup(version: String, callback: MatrixCallback<Unit>?) {
GlobalScope.launch(coroutineDispatchers.main) { cryptoCoroutineScope.launch(coroutineDispatchers.main) {
withContext(coroutineDispatchers.crypto) { withContext(coroutineDispatchers.crypto) {
// If we're currently backing up to this backup... stop. // If we're currently backing up to this backup... stop.
// (We start using it automatically in createKeysBackupVersion so this is symmetrical). // (We start using it automatically in createKeysBackupVersion so this is symmetrical).
@ -344,9 +345,7 @@ internal class KeysBackup @Inject constructor(
} }
}) })
} }
} }.also { keysBackupStateManager.addListener(it) }
keysBackupStateManager.addListener(keysBackupStateListener!!)
backupKeys() backupKeys()
} }
@ -448,7 +447,7 @@ internal class KeysBackup @Inject constructor(
callback.onFailure(IllegalArgumentException("Missing element")) callback.onFailure(IllegalArgumentException("Missing element"))
} else { } else {
GlobalScope.launch(coroutineDispatchers.main) { cryptoCoroutineScope.launch(coroutineDispatchers.main) {
val updateKeysBackupVersionBody = withContext(coroutineDispatchers.crypto) { val updateKeysBackupVersionBody = withContext(coroutineDispatchers.crypto) {
// Get current signatures, or create an empty set // Get current signatures, or create an empty set
val myUserSignatures = authData.signatures?.get(userId)?.toMutableMap() val myUserSignatures = authData.signatures?.get(userId)?.toMutableMap()
@ -523,7 +522,7 @@ internal class KeysBackup @Inject constructor(
callback: MatrixCallback<Unit>) { callback: MatrixCallback<Unit>) {
Timber.v("trustKeysBackupVersionWithRecoveryKey: version ${keysBackupVersion.version}") Timber.v("trustKeysBackupVersionWithRecoveryKey: version ${keysBackupVersion.version}")
GlobalScope.launch(coroutineDispatchers.main) { cryptoCoroutineScope.launch(coroutineDispatchers.main) {
val isValid = withContext(coroutineDispatchers.crypto) { val isValid = withContext(coroutineDispatchers.crypto) {
isValidRecoveryKeyForKeysBackupVersion(recoveryKey, keysBackupVersion) isValidRecoveryKeyForKeysBackupVersion(recoveryKey, keysBackupVersion)
} }
@ -543,7 +542,7 @@ internal class KeysBackup @Inject constructor(
callback: MatrixCallback<Unit>) { callback: MatrixCallback<Unit>) {
Timber.v("trustKeysBackupVersionWithPassphrase: version ${keysBackupVersion.version}") Timber.v("trustKeysBackupVersionWithPassphrase: version ${keysBackupVersion.version}")
GlobalScope.launch(coroutineDispatchers.main) { cryptoCoroutineScope.launch(coroutineDispatchers.main) {
val recoveryKey = withContext(coroutineDispatchers.crypto) { val recoveryKey = withContext(coroutineDispatchers.crypto) {
recoveryKeyFromPassword(password, keysBackupVersion, null) recoveryKeyFromPassword(password, keysBackupVersion, null)
} }
@ -614,7 +613,7 @@ internal class KeysBackup @Inject constructor(
callback: MatrixCallback<ImportRoomKeysResult>) { callback: MatrixCallback<ImportRoomKeysResult>) {
Timber.v("restoreKeysWithRecoveryKey: From backup version: ${keysVersionResult.version}") Timber.v("restoreKeysWithRecoveryKey: From backup version: ${keysVersionResult.version}")
GlobalScope.launch(coroutineDispatchers.main) { cryptoCoroutineScope.launch(coroutineDispatchers.main) {
runCatching { runCatching {
val decryption = withContext(coroutineDispatchers.crypto) { val decryption = withContext(coroutineDispatchers.crypto) {
// Check if the recovery is valid before going any further // Check if the recovery is valid before going any further
@ -695,7 +694,7 @@ internal class KeysBackup @Inject constructor(
callback: MatrixCallback<ImportRoomKeysResult>) { callback: MatrixCallback<ImportRoomKeysResult>) {
Timber.v("[MXKeyBackup] restoreKeyBackup with password: From backup version: ${keysBackupVersion.version}") Timber.v("[MXKeyBackup] restoreKeyBackup with password: From backup version: ${keysBackupVersion.version}")
GlobalScope.launch(coroutineDispatchers.main) { cryptoCoroutineScope.launch(coroutineDispatchers.main) {
runCatching { runCatching {
val progressListener = if (stepProgressListener != null) { val progressListener = if (stepProgressListener != null) {
object : ProgressListener { object : ProgressListener {
@ -729,8 +728,8 @@ internal class KeysBackup @Inject constructor(
* parameters and always returns a KeysBackupData object through the Callback * parameters and always returns a KeysBackupData object through the Callback
*/ */
private suspend fun getKeys(sessionId: String?, private suspend fun getKeys(sessionId: String?,
roomId: String?, roomId: String?,
version: String): KeysBackupData { version: String): KeysBackupData {
return if (roomId != null && sessionId != null) { return if (roomId != null && sessionId != null) {
// Get key for the room and for the session // Get key for the room and for the session
val data = getRoomSessionDataTask.execute(GetRoomSessionDataTask.Params(roomId, sessionId, version)) val data = getRoomSessionDataTask.execute(GetRoomSessionDataTask.Params(roomId, sessionId, version))
@ -1154,7 +1153,7 @@ internal class KeysBackup @Inject constructor(
keysBackupStateManager.state = KeysBackupState.BackingUp keysBackupStateManager.state = KeysBackupState.BackingUp
GlobalScope.launch(coroutineDispatchers.main) { cryptoCoroutineScope.launch(coroutineDispatchers.main) {
withContext(coroutineDispatchers.crypto) { withContext(coroutineDispatchers.crypto) {
Timber.v("backupKeys: 2 - Encrypting keys") Timber.v("backupKeys: 2 - Encrypting keys")

View File

@ -53,10 +53,10 @@ internal class DefaultUploadKeysTask @Inject constructor(private val cryptoApi:
} }
return executeRequest { return executeRequest {
if (encodedDeviceId.isNullOrBlank()) { apiCall = if (encodedDeviceId.isBlank()) {
apiCall = cryptoApi.uploadKeys(body) cryptoApi.uploadKeys(body)
} else { } else {
apiCall = cryptoApi.uploadKeys(encodedDeviceId, body) cryptoApi.uploadKeys(encodedDeviceId, body)
} }
} }
} }

View File

@ -83,7 +83,7 @@ internal class SessionRealmConfigurationFactory @Inject constructor(private val
try { try {
File(directory, file).deleteRecursively() File(directory, file).deleteRecursively()
} catch (e: Exception) { } catch (e: Exception) {
Timber.e(e, "Unable to move files") Timber.e(e, "Unable to delete files")
} }
} }
} }

View File

@ -16,26 +16,27 @@
package im.vector.matrix.android.internal.database.query package im.vector.matrix.android.internal.database.query
import im.vector.matrix.android.internal.database.awaitTransaction
import im.vector.matrix.android.internal.database.model.FilterEntity import im.vector.matrix.android.internal.database.model.FilterEntity
import im.vector.matrix.android.internal.session.filter.FilterFactory import im.vector.matrix.android.internal.session.filter.FilterFactory
import io.realm.Realm import io.realm.Realm
import io.realm.kotlin.createObject
import io.realm.kotlin.where import io.realm.kotlin.where
/**
* Get the current filter
*/
internal fun FilterEntity.Companion.get(realm: Realm): FilterEntity? {
return realm.where<FilterEntity>().findFirst()
}
/** /**
* Get the current filter, create one if it does not exist * Get the current filter, create one if it does not exist
*/ */
internal suspend fun FilterEntity.Companion.getFilter(realm: Realm): FilterEntity { internal fun FilterEntity.Companion.getOrCreate(realm: Realm): FilterEntity {
var filter = realm.where<FilterEntity>().findFirst() return get(realm) ?: realm.createObject<FilterEntity>()
if (filter == null) { .apply {
filter = FilterEntity().apply { filterBodyJson = FilterFactory.createDefaultFilterBody().toJSONString()
filterBodyJson = FilterFactory.createDefaultFilterBody().toJSONString() roomEventFilterJson = FilterFactory.createDefaultRoomFilter().toJSONString()
roomEventFilterJson = FilterFactory.createDefaultRoomFilter().toJSONString() filterId = ""
filterId = "" }
}
awaitTransaction(realm.configuration) {
it.insert(filter)
}
}
return filter
} }

View File

@ -22,12 +22,14 @@ import arrow.core.Try
import im.vector.matrix.android.api.MatrixCallback import im.vector.matrix.android.api.MatrixCallback
import im.vector.matrix.android.api.session.content.ContentUrlResolver import im.vector.matrix.android.api.session.content.ContentUrlResolver
import im.vector.matrix.android.api.session.file.FileService import im.vector.matrix.android.api.session.file.FileService
import im.vector.matrix.android.api.util.Cancelable
import im.vector.matrix.android.internal.crypto.attachments.ElementToDecrypt import im.vector.matrix.android.internal.crypto.attachments.ElementToDecrypt
import im.vector.matrix.android.internal.crypto.attachments.MXEncryptedAttachments import im.vector.matrix.android.internal.crypto.attachments.MXEncryptedAttachments
import im.vector.matrix.android.internal.di.UserMd5 import im.vector.matrix.android.internal.di.UserMd5
import im.vector.matrix.android.internal.extensions.foldToCallback import im.vector.matrix.android.internal.extensions.foldToCallback
import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
import im.vector.matrix.android.internal.util.md5 import im.vector.matrix.android.internal.util.md5
import im.vector.matrix.android.internal.util.toCancelable
import im.vector.matrix.android.internal.util.writeToFile import im.vector.matrix.android.internal.util.writeToFile
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -55,8 +57,8 @@ internal class DefaultFileService @Inject constructor(private val context: Conte
fileName: String, fileName: String,
url: String?, url: String?,
elementToDecrypt: ElementToDecrypt?, elementToDecrypt: ElementToDecrypt?,
callback: MatrixCallback<File>) { callback: MatrixCallback<File>): Cancelable {
GlobalScope.launch(coroutineDispatchers.main) { return GlobalScope.launch(coroutineDispatchers.main) {
withContext(coroutineDispatchers.io) { withContext(coroutineDispatchers.io) {
Try { Try {
val folder = getFolder(downloadMode, id) val folder = getFolder(downloadMode, id)
@ -96,7 +98,7 @@ internal class DefaultFileService @Inject constructor(private val context: Conte
} }
} }
.foldToCallback(callback) .foldToCallback(callback)
} }.toCancelable()
} }
private fun getFolder(downloadMode: FileService.DownloadMode, id: String): File { private fun getFolder(downloadMode: FileService.DownloadMode, id: String): File {

View File

@ -101,7 +101,7 @@ class DefaultInitialSyncProgressService @Inject constructor() : InitialSyncProgr
val parentProgress = (currentProgress * parentWeight).toInt() val parentProgress = (currentProgress * parentWeight).toInt()
it.setProgress(offset + parentProgress) it.setProgress(offset + parentProgress)
} ?: run { } ?: run {
Timber.e("--- ${leaf().nameRes}: $currentProgress") Timber.v("--- ${leaf().nameRes}: $currentProgress")
status.postValue( status.postValue(
InitialSyncProgressService.Status(leaf().nameRes, currentProgress) InitialSyncProgressService.Status(leaf().nameRes, currentProgress)
) )

View File

@ -19,7 +19,8 @@ package im.vector.matrix.android.internal.session.filter
import com.zhuinden.monarchy.Monarchy import com.zhuinden.monarchy.Monarchy
import im.vector.matrix.android.internal.database.model.FilterEntity import im.vector.matrix.android.internal.database.model.FilterEntity
import im.vector.matrix.android.internal.database.model.FilterEntityFields import im.vector.matrix.android.internal.database.model.FilterEntityFields
import im.vector.matrix.android.internal.database.query.getFilter import im.vector.matrix.android.internal.database.query.get
import im.vector.matrix.android.internal.database.query.getOrCreate
import im.vector.matrix.android.internal.util.awaitTransaction import im.vector.matrix.android.internal.util.awaitTransaction
import io.realm.Realm import io.realm.Realm
import io.realm.kotlin.where import io.realm.kotlin.where
@ -29,26 +30,28 @@ internal class DefaultFilterRepository @Inject constructor(private val monarchy:
override suspend fun storeFilter(filterBody: FilterBody, roomEventFilter: RoomEventFilter): Boolean { override suspend fun storeFilter(filterBody: FilterBody, roomEventFilter: RoomEventFilter): Boolean {
return Realm.getInstance(monarchy.realmConfiguration).use { realm -> return Realm.getInstance(monarchy.realmConfiguration).use { realm ->
val filter = FilterEntity.getFilter(realm) val filter = FilterEntity.get(realm)
val result = if (filter.filterBodyJson != filterBody.toJSONString()) { // Filter has changed, or no filter Id yet
// Filter has changed, store it and reset the filter Id filter == null
monarchy.awaitTransaction { || filter.filterBodyJson != filterBody.toJSONString()
|| filter.filterId.isBlank()
}.also { hasChanged ->
if (hasChanged) {
// Filter is new or has changed, store it and reset the filter Id.
// This has to be done outside of the Realm.use(), because awaitTransaction change the current thread
monarchy.awaitTransaction { realm ->
// We manage only one filter for now // We manage only one filter for now
val filterBodyJson = filterBody.toJSONString() val filterBodyJson = filterBody.toJSONString()
val roomEventFilterJson = roomEventFilter.toJSONString() val roomEventFilterJson = roomEventFilter.toJSONString()
val filterEntity = FilterEntity.getFilter(it) val filterEntity = FilterEntity.getOrCreate(realm)
filterEntity.filterBodyJson = filterBodyJson filterEntity.filterBodyJson = filterBodyJson
filterEntity.roomEventFilterJson = roomEventFilterJson filterEntity.roomEventFilterJson = roomEventFilterJson
// Reset filterId // Reset filterId
filterEntity.filterId = "" filterEntity.filterId = ""
} }
true
} else {
filter.filterId.isBlank()
} }
result
} }
} }
@ -67,7 +70,7 @@ internal class DefaultFilterRepository @Inject constructor(private val monarchy:
override suspend fun getFilter(): String { override suspend fun getFilter(): String {
return Realm.getInstance(monarchy.realmConfiguration).use { return Realm.getInstance(monarchy.realmConfiguration).use {
val filter = FilterEntity.getFilter(it) val filter = FilterEntity.getOrCreate(it)
if (filter.filterId.isBlank()) { if (filter.filterId.isBlank()) {
// Use the Json format // Use the Json format
filter.filterBodyJson filter.filterBodyJson
@ -80,7 +83,7 @@ internal class DefaultFilterRepository @Inject constructor(private val monarchy:
override suspend fun getRoomFilter(): String { override suspend fun getRoomFilter(): String {
return Realm.getInstance(monarchy.realmConfiguration).use { return Realm.getInstance(monarchy.realmConfiguration).use {
FilterEntity.getFilter(it).roomEventFilterJson FilterEntity.getOrCreate(it).roomEventFilterJson
} }
} }
} }

View File

@ -17,6 +17,7 @@
package im.vector.matrix.android.internal.session.signout package im.vector.matrix.android.internal.session.signout
import android.content.Context import android.content.Context
import im.vector.matrix.android.BuildConfig
import im.vector.matrix.android.internal.SessionManager import im.vector.matrix.android.internal.SessionManager
import im.vector.matrix.android.internal.auth.SessionParamsStore import im.vector.matrix.android.internal.auth.SessionParamsStore
import im.vector.matrix.android.internal.crypto.CryptoModule import im.vector.matrix.android.internal.crypto.CryptoModule
@ -27,6 +28,8 @@ import im.vector.matrix.android.internal.session.SessionModule
import im.vector.matrix.android.internal.session.cache.ClearCacheTask import im.vector.matrix.android.internal.session.cache.ClearCacheTask
import im.vector.matrix.android.internal.task.Task import im.vector.matrix.android.internal.task.Task
import im.vector.matrix.android.internal.worker.WorkManagerUtil import im.vector.matrix.android.internal.worker.WorkManagerUtil
import io.realm.Realm
import io.realm.RealmConfiguration
import timber.log.Timber import timber.log.Timber
import java.io.File import java.io.File
import javax.inject.Inject import javax.inject.Inject
@ -42,6 +45,8 @@ internal class DefaultSignOutTask @Inject constructor(private val context: Conte
@CryptoDatabase private val clearCryptoDataTask: ClearCacheTask, @CryptoDatabase private val clearCryptoDataTask: ClearCacheTask,
@UserCacheDirectory private val userFile: File, @UserCacheDirectory private val userFile: File,
private val realmKeysUtils: RealmKeysUtils, private val realmKeysUtils: RealmKeysUtils,
@SessionDatabase private val realmSessionConfiguration: RealmConfiguration,
@CryptoDatabase private val realmCryptoConfiguration: RealmConfiguration,
@UserMd5 private val userMd5: String) : SignOutTask { @UserMd5 private val userMd5: String) : SignOutTask {
override suspend fun execute(params: Unit) { override suspend fun execute(params: Unit) {
@ -71,5 +76,15 @@ internal class DefaultSignOutTask @Inject constructor(private val context: Conte
Timber.d("SignOut: clear the database keys") Timber.d("SignOut: clear the database keys")
realmKeysUtils.clear(SessionModule.DB_ALIAS_PREFIX + userMd5) realmKeysUtils.clear(SessionModule.DB_ALIAS_PREFIX + userMd5)
realmKeysUtils.clear(CryptoModule.DB_ALIAS_PREFIX + userMd5) realmKeysUtils.clear(CryptoModule.DB_ALIAS_PREFIX + userMd5)
// Sanity check
if (BuildConfig.DEBUG) {
Realm.getGlobalInstanceCount(realmSessionConfiguration)
.takeIf { it > 0 }
?.let { Timber.e("All realm instance for session has not been closed ($it)") }
Realm.getGlobalInstanceCount(realmCryptoConfiguration)
.takeIf { it > 0 }
?.let { Timber.e("All realm instance for crypto has not been closed ($it)") }
}
} }
} }

View File

@ -97,7 +97,7 @@ class HomeActivity : VectorBaseActivity(), ToolbarConfigurable {
if (status == null) { if (status == null) {
waiting_view.isVisible = false waiting_view.isVisible = false
} else { } else {
Timber.e("${getString(status.statusText)} ${status.percentProgress}") Timber.v("${getString(status.statusText)} ${status.percentProgress}")
waiting_view.setOnClickListener { waiting_view.setOnClickListener {
// block interactions // block interactions
} }

View File

@ -16,5 +16,4 @@
package im.vector.riotx.features.login package im.vector.riotx.features.login
// TODO Check the link with Nad const val MODULAR_LINK = "https://modular.im/?utm_source=riot-x-android&utm_medium=native&utm_campaign=riot-x-android-authentication"
const val MODULAR_LINK = "https://modular.im/?utm_source=riot-web&utm_medium=web&utm_campaign=riot-web-authentication"

View File

@ -109,8 +109,7 @@ class LoginFragment @Inject constructor(
ServerType.Modular -> { ServerType.Modular -> {
loginServerIcon.isVisible = true loginServerIcon.isVisible = true
loginServerIcon.setImageResource(R.drawable.ic_logo_modular) loginServerIcon.setImageResource(R.drawable.ic_logo_modular)
// TODO loginTitle.text = getString(resId, "Modular")
loginTitle.text = getString(resId, "TODO")
loginNotice.text = getString(R.string.login_server_modular_text) loginNotice.text = getString(R.string.login_server_modular_text)
} }
ServerType.Other -> { ServerType.Other -> {

View File

@ -46,8 +46,7 @@ class LoginSignUpSignInSelectionFragment @Inject constructor(
ServerType.Modular -> { ServerType.Modular -> {
loginSignupSigninServerIcon.setImageResource(R.drawable.ic_logo_modular) loginSignupSigninServerIcon.setImageResource(R.drawable.ic_logo_modular)
loginSignupSigninServerIcon.isVisible = true loginSignupSigninServerIcon.isVisible = true
// TODO loginSignupSigninTitle.text = getString(R.string.login_connect_to_modular)
loginSignupSigninTitle.text = getString(R.string.login_connect_to, "TODO MODULAR NAME")
loginSignupSigninText.text = state.homeServerUrlSimple loginSignupSigninText.text = state.homeServerUrlSimple
} }
ServerType.Other -> { ServerType.Other -> {