Update local backup v2 support.

This commit is contained in:
Cody Henthorne
2025-12-18 16:33:30 -05:00
committed by jeffrey-signal
parent 71b15d269e
commit d9ecab5240
55 changed files with 2291 additions and 274 deletions

View File

@@ -25,6 +25,9 @@ class ArchivedAttachment : Attachment {
@JvmField
val plaintextHash: ByteArray
@JvmField
val localBackupKey: ByteArray?
constructor(
contentType: String?,
size: Long,
@@ -47,7 +50,8 @@ class ArchivedAttachment : Attachment {
quote: Boolean,
quoteTargetContentType: String?,
uuid: UUID?,
fileName: String?
fileName: String?,
localBackupKey: ByteArray?
) : super(
contentType = contentType ?: "",
quote = quote,
@@ -77,17 +81,20 @@ class ArchivedAttachment : Attachment {
) {
this.archiveCdn = archiveCdn
this.plaintextHash = plaintextHash
this.localBackupKey = localBackupKey
}
constructor(parcel: Parcel) : super(parcel) {
archiveCdn = parcel.readInt().takeIf { it != NO_ARCHIVE_CDN }
plaintextHash = parcel.createByteArray()!!
localBackupKey = parcel.createByteArray()
}
override fun writeToParcel(dest: Parcel, flags: Int) {
super.writeToParcel(dest, flags)
dest.writeInt(archiveCdn ?: NO_ARCHIVE_CDN)
dest.writeByteArray(plaintextHash)
dest.writeByteArray(localBackupKey)
}
override val uri: Uri? = null

View File

@@ -18,7 +18,9 @@ object AttachmentCreator : Parcelable.Creator<Attachment> {
POINTER(PointerAttachment::class.java, "pointer"),
TOMBSTONE(TombstoneAttachment::class.java, "tombstone"),
URI(UriAttachment::class.java, "uri"),
ARCHIVED(ArchivedAttachment::class.java, "archived")
ARCHIVED(ArchivedAttachment::class.java, "archived"),
LOCAL_STICKER(LocalStickerAttachment::class.java, "local_sticker"),
WALLPAPER(WallpaperAttachment::class.java, "wallpaper")
}
@JvmStatic
@@ -36,6 +38,8 @@ object AttachmentCreator : Parcelable.Creator<Attachment> {
Subclass.TOMBSTONE -> TombstoneAttachment(source)
Subclass.URI -> UriAttachment(source)
Subclass.ARCHIVED -> ArchivedAttachment(source)
Subclass.LOCAL_STICKER -> LocalStickerAttachment(source)
Subclass.WALLPAPER -> WallpaperAttachment(source)
}
}

View File

@@ -0,0 +1,19 @@
/*
* Copyright 2025 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.thoughtcrime.securesms.attachments
import android.os.Parcelable
import kotlinx.parcelize.Parcelize
import kotlinx.parcelize.RawValue
/**
* Metadata for a specific attachment, specifically per data file. So there can be a
* many-to-one relationship from attachments to metadata.
*/
@Parcelize
class AttachmentMetadata(
val localBackupKey: @RawValue LocalBackupKey?
) : Parcelable

View File

@@ -39,6 +39,10 @@ class DatabaseAttachment : Attachment {
@JvmField
val archiveTransferState: AttachmentTable.ArchiveTransferState
/** Metadata for this attachment, if null, no attempt was made to load the metadata and does not imply there is none */
@JvmField
val metadata: AttachmentMetadata?
private val hasThumbnail: Boolean
val displayOrder: Int
@@ -76,7 +80,8 @@ class DatabaseAttachment : Attachment {
thumbnailRestoreState: AttachmentTable.ThumbnailRestoreState,
archiveTransferState: AttachmentTable.ArchiveTransferState,
uuid: UUID?,
quoteTargetContentType: String?
quoteTargetContentType: String?,
metadata: AttachmentMetadata?
) : super(
contentType = contentType,
transferState = transferProgress,
@@ -112,6 +117,7 @@ class DatabaseAttachment : Attachment {
this.archiveCdn = archiveCdn
this.thumbnailRestoreState = thumbnailRestoreState
this.archiveTransferState = archiveTransferState
this.metadata = metadata
}
constructor(parcel: Parcel) : super(parcel) {
@@ -124,6 +130,7 @@ class DatabaseAttachment : Attachment {
archiveCdn = parcel.readInt().takeIf { it != NO_ARCHIVE_CDN }
thumbnailRestoreState = AttachmentTable.ThumbnailRestoreState.deserialize(parcel.readInt())
archiveTransferState = AttachmentTable.ArchiveTransferState.deserialize(parcel.readInt())
metadata = ParcelCompat.readParcelable(parcel, AttachmentMetadata::class.java.classLoader, AttachmentMetadata::class.java)
}
override fun writeToParcel(dest: Parcel, flags: Int) {
@@ -137,6 +144,7 @@ class DatabaseAttachment : Attachment {
dest.writeInt(archiveCdn ?: NO_ARCHIVE_CDN)
dest.writeInt(thumbnailRestoreState.value)
dest.writeInt(archiveTransferState.value)
dest.writeParcelable(metadata, 0)
}
override val uri: Uri?

View File

@@ -0,0 +1,19 @@
/*
* Copyright 2025 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.thoughtcrime.securesms.attachments
import okio.ByteString
import okio.ByteString.Companion.toByteString
/**
* Combined key used to encrypt/decrypt attachments for local backups.
*/
@JvmInline
value class LocalBackupKey(val key: ByteArray) {
fun toByteString(): ByteString {
return key.toByteString()
}
}

View File

@@ -5,6 +5,7 @@
package org.thoughtcrime.securesms.attachments
import android.os.Parcel
import org.thoughtcrime.securesms.database.AttachmentTable
import org.thoughtcrime.securesms.database.AttachmentTable.TransformProperties
import org.thoughtcrime.securesms.util.MediaUtil
@@ -12,34 +13,39 @@ import org.thoughtcrime.securesms.util.MediaUtil
/**
* A basically-empty [Attachment] that is solely used for inserting an attachment into the [AttachmentTable].
*/
class WallpaperAttachment() : Attachment(
contentType = MediaUtil.IMAGE_WEBP,
transferState = AttachmentTable.TRANSFER_PROGRESS_DONE,
size = 0,
fileName = null,
cdn = Cdn.CDN_0,
remoteLocation = null,
remoteKey = null,
remoteDigest = null,
incrementalDigest = null,
fastPreflightId = null,
voiceNote = false,
borderless = false,
videoGif = false,
width = 0,
height = 0,
incrementalMacChunkSize = 0,
quote = false,
quoteTargetContentType = null,
uploadTimestamp = 0,
caption = null,
stickerLocator = null,
blurHash = null,
audioHash = null,
transformProperties = TransformProperties.empty(),
uuid = null
) {
class WallpaperAttachment : Attachment {
override val uri = null
override val publicUri = null
override val thumbnailUri = null
constructor() : super(
contentType = MediaUtil.IMAGE_WEBP,
transferState = AttachmentTable.TRANSFER_PROGRESS_DONE,
size = 0,
fileName = null,
cdn = Cdn.CDN_0,
remoteLocation = null,
remoteKey = null,
remoteDigest = null,
incrementalDigest = null,
fastPreflightId = null,
voiceNote = false,
borderless = false,
videoGif = false,
width = 0,
height = 0,
incrementalMacChunkSize = 0,
quote = false,
quoteTargetContentType = null,
uploadTimestamp = 0,
caption = null,
stickerLocator = null,
blurHash = null,
audioHash = null,
transformProperties = TransformProperties.empty(),
uuid = null
)
@Suppress("unused")
constructor(parcel: Parcel) : super(parcel)
}