diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/MessageSendLogDatabase.kt b/app/src/main/java/org/thoughtcrime/securesms/database/MessageSendLogDatabase.kt index 914329ca2b..7d021ca5b8 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/MessageSendLogDatabase.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/MessageSendLogDatabase.kt @@ -3,6 +3,7 @@ package org.thoughtcrime.securesms.database import android.content.ContentValues import android.content.Context import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper +import org.thoughtcrime.securesms.database.model.MessageId import org.thoughtcrime.securesms.database.model.MessageLogEntry import org.thoughtcrime.securesms.recipients.Recipient import org.thoughtcrime.securesms.recipients.RecipientId @@ -17,41 +18,51 @@ import java.util.UUID /** * Stores a 24-hr buffer of all outgoing messages. Used for the retry logic required for sender key. * - * General note: This class is actually two tables -- one to store the entry, and another to store all the devices that were sent it. + * General note: This class is actually three tables: + * - one to store the entry + * - one to store all the devices that were sent it, and + * - one to store the set of related messages. * * The general lifecycle of entries in the store goes something like this: - * - Upon sending a message, throw an entry in the 'message table' and throw an entry for each recipient you sent it to in the 'recipient table' + * - Upon sending a message, put an entry in the 'payload table', an entry for each recipient you sent it to in the 'recipient table', and an entry for each + * related message in the 'message table' * - Whenever you get a delivery receipt, delete the entries in the 'recipient table' * - Whenever there's no more records in the 'recipient table' for a given message, delete the entry in the 'message table' - * - Whenever you delete a message, delete the entry in the 'message table' + * - Whenever you delete a message, delete the relevant entries from the 'payload table' * - Whenever you read an entry from the table, first trim off all the entries that are too old * * Because of all of this, you can be sure that if an entry is in this store, it's safe to resend to someone upon request * * Worth noting that we use triggers + foreign keys to make sure entries in this table are properly cleaned up. Triggers for when you delete a message, and - * a cascading delete foreign key between these two tables. + * cascading delete foreign keys between these three tables. + * + * Performance considerations: + * - The most common operations by far are: + * - Inserting into the table + * - Deleting a recipient (in response to a delivery receipt) + * - We should also optimize for when we delete messages from the sms/mms tables, since you can delete a bunch at once + * - We *don't* really need to optimize for retrieval, since that happens very infrequently. In particular, we don't want to slow down inserts in order to + * improve retrieval time. That means we shouldn't be adding indexes that optimize for retrieval. */ class MessageSendLogDatabase constructor(context: Context?, databaseHelper: SQLCipherOpenHelper?) : Database(context, databaseHelper) { companion object { @JvmField - val CREATE_TABLE: Array = arrayOf(MessageTable.CREATE_TABLE, RecipientTable.CREATE_TABLE) + val CREATE_TABLE: Array = arrayOf(PayloadTable.CREATE_TABLE, RecipientTable.CREATE_TABLE, MessageTable.CREATE_TABLE) @JvmField - val CREATE_INDEXES: Array = MessageTable.CREATE_INDEXES + RecipientTable.CREATE_INDEXES + val CREATE_INDEXES: Array = PayloadTable.CREATE_INDEXES + RecipientTable.CREATE_INDEXES + MessageTable.CREATE_INDEXES @JvmField - val CREATE_TRIGGERS: Array = MessageTable.CREATE_TRIGGERS + val CREATE_TRIGGERS: Array = PayloadTable.CREATE_TRIGGERS } - private object MessageTable { - const val TABLE_NAME = "message_send_log" + private object PayloadTable { + const val TABLE_NAME = "msl_payload" const val ID = "_id" const val DATE_SENT = "date_sent" const val CONTENT = "content" - const val RELATED_MESSAGE_ID = "related_message_id" - const val IS_RELATED_MESSAGE_MMS = "is_related_message_mms" const val CONTENT_HINT = "content_hint" const val CREATE_TABLE = """ @@ -59,54 +70,75 @@ class MessageSendLogDatabase constructor(context: Context?, databaseHelper: SQLC $ID INTEGER PRIMARY KEY, $DATE_SENT INTEGER NOT NULL, $CONTENT BLOB NOT NULL, - $RELATED_MESSAGE_ID INTEGER DEFAULT -1, - $IS_RELATED_MESSAGE_MMS INTEGER DEFAULT 0, $CONTENT_HINT INTEGER NOT NULL ) """ - @JvmField + /** Created for [deleteEntriesForRecipient] */ val CREATE_INDEXES = arrayOf( - "CREATE INDEX message_log_date_sent_index ON $TABLE_NAME ($DATE_SENT)", - "CREATE INDEX message_log_related_message_index ON $TABLE_NAME ($RELATED_MESSAGE_ID, $IS_RELATED_MESSAGE_MMS)" + "CREATE INDEX msl_payload_date_sent_index ON $TABLE_NAME ($DATE_SENT)", ) - @JvmField val CREATE_TRIGGERS = arrayOf( """ CREATE TRIGGER msl_sms_delete AFTER DELETE ON ${SmsDatabase.TABLE_NAME} BEGIN - DELETE FROM $TABLE_NAME WHERE $RELATED_MESSAGE_ID = old.${SmsDatabase.ID} AND $IS_RELATED_MESSAGE_MMS = 0; + DELETE FROM $TABLE_NAME WHERE $ID IN (SELECT ${MessageTable.PAYLOAD_ID} FROM ${MessageTable.TABLE_NAME} WHERE ${MessageTable.MESSAGE_ID} = old.${SmsDatabase.ID} AND ${MessageTable.IS_MMS} = 0); END """, """ CREATE TRIGGER msl_mms_delete AFTER DELETE ON ${MmsDatabase.TABLE_NAME} BEGIN - DELETE FROM $TABLE_NAME WHERE $RELATED_MESSAGE_ID = old.${MmsDatabase.ID} AND $IS_RELATED_MESSAGE_MMS = 1; + DELETE FROM $TABLE_NAME WHERE $ID IN (SELECT ${MessageTable.PAYLOAD_ID} FROM ${MessageTable.TABLE_NAME} WHERE ${MessageTable.MESSAGE_ID} = old.${MmsDatabase.ID} AND ${MessageTable.IS_MMS} = 1); END """ ) } private object RecipientTable { - const val TABLE_NAME = "message_send_log_recipients" + const val TABLE_NAME = "msl_recipient" const val ID = "_id" - const val MESSAGE_LOG_ID = "message_send_log_id" + const val PAYLOAD_ID = "payload_id" const val RECIPIENT_ID = "recipient_id" const val DEVICE = "device" const val CREATE_TABLE = """ CREATE TABLE $TABLE_NAME ( $ID INTEGER PRIMARY KEY, - $MESSAGE_LOG_ID INTEGER NOT NULL REFERENCES ${MessageTable.TABLE_NAME} (${MessageTable.ID}) ON DELETE CASCADE, + $PAYLOAD_ID INTEGER NOT NULL REFERENCES ${PayloadTable.TABLE_NAME} (${PayloadTable.ID}) ON DELETE CASCADE, $RECIPIENT_ID INTEGER NOT NULL, $DEVICE INTEGER NOT NULL ) """ + /** Created for [deleteEntriesForRecipient] */ val CREATE_INDEXES = arrayOf( - "CREATE INDEX message_send_log_recipients_recipient_index ON $TABLE_NAME ($RECIPIENT_ID, $DEVICE)" + "CREATE INDEX msl_recipient_recipient_index ON $TABLE_NAME ($RECIPIENT_ID, $DEVICE, $PAYLOAD_ID)", + "CREATE INDEX msl_recipient_payload_index ON $TABLE_NAME ($PAYLOAD_ID)" + ) + } + + private object MessageTable { + const val TABLE_NAME = "msl_message" + + const val ID = "_id" + const val PAYLOAD_ID = "payload_id" + const val MESSAGE_ID = "message_id" + const val IS_MMS = "is_mms" + + const val CREATE_TABLE = """ + CREATE TABLE $TABLE_NAME ( + $ID INTEGER PRIMARY KEY, + $PAYLOAD_ID INTEGER NOT NULL REFERENCES ${PayloadTable.TABLE_NAME} (${PayloadTable.ID}) ON DELETE CASCADE, + $MESSAGE_ID INTEGER NOT NULL, + $IS_MMS INTEGER NOT NULL + ) + """ + + /** Created for [PayloadTable.CREATE_TRIGGERS] and [deleteAllRelatedToMessage] */ + val CREATE_INDEXES = arrayOf( + "CREATE INDEX msl_message_message_index ON $TABLE_NAME ($MESSAGE_ID, $IS_MMS, $PAYLOAD_ID)" ) } @@ -115,7 +147,7 @@ class MessageSendLogDatabase constructor(context: Context?, databaseHelper: SQLC if (sendMessageResult.isSuccess && sendMessageResult.success.content.isPresent) { val recipientDevice = listOf(RecipientDevice(recipientId, sendMessageResult.success.devices)) - insert(recipientDevice, sentTimestamp, sendMessageResult.success.content.get(), contentHint, relatedMessageId, isRelatedMessageMms) + insert(recipientDevice, sentTimestamp, sendMessageResult.success.content.get(), contentHint, listOf(MessageId(relatedMessageId, isRelatedMessageMms))) } } @@ -140,35 +172,44 @@ class MessageSendLogDatabase constructor(context: Context?, databaseHelper: SQLC val content: SignalServiceProtos.Content = results.first { it.isSuccess && it.success.content.isPresent }.success.content.get() - insert(recipientDevices, sentTimestamp, content, contentHint, relatedMessageId, isRelatedMessageMms) + insert(recipientDevices, sentTimestamp, content, contentHint, listOf(MessageId(relatedMessageId, isRelatedMessageMms))) } - private fun insert(recipients: List, dateSent: Long, content: SignalServiceProtos.Content, contentHint: ContentHint, relatedMessageId: Long, isRelatedMessageMms: Boolean) { + private fun insert(recipients: List, dateSent: Long, content: SignalServiceProtos.Content, contentHint: ContentHint, messageIds: List) { val db = databaseHelper.writableDatabase db.beginTransaction() try { - val logValues = ContentValues().apply { - put(MessageTable.DATE_SENT, dateSent) - put(MessageTable.CONTENT, content.toByteArray()) - put(MessageTable.CONTENT_HINT, contentHint.type) - put(MessageTable.RELATED_MESSAGE_ID, relatedMessageId) - put(MessageTable.IS_RELATED_MESSAGE_MMS, if (isRelatedMessageMms) 1 else 0) + val payloadValues = ContentValues().apply { + put(PayloadTable.DATE_SENT, dateSent) + put(PayloadTable.CONTENT, content.toByteArray()) + put(PayloadTable.CONTENT_HINT, contentHint.type) } - val messageLogId: Long = db.insert(MessageTable.TABLE_NAME, null, logValues) + val payloadId: Long = db.insert(PayloadTable.TABLE_NAME, null, payloadValues) recipients.forEach { recipientDevice -> recipientDevice.devices.forEach { device -> - val recipientValues = ContentValues() - recipientValues.put(RecipientTable.MESSAGE_LOG_ID, messageLogId) - recipientValues.put(RecipientTable.RECIPIENT_ID, recipientDevice.recipientId.serialize()) - recipientValues.put(RecipientTable.DEVICE, device) + val recipientValues = ContentValues().apply { + put(RecipientTable.PAYLOAD_ID, payloadId) + put(RecipientTable.RECIPIENT_ID, recipientDevice.recipientId.serialize()) + put(RecipientTable.DEVICE, device) + } db.insert(RecipientTable.TABLE_NAME, null, recipientValues) } } + messageIds.forEach { messageId -> + val messageValues = ContentValues().apply { + put(MessageTable.PAYLOAD_ID, payloadId) + put(MessageTable.MESSAGE_ID, messageId.id) + put(MessageTable.IS_MMS, if (messageId.mms) 1 else 0) + } + + db.insert(MessageTable.TABLE_NAME, null, messageValues) + } + db.setTransactionSuccessful() } finally { db.endTransaction() @@ -181,20 +222,34 @@ class MessageSendLogDatabase constructor(context: Context?, databaseHelper: SQLC trimOldMessages(System.currentTimeMillis(), FeatureFlags.retryRespondMaxAge()) val db = databaseHelper.readableDatabase - val table = "${MessageTable.TABLE_NAME} LEFT JOIN ${RecipientTable.TABLE_NAME} ON ${MessageTable.TABLE_NAME}.${MessageTable.ID} = ${RecipientTable.TABLE_NAME}.${RecipientTable.MESSAGE_LOG_ID}" - val query = "${MessageTable.DATE_SENT} = ? AND ${RecipientTable.RECIPIENT_ID} = ? AND ${RecipientTable.DEVICE} = ?" + val table = "${PayloadTable.TABLE_NAME} LEFT JOIN ${RecipientTable.TABLE_NAME} ON ${PayloadTable.TABLE_NAME}.${PayloadTable.ID} = ${RecipientTable.TABLE_NAME}.${RecipientTable.PAYLOAD_ID}" + val query = "${PayloadTable.DATE_SENT} = ? AND ${RecipientTable.RECIPIENT_ID} = ? AND ${RecipientTable.DEVICE} = ?" val args = SqlUtil.buildArgs(dateSent, recipientId, device) - db.query(table, null, query, args, null, null, null).use { cursor -> - if (cursor.moveToFirst()) { - return MessageLogEntry( - recipientId = RecipientId.from(CursorUtil.requireLong(cursor, RecipientTable.RECIPIENT_ID)), - dateSent = CursorUtil.requireLong(cursor, MessageTable.DATE_SENT), - content = SignalServiceProtos.Content.parseFrom(CursorUtil.requireBlob(cursor, MessageTable.CONTENT)), - contentHint = ContentHint.fromType(CursorUtil.requireInt(cursor, MessageTable.CONTENT_HINT)), - relatedMessageId = CursorUtil.requireLong(cursor, MessageTable.RELATED_MESSAGE_ID), - isRelatedMessageMms = CursorUtil.requireBoolean(cursor, MessageTable.IS_RELATED_MESSAGE_MMS) - ) + db.query(table, null, query, args, null, null, null).use { entryCursor -> + if (entryCursor.moveToFirst()) { + val payloadId = CursorUtil.requireLong(entryCursor, RecipientTable.PAYLOAD_ID) + + db.query(MessageTable.TABLE_NAME, null, "${MessageTable.PAYLOAD_ID} = ?", SqlUtil.buildArgs(payloadId), null, null, null).use { messageCursor -> + val messageIds: MutableList = mutableListOf() + + while (messageCursor.moveToNext()) { + messageIds.add( + MessageId( + id = CursorUtil.requireLong(messageCursor, MessageTable.MESSAGE_ID), + mms = CursorUtil.requireBoolean(messageCursor, MessageTable.IS_MMS) + ) + ) + } + + return MessageLogEntry( + recipientId = RecipientId.from(CursorUtil.requireLong(entryCursor, RecipientTable.RECIPIENT_ID)), + dateSent = CursorUtil.requireLong(entryCursor, PayloadTable.DATE_SENT), + content = SignalServiceProtos.Content.parseFrom(CursorUtil.requireBlob(entryCursor, PayloadTable.CONTENT)), + contentHint = ContentHint.fromType(CursorUtil.requireInt(entryCursor, PayloadTable.CONTENT_HINT)), + relatedMessages = messageIds + ) + } } } @@ -205,10 +260,10 @@ class MessageSendLogDatabase constructor(context: Context?, databaseHelper: SQLC if (!FeatureFlags.senderKey()) return val db = databaseHelper.writableDatabase - val query = "${MessageTable.RELATED_MESSAGE_ID} = ? AND ${MessageTable.IS_RELATED_MESSAGE_MMS} = ?" + val query = "${PayloadTable.ID} IN (SELECT ${MessageTable.PAYLOAD_ID} FROM ${MessageTable.TABLE_NAME} WHERE ${MessageTable.MESSAGE_ID} = ? AND ${MessageTable.IS_MMS} = ?)" val args = SqlUtil.buildArgs(messageId, if (mms) 1 else 0) - db.delete(MessageTable.TABLE_NAME, query, args) + db.delete(PayloadTable.TABLE_NAME, query, args) } fun deleteEntryForRecipient(dateSent: Long, recipientId: RecipientId, device: Int) { @@ -227,17 +282,17 @@ class MessageSendLogDatabase constructor(context: Context?, databaseHelper: SQLC val query = """ ${RecipientTable.RECIPIENT_ID} = ? AND ${RecipientTable.DEVICE} = ? AND - ${RecipientTable.MESSAGE_LOG_ID} IN ( - SELECT ${MessageTable.ID} - FROM ${MessageTable.TABLE_NAME} - WHERE ${MessageTable.DATE_SENT} IN (${dateSent.joinToString(",")}) + ${RecipientTable.PAYLOAD_ID} IN ( + SELECT ${PayloadTable.ID} + FROM ${PayloadTable.TABLE_NAME} + WHERE ${PayloadTable.DATE_SENT} IN (${dateSent.joinToString(",")}) )""" val args = SqlUtil.buildArgs(recipientId, device) db.delete(RecipientTable.TABLE_NAME, query, args) - val cleanQuery = "${MessageTable.ID} NOT IN (SELECT ${RecipientTable.MESSAGE_LOG_ID} FROM ${RecipientTable.TABLE_NAME})" - db.delete(MessageTable.TABLE_NAME, cleanQuery, null) + val cleanQuery = "${PayloadTable.ID} NOT IN (SELECT ${RecipientTable.PAYLOAD_ID} FROM ${RecipientTable.TABLE_NAME})" + db.delete(PayloadTable.TABLE_NAME, cleanQuery, null) db.setTransactionSuccessful() } finally { @@ -248,17 +303,17 @@ class MessageSendLogDatabase constructor(context: Context?, databaseHelper: SQLC fun deleteAll() { if (!FeatureFlags.senderKey()) return - databaseHelper.writableDatabase.delete(MessageTable.TABLE_NAME, null, null) + databaseHelper.writableDatabase.delete(PayloadTable.TABLE_NAME, null, null) } fun trimOldMessages(currentTime: Long, maxAge: Long) { if (!FeatureFlags.senderKey()) return val db = databaseHelper.writableDatabase - val query = "${MessageTable.DATE_SENT} < ?" + val query = "${PayloadTable.DATE_SENT} < ?" val args = SqlUtil.buildArgs(currentTime - maxAge) - db.delete(MessageTable.TABLE_NAME, query, args) + db.delete(PayloadTable.TABLE_NAME, query, args) } private data class RecipientDevice(val recipientId: RecipientId, val devices: List) diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java index d749cce45b..be264b19dd 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java @@ -201,8 +201,9 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper implements SignalDatab private static final int SENDER_KEY = 103; private static final int MESSAGE_DUPE_INDEX = 104; private static final int MESSAGE_LOG = 105; + private static final int MESSAGE_LOG_2 = 106; - private static final int DATABASE_VERSION = 105; + private static final int DATABASE_VERSION = 106; private static final String DATABASE_NAME = "signal.db"; private final Context context; @@ -1598,6 +1599,41 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper implements SignalDatab db.execSQL("CREATE INDEX message_send_log_recipients_recipient_index ON message_send_log_recipients (recipient_id, device)"); } + if (oldVersion < MESSAGE_LOG_2) { + db.execSQL("DROP TABLE message_send_log"); + db.execSQL("DROP INDEX IF EXISTS message_log_date_sent_index"); + db.execSQL("DROP INDEX IF EXISTS message_log_related_message_index"); + db.execSQL("DROP TRIGGER msl_sms_delete"); + db.execSQL("DROP TRIGGER msl_mms_delete"); + db.execSQL("DROP TABLE message_send_log_recipients"); + db.execSQL("DROP INDEX IF EXISTS message_send_log_recipients_recipient_index"); + + db.execSQL("CREATE TABLE msl_payload (_id INTEGER PRIMARY KEY, " + + "date_sent INTEGER NOT NULL, " + + "content BLOB NOT NULL, " + + "content_hint INTEGER NOT NULL)"); + + db.execSQL("CREATE INDEX msl_payload_date_sent_index ON msl_payload (date_sent)"); + + db.execSQL("CREATE TABLE msl_recipient (_id INTEGER PRIMARY KEY, " + + "payload_id INTEGER NOT NULL REFERENCES msl_payload (_id) ON DELETE CASCADE, " + + "recipient_id INTEGER NOT NULL, " + + "device INTEGER NOT NULL)"); + + db.execSQL("CREATE INDEX msl_recipient_recipient_index ON msl_recipient (recipient_id, device, payload_id)"); + db.execSQL("CREATE INDEX msl_recipient_payload_index ON msl_recipient (payload_id)"); + + db.execSQL("CREATE TABLE msl_message (_id INTEGER PRIMARY KEY, " + + "payload_id INTEGER NOT NULL REFERENCES msl_payload (_id) ON DELETE CASCADE, " + + "message_id INTEGER NOT NULL, " + + "is_mms INTEGER NOT NULL)"); + + db.execSQL("CREATE INDEX msl_message_message_index ON msl_message (message_id, is_mms, payload_id)"); + + db.execSQL("CREATE TRIGGER msl_sms_delete AFTER DELETE ON sms BEGIN DELETE FROM msl_payload WHERE _id IN (SELECT payload_id FROM msl_message WHERE message_id = old._id AND is_mms = 0); END"); + db.execSQL("CREATE TRIGGER msl_mms_delete AFTER DELETE ON mms BEGIN DELETE FROM msl_payload WHERE _id IN (SELECT payload_id FROM msl_message WHERE message_id = old._id AND is_mms = 1); END"); + } + db.setTransactionSuccessful(); } finally { db.endTransaction(); diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/model/MessageId.kt b/app/src/main/java/org/thoughtcrime/securesms/database/model/MessageId.kt new file mode 100644 index 0000000000..4186c9d973 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/database/model/MessageId.kt @@ -0,0 +1,10 @@ +package org.thoughtcrime.securesms.database.model + +/** + * Represents a pair of values that can be used to find a message. Because we have two tables, + * that means this has both the primary key and a boolean indicating which table it's in. + */ +data class MessageId( + val id: Long, + @get:JvmName("isMms") val mms: Boolean +) diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/model/MessageLogEntry.kt b/app/src/main/java/org/thoughtcrime/securesms/database/model/MessageLogEntry.kt index 34b8ec4f1f..1ab476654c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/model/MessageLogEntry.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/model/MessageLogEntry.kt @@ -12,10 +12,9 @@ data class MessageLogEntry( val dateSent: Long, val content: SignalServiceProtos.Content, val contentHint: ContentHint, - val relatedMessageId: Long, - val isRelatedMessageMms: Boolean, + val relatedMessages: List ) { val hasRelatedMessage: Boolean @JvmName("hasRelatedMessage") - get() = relatedMessageId > 0 + get() = relatedMessages.isNotEmpty() } diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/SenderKeyDistributionSendJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/SenderKeyDistributionSendJob.java index 374924cced..a79721dda9 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/SenderKeyDistributionSendJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/SenderKeyDistributionSendJob.java @@ -52,6 +52,7 @@ public final class SenderKeyDistributionSendJob extends BaseJob { .addConstraint(NetworkConstraint.KEY) .setLifespan(TimeUnit.DAYS.toMillis(1)) .setMaxAttempts(Parameters.UNLIMITED) + .setMaxInstancesForQueue(1) .build()); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/messages/MessageContentProcessor.java b/app/src/main/java/org/thoughtcrime/securesms/messages/MessageContentProcessor.java index 91fd885efc..b5263d1886 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/messages/MessageContentProcessor.java +++ b/app/src/main/java/org/thoughtcrime/securesms/messages/MessageContentProcessor.java @@ -42,6 +42,7 @@ import org.thoughtcrime.securesms.database.RecipientDatabase; import org.thoughtcrime.securesms.database.StickerDatabase; import org.thoughtcrime.securesms.database.ThreadDatabase; import org.thoughtcrime.securesms.database.model.Mention; +import org.thoughtcrime.securesms.database.model.MessageId; import org.thoughtcrime.securesms.database.model.MessageLogEntry; import org.thoughtcrime.securesms.database.model.MessageRecord; import org.thoughtcrime.securesms.database.model.MmsMessageRecord; @@ -1795,9 +1796,10 @@ public final class MessageContentProcessor { return; } - DistributionId distributionId = DatabaseFactory.getGroupDatabase(context).getOrCreateDistributionId(threadRecipient.requireGroupId().requireV2()); - + GroupId.V2 groupId = threadRecipient.requireGroupId().requireV2(); + DistributionId distributionId = DatabaseFactory.getGroupDatabase(context).getOrCreateDistributionId(groupId); SignalProtocolAddress requesterAddress = new SignalProtocolAddress(requester.requireUuid().toString(), decryptionErrorMessage.getDeviceId()); + DatabaseFactory.getSenderKeySharedDatabase(context).delete(distributionId, Collections.singleton(requesterAddress)); if (messageLogEntry != null) { @@ -1807,29 +1809,11 @@ public final class MessageContentProcessor { messageLogEntry.getDateSent(), messageLogEntry.getContent(), messageLogEntry.getContentHint(), - threadRecipient.requireGroupId().requireV2(), + groupId, distributionId)); } else { warn(content.getTimestamp(), "[RetryReceipt-SK] Unable to find MSL entry for " + requester.getId() + " with timestamp " + sentTimestamp + "."); - if (!content.getGroupId().isPresent()) { - warn(content.getTimestamp(), "[RetryReceipt-SK] No groupId on the Content, so we cannot send them a SenderKeyDistributionMessage."); - return; - } - - GroupId groupId; - try { - groupId = GroupId.push(content.getGroupId().get()); - } catch (BadGroupIdException e) { - warn(String.valueOf(content.getTimestamp()), "[RetryReceipt-SK] Bad groupId!"); - return; - } - - if (!groupId.isV2()) { - warn(String.valueOf(content.getTimestamp()), "[RetryReceipt-SK] Not a valid GV2 ID!"); - return; - } - Optional groupRecord = DatabaseFactory.getGroupDatabase(context).getGroup(groupId); if (!groupRecord.isPresent()) { @@ -1875,12 +1859,14 @@ public final class MessageContentProcessor { } } - private static @Nullable MessageRecord findRetryReceiptRelatedMessage(@NonNull Context context, @Nullable MessageLogEntry messageLogEntry, long sentTimestamp) { + private @Nullable MessageRecord findRetryReceiptRelatedMessage(@NonNull Context context, @Nullable MessageLogEntry messageLogEntry, long sentTimestamp) { if (messageLogEntry != null && messageLogEntry.hasRelatedMessage()) { - if (messageLogEntry.isRelatedMessageMms()) { - return DatabaseFactory.getMmsDatabase(context).getMessageRecordOrNull(messageLogEntry.getRelatedMessageId()); + MessageId relatedMessage = messageLogEntry.getRelatedMessages().get(0); + + if (relatedMessage.isMms()) { + return DatabaseFactory.getMmsDatabase(context).getMessageRecordOrNull(relatedMessage.getId()); } else { - return DatabaseFactory.getSmsDatabase(context).getMessageRecordOrNull(messageLogEntry.getRelatedMessageId()); + return DatabaseFactory.getSmsDatabase(context).getMessageRecordOrNull(relatedMessage.getId()); } } else { return DatabaseFactory.getMmsSmsDatabase(context).getMessageFor(sentTimestamp, Recipient.self().getId());