Ensure we are restoring media per spec with full media and thumbnail rules.

This commit is contained in:
Alex Hart
2025-07-14 15:56:41 -03:00
committed by Jeffrey Starke
parent 1137bbd8a5
commit 049e9460a0
4 changed files with 166 additions and 7 deletions

View File

@@ -119,6 +119,7 @@ import java.util.Optional
import java.util.UUID
import kotlin.time.Duration
import kotlin.time.Duration.Companion.days
import kotlin.time.Duration.Companion.milliseconds
class AttachmentTable(
context: Context,
@@ -510,6 +511,54 @@ class AttachmentTable(
}
}
/**
* Grabs the last 30 days worth of restorable attachments with respect to the message's server timestamp,
* up to the given batch size.
*/
fun getLast30DaysOfRestorableAttachments(batchSize: Int): List<RestorableAttachment> {
val thirtyDaysAgo = System.currentTimeMillis().milliseconds - 30.days
return readableDatabase
.select("$TABLE_NAME.$ID", MESSAGE_ID, DATA_SIZE, DATA_HASH_END, REMOTE_KEY)
.from("$TABLE_NAME INNER JOIN ${MessageTable.TABLE_NAME} ON ${MessageTable.TABLE_NAME}.${MessageTable.ID} = $TABLE_NAME.$MESSAGE_ID")
.where("$TRANSFER_STATE = ? AND ${MessageTable.TABLE_NAME}.${MessageTable.DATE_RECEIVED} >= ?", TRANSFER_NEEDS_RESTORE, thirtyDaysAgo.inWholeMilliseconds)
.limit(batchSize)
.orderBy("$TABLE_NAME.$ID DESC")
.run()
.readToList {
RestorableAttachment(
attachmentId = AttachmentId(it.requireLong(ID)),
mmsId = it.requireLong(MESSAGE_ID),
size = it.requireLong(DATA_SIZE),
plaintextHash = it.requireBlob(DATA_HASH_END),
remoteKey = it.requireBlob(REMOTE_KEY)
)
}
}
/**
* Grabs attachments outside of the last 30 days with respect to the message's server timestamp,
* up to the given batch size.
*/
fun getOlderRestorableAttachments(batchSize: Int): List<RestorableAttachment> {
val thirtyDaysAgo = System.currentTimeMillis().milliseconds - 30.days
return readableDatabase
.select("$TABLE_NAME.$ID", MESSAGE_ID, DATA_SIZE, DATA_HASH_END, REMOTE_KEY)
.from("$TABLE_NAME INNER JOIN ${MessageTable.TABLE_NAME} ON ${MessageTable.TABLE_NAME}.${MessageTable.ID} = $TABLE_NAME.$MESSAGE_ID")
.where("$TRANSFER_STATE = ? AND ${MessageTable.TABLE_NAME}.${MessageTable.DATE_RECEIVED} < ?", TRANSFER_NEEDS_RESTORE, thirtyDaysAgo.inWholeMilliseconds)
.limit(batchSize)
.orderBy("$TABLE_NAME.$ID DESC")
.run()
.readToList {
RestorableAttachment(
attachmentId = AttachmentId(it.requireLong(ID)),
mmsId = it.requireLong(MESSAGE_ID),
size = it.requireLong(DATA_SIZE),
plaintextHash = it.requireBlob(DATA_HASH_END),
remoteKey = it.requireBlob(REMOTE_KEY)
)
}
}
fun getRestorableAttachments(batchSize: Int): List<RestorableAttachment> {
return readableDatabase
.select(ID, MESSAGE_ID, DATA_SIZE, DATA_HASH_END, REMOTE_KEY)

View File

@@ -61,7 +61,16 @@ class BackupRestoreMediaJob private constructor(parameters: Parameters) : BaseJo
val restoreThumbnailOnlyAttachmentsIds: MutableList<AttachmentId> = mutableListOf()
val notRestorable: MutableList<AttachmentId> = mutableListOf()
val attachmentBatch = SignalDatabase.attachments.getRestorableAttachments(batchSize)
val last30DaysAttachments = SignalDatabase.attachments.getLast30DaysOfRestorableAttachments(batchSize)
val remainingSize = batchSize - last30DaysAttachments.size
val remaining = if (remainingSize > 0) {
SignalDatabase.attachments.getOlderRestorableAttachments(batchSize = remainingSize)
} else {
listOf()
}
val attachmentBatch = last30DaysAttachments + remaining
val messageIds = attachmentBatch.map { it.mmsId }.toSet()
val messageMap = SignalDatabase.messages.getMessages(messageIds).associate { it.id to (it as MmsMessageRecord) }
@@ -75,18 +84,18 @@ class BackupRestoreMediaJob private constructor(parameters: Parameters) : BaseJo
continue
}
restoreThumbnailJobs += RestoreAttachmentThumbnailJob(
messageId = attachment.mmsId,
attachmentId = attachment.attachmentId,
highPriority = false
)
if (isWallpaper || shouldRestoreFullSize(message!!, restoreTime, SignalStore.backup.optimizeStorage)) {
restoreFullAttachmentJobs += RestoreAttachmentJob.forInitialRestore(
messageId = attachment.mmsId,
attachmentId = attachment.attachmentId
)
} else {
restoreThumbnailJobs += RestoreAttachmentThumbnailJob(
messageId = attachment.mmsId,
attachmentId = attachment.attachmentId,
highPriority = false
)
restoreThumbnailOnlyAttachmentsIds += attachment.attachmentId
}
}

View File

@@ -203,6 +203,15 @@ class RestoreAttachmentJob private constructor(
Log.w(TAG, format(this, "onFailure() messageId: $messageId attachmentId: $attachmentId"))
markFailed(attachmentId)
Log.w(TAG, "onFailure(): Attempting to fall back on attachment thumbnail.")
val restoreThumbnailAttachmentJob = RestoreAttachmentThumbnailJob(
messageId = messageId,
attachmentId = attachmentId,
highPriority = manual
)
AppDependencies.jobManager.add(restoreThumbnailAttachmentJob)
}
}