Add streaming video support for attachment files.
This commit is contained in:
parent
bc5cb454bf
commit
5c3ea712fe
4 changed files with 84 additions and 12 deletions
|
@ -116,6 +116,7 @@ import org.thoughtcrime.securesms.giph.mp4.GiphyMp4PlaybackPolicy;
|
|||
import org.thoughtcrime.securesms.giph.mp4.GiphyMp4PlaybackPolicyEnforcer;
|
||||
import org.thoughtcrime.securesms.jobmanager.JobManager;
|
||||
import org.thoughtcrime.securesms.jobs.AttachmentDownloadJob;
|
||||
import org.thoughtcrime.securesms.jobs.RestoreAttachmentJob;
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
||||
import org.thoughtcrime.securesms.linkpreview.LinkPreview;
|
||||
import org.thoughtcrime.securesms.mediapreview.MediaIntentFactory;
|
||||
|
@ -2489,7 +2490,22 @@ public final class ConversationItem extends RelativeLayout implements BindableCo
|
|||
}
|
||||
if (MediaUtil.isInstantVideoSupported(slide)) {
|
||||
final DatabaseAttachment databaseAttachment = (DatabaseAttachment) slide.asAttachment();
|
||||
if (databaseAttachment.transferState != AttachmentTable.TRANSFER_PROGRESS_STARTED) {
|
||||
if (databaseAttachment.transferState == AttachmentTable.TRANSFER_RESTORE_OFFLOADED) {
|
||||
final AttachmentId attachmentId = databaseAttachment.attachmentId;
|
||||
final JobManager jobManager = ApplicationDependencies.getJobManager();
|
||||
final String queue = RestoreAttachmentJob.constructQueueString(attachmentId);
|
||||
setup(v, slide);
|
||||
jobManager.add(new RestoreAttachmentJob(messageRecord.getId(),
|
||||
attachmentId,
|
||||
true,
|
||||
false,
|
||||
RestoreAttachmentJob.RestoreMode.ORIGINAL));
|
||||
jobManager.addListener(queue, (job, jobState) -> {
|
||||
if (jobState.isComplete()) {
|
||||
cleanup();
|
||||
}
|
||||
});
|
||||
} else if (databaseAttachment.transferState != AttachmentTable.TRANSFER_PROGRESS_STARTED) {
|
||||
final AttachmentId attachmentId = databaseAttachment.attachmentId;
|
||||
final JobManager jobManager = ApplicationDependencies.getJobManager();
|
||||
final String queue = AttachmentDownloadJob.constructQueueString(attachmentId);
|
||||
|
|
|
@ -16,9 +16,14 @@ import org.signal.libsignal.protocol.InvalidMessageException;
|
|||
import org.thoughtcrime.securesms.attachments.DatabaseAttachment;
|
||||
import org.thoughtcrime.securesms.database.AttachmentTable;
|
||||
import org.thoughtcrime.securesms.database.SignalDatabase;
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
||||
import org.thoughtcrime.securesms.mms.PartUriParser;
|
||||
import org.signal.core.util.Base64;
|
||||
import org.whispersystems.signalservice.api.backup.BackupKey;
|
||||
import org.whispersystems.signalservice.api.backup.MediaId;
|
||||
import org.whispersystems.signalservice.api.crypto.AttachmentCipherInputStream;
|
||||
import org.whispersystems.signalservice.api.crypto.AttachmentCipherStreamUtil;
|
||||
import org.whispersystems.signalservice.internal.crypto.PaddingInputStream;
|
||||
|
||||
import java.io.EOFException;
|
||||
import java.io.File;
|
||||
|
@ -62,20 +67,30 @@ class PartDataSource implements DataSource {
|
|||
|
||||
if (inProgress && !hasData && hasIncrementalDigest && attachmentKey != null) {
|
||||
final byte[] decode = Base64.decode(attachmentKey);
|
||||
final File transferFile = attachmentDatabase.getOrCreateTransferFile(attachment.attachmentId);
|
||||
try {
|
||||
this.inputStream = AttachmentCipherInputStream.createForAttachment(transferFile, attachment.size, decode, attachment.remoteDigest, attachment.getIncrementalDigest(), attachment.incrementalMacChunkSize);
|
||||
if (attachment.transferState == AttachmentTable.TRANSFER_RESTORE_IN_PROGRESS && attachment.archiveMediaId != null) {
|
||||
final File archiveFile = attachmentDatabase.getOrCreateArchiveTransferFile(attachment.attachmentId);
|
||||
try {
|
||||
BackupKey.MediaKeyMaterial mediaKeyMaterial = SignalStore.svr().getOrCreateMasterKey().deriveBackupKey().deriveMediaSecretsFromMediaId(attachment.archiveMediaId);
|
||||
long originalCipherLength = AttachmentCipherStreamUtil.getCiphertextLength(PaddingInputStream.getPaddedSize(attachment.size));
|
||||
|
||||
long skipped = 0;
|
||||
while (skipped < dataSpec.position) {
|
||||
skipped += this.inputStream.read();
|
||||
this.inputStream = AttachmentCipherInputStream.createStreamingForArchivedAttachment(mediaKeyMaterial, archiveFile, originalCipherLength, attachment.size, attachment.remoteDigest, decode, attachment.getIncrementalDigest(), attachment.incrementalMacChunkSize);
|
||||
} catch (InvalidMessageException e) {
|
||||
throw new IOException("Error decrypting attachment stream!", e);
|
||||
}
|
||||
} else {
|
||||
final File transferFile = attachmentDatabase.getOrCreateTransferFile(attachment.attachmentId);
|
||||
try {
|
||||
this.inputStream = AttachmentCipherInputStream.createForAttachment(transferFile, attachment.size, decode, attachment.remoteDigest, attachment.getIncrementalDigest(), attachment.incrementalMacChunkSize);
|
||||
} catch (InvalidMessageException e) {
|
||||
throw new IOException("Error decrypting attachment stream!", e);
|
||||
}
|
||||
|
||||
Log.d(TAG, "Successfully loaded partial attachment file.");
|
||||
|
||||
} catch (InvalidMessageException e) {
|
||||
throw new IOException("Error decrypting attachment stream!", e);
|
||||
}
|
||||
long skipped = 0;
|
||||
while (skipped < dataSpec.position) {
|
||||
skipped += this.inputStream.read();
|
||||
}
|
||||
|
||||
Log.d(TAG, "Successfully loaded partial attachment file.");
|
||||
} else if (!inProgress || hasData) {
|
||||
this.inputStream = attachmentDatabase.getAttachmentStream(partUri.getPartId(), dataSpec.position);
|
||||
|
||||
|
|
|
@ -42,6 +42,10 @@ class BackupKey(val value: ByteArray) {
|
|||
return deriveMediaSecrets(deriveMediaId(mediaName))
|
||||
}
|
||||
|
||||
fun deriveMediaSecretsFromMediaId(base64MediaId: String): MediaKeyMaterial {
|
||||
return deriveMediaSecrets(MediaId(base64MediaId))
|
||||
}
|
||||
|
||||
fun deriveThumbnailTransitKey(thumbnailMediaName: MediaName): ByteArray {
|
||||
return HKDF.deriveSecrets(value, deriveMediaId(thumbnailMediaName).value, "20240513_Signal_Backups_EncryptThumbnail".toByteArray(), 64)
|
||||
}
|
||||
|
|
|
@ -138,6 +138,43 @@ public class AttachmentCipherInputStream extends FilterInputStream {
|
|||
return inputStream;
|
||||
}
|
||||
|
||||
public static InputStream createStreamingForArchivedAttachment(BackupKey.MediaKeyMaterial archivedMediaKeyMaterial, File file, long originalCipherTextLength, long plaintextLength, byte[] combinedKeyMaterial, byte[] digest, byte[] incrementalDigest, int incrementalMacChunkSize)
|
||||
throws InvalidMessageException, IOException
|
||||
{
|
||||
final InputStream archiveStream = createForArchivedMedia(archivedMediaKeyMaterial, file, originalCipherTextLength);
|
||||
|
||||
byte[][] parts = Util.split(combinedKeyMaterial, CIPHER_KEY_SIZE, MAC_KEY_SIZE);
|
||||
Mac mac = initMac(parts[1]);
|
||||
|
||||
if (originalCipherTextLength <= BLOCK_SIZE + mac.getMacLength()) {
|
||||
throw new InvalidMessageException("Message shorter than crypto overhead!");
|
||||
}
|
||||
|
||||
if (digest == null) {
|
||||
throw new InvalidMessageException("Missing digest!");
|
||||
}
|
||||
|
||||
final InputStream wrappedStream;
|
||||
wrappedStream = new IncrementalMacInputStream(
|
||||
new IncrementalMacAdditionalValidationsInputStream(
|
||||
archiveStream,
|
||||
file.length(),
|
||||
mac,
|
||||
digest
|
||||
),
|
||||
parts[1],
|
||||
ChunkSizeChoice.everyNthByte(incrementalMacChunkSize),
|
||||
incrementalDigest);
|
||||
|
||||
InputStream inputStream = new AttachmentCipherInputStream(wrappedStream, parts[0], file.length() - BLOCK_SIZE - mac.getMacLength());
|
||||
|
||||
if (plaintextLength != 0) {
|
||||
inputStream = new ContentLengthInputStream(inputStream, plaintextLength);
|
||||
}
|
||||
|
||||
return inputStream;
|
||||
}
|
||||
|
||||
public static InputStream createForStickerData(byte[] data, byte[] packKey)
|
||||
throws InvalidMessageException, IOException
|
||||
{
|
||||
|
|
Loading…
Add table
Reference in a new issue