From 1fad4d4f651133727f93de3d8deb4e9de7c07f0b Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Fri, 29 Apr 2022 15:09:54 -0400 Subject: [PATCH] Handle early read receipt sync messages. --- .../securesms/database/MessageDatabase.java | 13 +++++++ .../securesms/database/MmsSmsDatabase.java | 18 +++++++-- .../messages/MessageContentProcessor.java | 39 ++++++++++++++----- 3 files changed, 56 insertions(+), 14 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/MessageDatabase.java b/app/src/main/java/org/thoughtcrime/securesms/database/MessageDatabase.java index 8d4bee04b..6df970d73 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/MessageDatabase.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/MessageDatabase.java @@ -556,6 +556,19 @@ public abstract class MessageDatabase extends Database implements MmsSmsColumns public long getTimetamp() { return timetamp; } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + final SyncMessageId that = (SyncMessageId) o; + return timetamp == that.timetamp && Objects.equals(recipientId, that.recipientId); + } + + @Override + public int hashCode() { + return Objects.hash(recipientId, timetamp); + } } public static class ExpirationInfo { diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/MmsSmsDatabase.java b/app/src/main/java/org/thoughtcrime/securesms/database/MmsSmsDatabase.java index a37ee4870..236cf27ce 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/MmsSmsDatabase.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/MmsSmsDatabase.java @@ -534,12 +534,16 @@ public class MmsSmsDatabase extends Database { SignalDatabase.mms().updateViewedStories(syncMessageIds); } - public void setTimestampRead(@NonNull Recipient senderRecipient, @NonNull List readMessages, long proposedExpireStarted, @NonNull Map threadToLatestRead) { + /** + * @return Unhandled ids + */ + public Collection setTimestampRead(@NonNull Recipient senderRecipient, @NonNull List readMessages, long proposedExpireStarted, @NonNull Map threadToLatestRead) { SQLiteDatabase db = getWritableDatabase(); - List> expiringText = new LinkedList<>(); - List> expiringMedia = new LinkedList<>(); - Set updatedThreads = new HashSet<>(); + List> expiringText = new LinkedList<>(); + List> expiringMedia = new LinkedList<>(); + Set updatedThreads = new HashSet<>(); + Collection unhandled = new LinkedList<>(); db.beginTransaction(); try { @@ -556,6 +560,10 @@ public class MmsSmsDatabase extends Database { updatedThreads.addAll(textResult.threads); updatedThreads.addAll(mediaResult.threads); + + if (textResult.threads.isEmpty() && mediaResult.threads.isEmpty()) { + unhandled.add(new SyncMessageId(senderRecipient.getId(), readMessage.getTimestamp())); + } } for (long threadId : updatedThreads) { @@ -581,6 +589,8 @@ public class MmsSmsDatabase extends Database { for (long threadId : updatedThreads) { notifyConversationListeners(threadId); } + + return unhandled; } public int getQuotedMessagePosition(long threadId, long quoteId, @NonNull RecipientId recipientId) { 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 77d471176..47c932204 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/messages/MessageContentProcessor.java +++ b/app/src/main/java/org/thoughtcrime/securesms/messages/MessageContentProcessor.java @@ -332,9 +332,9 @@ public final class MessageContentProcessor { if (syncMessage.getSent().isPresent()) handleSynchronizeSentMessage(content, syncMessage.getSent().get(), senderRecipient); else if (syncMessage.getRequest().isPresent()) handleSynchronizeRequestMessage(syncMessage.getRequest().get(), content.getTimestamp()); - else if (syncMessage.getRead().isPresent()) handleSynchronizeReadMessage(syncMessage.getRead().get(), content.getTimestamp(), senderRecipient); + else if (syncMessage.getRead().isPresent()) handleSynchronizeReadMessage(content, syncMessage.getRead().get(), content.getTimestamp(), senderRecipient); else if (syncMessage.getViewed().isPresent()) handleSynchronizeViewedMessage(syncMessage.getViewed().get(), content.getTimestamp()); - else if (syncMessage.getViewOnceOpen().isPresent()) handleSynchronizeViewOnceOpenMessage(syncMessage.getViewOnceOpen().get(), content.getTimestamp()); + else if (syncMessage.getViewOnceOpen().isPresent()) handleSynchronizeViewOnceOpenMessage(content, syncMessage.getViewOnceOpen().get(), content.getTimestamp()); else if (syncMessage.getVerified().isPresent()) handleSynchronizeVerifiedMessage(syncMessage.getVerified().get()); else if (syncMessage.getStickerPackOperations().isPresent()) handleSynchronizeStickerPackOperation(syncMessage.getStickerPackOperations().get(), content.getTimestamp()); else if (syncMessage.getConfiguration().isPresent()) handleSynchronizeConfigurationMessage(syncMessage.getConfiguration().get(), content.getTimestamp()); @@ -1295,13 +1295,16 @@ public final class MessageContentProcessor { } } - private void handleSynchronizeReadMessage(@NonNull List readMessages, long envelopeTimestamp, @NonNull Recipient senderRecipient) + private void handleSynchronizeReadMessage(@NonNull SignalServiceContent content, + @NonNull List readMessages, + long envelopeTimestamp, + @NonNull Recipient senderRecipient) { log(envelopeTimestamp, "Synchronize read message. Count: " + readMessages.size() + ", Timestamps: " + Stream.of(readMessages).map(ReadMessage::getTimestamp).toList()); Map threadToLatestRead = new HashMap<>(); - SignalDatabase.mmsSms().setTimestampRead(senderRecipient, readMessages, envelopeTimestamp, threadToLatestRead); + Collection unhandled = SignalDatabase.mmsSms().setTimestampRead(senderRecipient, readMessages, envelopeTimestamp, threadToLatestRead); List markedMessages = SignalDatabase.threads().setReadSince(threadToLatestRead, false); @@ -1310,6 +1313,17 @@ public final class MessageContentProcessor { MarkReadReceiver.process(context, markedMessages); } + for (SyncMessageId id : unhandled) { + warn(String.valueOf(content.getTimestamp()), "[handleSynchronizeReadMessage] Could not find matching message! timestamp: " + id.getTimetamp() + " author: " + id.getRecipientId()); + if (!processingEarlyContent) { + ApplicationDependencies.getEarlyMessageCache().store(id.getRecipientId(), id.getTimetamp(), content); + } + } + + if (unhandled.size() > 0 && !processingEarlyContent) { + PushProcessEarlyMessagesJob.enqueue(); + } + MessageNotifier messageNotifier = ApplicationDependencies.getMessageNotifier(); messageNotifier.setLastDesktopActivityTimestamp(envelopeTimestamp); messageNotifier.cancelDelayedNotifications(); @@ -1336,7 +1350,7 @@ public final class MessageContentProcessor { messageNotifier.updateNotification(context); } - private void handleSynchronizeViewOnceOpenMessage(@NonNull ViewOnceOpenMessage openMessage, long envelopeTimestamp) { + private void handleSynchronizeViewOnceOpenMessage(@NonNull SignalServiceContent content, @NonNull ViewOnceOpenMessage openMessage, long envelopeTimestamp) { log(envelopeTimestamp, "Handling a view-once open for message: " + openMessage.getTimestamp()); RecipientId author = Recipient.externalPush(openMessage.getSender()).getId(); @@ -1347,6 +1361,11 @@ public final class MessageContentProcessor { SignalDatabase.attachments().deleteAttachmentFilesForViewOnceMessage(record.getId()); } else { warn(String.valueOf(envelopeTimestamp), "Got a view-once open message for a message we don't have!"); + + if (!processingEarlyContent) { + ApplicationDependencies.getEarlyMessageCache().store(author, timestamp, content); + PushProcessEarlyMessagesJob.enqueue(); + } } MessageNotifier messageNotifier = ApplicationDependencies.getMessageNotifier(); @@ -2302,9 +2321,9 @@ public final class MessageContentProcessor { SignalDatabase.mmsSms().updateViewedStories(handled); for (SyncMessageId id : unhandled) { - warn(String.valueOf(content.getTimestamp()), "[handleViewedReceipt] Could not find matching message! timestamp: " + id.getTimetamp() + " author: " + senderRecipient.getId()); + warn(String.valueOf(content.getTimestamp()), "[handleViewedReceipt] Could not find matching message! timestamp: " + id.getTimetamp() + " author: " + id.getRecipientId()); if (!processingEarlyContent) { - ApplicationDependencies.getEarlyMessageCache().store(senderRecipient.getId(), id.getTimetamp(), content); + ApplicationDependencies.getEarlyMessageCache().store(id.getRecipientId(), id.getTimetamp(), content); } } @@ -2327,7 +2346,7 @@ public final class MessageContentProcessor { Collection unhandled = SignalDatabase.mmsSms().incrementDeliveryReceiptCounts(ids, System.currentTimeMillis()); for (SyncMessageId id : unhandled) { - warn(String.valueOf(content.getTimestamp()), "[handleDeliveryReceipt] Could not find matching message! timestamp: " + id.getTimetamp() + " author: " + senderRecipient.getId()); + warn(String.valueOf(content.getTimestamp()), "[handleDeliveryReceipt] Could not find matching message! timestamp: " + id.getTimetamp() + " author: " + id.getRecipientId()); // Early delivery receipts are special-cased in the database methods } @@ -2357,9 +2376,9 @@ public final class MessageContentProcessor { Collection unhandled = SignalDatabase.mmsSms().incrementReadReceiptCounts(ids, content.getTimestamp()); for (SyncMessageId id : unhandled) { - warn(String.valueOf(content.getTimestamp()), "[handleReadReceipt] Could not find matching message! timestamp: " + id.getTimetamp() + " author: " + senderRecipient.getId()); + warn(String.valueOf(content.getTimestamp()), "[handleReadReceipt] Could not find matching message! timestamp: " + id.getTimetamp() + " author: " + id.getRecipientId()); if (!processingEarlyContent) { - ApplicationDependencies.getEarlyMessageCache().store(senderRecipient.getId(), id.getTimetamp(), content); + ApplicationDependencies.getEarlyMessageCache().store(id.getRecipientId(), id.getTimetamp(), content); } }