Skip to content

Commit

Permalink
Add system to allow skipping attachment compression.
Browse files Browse the repository at this point in the history
  • Loading branch information
greyson-signal committed Oct 21, 2019
1 parent 95d3db3 commit 097f97b
Show file tree
Hide file tree
Showing 19 changed files with 181 additions and 55 deletions.
43 changes: 26 additions & 17 deletions src/org/thoughtcrime/securesms/attachments/Attachment.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

import org.thoughtcrime.securesms.blurhash.BlurHash;
import org.thoughtcrime.securesms.database.AttachmentDatabase;
import org.thoughtcrime.securesms.database.AttachmentDatabase.TransformProperties;
import org.thoughtcrime.securesms.stickers.StickerLocator;

public abstract class Attachment {
Expand Down Expand Up @@ -48,28 +49,32 @@ public abstract class Attachment {
@Nullable
private final BlurHash blurHash;

@NonNull
private final TransformProperties transformProperties;

public Attachment(@NonNull String contentType, int transferState, long size, @Nullable String fileName,
@Nullable String location, @Nullable String key, @Nullable String relay,
@Nullable byte[] digest, @Nullable String fastPreflightId, boolean voiceNote,
int width, int height, boolean quote, @Nullable String caption, @Nullable StickerLocator stickerLocator,
@Nullable BlurHash blurHash)
@Nullable BlurHash blurHash, @Nullable TransformProperties transformProperties)
{
this.contentType = contentType;
this.transferState = transferState;
this.size = size;
this.fileName = fileName;
this.location = location;
this.key = key;
this.relay = relay;
this.digest = digest;
this.fastPreflightId = fastPreflightId;
this.voiceNote = voiceNote;
this.width = width;
this.height = height;
this.quote = quote;
this.stickerLocator = stickerLocator;
this.caption = caption;
this.blurHash = blurHash;
this.contentType = contentType;
this.transferState = transferState;
this.size = size;
this.fileName = fileName;
this.location = location;
this.key = key;
this.relay = relay;
this.digest = digest;
this.fastPreflightId = fastPreflightId;
this.voiceNote = voiceNote;
this.width = width;
this.height = height;
this.quote = quote;
this.stickerLocator = stickerLocator;
this.caption = caption;
this.blurHash = blurHash;
this.transformProperties = transformProperties != null ? transformProperties : TransformProperties.empty();
}

@Nullable
Expand Down Expand Up @@ -157,4 +162,8 @@ public boolean isSticker() {
public @Nullable String getCaption() {
return caption;
}

public @NonNull TransformProperties getTransformProperties() {
return transformProperties;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
import androidx.annotation.Nullable;

import org.thoughtcrime.securesms.blurhash.BlurHash;
import org.thoughtcrime.securesms.database.AttachmentDatabase;
import org.thoughtcrime.securesms.database.AttachmentDatabase.TransformProperties;
import org.thoughtcrime.securesms.mms.PartAuthority;
import org.thoughtcrime.securesms.stickers.StickerLocator;

Expand All @@ -22,9 +24,10 @@ public DatabaseAttachment(AttachmentId attachmentId, long mmsId,
String fileName, String location, String key, String relay,
byte[] digest, String fastPreflightId, boolean voiceNote,
int width, int height, boolean quote, @Nullable String caption,
@Nullable StickerLocator stickerLocator, @Nullable BlurHash blurHash)
@Nullable StickerLocator stickerLocator, @Nullable BlurHash blurHash,
@Nullable TransformProperties transformProperties)
{
super(contentType, transferProgress, size, fileName, location, key, relay, digest, fastPreflightId, voiceNote, width, height, quote, caption, stickerLocator, blurHash);
super(contentType, transferProgress, size, fileName, location, key, relay, digest, fastPreflightId, voiceNote, width, height, quote, caption, stickerLocator, blurHash, transformProperties);
this.attachmentId = attachmentId;
this.hasData = hasData;
this.hasThumbnail = hasThumbnail;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
public class MmsNotificationAttachment extends Attachment {

public MmsNotificationAttachment(int status, long size) {
super("application/mms", getTransferStateFromStatus(status), size, null, null, null, null, null, null, false, 0, 0, false, null, null, null);
super("application/mms", getTransferStateFromStatus(status), size, null, null, null, null, null, null, false, 0, 0, false, null, null, null, null);
}

@Nullable
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ private PointerAttachment(@NonNull String contentType, int transferState, long s
int width, int height, @Nullable String caption, @Nullable StickerLocator stickerLocator,
@Nullable BlurHash blurHash)
{
super(contentType, transferState, size, fileName, location, key, relay, digest, fastPreflightId, voiceNote, width, height, false, caption, stickerLocator, blurHash);
super(contentType, transferState, size, fileName, location, key, relay, digest, fastPreflightId, voiceNote, width, height, false, caption, stickerLocator, blurHash, null);
}

@Nullable
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
public class TombstoneAttachment extends Attachment {

public TombstoneAttachment(@NonNull String contentType, boolean quote) {
super(contentType, AttachmentDatabase.TRANSFER_PROGRESS_DONE, 0, null, null, null, null, null, null, false, 0, 0, quote, null, null, null);
super(contentType, AttachmentDatabase.TRANSFER_PROGRESS_DONE, 0, null, null, null, null, null, null, false, 0, 0, quote, null, null, null, null);
}

@Override
Expand Down
10 changes: 6 additions & 4 deletions src/org/thoughtcrime/securesms/attachments/UriAttachment.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
import androidx.annotation.Nullable;

import org.thoughtcrime.securesms.blurhash.BlurHash;
import org.thoughtcrime.securesms.database.AttachmentDatabase;
import org.thoughtcrime.securesms.database.AttachmentDatabase.TransformProperties;
import org.thoughtcrime.securesms.stickers.StickerLocator;

public class UriAttachment extends Attachment {
Expand All @@ -14,18 +16,18 @@ public class UriAttachment extends Attachment {

public UriAttachment(@NonNull Uri uri, @NonNull String contentType, int transferState, long size,
@Nullable String fileName, boolean voiceNote, boolean quote, @Nullable String caption,
@Nullable StickerLocator stickerLocator, @Nullable BlurHash blurHash)
@Nullable StickerLocator stickerLocator, @Nullable BlurHash blurHash, @Nullable TransformProperties transformProperties)
{
this(uri, uri, contentType, transferState, size, 0, 0, fileName, null, voiceNote, quote, caption, stickerLocator, blurHash);
this(uri, uri, contentType, transferState, size, 0, 0, fileName, null, voiceNote, quote, caption, stickerLocator, blurHash, transformProperties);
}

public UriAttachment(@NonNull Uri dataUri, @Nullable Uri thumbnailUri,
@NonNull String contentType, int transferState, long size, int width, int height,
@Nullable String fileName, @Nullable String fastPreflightId,
boolean voiceNote, boolean quote, @Nullable String caption, @Nullable StickerLocator stickerLocator,
@Nullable BlurHash blurHash)
@Nullable BlurHash blurHash, @Nullable TransformProperties transformProperties)
{
super(contentType, transferState, size, fileName, null, null, null, null, fastPreflightId, voiceNote, width, height, quote, caption, stickerLocator, blurHash);
super(contentType, transferState, size, fileName, null, null, null, null, fastPreflightId, voiceNote, width, height, quote, caption, stickerLocator, blurHash, transformProperties);
this.dataUri = dataUri;
this.thumbnailUri = thumbnailUri;
}
Expand Down
2 changes: 1 addition & 1 deletion src/org/thoughtcrime/securesms/contactshare/Contact.java
Original file line number Diff line number Diff line change
Expand Up @@ -643,7 +643,7 @@ public int describeContents() {

private static Attachment attachmentFromUri(@Nullable Uri uri) {
if (uri == null) return null;
return new UriAttachment(uri, MediaUtil.IMAGE_JPEG, AttachmentDatabase.TRANSFER_PROGRESS_DONE, 0, null, false, false, null, null, null);
return new UriAttachment(uri, MediaUtil.IMAGE_JPEG, AttachmentDatabase.TRANSFER_PROGRESS_DONE, 0, null, false, false, null, null, null, null);
}

@Override
Expand Down
131 changes: 111 additions & 20 deletions src/org/thoughtcrime/securesms/database/AttachmentDatabase.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import androidx.annotation.VisibleForTesting;

import com.bumptech.glide.Glide;
import com.fasterxml.jackson.annotation.JsonProperty;

import net.sqlcipher.DatabaseUtils;
import net.sqlcipher.database.SQLiteDatabase;
Expand Down Expand Up @@ -64,6 +65,7 @@
import org.thoughtcrime.securesms.util.Util;
import org.thoughtcrime.securesms.video.EncryptedMediaDataSource;
import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.internal.util.JsonUtil;

import java.io.File;
import java.io.FileNotFoundException;
Expand Down Expand Up @@ -115,6 +117,7 @@ public class AttachmentDatabase extends Database {
static final String CAPTION = "caption";
private static final String DATA_HASH = "data_hash";
static final String BLUR_HASH = "blur_hash";
static final String TRANSFORM_PROPERTIES = "transform_properties";

public static final String DIRECTORY = "parts";

Expand All @@ -133,7 +136,7 @@ public class AttachmentDatabase extends Database {
UNIQUE_ID, DIGEST, FAST_PREFLIGHT_ID, VOICE_NOTE,
QUOTE, DATA_RANDOM, THUMBNAIL_RANDOM, WIDTH, HEIGHT,
CAPTION, STICKER_PACK_ID, STICKER_PACK_KEY, STICKER_ID,
DATA_HASH, BLUR_HASH};
DATA_HASH, BLUR_HASH, TRANSFORM_PROPERTIES};

public static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + " (" + ROW_ID + " INTEGER PRIMARY KEY, " +
MMS_ID + " INTEGER, " + "seq" + " INTEGER DEFAULT 0, " +
Expand All @@ -148,7 +151,8 @@ public class AttachmentDatabase extends Database {
QUOTE + " INTEGER DEFAULT 0, " + WIDTH + " INTEGER DEFAULT 0, " + HEIGHT + " INTEGER DEFAULT 0, " +
CAPTION + " TEXT DEFAULT NULL, " + STICKER_PACK_ID + " TEXT DEFAULT NULL, " +
STICKER_PACK_KEY + " DEFAULT NULL, " + STICKER_ID + " INTEGER DEFAULT -1, " +
DATA_HASH + " TEXT DEFAULT NULL, " + BLUR_HASH + " TEXT DEFAULT NULL);";
DATA_HASH + " TEXT DEFAULT NULL, " + BLUR_HASH + " TEXT DEFAULT NULL, " +
TRANSFORM_PROPERTIES + " TEXT DEFAULT NULL);";

public static final String[] CREATE_INDEXS = {
"CREATE INDEX IF NOT EXISTS part_mms_id_index ON " + TABLE_NAME + " (" + MMS_ID + ");",
Expand Down Expand Up @@ -586,14 +590,37 @@ public void updateAttachmentData(@NonNull DatabaseAttachment databaseAttachment,
contentValues.put(DATA_RANDOM, dataInfo.random);
contentValues.put(DATA_HASH, dataInfo.hash);

int updateCount = updateAttachmentAndMatchingHashes(database, databaseAttachment.getAttachmentId(), dataInfo.hash, contentValues);
Log.i(TAG, "[updateAttachmentData] Updated " + updateCount + " rows.");
}

public void markAttachmentAsTransformed(@NonNull AttachmentId attachmentId) {
DataInfo dataInfo = getAttachmentDataFileInfo(attachmentId, DATA);

if (dataInfo == null) {
Log.w(TAG, "[markAttachmentAsTransformed] No data info found!");
return;
}

ContentValues contentValues = new ContentValues();
contentValues.put(TRANSFORM_PROPERTIES, TransformProperties.forSkipTransform().serialize());

int updateCount = updateAttachmentAndMatchingHashes(databaseHelper.getWritableDatabase(), attachmentId, dataInfo.hash, contentValues);
Log.i(TAG, "[markAttachmentAsTransformed] Updated " + updateCount + " rows.");
}

private static int updateAttachmentAndMatchingHashes(@NonNull SQLiteDatabase database,
@NonNull AttachmentId attachmentId,
@Nullable String dataHash,
@NonNull ContentValues contentValues)
{
String selection = "(" + ROW_ID + " = ? AND " + UNIQUE_ID + " = ?) OR " +
"(" + DATA_HASH + " NOT NULL AND " + DATA_HASH + " = ?)";
String[] args = new String[]{String.valueOf(databaseAttachment.getAttachmentId().getRowId()),
String.valueOf(databaseAttachment.getAttachmentId().getUniqueId()),
oldDataInfo.hash};
String[] args = new String[]{String.valueOf(attachmentId.getRowId()),
String.valueOf(attachmentId.getUniqueId()),
String.valueOf(dataHash)};

int updateCount = database.update(TABLE_NAME, contentValues, selection, args);
Log.i(TAG, "[updateAttachmentData] Updated " + updateCount + " rows.");
return database.update(TABLE_NAME, contentValues, selection, args);
}

private static void updateAttachmentDataHash(@NonNull SQLiteDatabase database,
Expand Down Expand Up @@ -723,14 +750,14 @@ public boolean hasStickerAttachments() {
null, null, null);

if (cursor != null && cursor.moveToFirst()) {
if (cursor.isNull(0)) {
if (cursor.isNull(cursor.getColumnIndexOrThrow(dataType))) {
return null;
}

return new DataInfo(new File(cursor.getString(0)),
cursor.getLong(1),
cursor.getBlob(2),
cursor.getString(3));
return new DataInfo(new File(cursor.getString(cursor.getColumnIndexOrThrow(dataType))),
cursor.getLong(cursor.getColumnIndexOrThrow(SIZE)),
cursor.getBlob(cursor.getColumnIndexOrThrow(randomColumn)),
cursor.getString(cursor.getColumnIndexOrThrow(DATA_HASH)));
} else {
return null;
}
Expand Down Expand Up @@ -886,7 +913,8 @@ public List<DatabaseAttachment> getAttachment(@NonNull Cursor cursor) {
object.getString(STICKER_PACK_KEY),
object.getInt(STICKER_ID))
: null,
BlurHash.parseOrNull(object.getString(BLUR_HASH))));
BlurHash.parseOrNull(object.getString(BLUR_HASH)),
TransformProperties.parse(object.getString(TRANSFORM_PROPERTIES))));
}
}

Expand Down Expand Up @@ -916,7 +944,8 @@ public List<DatabaseAttachment> getAttachment(@NonNull Cursor cursor) {
cursor.getString(cursor.getColumnIndexOrThrow(STICKER_PACK_KEY)),
cursor.getInt(cursor.getColumnIndexOrThrow(STICKER_ID)))
: null,
BlurHash.parseOrNull(cursor.getString(cursor.getColumnIndex(BLUR_HASH)))));
BlurHash.parseOrNull(cursor.getString(cursor.getColumnIndexOrThrow(BLUR_HASH))),
TransformProperties.parse(cursor.getString(cursor.getColumnIndexOrThrow(TRANSFORM_PROPERTIES)))));
}
} catch (JSONException e) {
throw new AssertionError(e);
Expand All @@ -938,24 +967,36 @@ private AttachmentId insertAttachment(long mmsId, Attachment attachment, boolean
Log.d(TAG, "Wrote part to file: " + dataInfo.file.getAbsolutePath());
}

Attachment template = attachment;

if (dataInfo != null && dataInfo.hash != null) {
Attachment possibleTemplate = findTemplateAttachment(dataInfo.hash);

if (possibleTemplate != null) {
Log.i(TAG, "Found a duplicate attachment upon insertion. Using it as a template.");
template = possibleTemplate;
}
}

ContentValues contentValues = new ContentValues();
contentValues.put(MMS_ID, mmsId);
contentValues.put(CONTENT_TYPE, attachment.getContentType());
contentValues.put(CONTENT_TYPE, template.getContentType());
contentValues.put(TRANSFER_STATE, attachment.getTransferState());
contentValues.put(UNIQUE_ID, uniqueId);
contentValues.put(CONTENT_LOCATION, attachment.getLocation());
contentValues.put(DIGEST, attachment.getDigest());
contentValues.put(CONTENT_DISPOSITION, attachment.getKey());
contentValues.put(NAME, attachment.getRelay());
contentValues.put(FILE_NAME, StorageUtil.getCleanFileName(attachment.getFileName()));
contentValues.put(SIZE, attachment.getSize());
contentValues.put(SIZE, template.getSize());
contentValues.put(FAST_PREFLIGHT_ID, attachment.getFastPreflightId());
contentValues.put(VOICE_NOTE, attachment.isVoiceNote() ? 1 : 0);
contentValues.put(WIDTH, attachment.getWidth());
contentValues.put(HEIGHT, attachment.getHeight());
contentValues.put(WIDTH, template.getWidth());
contentValues.put(HEIGHT, template.getHeight());
contentValues.put(QUOTE, quote);
contentValues.put(CAPTION, attachment.getCaption());
contentValues.put(BLUR_HASH, getBlurHashStringOrNull(attachment.getBlurHash()));
contentValues.put(TRANSFORM_PROPERTIES, template.getTransformProperties().serialize());

if (attachment.isSticker()) {
contentValues.put(STICKER_PACK_ID, attachment.getSticker().getPackId());
Expand Down Expand Up @@ -1013,6 +1054,19 @@ private AttachmentId insertAttachment(long mmsId, Attachment attachment, boolean
return attachmentId;
}

private @Nullable DatabaseAttachment findTemplateAttachment(@NonNull String dataHash) {
String selection = DATA_HASH + " = ?";
String[] args = new String[] { dataHash };

try (Cursor cursor = databaseHelper.getWritableDatabase().query(TABLE_NAME, null, selection, args, null, null, null)) {
if (cursor != null && cursor.moveToFirst()) {
return getAttachment(cursor).get(0);
}
}

return null;
}

@SuppressWarnings("WeakerAccess")
@VisibleForTesting
protected void updateAttachmentThumbnail(AttachmentId attachmentId, InputStream in, float aspectRatio)
Expand Down Expand Up @@ -1121,10 +1175,47 @@ private static class DataInfo {
private final String hash;

private DataInfo(File file, long length, byte[] random, String hash) {
this.file = file;
this.file = file;
this.length = length;
this.random = random;
this.hash = hash;
this.hash = hash;
}
}
public static final class TransformProperties {

@JsonProperty private final boolean skipTransform;

public TransformProperties(@JsonProperty("skipTransform") boolean skipTransform) {
this.skipTransform = skipTransform;
}

public static @NonNull TransformProperties empty() {
return new TransformProperties(false);
}

public static @NonNull TransformProperties forSkipTransform() {
return new TransformProperties(true);
}

public boolean shouldSkipTransform() {
return skipTransform;
}

@NonNull String serialize() {
return JsonUtil.toJson(this);
}

static @NonNull TransformProperties parse(@Nullable String serialized) {
if (serialized == null) {
return empty();
}

try {
return JsonUtil.fromJson(serialized, TransformProperties.class);
} catch (IOException e) {
Log.w(TAG, "Failed to parse TransformProperties!", e);
return empty();
}
}
}
}

0 comments on commit 097f97b

Please sign in to comment.