Make cryptoDevice calls suspendable

This commit is contained in:
valere 2023-05-30 23:47:50 +02:00
parent 83795344ed
commit 268cbb83cd
15 changed files with 88 additions and 42 deletions

1
changelog.d/8482.bugfix Normal file
View file

@ -0,0 +1 @@
fix: Make some crypto calls suspendable to avoid reported ANR

View file

@ -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
} }
@ -536,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()
} }
// //

View file

@ -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>>

View file

@ -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 {

View file

@ -344,8 +344,8 @@ internal class RustCryptoService @Inject constructor(
return olmMachine.getCryptoDeviceInfo(userId, deviceId) return 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)
} }
} }

View file

@ -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)
}
} }
} })
}) }
} }
} }

View file

@ -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 ->

View file

@ -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))
}
} }
} }

View file

@ -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()
} }
} }

View file

@ -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(

View file

@ -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)
}
} }
} }
} }

View file

@ -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))

View file

@ -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(Dispatchers.IO) {
.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(

View file

@ -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 {

View file

@ -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)
}
} }
} }
} }