mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-04-19 16:19:33 +01:00
Add kotlin/proto level message processing.
This commit is contained in:
committed by
Alex Hart
parent
28f27915c5
commit
2e45bd719a
@@ -11,6 +11,7 @@ import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Sometimes a message that is referencing another message can arrive out of order. In these cases,
|
||||
@@ -19,7 +20,8 @@ import java.util.Optional;
|
||||
*/
|
||||
public final class EarlyMessageCache {
|
||||
|
||||
private final LRUCache<ServiceMessageId, List<SignalServiceContent>> cache = new LRUCache<>(100);
|
||||
private final LRUCache<ServiceMessageId, List<SignalServiceContent>> cache = new LRUCache<>(100);
|
||||
private final LRUCache<ServiceMessageId, List<EarlyMessageCacheEntry>> cacheV2 = new LRUCache<>(100);
|
||||
|
||||
/**
|
||||
* @param targetSender The sender of the message this message depends on.
|
||||
@@ -38,8 +40,25 @@ public final class EarlyMessageCache {
|
||||
cache.put(messageId, contentList);
|
||||
}
|
||||
|
||||
public synchronized void store(@NonNull RecipientId targetSender,
|
||||
long targetSentTimestamp,
|
||||
@NonNull EarlyMessageCacheEntry cacheEntry)
|
||||
{
|
||||
ServiceMessageId messageId = new ServiceMessageId(targetSender, targetSentTimestamp);
|
||||
List<EarlyMessageCacheEntry> envelopeList = cacheV2.get(messageId);
|
||||
|
||||
if (envelopeList == null) {
|
||||
envelopeList = new LinkedList<>();
|
||||
}
|
||||
|
||||
envelopeList.add(cacheEntry);
|
||||
|
||||
cacheV2.put(messageId, envelopeList);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns and removes any content that is dependent on the provided message id.
|
||||
*
|
||||
* @param sender The sender of the message in question.
|
||||
* @param sentTimestamp The sent timestamp of the message in question.
|
||||
*/
|
||||
@@ -47,11 +66,17 @@ public final class EarlyMessageCache {
|
||||
return Optional.ofNullable(cache.remove(new ServiceMessageId(sender, sentTimestamp)));
|
||||
}
|
||||
|
||||
public synchronized Optional<List<EarlyMessageCacheEntry>> retrieveV2(@NonNull RecipientId sender, long sentTimestamp) {
|
||||
return Optional.ofNullable(cacheV2.remove(new ServiceMessageId(sender, sentTimestamp)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a collection of all of the {@link ServiceMessageId}s referenced in the cache at the moment of inquiry.
|
||||
* Caution: There is no guarantee that this list will be relevant for any amount of time afterwards.
|
||||
*/
|
||||
public synchronized @NonNull Collection<ServiceMessageId> getAllReferencedIds() {
|
||||
return new HashSet<>(cache.keySet());
|
||||
Set<ServiceMessageId> allIds = new HashSet<>(cache.keySet());
|
||||
allIds.addAll(cacheV2.keySet());
|
||||
return allIds;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
package org.thoughtcrime.securesms.util
|
||||
|
||||
import org.whispersystems.signalservice.api.crypto.EnvelopeMetadata
|
||||
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.Content
|
||||
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.Envelope
|
||||
|
||||
/**
|
||||
* The tuple of information needed to process a message. Used to in [EarlyMessageCache]
|
||||
* to store potentially out-of-order messages.
|
||||
*/
|
||||
data class EarlyMessageCacheEntry(
|
||||
val envelope: Envelope,
|
||||
val content: Content,
|
||||
val metadata: EnvelopeMetadata,
|
||||
val serverDeliveredTimestamp: Long
|
||||
)
|
||||
@@ -6,7 +6,6 @@ import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.WorkerThread;
|
||||
|
||||
|
||||
import org.signal.core.util.StringUtil;
|
||||
import org.signal.core.util.logging.Log;
|
||||
import org.signal.libsignal.zkgroup.InvalidInputException;
|
||||
@@ -16,12 +15,14 @@ import org.thoughtcrime.securesms.database.GroupTable;
|
||||
import org.thoughtcrime.securesms.database.SignalDatabase;
|
||||
import org.thoughtcrime.securesms.database.model.GroupRecord;
|
||||
import org.thoughtcrime.securesms.groups.GroupId;
|
||||
import org.thoughtcrime.securesms.messages.SignalServiceProtoUtil;
|
||||
import org.thoughtcrime.securesms.mms.MessageGroupContext;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceContent;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceGroupV2;
|
||||
import org.whispersystems.signalservice.internal.push.SignalServiceProtos;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
@@ -55,6 +56,23 @@ public final class GroupUtil {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static @Nullable SignalServiceProtos.GroupContextV2 getGroupContextIfPresent(@NonNull SignalServiceProtos.Content content) {
|
||||
if (content.hasDataMessage() && SignalServiceProtoUtil.INSTANCE.getHasGroupContext(content.getDataMessage())) {
|
||||
return content.getDataMessage().getGroupV2();
|
||||
} else if (content.hasSyncMessage() &&
|
||||
content.getSyncMessage().hasSent() &&
|
||||
content.getSyncMessage().getSent().hasMessage() &&
|
||||
SignalServiceProtoUtil.INSTANCE.getHasGroupContext(content.getSyncMessage().getSent().getMessage()))
|
||||
{
|
||||
return content.getSyncMessage().getSent().getMessage().getGroupV2();
|
||||
} else if (content.hasStoryMessage() && SignalServiceProtoUtil.INSTANCE.isValid(content.getStoryMessage().getGroup())) {
|
||||
return content.getStoryMessage().getGroup();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Result may be a v1 or v2 GroupId.
|
||||
*/
|
||||
|
||||
@@ -7,8 +7,10 @@ import androidx.annotation.Nullable;
|
||||
import androidx.annotation.StringRes;
|
||||
|
||||
import org.signal.core.util.concurrent.SignalExecutors;
|
||||
import org.signal.core.util.concurrent.SimpleTask;
|
||||
import org.signal.core.util.logging.Log;
|
||||
import org.signal.libsignal.protocol.IdentityKey;
|
||||
import org.signal.libsignal.protocol.InvalidKeyException;
|
||||
import org.signal.libsignal.protocol.SignalProtocolAddress;
|
||||
import org.signal.libsignal.protocol.state.SessionRecord;
|
||||
import org.signal.libsignal.protocol.state.SessionStore;
|
||||
@@ -23,7 +25,6 @@ import org.thoughtcrime.securesms.database.SignalDatabase;
|
||||
import org.thoughtcrime.securesms.database.model.GroupRecord;
|
||||
import org.thoughtcrime.securesms.database.model.IdentityRecord;
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
||||
import org.thoughtcrime.securesms.mms.MmsException;
|
||||
import org.thoughtcrime.securesms.mms.OutgoingMessage;
|
||||
import org.thoughtcrime.securesms.notifications.v2.ConversationId;
|
||||
@@ -35,10 +36,11 @@ import org.thoughtcrime.securesms.sms.IncomingIdentityVerifiedMessage;
|
||||
import org.thoughtcrime.securesms.sms.IncomingTextMessage;
|
||||
import org.thoughtcrime.securesms.util.concurrent.ListenableFuture;
|
||||
import org.thoughtcrime.securesms.util.concurrent.SettableFuture;
|
||||
import org.signal.core.util.concurrent.SimpleTask;
|
||||
import org.whispersystems.signalservice.api.SignalSessionLock;
|
||||
import org.whispersystems.signalservice.api.messages.multidevice.VerifiedMessage;
|
||||
import org.whispersystems.signalservice.api.push.ServiceId;
|
||||
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
|
||||
import org.whispersystems.signalservice.internal.push.SignalServiceProtos;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
@@ -175,6 +177,28 @@ public final class IdentityUtil {
|
||||
}
|
||||
}
|
||||
|
||||
public static void processVerifiedMessage(Context context, SignalServiceProtos.Verified verified) throws InvalidKeyException {
|
||||
SignalServiceAddress destination = new SignalServiceAddress(ServiceId.parseOrThrow(verified.getDestinationUuid()));
|
||||
IdentityKey identityKey = new IdentityKey(verified.getIdentityKey().toByteArray(), 0);
|
||||
VerifiedMessage.VerifiedState state;
|
||||
|
||||
switch (verified.getState()) {
|
||||
case DEFAULT:
|
||||
state = VerifiedMessage.VerifiedState.DEFAULT;
|
||||
break;
|
||||
case VERIFIED:
|
||||
state = VerifiedMessage.VerifiedState.VERIFIED;
|
||||
break;
|
||||
case UNVERIFIED:
|
||||
state = VerifiedMessage.VerifiedState.UNVERIFIED;
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
|
||||
processVerifiedMessage(context, new VerifiedMessage(destination, identityKey, state, System.currentTimeMillis()));
|
||||
}
|
||||
|
||||
public static void processVerifiedMessage(Context context, VerifiedMessage verifiedMessage) {
|
||||
try(SignalSessionLock.Lock unused = ReentrantSessionLock.INSTANCE.acquire()) {
|
||||
SignalIdentityKeyStore identityStore = ApplicationDependencies.getProtocolStore().aci().identities();
|
||||
|
||||
@@ -6,6 +6,7 @@ import com.annimon.stream.Stream;
|
||||
|
||||
import org.thoughtcrime.securesms.database.model.MessageRecord;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
@@ -18,18 +19,23 @@ public final class RemoteDeleteUtil {
|
||||
private RemoteDeleteUtil() {}
|
||||
|
||||
public static boolean isValidReceive(@NonNull MessageRecord targetMessage, @NonNull Recipient deleteSender, long deleteServerTimestamp) {
|
||||
boolean isValidIncomingOutgoing = (deleteSender.isSelf() && targetMessage.isOutgoing()) ||
|
||||
(!deleteSender.isSelf() && !targetMessage.isOutgoing());
|
||||
return isValidReceive(targetMessage, deleteSender.getId(), deleteServerTimestamp);
|
||||
}
|
||||
|
||||
boolean isValidSender = targetMessage.getIndividualRecipient().equals(deleteSender) ||
|
||||
deleteSender.isSelf() && targetMessage.isOutgoing();
|
||||
public static boolean isValidReceive(@NonNull MessageRecord targetMessage, @NonNull RecipientId deleteSenderId, long deleteServerTimestamp) {
|
||||
boolean selfIsDeleteSender = isSelf(deleteSenderId);
|
||||
|
||||
long messageTimestamp = deleteSender.isSelf() && targetMessage.isOutgoing() ? targetMessage.getDateSent()
|
||||
: targetMessage.getServerTimestamp();
|
||||
boolean isValidIncomingOutgoing = (selfIsDeleteSender && targetMessage.isOutgoing()) ||
|
||||
(!selfIsDeleteSender && !targetMessage.isOutgoing());
|
||||
|
||||
boolean isValidSender = targetMessage.getIndividualRecipient().getId().equals(deleteSenderId) || selfIsDeleteSender && targetMessage.isOutgoing();
|
||||
|
||||
long messageTimestamp = selfIsDeleteSender && targetMessage.isOutgoing() ? targetMessage.getDateSent()
|
||||
: targetMessage.getServerTimestamp();
|
||||
|
||||
return isValidIncomingOutgoing &&
|
||||
isValidSender &&
|
||||
(((deleteServerTimestamp - messageTimestamp) < RECEIVE_THRESHOLD) || (deleteSender.isSelf() && targetMessage.isOutgoing()));
|
||||
isValidSender &&
|
||||
(((deleteServerTimestamp - messageTimestamp) < RECEIVE_THRESHOLD) || (selfIsDeleteSender && targetMessage.isOutgoing()));
|
||||
}
|
||||
|
||||
public static boolean isValidSend(@NonNull Collection<MessageRecord> targetMessages, long currentTime) {
|
||||
@@ -38,13 +44,17 @@ public final class RemoteDeleteUtil {
|
||||
}
|
||||
|
||||
private static boolean isValidSend(MessageRecord message, long currentTime) {
|
||||
return !message.isUpdate() &&
|
||||
message.isOutgoing() &&
|
||||
message.isPush() &&
|
||||
return !message.isUpdate() &&
|
||||
message.isOutgoing() &&
|
||||
message.isPush() &&
|
||||
(!message.getRecipient().isGroup() || message.getRecipient().isActiveGroup()) &&
|
||||
!message.isRemoteDelete() &&
|
||||
!MessageRecordUtil.hasGiftBadge(message) &&
|
||||
!message.isPaymentNotification() &&
|
||||
!message.isRemoteDelete() &&
|
||||
!MessageRecordUtil.hasGiftBadge(message) &&
|
||||
!message.isPaymentNotification() &&
|
||||
(((currentTime - message.getDateSent()) < SEND_THRESHOLD) || message.getRecipient().isSelf());
|
||||
}
|
||||
|
||||
private static boolean isSelf(@NonNull RecipientId recipientId) {
|
||||
return Recipient.isSelfSet() && Recipient.self().getId().equals(recipientId);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user