mirror of
https://github.com/Novaloop-AG/element-x-android.git
synced 2026-11-04 01:19:33 +00:00
Fix ANRs when calculating DB sizes for Sentry (#7017)
* Fix ANRs when calculating DB sizes for Sentry Instead of using the SDK's `Client.getStoreSizes()` method, manually check the sizes using the file system. For this, the `SessionPaths` need to be synchronously accessible from a `MatrixClient`. I removed `SessionPathsProvider` too since it's not needed anymore.
This commit is contained in:
committed by
GitHub
parent
8e9c0bedf2
commit
ac93044f49
+2
@@ -27,6 +27,7 @@ import io.element.android.libraries.matrix.api.media.MediaPreviewService
|
|||||||
import io.element.android.libraries.matrix.api.notification.NotificationService
|
import io.element.android.libraries.matrix.api.notification.NotificationService
|
||||||
import io.element.android.libraries.matrix.api.notificationsettings.NotificationSettingsService
|
import io.element.android.libraries.matrix.api.notificationsettings.NotificationSettingsService
|
||||||
import io.element.android.libraries.matrix.api.oauth.AccountManagementAction
|
import io.element.android.libraries.matrix.api.oauth.AccountManagementAction
|
||||||
|
import io.element.android.libraries.matrix.api.paths.SessionPaths
|
||||||
import io.element.android.libraries.matrix.api.pusher.PushersService
|
import io.element.android.libraries.matrix.api.pusher.PushersService
|
||||||
import io.element.android.libraries.matrix.api.room.BaseRoom
|
import io.element.android.libraries.matrix.api.room.BaseRoom
|
||||||
import io.element.android.libraries.matrix.api.room.JoinedRoom
|
import io.element.android.libraries.matrix.api.room.JoinedRoom
|
||||||
@@ -53,6 +54,7 @@ import java.util.Optional
|
|||||||
interface MatrixClient {
|
interface MatrixClient {
|
||||||
val sessionId: SessionId
|
val sessionId: SessionId
|
||||||
val deviceId: DeviceId
|
val deviceId: DeviceId
|
||||||
|
val sessionPaths: SessionPaths
|
||||||
val userProfile: StateFlow<MatrixUser>
|
val userProfile: StateFlow<MatrixUser>
|
||||||
val roomListService: RoomListService
|
val roomListService: RoomListService
|
||||||
val spaceService: SpaceService
|
val spaceService: SpaceService
|
||||||
|
|||||||
+1
-1
@@ -10,5 +10,5 @@ package io.element.android.libraries.matrix.api.analytics
|
|||||||
import io.element.android.libraries.matrix.api.core.SessionId
|
import io.element.android.libraries.matrix.api.core.SessionId
|
||||||
|
|
||||||
fun interface GetDatabaseSizesUseCase {
|
fun interface GetDatabaseSizesUseCase {
|
||||||
suspend operator fun invoke(sessionId: SessionId): Result<SdkStoreSizes>
|
operator fun invoke(sessionId: SessionId): Result<SdkStoreSizes>
|
||||||
}
|
}
|
||||||
|
|||||||
+20
@@ -0,0 +1,20 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2026 Element Creations Ltd.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||||
|
* Please see LICENSE files in the repository root for full details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.element.android.libraries.matrix.api.paths
|
||||||
|
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
data class SessionPaths(
|
||||||
|
val fileDirectory: File,
|
||||||
|
val cacheDirectory: File,
|
||||||
|
) {
|
||||||
|
fun deleteRecursively() {
|
||||||
|
fileDirectory.deleteRecursively()
|
||||||
|
cacheDirectory.deleteRecursively()
|
||||||
|
}
|
||||||
|
}
|
||||||
+6
-8
@@ -32,6 +32,7 @@ import io.element.android.libraries.matrix.api.linknewdevice.LinkDesktopHandler
|
|||||||
import io.element.android.libraries.matrix.api.linknewdevice.LinkMobileHandler
|
import io.element.android.libraries.matrix.api.linknewdevice.LinkMobileHandler
|
||||||
import io.element.android.libraries.matrix.api.media.MatrixMediaLoader
|
import io.element.android.libraries.matrix.api.media.MatrixMediaLoader
|
||||||
import io.element.android.libraries.matrix.api.oauth.AccountManagementAction
|
import io.element.android.libraries.matrix.api.oauth.AccountManagementAction
|
||||||
|
import io.element.android.libraries.matrix.api.paths.SessionPaths
|
||||||
import io.element.android.libraries.matrix.api.room.BaseRoom
|
import io.element.android.libraries.matrix.api.room.BaseRoom
|
||||||
import io.element.android.libraries.matrix.api.room.CurrentUserMembership
|
import io.element.android.libraries.matrix.api.room.CurrentUserMembership
|
||||||
import io.element.android.libraries.matrix.api.room.JoinedRoom
|
import io.element.android.libraries.matrix.api.room.JoinedRoom
|
||||||
@@ -81,7 +82,6 @@ import io.element.android.libraries.matrix.impl.spaces.RustSpaceService
|
|||||||
import io.element.android.libraries.matrix.impl.sync.RustSyncService
|
import io.element.android.libraries.matrix.impl.sync.RustSyncService
|
||||||
import io.element.android.libraries.matrix.impl.sync.map
|
import io.element.android.libraries.matrix.impl.sync.map
|
||||||
import io.element.android.libraries.matrix.impl.usersearch.UserSearchResultMapper
|
import io.element.android.libraries.matrix.impl.usersearch.UserSearchResultMapper
|
||||||
import io.element.android.libraries.matrix.impl.util.SessionPathsProvider
|
|
||||||
import io.element.android.libraries.matrix.impl.util.cancelAndDestroy
|
import io.element.android.libraries.matrix.impl.util.cancelAndDestroy
|
||||||
import io.element.android.libraries.matrix.impl.util.mxCallbackFlow
|
import io.element.android.libraries.matrix.impl.util.mxCallbackFlow
|
||||||
import io.element.android.libraries.matrix.impl.verification.RustSessionVerificationService
|
import io.element.android.libraries.matrix.impl.verification.RustSessionVerificationService
|
||||||
@@ -137,6 +137,7 @@ import org.matrix.rustcomponents.sdk.RoomPreset as RustRoomPreset
|
|||||||
import org.matrix.rustcomponents.sdk.SyncService as ClientSyncService
|
import org.matrix.rustcomponents.sdk.SyncService as ClientSyncService
|
||||||
|
|
||||||
class RustMatrixClient(
|
class RustMatrixClient(
|
||||||
|
override val sessionPaths: SessionPaths,
|
||||||
private val innerClient: Client,
|
private val innerClient: Client,
|
||||||
private val sessionStore: SessionStore,
|
private val sessionStore: SessionStore,
|
||||||
private val sessionDelegate: RustClientSessionDelegate,
|
private val sessionDelegate: RustClientSessionDelegate,
|
||||||
@@ -187,8 +188,6 @@ class RustMatrixClient(
|
|||||||
sessionDispatcher = sessionDispatcher,
|
sessionDispatcher = sessionDispatcher,
|
||||||
)
|
)
|
||||||
|
|
||||||
private val sessionPathsProvider = SessionPathsProvider(sessionStore)
|
|
||||||
|
|
||||||
private val roomSyncSubscriber: RoomSyncSubscriber = RoomSyncSubscriber(innerRoomListService, dispatchers)
|
private val roomSyncSubscriber: RoomSyncSubscriber = RoomSyncSubscriber(innerRoomListService, dispatchers)
|
||||||
|
|
||||||
override val roomListService: RoomListService = RustRoomListService(
|
override val roomListService: RoomListService = RustRoomListService(
|
||||||
@@ -824,17 +823,16 @@ class RustMatrixClient(
|
|||||||
private suspend fun getCacheSize(
|
private suspend fun getCacheSize(
|
||||||
includeCryptoDb: Boolean = false,
|
includeCryptoDb: Boolean = false,
|
||||||
): Long = withContext(sessionDispatcher) {
|
): Long = withContext(sessionDispatcher) {
|
||||||
val sessionDirectory = sessionPathsProvider.provides(sessionId) ?: return@withContext 0L
|
val cacheSize = sessionPaths.cacheDirectory.getSizeOfFiles()
|
||||||
val cacheSize = sessionDirectory.cacheDirectory.getSizeOfFiles()
|
|
||||||
if (includeCryptoDb) {
|
if (includeCryptoDb) {
|
||||||
cacheSize + sessionDirectory.fileDirectory.getSizeOfFiles()
|
cacheSize + sessionPaths.fileDirectory.getSizeOfFiles()
|
||||||
} else {
|
} else {
|
||||||
cacheSize + listOf(
|
cacheSize + listOf(
|
||||||
"matrix-sdk-state.sqlite3",
|
"matrix-sdk-state.sqlite3",
|
||||||
"matrix-sdk-state.sqlite3-shm",
|
"matrix-sdk-state.sqlite3-shm",
|
||||||
"matrix-sdk-state.sqlite3-wal",
|
"matrix-sdk-state.sqlite3-wal",
|
||||||
).map { fileName ->
|
).map { fileName ->
|
||||||
File(sessionDirectory.fileDirectory, fileName)
|
File(sessionPaths.fileDirectory, fileName)
|
||||||
}.sumOf { file ->
|
}.sumOf { file ->
|
||||||
file.length()
|
file.length()
|
||||||
}
|
}
|
||||||
@@ -843,7 +841,7 @@ class RustMatrixClient(
|
|||||||
|
|
||||||
private suspend fun deleteSessionDirectory() = withContext(sessionDispatcher) {
|
private suspend fun deleteSessionDirectory() = withContext(sessionDispatcher) {
|
||||||
// Delete all the files for this session
|
// Delete all the files for this session
|
||||||
sessionPathsProvider.provides(sessionId)?.deleteRecursively()
|
sessionPaths.deleteRecursively()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun scheduleDatabaseVacuum() {
|
private fun scheduleDatabaseVacuum() {
|
||||||
|
|||||||
+4
-3
@@ -17,9 +17,9 @@ import io.element.android.libraries.di.CacheDirectory
|
|||||||
import io.element.android.libraries.di.annotations.AppCoroutineScope
|
import io.element.android.libraries.di.annotations.AppCoroutineScope
|
||||||
import io.element.android.libraries.featureflag.api.FeatureFlagService
|
import io.element.android.libraries.featureflag.api.FeatureFlagService
|
||||||
import io.element.android.libraries.featureflag.api.FeatureFlags
|
import io.element.android.libraries.featureflag.api.FeatureFlags
|
||||||
|
import io.element.android.libraries.matrix.api.paths.SessionPaths
|
||||||
import io.element.android.libraries.matrix.impl.analytics.UtdTracker
|
import io.element.android.libraries.matrix.impl.analytics.UtdTracker
|
||||||
import io.element.android.libraries.matrix.impl.certificates.UserCertificatesProvider
|
import io.element.android.libraries.matrix.impl.certificates.UserCertificatesProvider
|
||||||
import io.element.android.libraries.matrix.impl.paths.SessionPaths
|
|
||||||
import io.element.android.libraries.matrix.impl.paths.getSessionPaths
|
import io.element.android.libraries.matrix.impl.paths.getSessionPaths
|
||||||
import io.element.android.libraries.matrix.impl.proxy.ProxyProvider
|
import io.element.android.libraries.matrix.impl.proxy.ProxyProvider
|
||||||
import io.element.android.libraries.matrix.impl.room.TimelineEventFilterFactory
|
import io.element.android.libraries.matrix.impl.room.TimelineEventFilterFactory
|
||||||
@@ -102,10 +102,10 @@ class RustMatrixClientFactory(
|
|||||||
|
|
||||||
client.restoreSession(sessionData.toSession())
|
client.restoreSession(sessionData.toSession())
|
||||||
|
|
||||||
create(client)
|
create(client, sessionData)
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun create(client: Client): RustMatrixClient {
|
suspend fun create(client: Client, sessionData: SessionData): RustMatrixClient {
|
||||||
val (anonymizedAccessToken, anonymizedRefreshToken) = client.session().anonymizedTokens()
|
val (anonymizedAccessToken, anonymizedRefreshToken) = client.session().anonymizedTokens()
|
||||||
|
|
||||||
// Must be called before creating the sync service, timelines etc.
|
// Must be called before creating the sync service, timelines etc.
|
||||||
@@ -121,6 +121,7 @@ class RustMatrixClientFactory(
|
|||||||
.finish()
|
.finish()
|
||||||
|
|
||||||
return RustMatrixClient(
|
return RustMatrixClient(
|
||||||
|
sessionPaths = sessionData.getSessionPaths(),
|
||||||
innerClient = client,
|
innerClient = client,
|
||||||
sessionStore = sessionStore,
|
sessionStore = sessionStore,
|
||||||
appCoroutineScope = appCoroutineScope,
|
appCoroutineScope = appCoroutineScope,
|
||||||
|
|||||||
+37
-3
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2025 Element Creations Ltd.
|
* Copyright (c) 2026 Element Creations Ltd.
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||||
* Please see LICENSE files in the repository root for full details.
|
* Please see LICENSE files in the repository root for full details.
|
||||||
@@ -9,19 +9,53 @@ package io.element.android.libraries.matrix.impl.analytics
|
|||||||
|
|
||||||
import dev.zacsweers.metro.AppScope
|
import dev.zacsweers.metro.AppScope
|
||||||
import dev.zacsweers.metro.ContributesBinding
|
import dev.zacsweers.metro.ContributesBinding
|
||||||
|
import io.element.android.libraries.core.data.bytes
|
||||||
import io.element.android.libraries.matrix.api.MatrixClientProvider
|
import io.element.android.libraries.matrix.api.MatrixClientProvider
|
||||||
import io.element.android.libraries.matrix.api.analytics.GetDatabaseSizesUseCase
|
import io.element.android.libraries.matrix.api.analytics.GetDatabaseSizesUseCase
|
||||||
import io.element.android.libraries.matrix.api.analytics.SdkStoreSizes
|
import io.element.android.libraries.matrix.api.analytics.SdkStoreSizes
|
||||||
import io.element.android.libraries.matrix.api.core.SessionId
|
import io.element.android.libraries.matrix.api.core.SessionId
|
||||||
|
import java.io.File
|
||||||
|
import kotlin.math.max
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An implementation of [GetDatabaseSizesUseCase] that manually calculates the database sizes by checking the file system.
|
||||||
|
* This is not as accurate as the SDK's internal method, but is used as a fallback for Sentry's non-suspendable hook where
|
||||||
|
* calling the SDK's suspendable method blocks the main thread and causes ANRs.
|
||||||
|
*/
|
||||||
@ContributesBinding(AppScope::class)
|
@ContributesBinding(AppScope::class)
|
||||||
class DefaultGetDatabaseSizesUseCase(
|
class DefaultGetDatabaseSizesUseCase(
|
||||||
private val clientProvider: Lazy<MatrixClientProvider>,
|
private val clientProvider: Lazy<MatrixClientProvider>,
|
||||||
) : GetDatabaseSizesUseCase {
|
) : GetDatabaseSizesUseCase {
|
||||||
override suspend fun invoke(sessionId: SessionId): Result<SdkStoreSizes> {
|
override fun invoke(sessionId: SessionId): Result<SdkStoreSizes> {
|
||||||
val client = clientProvider.value.getOrNull(sessionId)
|
val client = clientProvider.value.getOrNull(sessionId)
|
||||||
?: return Result.failure(IllegalArgumentException("No MatrixClient for session $sessionId"))
|
?: return Result.failure(IllegalArgumentException("No MatrixClient for session $sessionId"))
|
||||||
|
|
||||||
return client.getDatabaseSizes()
|
val fileDir = client.sessionPaths.fileDirectory
|
||||||
|
val cacheDir = client.sessionPaths.cacheDirectory
|
||||||
|
|
||||||
|
val cryptoSize = getDatabaseSize(fileDir, "matrix-sdk-crypto")
|
||||||
|
val stateSize = getDatabaseSize(fileDir, "matrix-sdk-state")
|
||||||
|
|
||||||
|
val eventCacheSize = getDatabaseSize(cacheDir, "matrix-sdk-event-cache")
|
||||||
|
val mediaCacheSize = getDatabaseSize(cacheDir, "matrix-sdk-media")
|
||||||
|
|
||||||
|
return Result.success(SdkStoreSizes(
|
||||||
|
stateStore = stateSize.bytes,
|
||||||
|
cryptoStore = cryptoSize.bytes,
|
||||||
|
eventCacheStore = eventCacheSize.bytes,
|
||||||
|
mediaStore = mediaCacheSize.bytes,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getDatabaseSize(baseDirectory: File, databaseFileName: String): Long {
|
||||||
|
// Check the sizes of both the main database file and the -wal file, and return the max of the two:
|
||||||
|
// The -wal file can contain additional data not yet merged into the main file, becoming larger than it.
|
||||||
|
val databaseFile = File(baseDirectory, "$databaseFileName.sqlite3")
|
||||||
|
val walFile = File(baseDirectory, "$databaseFileName.sqlite3-wal")
|
||||||
|
|
||||||
|
val databaseSize = if (databaseFile.exists()) databaseFile.length() else 0L
|
||||||
|
val walSize = if (walFile.exists()) walFile.length() else 0L
|
||||||
|
|
||||||
|
return max(databaseSize, walSize)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-5
@@ -29,6 +29,7 @@ import io.element.android.libraries.matrix.api.auth.qrlogin.MatrixQrCodeLoginDat
|
|||||||
import io.element.android.libraries.matrix.api.auth.qrlogin.QrCodeLoginStep
|
import io.element.android.libraries.matrix.api.auth.qrlogin.QrCodeLoginStep
|
||||||
import io.element.android.libraries.matrix.api.core.SessionId
|
import io.element.android.libraries.matrix.api.core.SessionId
|
||||||
import io.element.android.libraries.matrix.api.core.UserId
|
import io.element.android.libraries.matrix.api.core.UserId
|
||||||
|
import io.element.android.libraries.matrix.api.paths.SessionPaths
|
||||||
import io.element.android.libraries.matrix.api.verification.SessionVerifiedStatus
|
import io.element.android.libraries.matrix.api.verification.SessionVerifiedStatus
|
||||||
import io.element.android.libraries.matrix.impl.ClientBuilderSlidingSync
|
import io.element.android.libraries.matrix.impl.ClientBuilderSlidingSync
|
||||||
import io.element.android.libraries.matrix.impl.RustMatrixClientFactory
|
import io.element.android.libraries.matrix.impl.RustMatrixClientFactory
|
||||||
@@ -38,7 +39,6 @@ import io.element.android.libraries.matrix.impl.auth.qrlogin.toStep
|
|||||||
import io.element.android.libraries.matrix.impl.exception.mapClientException
|
import io.element.android.libraries.matrix.impl.exception.mapClientException
|
||||||
import io.element.android.libraries.matrix.impl.keys.SecretGenerator
|
import io.element.android.libraries.matrix.impl.keys.SecretGenerator
|
||||||
import io.element.android.libraries.matrix.impl.mapper.toSessionData
|
import io.element.android.libraries.matrix.impl.mapper.toSessionData
|
||||||
import io.element.android.libraries.matrix.impl.paths.SessionPaths
|
|
||||||
import io.element.android.libraries.matrix.impl.paths.SessionPathsFactory
|
import io.element.android.libraries.matrix.impl.paths.SessionPathsFactory
|
||||||
import io.element.android.libraries.matrix.impl.toSession
|
import io.element.android.libraries.matrix.impl.toSession
|
||||||
import io.element.android.libraries.sessionstorage.api.LoginType
|
import io.element.android.libraries.sessionstorage.api.LoginType
|
||||||
@@ -160,7 +160,7 @@ class RustMatrixAuthenticationService(
|
|||||||
passphrase = pendingKey.formattedAsString(),
|
passphrase = pendingKey.formattedAsString(),
|
||||||
sessionPaths = currentSessionPaths,
|
sessionPaths = currentSessionPaths,
|
||||||
)
|
)
|
||||||
val matrixClient = rustMatrixClientFactory.create(client)
|
val matrixClient = rustMatrixClientFactory.create(client, sessionData)
|
||||||
newMatrixClientObservers.forEach { it.invoke(matrixClient) }
|
newMatrixClientObservers.forEach { it.invoke(matrixClient) }
|
||||||
sessionStore.addSession(sessionData)
|
sessionStore.addSession(sessionData)
|
||||||
|
|
||||||
@@ -235,7 +235,7 @@ class RustMatrixAuthenticationService(
|
|||||||
|
|
||||||
// We restore the client using the just retrieved session data
|
// We restore the client using the just retrieved session data
|
||||||
client.restoreSession(sessionData.toSession())
|
client.restoreSession(sessionData.toSession())
|
||||||
val matrixClient = rustMatrixClientFactory.create(client)
|
val matrixClient = rustMatrixClientFactory.create(client, sessionData)
|
||||||
|
|
||||||
// We wait for the verification state to be known
|
// We wait for the verification state to be known
|
||||||
matrixClient.waitForKnownVerificationState()
|
matrixClient.waitForKnownVerificationState()
|
||||||
@@ -325,7 +325,7 @@ class RustMatrixAuthenticationService(
|
|||||||
passphrase = pendingKey.formattedAsString(),
|
passphrase = pendingKey.formattedAsString(),
|
||||||
sessionPaths = currentSessionPaths,
|
sessionPaths = currentSessionPaths,
|
||||||
)
|
)
|
||||||
val matrixClient = rustMatrixClientFactory.create(client)
|
val matrixClient = rustMatrixClientFactory.create(client, sessionData)
|
||||||
matrixClient.waitForKnownVerificationState()
|
matrixClient.waitForKnownVerificationState()
|
||||||
|
|
||||||
newMatrixClientObservers.forEach { it.invoke(matrixClient) }
|
newMatrixClientObservers.forEach { it.invoke(matrixClient) }
|
||||||
@@ -390,7 +390,7 @@ class RustMatrixAuthenticationService(
|
|||||||
passphrase = pendingKey.formattedAsString(),
|
passphrase = pendingKey.formattedAsString(),
|
||||||
sessionPaths = emptySessionPaths,
|
sessionPaths = emptySessionPaths,
|
||||||
)
|
)
|
||||||
val matrixClient = rustMatrixClientFactory.create(client)
|
val matrixClient = rustMatrixClientFactory.create(client, sessionData)
|
||||||
newMatrixClientObservers.forEach { it.invoke(matrixClient) }
|
newMatrixClientObservers.forEach { it.invoke(matrixClient) }
|
||||||
sessionStore.addSession(sessionData)
|
sessionStore.addSession(sessionData)
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -9,7 +9,7 @@
|
|||||||
package io.element.android.libraries.matrix.impl.mapper
|
package io.element.android.libraries.matrix.impl.mapper
|
||||||
|
|
||||||
import io.element.android.libraries.matrix.api.auth.external.ExternalSession
|
import io.element.android.libraries.matrix.api.auth.external.ExternalSession
|
||||||
import io.element.android.libraries.matrix.impl.paths.SessionPaths
|
import io.element.android.libraries.matrix.api.paths.SessionPaths
|
||||||
import io.element.android.libraries.sessionstorage.api.LoginType
|
import io.element.android.libraries.sessionstorage.api.LoginType
|
||||||
import io.element.android.libraries.sessionstorage.api.SessionData
|
import io.element.android.libraries.sessionstorage.api.SessionData
|
||||||
import org.matrix.rustcomponents.sdk.Session
|
import org.matrix.rustcomponents.sdk.Session
|
||||||
|
|||||||
+1
-10
@@ -8,19 +8,10 @@
|
|||||||
|
|
||||||
package io.element.android.libraries.matrix.impl.paths
|
package io.element.android.libraries.matrix.impl.paths
|
||||||
|
|
||||||
|
import io.element.android.libraries.matrix.api.paths.SessionPaths
|
||||||
import io.element.android.libraries.sessionstorage.api.SessionData
|
import io.element.android.libraries.sessionstorage.api.SessionData
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
data class SessionPaths(
|
|
||||||
val fileDirectory: File,
|
|
||||||
val cacheDirectory: File,
|
|
||||||
) {
|
|
||||||
fun deleteRecursively() {
|
|
||||||
fileDirectory.deleteRecursively()
|
|
||||||
cacheDirectory.deleteRecursively()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun SessionData.getSessionPaths(): SessionPaths {
|
internal fun SessionData.getSessionPaths(): SessionPaths {
|
||||||
return SessionPaths(
|
return SessionPaths(
|
||||||
fileDirectory = File(sessionPath),
|
fileDirectory = File(sessionPath),
|
||||||
|
|||||||
+1
@@ -11,6 +11,7 @@ package io.element.android.libraries.matrix.impl.paths
|
|||||||
import dev.zacsweers.metro.Inject
|
import dev.zacsweers.metro.Inject
|
||||||
import io.element.android.libraries.di.BaseDirectory
|
import io.element.android.libraries.di.BaseDirectory
|
||||||
import io.element.android.libraries.di.CacheDirectory
|
import io.element.android.libraries.di.CacheDirectory
|
||||||
|
import io.element.android.libraries.matrix.api.paths.SessionPaths
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -10,7 +10,7 @@ package io.element.android.libraries.matrix.impl.storage
|
|||||||
import io.element.android.libraries.androidutils.crypto.ClientSecret
|
import io.element.android.libraries.androidutils.crypto.ClientSecret
|
||||||
import io.element.android.libraries.core.data.ByteUnit
|
import io.element.android.libraries.core.data.ByteUnit
|
||||||
import io.element.android.libraries.core.data.megaBytes
|
import io.element.android.libraries.core.data.megaBytes
|
||||||
import io.element.android.libraries.matrix.impl.paths.SessionPaths
|
import io.element.android.libraries.matrix.api.paths.SessionPaths
|
||||||
import org.matrix.rustcomponents.sdk.ClientBuilder
|
import org.matrix.rustcomponents.sdk.ClientBuilder
|
||||||
import org.matrix.rustcomponents.sdk.SqliteStoreBuilder as SdkSqliteStoreBuilder
|
import org.matrix.rustcomponents.sdk.SqliteStoreBuilder as SdkSqliteStoreBuilder
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -9,7 +9,7 @@ package io.element.android.libraries.matrix.impl.storage
|
|||||||
|
|
||||||
import dev.zacsweers.metro.AppScope
|
import dev.zacsweers.metro.AppScope
|
||||||
import dev.zacsweers.metro.ContributesBinding
|
import dev.zacsweers.metro.ContributesBinding
|
||||||
import io.element.android.libraries.matrix.impl.paths.SessionPaths
|
import io.element.android.libraries.matrix.api.paths.SessionPaths
|
||||||
|
|
||||||
interface SqliteStoreBuilderProvider {
|
interface SqliteStoreBuilderProvider {
|
||||||
fun provide(sessionPaths: SessionPaths): SqliteStoreBuilder
|
fun provide(sessionPaths: SessionPaths): SqliteStoreBuilder
|
||||||
|
|||||||
-23
@@ -1,23 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2025 Element Creations Ltd.
|
|
||||||
* Copyright 2024, 2025 New Vector Ltd.
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
|
||||||
* Please see LICENSE files in the repository root for full details.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package io.element.android.libraries.matrix.impl.util
|
|
||||||
|
|
||||||
import io.element.android.libraries.matrix.api.core.SessionId
|
|
||||||
import io.element.android.libraries.matrix.impl.paths.SessionPaths
|
|
||||||
import io.element.android.libraries.matrix.impl.paths.getSessionPaths
|
|
||||||
import io.element.android.libraries.sessionstorage.api.SessionStore
|
|
||||||
|
|
||||||
class SessionPathsProvider(
|
|
||||||
private val sessionStore: SessionStore,
|
|
||||||
) {
|
|
||||||
suspend fun provides(sessionId: SessionId): SessionPaths? {
|
|
||||||
val sessionData = sessionStore.getSession(sessionId.value) ?: return null
|
|
||||||
return sessionData.getSessionPaths()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+2
@@ -13,6 +13,7 @@ package io.element.android.libraries.matrix.impl
|
|||||||
import com.google.common.truth.Truth.assertThat
|
import com.google.common.truth.Truth.assertThat
|
||||||
import io.element.android.libraries.core.data.bytes
|
import io.element.android.libraries.core.data.bytes
|
||||||
import io.element.android.libraries.featureflag.test.FakeFeatureFlagService
|
import io.element.android.libraries.featureflag.test.FakeFeatureFlagService
|
||||||
|
import io.element.android.libraries.matrix.api.paths.SessionPaths
|
||||||
import io.element.android.libraries.matrix.impl.fixtures.fakes.FakeFfiClient
|
import io.element.android.libraries.matrix.impl.fixtures.fakes.FakeFfiClient
|
||||||
import io.element.android.libraries.matrix.impl.fixtures.fakes.FakeFfiSyncService
|
import io.element.android.libraries.matrix.impl.fixtures.fakes.FakeFfiSyncService
|
||||||
import io.element.android.libraries.matrix.impl.room.FakeTimelineEventFilterFactory
|
import io.element.android.libraries.matrix.impl.room.FakeTimelineEventFilterFactory
|
||||||
@@ -141,6 +142,7 @@ class RustMatrixClientTest {
|
|||||||
),
|
),
|
||||||
) = RustMatrixClient(
|
) = RustMatrixClient(
|
||||||
innerClient = client,
|
innerClient = client,
|
||||||
|
sessionPaths = SessionPaths(fileDirectory = File("files"), cacheDirectory = File("cache")),
|
||||||
sessionStore = sessionStore,
|
sessionStore = sessionStore,
|
||||||
appCoroutineScope = backgroundScope,
|
appCoroutineScope = backgroundScope,
|
||||||
sessionDelegate = aRustClientSessionDelegate(
|
sessionDelegate = aRustClientSessionDelegate(
|
||||||
|
|||||||
+1
-1
@@ -10,8 +10,8 @@ package io.element.android.libraries.matrix.impl.mapper
|
|||||||
|
|
||||||
import com.google.common.truth.Truth.assertThat
|
import com.google.common.truth.Truth.assertThat
|
||||||
import io.element.android.libraries.matrix.api.auth.external.ExternalSession
|
import io.element.android.libraries.matrix.api.auth.external.ExternalSession
|
||||||
|
import io.element.android.libraries.matrix.api.paths.SessionPaths
|
||||||
import io.element.android.libraries.matrix.impl.fixtures.factories.aRustSession
|
import io.element.android.libraries.matrix.impl.fixtures.factories.aRustSession
|
||||||
import io.element.android.libraries.matrix.impl.paths.SessionPaths
|
|
||||||
import io.element.android.libraries.matrix.test.A_DEVICE_ID
|
import io.element.android.libraries.matrix.test.A_DEVICE_ID
|
||||||
import io.element.android.libraries.matrix.test.A_HOMESERVER_URL
|
import io.element.android.libraries.matrix.test.A_HOMESERVER_URL
|
||||||
import io.element.android.libraries.matrix.test.A_HOMESERVER_URL_2
|
import io.element.android.libraries.matrix.test.A_HOMESERVER_URL_2
|
||||||
|
|||||||
+1
-1
@@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
package io.element.android.libraries.matrix.impl.storage
|
package io.element.android.libraries.matrix.impl.storage
|
||||||
|
|
||||||
import io.element.android.libraries.matrix.impl.paths.SessionPaths
|
import io.element.android.libraries.matrix.api.paths.SessionPaths
|
||||||
|
|
||||||
class FakeSqliteStoreBuilderProvider : SqliteStoreBuilderProvider {
|
class FakeSqliteStoreBuilderProvider : SqliteStoreBuilderProvider {
|
||||||
override fun provide(sessionPaths: SessionPaths): SqliteStoreBuilder {
|
override fun provide(sessionPaths: SessionPaths): SqliteStoreBuilder {
|
||||||
|
|||||||
-41
@@ -1,41 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2025 Element Creations Ltd.
|
|
||||||
* Copyright 2024, 2025 New Vector Ltd.
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
|
||||||
* Please see LICENSE files in the repository root for full details.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package io.element.android.libraries.matrix.impl.util
|
|
||||||
|
|
||||||
import com.google.common.truth.Truth.assertThat
|
|
||||||
import io.element.android.libraries.matrix.test.A_SESSION_ID
|
|
||||||
import io.element.android.libraries.sessionstorage.test.InMemorySessionStore
|
|
||||||
import io.element.android.libraries.sessionstorage.test.aSessionData
|
|
||||||
import kotlinx.coroutines.test.runTest
|
|
||||||
import org.junit.Test
|
|
||||||
|
|
||||||
class SessionPathsProviderTest {
|
|
||||||
@Test
|
|
||||||
fun `if session is not found, provides returns null`() = runTest {
|
|
||||||
val sut = SessionPathsProvider(InMemorySessionStore())
|
|
||||||
val result = sut.provides(A_SESSION_ID)
|
|
||||||
assertThat(result).isNull()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `if session is found, provides returns the data`() = runTest {
|
|
||||||
val store = InMemorySessionStore(
|
|
||||||
initialList = listOf(
|
|
||||||
aSessionData(
|
|
||||||
sessionPath = "/a/path/to/a/session",
|
|
||||||
cachePath = "/a/path/to/a/cache",
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
val sut = SessionPathsProvider(store)
|
|
||||||
val result = sut.provides(A_SESSION_ID)!!
|
|
||||||
assertThat(result.fileDirectory.absolutePath).isEqualTo("/a/path/to/a/session")
|
|
||||||
assertThat(result.cacheDirectory.absolutePath).isEqualTo("/a/path/to/a/cache")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+3
@@ -27,6 +27,7 @@ import io.element.android.libraries.matrix.api.media.MediaPreviewService
|
|||||||
import io.element.android.libraries.matrix.api.notification.NotificationService
|
import io.element.android.libraries.matrix.api.notification.NotificationService
|
||||||
import io.element.android.libraries.matrix.api.notificationsettings.NotificationSettingsService
|
import io.element.android.libraries.matrix.api.notificationsettings.NotificationSettingsService
|
||||||
import io.element.android.libraries.matrix.api.oauth.AccountManagementAction
|
import io.element.android.libraries.matrix.api.oauth.AccountManagementAction
|
||||||
|
import io.element.android.libraries.matrix.api.paths.SessionPaths
|
||||||
import io.element.android.libraries.matrix.api.pusher.PushersService
|
import io.element.android.libraries.matrix.api.pusher.PushersService
|
||||||
import io.element.android.libraries.matrix.api.room.BaseRoom
|
import io.element.android.libraries.matrix.api.room.BaseRoom
|
||||||
import io.element.android.libraries.matrix.api.room.JoinedRoom
|
import io.element.android.libraries.matrix.api.room.JoinedRoom
|
||||||
@@ -66,10 +67,12 @@ import kotlinx.coroutines.flow.StateFlow
|
|||||||
import kotlinx.coroutines.flow.emptyFlow
|
import kotlinx.coroutines.flow.emptyFlow
|
||||||
import kotlinx.coroutines.flow.flowOf
|
import kotlinx.coroutines.flow.flowOf
|
||||||
import kotlinx.coroutines.test.TestScope
|
import kotlinx.coroutines.test.TestScope
|
||||||
|
import java.io.File
|
||||||
import java.util.Optional
|
import java.util.Optional
|
||||||
|
|
||||||
class FakeMatrixClient(
|
class FakeMatrixClient(
|
||||||
override val sessionId: SessionId = A_SESSION_ID,
|
override val sessionId: SessionId = A_SESSION_ID,
|
||||||
|
override val sessionPaths: SessionPaths = SessionPaths(fileDirectory = File("files"), cacheDirectory = File("cache")),
|
||||||
override val deviceId: DeviceId = A_DEVICE_ID,
|
override val deviceId: DeviceId = A_DEVICE_ID,
|
||||||
override val sessionCoroutineScope: CoroutineScope = TestScope(),
|
override val sessionCoroutineScope: CoroutineScope = TestScope(),
|
||||||
private val userDisplayName: String? = A_USER_NAME,
|
private val userDisplayName: String? = A_USER_NAME,
|
||||||
|
|||||||
+1
-2
@@ -33,7 +33,6 @@ import io.sentry.Sentry
|
|||||||
import io.sentry.SentryOptions
|
import io.sentry.SentryOptions
|
||||||
import io.sentry.android.core.SentryAndroid
|
import io.sentry.android.core.SentryAndroid
|
||||||
import io.sentry.protocol.SentryTransaction
|
import io.sentry.protocol.SentryTransaction
|
||||||
import kotlinx.coroutines.runBlocking
|
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
|
||||||
@ContributesIntoSet(AppScope::class)
|
@ContributesIntoSet(AppScope::class)
|
||||||
@@ -132,7 +131,7 @@ class SentryAnalyticsProvider(
|
|||||||
if (sessionId != null) {
|
if (sessionId != null) {
|
||||||
// This runs in a separate thread, so although using `runBlocking` is not great, at least it shouldn't freeze the app
|
// This runs in a separate thread, so although using `runBlocking` is not great, at least it shouldn't freeze the app
|
||||||
// Also, the method is fairly quick, so the blocking shouldn't take longer than a few ms
|
// Also, the method is fairly quick, so the blocking shouldn't take longer than a few ms
|
||||||
val databaseSizes = runBlocking { getDatabaseSizesUseCase(sessionId) }.getOrNull()
|
val databaseSizes = getDatabaseSizesUseCase(sessionId).getOrNull()
|
||||||
|
|
||||||
databaseSizes?.stateStore?.let { transaction.setExtra(AnalyticsUserData.STATE_STORE_SIZE, it.into(ByteUnit.MB)) }
|
databaseSizes?.stateStore?.let { transaction.setExtra(AnalyticsUserData.STATE_STORE_SIZE, it.into(ByteUnit.MB)) }
|
||||||
databaseSizes?.eventCacheStore?.let { transaction.setExtra(AnalyticsUserData.EVENT_CACHE_SIZE, it.into(ByteUnit.MB)) }
|
databaseSizes?.eventCacheStore?.let { transaction.setExtra(AnalyticsUserData.EVENT_CACHE_SIZE, it.into(ByteUnit.MB)) }
|
||||||
|
|||||||
Reference in New Issue
Block a user