Allow syncing additional types of messages to linked devices.

This commit is contained in:
Greyson Parrelli
2025-01-31 15:33:14 -05:00
parent 112874c080
commit 0a90d9f003
6 changed files with 28 additions and 14 deletions

View File

@@ -23,13 +23,18 @@ object ArchiveValidator {
/** /**
* Validates the provided [backupFile] that is encrypted with the provided [backupKey]. * Validates the provided [backupFile] that is encrypted with the provided [backupKey].
*/ */
fun validate(backupFile: File, backupKey: MessageBackupKey): ValidationResult { fun validate(backupFile: File, backupKey: MessageBackupKey, forTransfer: Boolean): ValidationResult {
return try { return try {
val backupId = backupKey.deriveBackupId(SignalStore.account.requireAci()) val backupId = backupKey.deriveBackupId(SignalStore.account.requireAci())
val libSignalBackupKey = LibSignalBackupKey(backupKey.value) val libSignalBackupKey = LibSignalBackupKey(backupKey.value)
val backupKey = LibSignalMessageBackupKey(libSignalBackupKey, backupId.value) val backupKey = LibSignalMessageBackupKey(libSignalBackupKey, backupId.value)
MessageBackup.validate(backupKey, MessageBackup.Purpose.REMOTE_BACKUP, { backupFile.inputStream() }, backupFile.length()) MessageBackup.validate(
backupKey,
if (forTransfer) MessageBackup.Purpose.DEVICE_TRANSFER else MessageBackup.Purpose.REMOTE_BACKUP,
{ backupFile.inputStream() },
backupFile.length()
)
ValidationResult.Success ValidationResult.Success
} catch (e: IOException) { } catch (e: IOException) {

View File

@@ -460,6 +460,7 @@ object BackupRepository {
plaintext: Boolean = false, plaintext: Boolean = false,
currentTime: Long = System.currentTimeMillis(), currentTime: Long = System.currentTimeMillis(),
mediaBackupEnabled: Boolean = SignalStore.backup.backsUpMedia, mediaBackupEnabled: Boolean = SignalStore.backup.backsUpMedia,
forTransfer: Boolean = false,
progressEmitter: ExportProgressListener? = null, progressEmitter: ExportProgressListener? = null,
cancellationSignal: () -> Boolean = { false }, cancellationSignal: () -> Boolean = { false },
exportExtras: ((SignalDatabase) -> Unit)? = null exportExtras: ((SignalDatabase) -> Unit)? = null
@@ -481,6 +482,7 @@ object BackupRepository {
writer = writer, writer = writer,
progressEmitter = progressEmitter, progressEmitter = progressEmitter,
mediaBackupEnabled = mediaBackupEnabled, mediaBackupEnabled = mediaBackupEnabled,
forTransfer = forTransfer,
cancellationSignal = cancellationSignal, cancellationSignal = cancellationSignal,
exportExtras = exportExtras exportExtras = exportExtras
) )
@@ -500,6 +502,7 @@ object BackupRepository {
isLocal: Boolean, isLocal: Boolean,
writer: BackupExportWriter, writer: BackupExportWriter,
mediaBackupEnabled: Boolean = SignalStore.backup.backsUpMedia, mediaBackupEnabled: Boolean = SignalStore.backup.backsUpMedia,
forTransfer: Boolean = false,
progressEmitter: ExportProgressListener? = null, progressEmitter: ExportProgressListener? = null,
cancellationSignal: () -> Boolean = { false }, cancellationSignal: () -> Boolean = { false },
exportExtras: ((SignalDatabase) -> Unit)? = null exportExtras: ((SignalDatabase) -> Unit)? = null
@@ -515,7 +518,7 @@ object BackupRepository {
val signalStoreSnapshot: SignalStore = createSignalStoreSnapshot(keyValueDbName) val signalStoreSnapshot: SignalStore = createSignalStoreSnapshot(keyValueDbName)
eventTimer.emit("store-db-snapshot") eventTimer.emit("store-db-snapshot")
val exportState = ExportState(backupTime = currentTime, mediaBackupEnabled = mediaBackupEnabled) val exportState = ExportState(backupTime = currentTime, mediaBackupEnabled = mediaBackupEnabled, forTransfer = forTransfer)
val selfAci = signalStoreSnapshot.accountValues.aci!! val selfAci = signalStoreSnapshot.accountValues.aci!!
val selfRecipientId = dbSnapshot.recipientTable.getByAci(selfAci).get().toLong().let { RecipientId.from(it) } val selfRecipientId = dbSnapshot.recipientTable.getByAci(selfAci).get().toLong().let { RecipientId.from(it) }
@@ -1521,7 +1524,11 @@ data class ResumableMessagesBackupUploadSpec(
data class ArchivedMediaObject(val mediaId: String, val cdn: Int) data class ArchivedMediaObject(val mediaId: String, val cdn: Int)
class ExportState(val backupTime: Long, val mediaBackupEnabled: Boolean) { class ExportState(
val backupTime: Long,
val mediaBackupEnabled: Boolean,
val forTransfer: Boolean
) {
val recipientIds: MutableSet<Long> = hashSetOf() val recipientIds: MutableSet<Long> = hashSetOf()
val threadIds: MutableSet<Long> = hashSetOf() val threadIds: MutableSet<Long> = hashSetOf()
val contactRecipientIds: MutableSet<Long> = hashSetOf() val contactRecipientIds: MutableSet<Long> = hashSetOf()

View File

@@ -562,9 +562,14 @@ private fun BackupMessageRecord.toBasicChatItemBuilder(selfRecipientId: Recipien
} }
} }
if (!MessageTypes.isExpirationTimerUpdate(record.type) && builder.expiresInMs != null && builder.expireStartDate != null && builder.expireStartDate!! + builder.expiresInMs!! < backupStartTime + 1.days.inWholeMilliseconds) { if (!MessageTypes.isExpirationTimerUpdate(record.type) && builder.expiresInMs != null && builder.expireStartDate != null) {
Log.w(TAG, ExportSkips.messageExpiresTooSoon(record.dateSent)) val expiresAt = builder.expireStartDate!! + builder.expiresInMs!!
return null val threshold = if (exportState.forTransfer) backupStartTime else backupStartTime + 1.days.inWholeMilliseconds
if (expiresAt < threshold) {
Log.w(TAG, ExportSkips.messageExpiresTooSoon(record.dateSent))
return null
}
} }
if (builder.expireStartDate != null && builder.expiresInMs == null) { if (builder.expireStartDate != null && builder.expiresInMs == null) {
@@ -1500,10 +1505,6 @@ private fun Cursor.toBackupMessageRecord(pastIds: Set<Long>, backupStartTime: Lo
val expiresIn = this.requireLong(MessageTable.EXPIRES_IN) val expiresIn = this.requireLong(MessageTable.EXPIRES_IN)
val expireStarted = this.requireLong(MessageTable.EXPIRE_STARTED) val expireStarted = this.requireLong(MessageTable.EXPIRE_STARTED)
if (expireStarted != 0L && expireStarted + expiresIn < backupStartTime + 1.days.inWholeMilliseconds) {
return null
}
return BackupMessageRecord( return BackupMessageRecord(
id = id, id = id,
dateSent = this.requireLong(MessageTable.DATE_SENT).clampToValidBackupRange(), dateSent = this.requireLong(MessageTable.DATE_SENT).clampToValidBackupRange(),

View File

@@ -143,7 +143,7 @@ class InternalBackupPlaygroundViewModel : ViewModel() {
append = { bytes -> tempFile.appendBytes(bytes) } append = { bytes -> tempFile.appendBytes(bytes) }
) )
_state.value = _state.value.copy(statusMessage = "Export complete! Validating...") _state.value = _state.value.copy(statusMessage = "Export complete! Validating...")
ArchiveValidator.validate(tempFile, SignalStore.backup.messageBackupKey) ArchiveValidator.validate(tempFile, SignalStore.backup.messageBackupKey, forTransfer = false)
} }
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())

View File

@@ -215,7 +215,7 @@ class BackupMessagesJob private constructor(
stopwatch.split("export") stopwatch.split("export")
when (val result = ArchiveValidator.validate(tempBackupFile, backupKey)) { when (val result = ArchiveValidator.validate(tempBackupFile, backupKey, forTransfer = false)) {
ArchiveValidator.ValidationResult.Success -> { ArchiveValidator.ValidationResult.Success -> {
Log.d(TAG, "Successfully passed validation.") Log.d(TAG, "Successfully passed validation.")
} }

View File

@@ -254,6 +254,7 @@ object LinkDeviceRepository {
append = { tempBackupFile.appendBytes(it) }, append = { tempBackupFile.appendBytes(it) },
messageBackupKey = ephemeralMessageBackupKey, messageBackupKey = ephemeralMessageBackupKey,
mediaBackupEnabled = false, mediaBackupEnabled = false,
forTransfer = true,
cancellationSignal = cancellationSignal cancellationSignal = cancellationSignal
) )
} catch (e: Exception) { } catch (e: Exception) {
@@ -268,7 +269,7 @@ object LinkDeviceRepository {
return LinkUploadArchiveResult.BackupCreationCancelled return LinkUploadArchiveResult.BackupCreationCancelled
} }
when (val result = ArchiveValidator.validate(tempBackupFile, ephemeralMessageBackupKey)) { when (val result = ArchiveValidator.validate(tempBackupFile, ephemeralMessageBackupKey, forTransfer = true)) {
ArchiveValidator.ValidationResult.Success -> { ArchiveValidator.ValidationResult.Success -> {
Log.d(TAG, "[createAndUploadArchive] Successfully passed validation.") Log.d(TAG, "[createAndUploadArchive] Successfully passed validation.")
} }