Move local backup progress tracking to in-memory object.

This commit is contained in:
Alex Hart
2026-03-31 16:18:52 -03:00
parent 4dd30f4ec3
commit b284835545
11 changed files with 49 additions and 48 deletions

View File

@@ -214,7 +214,6 @@ public class ApplicationContext extends Application implements AppForegroundObse
.addNonBlocking(this::ensureProfileUploaded)
.addNonBlocking(() -> AppDependencies.getExpireStoriesManager().scheduleIfNecessary())
.addNonBlocking(BackupRepository::maybeFixAnyDanglingUploadProgress)
.addNonBlocking(BackupRepository::maybeFixAnyDanglingLocalExportProgress)
.addPostRender(() -> AppDependencies.getDeletedCallEventManager().scheduleIfNecessary())
.addPostRender(() -> RateLimitUtil.retryAllRateLimitedMessages(this))
.addPostRender(this::initializeExpiringMessageManager)

View File

@@ -0,0 +1,26 @@
/*
* Copyright 2026 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.thoughtcrime.securesms.backup
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import org.thoughtcrime.securesms.keyvalue.protos.LocalBackupCreationProgress
object LocalExportProgress {
val internalEncryptedProgress = MutableStateFlow(LocalBackupCreationProgress())
val internalPlaintextProgress = MutableStateFlow(LocalBackupCreationProgress())
val encryptedProgress: StateFlow<LocalBackupCreationProgress> = internalEncryptedProgress
val plaintextProgress: StateFlow<LocalBackupCreationProgress> = internalPlaintextProgress
fun setEncryptedProgress(progress: LocalBackupCreationProgress) {
internalEncryptedProgress.value = progress
}
fun setPlaintextProgress(progress: LocalBackupCreationProgress) {
internalPlaintextProgress.value = progress
}
}

View File

@@ -72,7 +72,6 @@ import org.thoughtcrime.securesms.attachments.Cdn
import org.thoughtcrime.securesms.attachments.DatabaseAttachment
import org.thoughtcrime.securesms.backup.ArchiveUploadProgress
import org.thoughtcrime.securesms.backup.DeletionState
import org.thoughtcrime.securesms.backup.isIdle
import org.thoughtcrime.securesms.backup.v2.BackupRepository.copyAttachmentToArchive
import org.thoughtcrime.securesms.backup.v2.BackupRepository.exportForDebugging
import org.thoughtcrime.securesms.backup.v2.importer.ChatItemArchiveImporter
@@ -115,7 +114,6 @@ import org.thoughtcrime.securesms.jobs.BackupMessagesJob
import org.thoughtcrime.securesms.jobs.BackupRestoreMediaJob
import org.thoughtcrime.securesms.jobs.CancelRestoreMediaJob
import org.thoughtcrime.securesms.jobs.CreateReleaseChannelJob
import org.thoughtcrime.securesms.jobs.LocalArchiveJob
import org.thoughtcrime.securesms.jobs.LocalBackupJob
import org.thoughtcrime.securesms.jobs.MultiDeviceKeysUpdateJob
import org.thoughtcrime.securesms.jobs.RequestGroupV2InfoJob
@@ -132,7 +130,6 @@ import org.thoughtcrime.securesms.keyvalue.KeyValueStore
import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.keyvalue.isDecisionPending
import org.thoughtcrime.securesms.keyvalue.protos.ArchiveUploadProgressState
import org.thoughtcrime.securesms.keyvalue.protos.LocalBackupCreationProgress
import org.thoughtcrime.securesms.logsubmit.SubmitDebugLogRepository
import org.thoughtcrime.securesms.net.SignalNetwork
import org.thoughtcrime.securesms.notifications.NotificationChannels
@@ -592,14 +589,6 @@ object BackupRepository {
SignalStore.backup.snoozeDownloadNotifier()
}
@JvmStatic
fun maybeFixAnyDanglingLocalExportProgress() {
if (!SignalStore.backup.newLocalBackupProgress.isIdle && AppDependencies.jobManager.find { it.factoryKey == LocalArchiveJob.KEY }.isEmpty()) {
Log.w(TAG, "Found stale local backup progress with no active job. Resetting to idle.")
SignalStore.backup.newLocalBackupProgress = LocalBackupCreationProgress(idle = LocalBackupCreationProgress.Idle())
}
}
@JvmStatic
fun maybeFixAnyDanglingUploadProgress() {
if (SignalStore.account.isLinkedDevice) {

View File

@@ -22,6 +22,7 @@ import org.signal.core.util.readFully
import org.signal.core.util.toJson
import org.signal.libsignal.crypto.Aes256Ctr32
import org.thoughtcrime.securesms.attachments.AttachmentId
import org.thoughtcrime.securesms.backup.LocalExportProgress
import org.thoughtcrime.securesms.backup.v2.BackupRepository
import org.thoughtcrime.securesms.database.AttachmentTable
import org.thoughtcrime.securesms.database.SignalDatabase
@@ -72,10 +73,10 @@ object LocalArchiver {
Log.i(TAG, "Listing all current files")
val allFiles = filesFileSystem.allFiles { completed, total ->
SignalStore.backup.newLocalBackupProgress = LocalBackupCreationProgress(exporting = LocalBackupCreationProgress.Exporting(phase = LocalBackupCreationProgress.ExportPhase.INITIALIZING, frameExportCount = completed.toLong(), frameTotalCount = total.toLong()))
LocalExportProgress.setEncryptedProgress(LocalBackupCreationProgress(exporting = LocalBackupCreationProgress.Exporting(phase = LocalBackupCreationProgress.ExportPhase.INITIALIZING, frameExportCount = completed.toLong(), frameTotalCount = total.toLong())))
}
stopwatch.split("files-list")
SignalStore.backup.newLocalBackupProgress = LocalBackupCreationProgress(exporting = LocalBackupCreationProgress.Exporting(phase = LocalBackupCreationProgress.ExportPhase.INITIALIZING))
LocalExportProgress.setEncryptedProgress(LocalBackupCreationProgress(exporting = LocalBackupCreationProgress.Exporting(phase = LocalBackupCreationProgress.ExportPhase.INITIALIZING)))
val mediaNames: MutableSet<MediaName> = Collections.synchronizedSet(HashSet())
@@ -387,7 +388,7 @@ object LocalArchiver {
}
private fun post(progress: LocalBackupCreationProgress) {
SignalStore.backup.newLocalBackupProgress = progress
LocalExportProgress.setEncryptedProgress(progress)
}
}
@@ -442,7 +443,7 @@ object LocalArchiver {
}
private fun post(progress: LocalBackupCreationProgress) {
SignalStore.backup.newLocalPlaintextBackupProgress = progress
LocalExportProgress.setPlaintextProgress(progress)
}
}
}

View File

@@ -18,6 +18,7 @@ import org.signal.core.ui.util.StorageUtil
import org.signal.core.util.logging.Log
import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.backup.BackupPassphrase
import org.thoughtcrime.securesms.backup.LocalExportProgress
import org.thoughtcrime.securesms.components.settings.app.backups.remote.BackupKeyCredentialManagerHandler
import org.thoughtcrime.securesms.components.settings.app.backups.remote.BackupKeySaveState
import org.thoughtcrime.securesms.dependencies.AppDependencies
@@ -74,7 +75,7 @@ class LocalBackupsViewModel : ViewModel(), BackupKeyCredentialManagerHandler {
}
viewModelScope.launch {
SignalStore.backup.newLocalBackupProgressFlow.collect { progress ->
LocalExportProgress.encryptedProgress.collect { progress ->
internalSettingsState.update { it.copy(progress = progress) }
}
}
@@ -108,7 +109,7 @@ class LocalBackupsViewModel : ViewModel(), BackupKeyCredentialManagerHandler {
}
fun onBackupStarted() {
SignalStore.backup.newLocalBackupProgress = LocalBackupCreationProgress(exporting = LocalBackupCreationProgress.Exporting(phase = LocalBackupCreationProgress.ExportPhase.NONE))
LocalExportProgress.setEncryptedProgress(LocalBackupCreationProgress(exporting = LocalBackupCreationProgress.Exporting(phase = LocalBackupCreationProgress.ExportPhase.NONE)))
}
fun turnOffAndDelete(context: Context) {

View File

@@ -1,6 +1,6 @@
package org.thoughtcrime.securesms.components.settings.app.chats
import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.backup.LocalExportProgress
import org.thoughtcrime.securesms.keyvalue.protos.LocalBackupCreationProgress
data class ChatsSettingsState(
@@ -14,7 +14,7 @@ data class ChatsSettingsState(
val userUnregistered: Boolean,
val clientDeprecated: Boolean,
val isPlaintextExportEnabled: Boolean,
val plaintextExportProgress: LocalBackupCreationProgress = SignalStore.backup.newLocalPlaintextBackupProgress,
val plaintextExportProgress: LocalBackupCreationProgress = LocalExportProgress.plaintextProgress.value,
val chatExportState: ChatExportState = ChatExportState.None,
val includeMediaInExport: Boolean = false
) {

View File

@@ -8,6 +8,7 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import org.thoughtcrime.securesms.backup.LocalExportProgress
import org.thoughtcrime.securesms.components.settings.app.chats.folders.ChatFoldersRepository
import org.thoughtcrime.securesms.dependencies.AppDependencies
import org.thoughtcrime.securesms.jobs.LocalBackupJob
@@ -44,7 +45,7 @@ class ChatsSettingsViewModel @JvmOverloads constructor(
init {
viewModelScope.launch {
SignalStore.backup.newLocalPlaintextBackupProgressFlow.collect { progress ->
LocalExportProgress.plaintextProgress.collect { progress ->
store.update {
it.copy(
plaintextExportProgress = progress,

View File

@@ -40,6 +40,7 @@ import org.signal.libsignal.zkgroup.profiles.ProfileKey
import org.thoughtcrime.securesms.attachments.AttachmentId
import org.thoughtcrime.securesms.attachments.DatabaseAttachment
import org.thoughtcrime.securesms.backup.ArchiveUploadProgress
import org.thoughtcrime.securesms.backup.LocalExportProgress
import org.thoughtcrime.securesms.backup.v2.ArchiveValidator
import org.thoughtcrime.securesms.backup.v2.BackupRepository
import org.thoughtcrime.securesms.backup.v2.DebugBackupMetadata
@@ -92,7 +93,7 @@ class InternalBackupPlaygroundViewModel : ViewModel() {
init {
viewModelScope.launch {
SignalStore.backup.newLocalPlaintextBackupProgressFlow.collect { progress ->
LocalExportProgress.plaintextProgress.collect { progress ->
_state.value = _state.value.copy(plaintextProgress = progress)
}
}

View File

@@ -11,6 +11,7 @@ import org.signal.core.util.logging.Log
import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.backup.BackupFileIOError
import org.thoughtcrime.securesms.backup.FullBackupExporter.BackupCanceledException
import org.thoughtcrime.securesms.backup.LocalExportProgress
import org.thoughtcrime.securesms.backup.v2.local.ArchiveFileSystem
import org.thoughtcrime.securesms.backup.v2.local.LocalArchiver
import org.thoughtcrime.securesms.backup.v2.local.SnapshotFileSystem
@@ -92,7 +93,7 @@ class LocalArchiveJob internal constructor(parameters: Parameters) : Job(paramet
val progressScope = CoroutineScope(SupervisorJob() + Dispatchers.Default)
progressScope.launch {
SignalStore.backup.newLocalBackupProgressFlow.collect { progress ->
LocalExportProgress.encryptedProgress.collect { progress ->
updateNotification(progress, notification)
}
}
@@ -153,11 +154,11 @@ class LocalArchiveJob internal constructor(parameters: Parameters) : Job(paramet
}
override fun onFailure() {
SignalStore.backup.newLocalBackupProgress = LocalBackupCreationProgress(idle = LocalBackupCreationProgress.Idle())
LocalExportProgress.setEncryptedProgress(LocalBackupCreationProgress(idle = LocalBackupCreationProgress.Idle()))
}
private fun setProgress(progress: LocalBackupCreationProgress, notification: NotificationController?) {
SignalStore.backup.newLocalBackupProgress = progress
LocalExportProgress.setEncryptedProgress(progress)
updateNotification(progress, notification)
}

View File

@@ -10,12 +10,12 @@ import kotlinx.coroutines.launch
import org.signal.core.util.Stopwatch
import org.signal.core.util.logging.Log
import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.backup.LocalExportProgress
import org.thoughtcrime.securesms.backup.v2.local.LocalArchiver
import org.thoughtcrime.securesms.database.SignalDatabase
import org.thoughtcrime.securesms.dependencies.AppDependencies
import org.thoughtcrime.securesms.jobmanager.Job
import org.thoughtcrime.securesms.jobmanager.JsonJobData
import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.keyvalue.protos.LocalBackupCreationProgress
import org.thoughtcrime.securesms.notifications.NotificationChannels
import org.thoughtcrime.securesms.service.GenericForegroundService
@@ -104,7 +104,7 @@ class LocalPlaintextArchiveJob internal constructor(
val progressScope = CoroutineScope(SupervisorJob() + Dispatchers.Default)
progressScope.launch {
SignalStore.backup.newLocalPlaintextBackupProgressFlow.collect { progress ->
LocalExportProgress.plaintextProgress.collect { progress ->
updateNotification(progress, notification)
}
}
@@ -146,14 +146,14 @@ class LocalPlaintextArchiveJob internal constructor(
override fun onFailure() {
zipFile?.delete()
val current = SignalStore.backup.newLocalPlaintextBackupProgress
val current = LocalExportProgress.plaintextProgress.value
if (current.canceled == null && current.failed == null) {
SignalStore.backup.newLocalPlaintextBackupProgress = LocalBackupCreationProgress(failed = LocalBackupCreationProgress.Failed())
LocalExportProgress.setPlaintextProgress(LocalBackupCreationProgress(failed = LocalBackupCreationProgress.Failed()))
}
}
private fun setProgress(progress: LocalBackupCreationProgress, notification: NotificationController?) {
SignalStore.backup.newLocalPlaintextBackupProgress = progress
LocalExportProgress.setPlaintextProgress(progress)
updateNotification(progress, notification)
}

View File

@@ -18,7 +18,6 @@ import org.thoughtcrime.securesms.jobmanager.impl.NoRemoteArchiveGarbageCollecti
import org.thoughtcrime.securesms.jobmanager.impl.RestoreAttachmentConstraintObserver
import org.thoughtcrime.securesms.keyvalue.protos.ArchiveUploadProgressState
import org.thoughtcrime.securesms.keyvalue.protos.BackupDownloadNotifierState
import org.thoughtcrime.securesms.keyvalue.protos.LocalBackupCreationProgress
import org.thoughtcrime.securesms.util.Environment
import org.whispersystems.signalservice.api.archive.ArchiveServiceCredential
import org.whispersystems.signalservice.api.archive.GetArchiveCdnCredentialsResponse
@@ -105,9 +104,6 @@ class BackupValues(store: KeyValueStore) : SignalStoreValues(store) {
private const val KEY_NEW_LOCAL_BACKUPS_DIRECTORY = "backup.new_local_backups_directory"
private const val KEY_NEW_LOCAL_BACKUPS_LAST_BACKUP_TIME = "backup.new_local_backups_last_backup_time"
private const val KEY_NEW_LOCAL_BACKUPS_SELECTED_SNAPSHOT_TIMESTAMP = "backup.new_local_backups_selected_snapshot_timestamp"
private const val KEY_NEW_LOCAL_BACKUPS_CREATION_PROGRESS = "backup.new_local_backups_creation_progress"
private const val KEY_NEW_LOCAL_PLAINTEXT_BACKUPS_CREATION_PROGRESS = "backup.new_local_plaintext_backups_creation_progress"
private const val KEY_LOCAL_RESTORE_ACCOUNT_ENTROPY_POOL = "backup.local_restore_account_entropy_pool"
private const val KEY_UPLOAD_BANNER_VISIBLE = "backup.upload_banner_visible"
@@ -478,20 +474,6 @@ class BackupValues(store: KeyValueStore) : SignalStoreValues(store) {
var newLocalBackupsEnabled: Boolean by newLocalBackupsEnabledValue
val newLocalBackupsEnabledFlow: Flow<Boolean> by lazy { newLocalBackupsEnabledValue.toFlow() }
/**
* Progress values for local backup progress.
*/
private val newLocalBackupProgressValue = protoValue(KEY_NEW_LOCAL_BACKUPS_CREATION_PROGRESS, LocalBackupCreationProgress(), LocalBackupCreationProgress.ADAPTER)
var newLocalBackupProgress: LocalBackupCreationProgress by newLocalBackupProgressValue
val newLocalBackupProgressFlow: Flow<LocalBackupCreationProgress> by lazy { newLocalBackupProgressValue.toFlow() }
/**
* Progress values for local plaintext backup progress.
*/
private val newLocalPlaintextBackupProgressValue = protoValue(KEY_NEW_LOCAL_PLAINTEXT_BACKUPS_CREATION_PROGRESS, LocalBackupCreationProgress(), LocalBackupCreationProgress.ADAPTER)
var newLocalPlaintextBackupProgress: LocalBackupCreationProgress by newLocalPlaintextBackupProgressValue
val newLocalPlaintextBackupProgressFlow: Flow<LocalBackupCreationProgress> by lazy { newLocalPlaintextBackupProgressValue.toFlow() }
/**IT
* The directory URI path selected for new local backups.
*/