Clear incrementalMac if we discover it's bad during playback.

This commit is contained in:
Greyson Parrelli
2025-08-25 12:35:17 -04:00
committed by Michelle Tang
parent 45c64f825d
commit 2046b44fce
9 changed files with 124 additions and 24 deletions

View File

@@ -39,19 +39,24 @@ import androidx.media3.common.Player;
import androidx.media3.common.Tracks;
import androidx.media3.common.util.UnstableApi;
import androidx.media3.exoplayer.ExoPlayer;
import androidx.media3.exoplayer.analytics.AnalyticsListener;
import androidx.media3.exoplayer.source.ClippingMediaSource;
import androidx.media3.exoplayer.source.DefaultMediaSourceFactory;
import androidx.media3.exoplayer.source.LoadEventInfo;
import androidx.media3.exoplayer.source.MediaLoadData;
import androidx.media3.exoplayer.source.MediaSource;
import androidx.media3.ui.AspectRatioFrameLayout;
import androidx.media3.ui.LegacyPlayerControlView;
import androidx.media3.ui.PlayerView;
import org.signal.core.util.logging.Log;
import org.signal.libsignal.protocol.incrementalmac.InvalidMacException;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.dependencies.AppDependencies;
import org.thoughtcrime.securesms.mediapreview.MediaPreviewPlayerControlView;
import org.thoughtcrime.securesms.mms.VideoSlide;
import java.io.IOException;
import java.util.Objects;
@OptIn(markerClass = UnstableApi.class)
@@ -74,6 +79,7 @@ public class VideoPlayer extends FrameLayout {
private long clippedStartUs;
private ExoPlayerListener exoPlayerListener;
private Player.Listener playerListener;
private AnalyticsListener analyticsListener;
private boolean muted;
private AudioFocusRequest audioFocusRequest;
private boolean requestAudioFocus = true;
@@ -119,6 +125,15 @@ public class VideoPlayer extends FrameLayout {
}
this.exoPlayerListener = new ExoPlayerListener();
this.analyticsListener = new AnalyticsListener() {
@Override
public void onLoadError(EventTime eventTime, LoadEventInfo loadEventInfo, MediaLoadData mediaLoadData, IOException error, boolean wasCanceled) {
if (error instanceof InvalidMacException) {
Log.w(TAG, "Bad incremental mac!", error);
playerCallback.onError(error);
}
}
};
this.playerListener = new Player.Listener() {
@Override
@@ -159,7 +174,6 @@ public class VideoPlayer extends FrameLayout {
);
}
}
}
@Override
@@ -201,7 +215,7 @@ public class VideoPlayer extends FrameLayout {
public void onPlayerError(@NonNull PlaybackException error) {
Log.w(TAG, "A player error occurred", error);
if (playerCallback != null) {
playerCallback.onError();
playerCallback.onError(error);
}
}
};
@@ -226,6 +240,7 @@ public class VideoPlayer extends FrameLayout {
exoPlayer = AppDependencies.getExoPlayerPool().require(poolTag);
exoPlayer.addListener(exoPlayerListener);
exoPlayer.addListener(playerListener);
exoPlayer.addAnalyticsListener(analyticsListener);
exoView.setPlayer(exoPlayer);
exoControls.setPlayer(exoPlayer);
if (muted) {
@@ -513,6 +528,6 @@ public class VideoPlayer extends FrameLayout {
void onStopped();
void onError();
void onError(Exception e);
}
}

View File

@@ -33,6 +33,7 @@ import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
@@ -75,6 +76,7 @@ class PartDataSource implements DataSource {
final byte[] decodedKey = Base64.decode(attachmentKey);
if (attachment.transferState == AttachmentTable.TRANSFER_RESTORE_IN_PROGRESS && attachment.archiveTransferState == AttachmentTable.ArchiveTransferState.FINISHED) {
Log.d(TAG, "Playing partial video content for archive attachment.");
final File archiveFile = attachmentDatabase.getOrCreateTransferFile(attachment.attachmentId);
try {
String mediaName = DatabaseAttachmentArchiveUtil.requireMediaNameAsString(attachment);
@@ -83,10 +85,6 @@ class PartDataSource implements DataSource {
MediaRootBackupKey.MediaKeyMaterial mediaKeyMaterial = SignalStore.backup().getMediaRootBackupKey().deriveMediaSecretsFromMediaId(mediaId);
long originalCipherLength = AttachmentCipherStreamUtil.getCiphertextLength(PaddingInputStream.getPaddedSize(attachment.size));
if (attachment.remoteDigest == null) {
throw new InvalidMessageException("Missing digest!");
}
if (attachment.dataHash == null || attachment.dataHash.isEmpty()) {
throw new InvalidMessageException("Missing plaintextHash!");
}
@@ -96,13 +94,14 @@ class PartDataSource implements DataSource {
throw new IOException("Error decrypting attachment stream!", e);
}
} else {
Log.d(TAG, "Playing partial video content for normal attachment.");
final File transferFile = attachmentDatabase.getOrCreateTransferFile(attachment.attachmentId);
try {
long streamLength = AttachmentCipherStreamUtil.getCiphertextLength(PaddingInputStream.getPaddedSize(attachment.size));
AttachmentCipherInputStream.StreamSupplier streamSupplier = () -> new TailerInputStream(() -> new FileInputStream(transferFile), streamLength);
if (attachment.remoteDigest == null && attachment.dataHash == null) {
throw new InvalidMessageException("Missing digest!");
throw new InvalidMessageException("Missing digest and plaintextHash!");
}
IntegrityCheck integrityCheck = IntegrityCheck.forEncryptedDigestAndPlaintextHash(attachment.remoteDigest, attachment.dataHash);
@@ -119,6 +118,7 @@ class PartDataSource implements DataSource {
Log.d(TAG, "Successfully loaded partial attachment file.");
} else if (!inProgress || hasData) {
Log.d(TAG, "Playing a fully downloaded attachment.");
this.inputStream = attachmentDatabase.getAttachmentStream(partUri.getPartId(), dataSpec.position);
Log.d(TAG, "Successfully loaded completed attachment file.");