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].
*/
fun validate(backupFile: File, backupKey: MessageBackupKey): ValidationResult {
fun validate(backupFile: File, backupKey: MessageBackupKey, forTransfer: Boolean): ValidationResult {
return try {
val backupId = backupKey.deriveBackupId(SignalStore.account.requireAci())
val libSignalBackupKey = LibSignalBackupKey(backupKey.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
} catch (e: IOException) {

View File

@@ -460,6 +460,7 @@ object BackupRepository {
plaintext: Boolean = false,
currentTime: Long = System.currentTimeMillis(),
mediaBackupEnabled: Boolean = SignalStore.backup.backsUpMedia,
forTransfer: Boolean = false,
progressEmitter: ExportProgressListener? = null,
cancellationSignal: () -> Boolean = { false },
exportExtras: ((SignalDatabase) -> Unit)? = null
@@ -481,6 +482,7 @@ object BackupRepository {
writer = writer,
progressEmitter = progressEmitter,
mediaBackupEnabled = mediaBackupEnabled,
forTransfer = forTransfer,
cancellationSignal = cancellationSignal,
exportExtras = exportExtras
)
@@ -500,6 +502,7 @@ object BackupRepository {
isLocal: Boolean,
writer: BackupExportWriter,
mediaBackupEnabled: Boolean = SignalStore.backup.backsUpMedia,
forTransfer: Boolean = false,
progressEmitter: ExportProgressListener? = null,
cancellationSignal: () -> Boolean = { false },
exportExtras: ((SignalDatabase) -> Unit)? = null
@@ -515,7 +518,7 @@ object BackupRepository {
val signalStoreSnapshot: SignalStore = createSignalStoreSnapshot(keyValueDbName)
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 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)
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 threadIds: MutableSet<Long> = hashSetOf()
val contactRecipientIds: MutableSet<Long> = hashSetOf()

View File

@@ -562,10 +562,15 @@ 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) {
val expiresAt = builder.expireStartDate!! + builder.expiresInMs!!
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) {
builder.expireStartDate = null
@@ -1500,10 +1505,6 @@ private fun Cursor.toBackupMessageRecord(pastIds: Set<Long>, backupStartTime: Lo
val expiresIn = this.requireLong(MessageTable.EXPIRES_IN)
val expireStarted = this.requireLong(MessageTable.EXPIRE_STARTED)
if (expireStarted != 0L && expireStarted + expiresIn < backupStartTime + 1.days.inWholeMilliseconds) {
return null
}
return BackupMessageRecord(
id = id,
dateSent = this.requireLong(MessageTable.DATE_SENT).clampToValidBackupRange(),

View File

@@ -143,7 +143,7 @@ class InternalBackupPlaygroundViewModel : ViewModel() {
append = { bytes -> tempFile.appendBytes(bytes) }
)
_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())
.observeOn(AndroidSchedulers.mainThread())

View File

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

View File

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