From 2d6b16b2cef8ecd489c220d1f6e11675dd4c6b27 Mon Sep 17 00:00:00 2001 From: Clark Date: Wed, 17 May 2023 13:54:37 -0400 Subject: [PATCH] Introduce extra caching for group message processing. --- .../securesms/database/GroupTable.kt | 5 +- .../securesms/database/RecipientTable.kt | 9 ++-- .../securesms/groups/GroupId.java | 23 ++++++++-- .../securesms/groups/GroupManager.java | 8 +++- .../securesms/groups/GroupManagerV2.java | 14 ++++-- .../securesms/groups/GroupsV1MigratedCache.kt | 46 +++++++++++++++++++ .../v2/processing/GroupsV2StateProcessor.java | 15 +++++- .../securesms/jobs/PushProcessMessageJob.java | 3 +- .../securesms/jobs/PushProcessMessageJobV2.kt | 3 +- .../messages/MessageContentProcessor.java | 9 ++-- .../messages/MessageContentProcessorV2.kt | 23 ++++++---- .../messages/SyncMessageProcessor.kt | 2 +- .../securesms/recipients/Recipient.java | 2 +- .../securesms/recipients/RecipientId.java | 11 +++++ .../recipients/RecipientIdCache.java | 9 ++++ 15 files changed, 149 insertions(+), 33 deletions(-) create mode 100644 app/src/main/java/org/thoughtcrime/securesms/groups/GroupsV1MigratedCache.kt diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/GroupTable.kt b/app/src/main/java/org/thoughtcrime/securesms/database/GroupTable.kt index 4fccfeab78..23892a585b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/GroupTable.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/GroupTable.kt @@ -44,6 +44,7 @@ import org.thoughtcrime.securesms.groups.BadGroupIdException import org.thoughtcrime.securesms.groups.GroupId import org.thoughtcrime.securesms.groups.GroupId.Push import org.thoughtcrime.securesms.groups.GroupMigrationMembershipChange +import org.thoughtcrime.securesms.groups.GroupsV1MigratedCache import org.thoughtcrime.securesms.groups.v2.processing.GroupsV2StateProcessor import org.thoughtcrime.securesms.jobs.RequestGroupV2InfoJob import org.thoughtcrime.securesms.keyvalue.SignalStore @@ -669,7 +670,7 @@ class GroupTable(context: Context?, databaseHelper: SignalDatabase?) : DatabaseT fun create(groupMasterKey: GroupMasterKey, groupState: DecryptedGroup, force: Boolean = false): GroupId.V2? { val groupId = GroupId.v2(groupMasterKey) - if (!force && getGroupV1ByExpectedV2(groupId).isPresent) { + if (!force && GroupsV1MigratedCache.hasV1Group(groupId)) { throw MissedGroupMigrationInsertException(groupId) } else if (force) { Log.w(TAG, "Forcing the creation of a group even though we already have a V1 ID!") @@ -688,7 +689,7 @@ class GroupTable(context: Context?, databaseHelper: SignalDatabase?) : DatabaseT */ fun fixMissingMasterKey(groupMasterKey: GroupMasterKey) { val groupId = GroupId.v2(groupMasterKey) - if (getGroupV1ByExpectedV2(groupId).isPresent) { + if (GroupsV1MigratedCache.hasV1Group(groupId)) { Log.w(TAG, "There already exists a V1 group that should be migrated into this group. But if the recipient already exists, there's not much we can do here.") } diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/RecipientTable.kt b/app/src/main/java/org/thoughtcrime/securesms/database/RecipientTable.kt index a8e1ff9114..f117e1093c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/RecipientTable.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/RecipientTable.kt @@ -77,6 +77,7 @@ import org.thoughtcrime.securesms.groups.BadGroupIdException import org.thoughtcrime.securesms.groups.GroupId import org.thoughtcrime.securesms.groups.GroupId.V1 import org.thoughtcrime.securesms.groups.GroupId.V2 +import org.thoughtcrime.securesms.groups.GroupsV1MigratedCache import org.thoughtcrime.securesms.groups.v2.ProfileKeySet import org.thoughtcrime.securesms.groups.v2.processing.GroupsV2StateProcessor import org.thoughtcrime.securesms.jobs.RequestGroupV2InfoJob @@ -576,7 +577,7 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da return existing.get() } else if (groupId.isV1 && groups.groupExists(groupId.requireV1().deriveV2MigrationGroupId())) { throw LegacyGroupInsertException(groupId) - } else if (groupId.isV2 && groups.getGroupV1ByExpectedV2(groupId.requireV2()).isPresent) { + } else if (groupId.isV2 && GroupsV1MigratedCache.hasV1Group(groupId.requireV2())) { throw MissedGroupMigrationInsertException(groupId) } else { val values = ContentValues().apply { @@ -641,10 +642,10 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da } if (groupId.isV2) { - val v1 = groups.getGroupV1ByExpectedV2(groupId.requireV2()) - if (v1.isPresent) { + val v1 = GroupsV1MigratedCache.getV1GroupByV2Id(groupId.requireV2()) + if (v1 != null) { db.setTransactionSuccessful() - return v1.get().recipientId + return v1.recipientId } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/groups/GroupId.java b/app/src/main/java/org/thoughtcrime/securesms/groups/GroupId.java index f411250d3d..673a465f3d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/groups/GroupId.java +++ b/app/src/main/java/org/thoughtcrime/securesms/groups/GroupId.java @@ -12,6 +12,7 @@ import org.signal.libsignal.zkgroup.InvalidInputException; import org.signal.libsignal.zkgroup.groups.GroupIdentifier; import org.signal.libsignal.zkgroup.groups.GroupMasterKey; import org.signal.libsignal.zkgroup.groups.GroupSecretParams; +import org.thoughtcrime.securesms.util.LRUCache; import org.thoughtcrime.securesms.util.Util; import java.io.IOException; @@ -29,6 +30,8 @@ public abstract class GroupId implements DatabaseId { private final String encodedId; + private static final LRUCache groupIdentifierCache = new LRUCache<>(1000); + private GroupId(@NonNull String prefix, @NonNull byte[] bytes) { this.encodedId = prefix + Hex.toStringCondensed(bytes); } @@ -80,9 +83,23 @@ public abstract class GroupId implements DatabaseId { } public static GroupId.V2 v2(@NonNull GroupMasterKey masterKey) { - return v2(GroupSecretParams.deriveFromMasterKey(masterKey) - .getPublicParams() - .getGroupIdentifier()); + return v2(getIdentifierForMasterKey(masterKey)); + } + + public static GroupIdentifier getIdentifierForMasterKey(@NonNull GroupMasterKey masterKey) { + GroupIdentifier cachedIdentifier; + synchronized (groupIdentifierCache) { + cachedIdentifier = groupIdentifierCache.get(masterKey); + } + if (cachedIdentifier == null) { + cachedIdentifier = GroupSecretParams.deriveFromMasterKey(masterKey) + .getPublicParams() + .getGroupIdentifier(); + synchronized (groupIdentifierCache) { + groupIdentifierCache.put(masterKey, cachedIdentifier); + } + } + return cachedIdentifier; } public static GroupId.Push push(ByteString bytes) throws BadGroupIdException { diff --git a/app/src/main/java/org/thoughtcrime/securesms/groups/GroupManager.java b/app/src/main/java/org/thoughtcrime/securesms/groups/GroupManager.java index 9d981ce8f9..ad4fe25782 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/groups/GroupManager.java +++ b/app/src/main/java/org/thoughtcrime/securesms/groups/GroupManager.java @@ -32,6 +32,7 @@ import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Set; import java.util.UUID; @@ -187,12 +188,15 @@ public final class GroupManager { @Nullable byte[] signedGroupChange) throws GroupChangeBusyException, IOException, GroupNotAMemberException { - return updateGroupFromServer(context, groupMasterKey, null, revision, timestamp, signedGroupChange); + try (GroupManagerV2.GroupUpdater updater = new GroupManagerV2(context).updater(groupMasterKey)) { + return updater.updateLocalToServerRevision(revision, timestamp, null, signedGroupChange); + } } @WorkerThread public static GroupsV2StateProcessor.GroupUpdateResult updateGroupFromServer(@NonNull Context context, @NonNull GroupMasterKey groupMasterKey, + @NonNull Optional groupRecord, @Nullable GroupSecretParams groupSecretParams, int revision, long timestamp, @@ -200,7 +204,7 @@ public final class GroupManager { throws GroupChangeBusyException, IOException, GroupNotAMemberException { try (GroupManagerV2.GroupUpdater updater = new GroupManagerV2(context).updater(groupMasterKey)) { - return updater.updateLocalToServerRevision(revision, timestamp, groupSecretParams, signedGroupChange); + return updater.updateLocalToServerRevision(revision, timestamp, groupRecord, groupSecretParams, signedGroupChange); } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/groups/GroupManagerV2.java b/app/src/main/java/org/thoughtcrime/securesms/groups/GroupManagerV2.java index e2d772f7b4..e0c3f93c9d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/groups/GroupManagerV2.java +++ b/app/src/main/java/org/thoughtcrime/securesms/groups/GroupManagerV2.java @@ -804,6 +804,14 @@ final class GroupManagerV2 { .updateLocalGroupToRevision(revision, timestamp, getDecryptedGroupChange(signedGroupChange)); } + @WorkerThread + GroupsV2StateProcessor.GroupUpdateResult updateLocalToServerRevision(int revision, long timestamp, @NonNull Optional localRecord, @Nullable GroupSecretParams groupSecretParams, @Nullable byte[] signedGroupChange) + throws IOException, GroupNotAMemberException + { + return new GroupsV2StateProcessor(context).forGroup(serviceIds, groupMasterKey, groupSecretParams) + .updateLocalGroupToRevision(revision, timestamp, localRecord, getDecryptedGroupChange(signedGroupChange)); + } + @WorkerThread void forceSanityUpdateFromServer(long timestamp) throws IOException, GroupNotAMemberException @@ -929,11 +937,11 @@ final class GroupManagerV2 { alreadyAMember = true; } - Optional unmigratedV1Group = groupDatabase.getGroupV1ByExpectedV2(groupId); + GroupRecord unmigratedV1Group = GroupsV1MigratedCache.getV1GroupByV2Id(groupId); - if (unmigratedV1Group.isPresent()) { + if (unmigratedV1Group != null) { Log.i(TAG, "Group link was for a migrated V1 group we know about! Migrating it and using that as the base."); - GroupsV1MigrationUtil.performLocalMigration(context, unmigratedV1Group.get().getId().requireV1()); + GroupsV1MigrationUtil.performLocalMigration(context, unmigratedV1Group.getId().requireV1()); } DecryptedGroup decryptedGroup = createPlaceholderGroup(joinInfo, requestToJoin); diff --git a/app/src/main/java/org/thoughtcrime/securesms/groups/GroupsV1MigratedCache.kt b/app/src/main/java/org/thoughtcrime/securesms/groups/GroupsV1MigratedCache.kt new file mode 100644 index 0000000000..c825895d70 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/groups/GroupsV1MigratedCache.kt @@ -0,0 +1,46 @@ +/* + * Copyright 2023 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.groups + +import androidx.annotation.WorkerThread +import org.signal.core.util.orNull +import org.thoughtcrime.securesms.database.SignalDatabase +import org.thoughtcrime.securesms.database.model.GroupRecord +import org.thoughtcrime.securesms.util.LRUCache + +/** + * Cache to keep track of groups we know do not need a migration run on. This is to save time looking for a gv1 group + * with the expected v2 id. + */ +object GroupsV1MigratedCache { + private const val MAX_CACHE = 1000 + + private val noV1GroupCache = LRUCache(MAX_CACHE) + + @JvmStatic + @WorkerThread + fun hasV1Group(groupId: GroupId.V2): Boolean { + return getV1GroupByV2Id(groupId) != null + } + + @JvmStatic + @WorkerThread + fun getV1GroupByV2Id(groupId: GroupId.V2): GroupRecord? { + synchronized(noV1GroupCache) { + if (noV1GroupCache.containsKey(groupId)) { + return null + } + } + + val v1Group = SignalDatabase.groups.getGroupV1ByExpectedV2(groupId) + if (!v1Group.isPresent) { + synchronized(noV1GroupCache) { + noV1GroupCache.put(groupId, true) + } + } + return v1Group.orNull() + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/groups/v2/processing/GroupsV2StateProcessor.java b/app/src/main/java/org/thoughtcrime/securesms/groups/v2/processing/GroupsV2StateProcessor.java index 98ecb1fb32..f9f3bd4334 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/groups/v2/processing/GroupsV2StateProcessor.java +++ b/app/src/main/java/org/thoughtcrime/securesms/groups/v2/processing/GroupsV2StateProcessor.java @@ -266,8 +266,21 @@ public class GroupsV2StateProcessor { @Nullable DecryptedGroupChange signedGroupChange) throws IOException, GroupNotAMemberException { - Optional localRecord = groupDatabase.getGroup(groupId); + return updateLocalGroupToRevision(revision, timestamp, groupDatabase.getGroup(groupId), signedGroupChange); + } + /** + * Using network where required, will attempt to bring the local copy of the group up to the revision specified. + * + * @param revision use {@link #LATEST} to get latest. + */ + @WorkerThread + public GroupUpdateResult updateLocalGroupToRevision(final int revision, + final long timestamp, + @NonNull Optional localRecord, + @Nullable DecryptedGroupChange signedGroupChange) + throws IOException, GroupNotAMemberException + { if (localIsAtLeast(localRecord, revision)) { return new GroupUpdateResult(GroupState.GROUP_CONSISTENT_OR_AHEAD, null); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/PushProcessMessageJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/PushProcessMessageJob.java index 7ea4ac2c7e..da20f9676f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/PushProcessMessageJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/PushProcessMessageJob.java @@ -11,6 +11,7 @@ import org.thoughtcrime.securesms.database.SignalDatabase; import org.thoughtcrime.securesms.dependencies.ApplicationDependencies; import org.thoughtcrime.securesms.groups.GroupChangeBusyException; import org.thoughtcrime.securesms.groups.GroupId; +import org.thoughtcrime.securesms.groups.GroupsV1MigratedCache; import org.thoughtcrime.securesms.jobmanager.JsonJobData; import org.thoughtcrime.securesms.jobmanager.Job; import org.thoughtcrime.securesms.jobmanager.impl.ChangeNumberConstraint; @@ -107,7 +108,7 @@ public final class PushProcessMessageJob extends BaseJob { int localRevision = SignalDatabase.groups().getGroupV2Revision(groupId.requireV2()); if (signalServiceGroupContext.getRevision() > localRevision || - SignalDatabase.groups().getGroupV1ByExpectedV2(groupId.requireV2()).isPresent()) + GroupsV1MigratedCache.hasV1Group(groupId.requireV2())) { Log.i(TAG, "Adding network constraint to group-related job."); builder.addConstraint(NetworkConstraint.KEY) diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/PushProcessMessageJobV2.kt b/app/src/main/java/org/thoughtcrime/securesms/jobs/PushProcessMessageJobV2.kt index 4b1c8ad7ae..6aa7df239c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/PushProcessMessageJobV2.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/PushProcessMessageJobV2.kt @@ -6,6 +6,7 @@ import okio.ByteString.Companion.toByteString import org.signal.core.util.logging.Log import org.thoughtcrime.securesms.database.SignalDatabase.Companion.groups import org.thoughtcrime.securesms.groups.GroupChangeBusyException +import org.thoughtcrime.securesms.groups.GroupsV1MigratedCache import org.thoughtcrime.securesms.jobmanager.Job import org.thoughtcrime.securesms.jobmanager.impl.ChangeNumberConstraint import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint @@ -126,7 +127,7 @@ class PushProcessMessageJobV2 private constructor( if (groupId.isV2) { val localRevision = groups.getGroupV2Revision(groupId.requireV2()) - if (groupContext.revision > localRevision || groups.getGroupV1ByExpectedV2(groupId.requireV2()).isPresent) { + if (groupContext.revision > localRevision || GroupsV1MigratedCache.hasV1Group(groupId)) { Log.i(TAG, "Adding network constraint to group-related job.") builder.addConstraint(NetworkConstraint.KEY).setLifespan(TimeUnit.DAYS.toMillis(30)) } 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 22a7c97262..2e221190a9 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/messages/MessageContentProcessor.java +++ b/app/src/main/java/org/thoughtcrime/securesms/messages/MessageContentProcessor.java @@ -74,6 +74,7 @@ import org.thoughtcrime.securesms.groups.GroupChangeBusyException; import org.thoughtcrime.securesms.groups.GroupId; import org.thoughtcrime.securesms.groups.GroupManager; import org.thoughtcrime.securesms.groups.GroupNotAMemberException; +import org.thoughtcrime.securesms.groups.GroupsV1MigratedCache; import org.thoughtcrime.securesms.groups.GroupsV1MigrationUtil; import org.thoughtcrime.securesms.jobmanager.JobManager; import org.thoughtcrime.securesms.jobs.AttachmentDownloadJob; @@ -521,11 +522,11 @@ public class MessageContentProcessor { private boolean handleGv2PreProcessing(@NonNull GroupId.V2 groupId, @NonNull SignalServiceContent content, @NonNull SignalServiceGroupV2 groupV2, @NonNull Recipient senderRecipient) throws IOException, GroupChangeBusyException { - GroupTable groupDatabase = SignalDatabase.groups(); - Optional possibleGv1 = groupDatabase.getGroupV1ByExpectedV2(groupId); + GroupTable groupDatabase = SignalDatabase.groups(); + GroupRecord possibleGv1 = GroupsV1MigratedCache.getV1GroupByV2Id(groupId); - if (possibleGv1.isPresent()) { - GroupsV1MigrationUtil.performLocalMigration(context, possibleGv1.get().getId().requireV1()); + if (possibleGv1 != null) { + GroupsV1MigrationUtil.performLocalMigration(context, possibleGv1.getId().requireV1()); } if (!updateGv2GroupFromServerOrP2PChange(content, groupV2)) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/messages/MessageContentProcessorV2.kt b/app/src/main/java/org/thoughtcrime/securesms/messages/MessageContentProcessorV2.kt index e2432a23ec..b49fbb1f58 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/messages/MessageContentProcessorV2.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/messages/MessageContentProcessorV2.kt @@ -9,6 +9,7 @@ import org.signal.libsignal.protocol.ecc.ECPublicKey import org.signal.libsignal.protocol.message.DecryptionErrorMessage import org.signal.libsignal.zkgroup.groups.GroupSecretParams import org.thoughtcrime.securesms.database.SignalDatabase +import org.thoughtcrime.securesms.database.model.GroupRecord import org.thoughtcrime.securesms.database.model.MessageLogEntry import org.thoughtcrime.securesms.database.model.MessageRecord import org.thoughtcrime.securesms.database.model.PendingRetryReceiptModel @@ -18,6 +19,7 @@ import org.thoughtcrime.securesms.groups.GroupChangeBusyException import org.thoughtcrime.securesms.groups.GroupId import org.thoughtcrime.securesms.groups.GroupManager import org.thoughtcrime.securesms.groups.GroupNotAMemberException +import org.thoughtcrime.securesms.groups.GroupsV1MigratedCache import org.thoughtcrime.securesms.groups.GroupsV1MigrationUtil import org.thoughtcrime.securesms.groups.v2.processing.GroupsV2StateProcessor import org.thoughtcrime.securesms.jobs.NullMessageSendJob @@ -50,6 +52,7 @@ import org.whispersystems.signalservice.internal.push.SignalServiceProtos.Conten import org.whispersystems.signalservice.internal.push.SignalServiceProtos.Envelope import org.whispersystems.signalservice.internal.push.SignalServiceProtos.TypingMessage import java.io.IOException +import java.util.Optional open class MessageContentProcessorV2(private val context: Context) { @@ -224,22 +227,21 @@ open class MessageContentProcessorV2(private val context: Context) { senderRecipient: Recipient, groupSecretParams: GroupSecretParams? = null ): Gv2PreProcessResult { - val possibleV1OrV2Group = SignalDatabase.groups.getGroupV1OrV2ByExpectedV2(groupId) - val needsV1Migration = possibleV1OrV2Group.isPresent && possibleV1OrV2Group.get().isV1Group - if (needsV1Migration) { - GroupsV1MigrationUtil.performLocalMigration(context, possibleV1OrV2Group.get().id.requireV1()) + val v1Group = GroupsV1MigratedCache.getV1GroupByV2Id(groupId) + if (v1Group != null) { + GroupsV1MigrationUtil.performLocalMigration(context, v1Group.id.requireV1()) } - - val groupUpdateResult = updateGv2GroupFromServerOrP2PChange(context, timestamp, groupV2, groupSecretParams) + val preUpdateGroupRecord = SignalDatabase.groups.getGroup(groupId) + val groupUpdateResult = updateGv2GroupFromServerOrP2PChange(context, timestamp, groupV2, preUpdateGroupRecord, groupSecretParams) if (groupUpdateResult == null) { log(timestamp, "Ignoring GV2 message for group we are not currently in $groupId") return Gv2PreProcessResult.IGNORE } - val groupRecord = if (groupUpdateResult.groupState == GroupsV2StateProcessor.GroupState.GROUP_UPDATED || needsV1Migration) { - SignalDatabase.groups.getGroup(groupId) + val groupRecord = if (groupUpdateResult.groupState == GroupsV2StateProcessor.GroupState.GROUP_CONSISTENT_OR_AHEAD) { + preUpdateGroupRecord } else { - possibleV1OrV2Group + SignalDatabase.groups.getGroup(groupId) } if (groupRecord.isPresent && !groupRecord.get().members.contains(senderRecipient.id)) { @@ -270,12 +272,13 @@ open class MessageContentProcessorV2(private val context: Context) { context: Context, timestamp: Long, groupV2: SignalServiceProtos.GroupContextV2, + localRecord: Optional, groupSecretParams: GroupSecretParams? = null ): GroupsV2StateProcessor.GroupUpdateResult? { return try { val signedGroupChange: ByteArray? = if (groupV2.hasSignedGroupChange) groupV2.signedGroupChange else null val updatedTimestamp = if (signedGroupChange != null) timestamp else timestamp - 1 - GroupManager.updateGroupFromServer(context, groupV2.groupMasterKey, groupSecretParams, groupV2.revision, updatedTimestamp, signedGroupChange) + GroupManager.updateGroupFromServer(context, groupV2.groupMasterKey, localRecord, groupSecretParams, groupV2.revision, updatedTimestamp, signedGroupChange) } catch (e: GroupNotAMemberException) { warn(timestamp, "Ignoring message for a group we're not in") null diff --git a/app/src/main/java/org/thoughtcrime/securesms/messages/SyncMessageProcessor.kt b/app/src/main/java/org/thoughtcrime/securesms/messages/SyncMessageProcessor.kt index 39c12f9986..22ef27040a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/messages/SyncMessageProcessor.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/messages/SyncMessageProcessor.kt @@ -591,7 +591,7 @@ object SyncMessageProcessor { val dataMessage: DataMessage = sent.message val groupId: GroupId.V2? = dataMessage.groupV2.groupId - if (MessageContentProcessorV2.updateGv2GroupFromServerOrP2PChange(context, envelope.timestamp, dataMessage.groupV2) == null) { + if (MessageContentProcessorV2.updateGv2GroupFromServerOrP2PChange(context, envelope.timestamp, dataMessage.groupV2, SignalDatabase.groups.getGroup(GroupId.v2(dataMessage.groupV2.groupMasterKey))) == null) { log(envelope.timestamp, "Ignoring GV2 message for group we are not currently in $groupId") } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/recipients/Recipient.java b/app/src/main/java/org/thoughtcrime/securesms/recipients/Recipient.java index 35531b62d1..d312b851f3 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/recipients/Recipient.java +++ b/app/src/main/java/org/thoughtcrime/securesms/recipients/Recipient.java @@ -336,7 +336,7 @@ public class Recipient { */ @WorkerThread public static @NonNull Recipient externalPossiblyMigratedGroup(@NonNull GroupId groupId) { - return Recipient.resolved(SignalDatabase.recipients().getOrInsertFromPossiblyMigratedGroupId(groupId)); + return Recipient.resolved(RecipientId.from(groupId)); } /** diff --git a/app/src/main/java/org/thoughtcrime/securesms/recipients/RecipientId.java b/app/src/main/java/org/thoughtcrime/securesms/recipients/RecipientId.java index b19c6751b3..18525aec50 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/recipients/RecipientId.java +++ b/app/src/main/java/org/thoughtcrime/securesms/recipients/RecipientId.java @@ -13,6 +13,7 @@ import com.annimon.stream.Stream; import org.signal.core.util.DatabaseId; import org.signal.core.util.LongSerializer; import org.thoughtcrime.securesms.database.SignalDatabase; +import org.thoughtcrime.securesms.groups.GroupId; import org.thoughtcrime.securesms.util.DelimiterUtil; import org.thoughtcrime.securesms.util.Util; import org.whispersystems.signalservice.api.push.ServiceId; @@ -69,6 +70,16 @@ public class RecipientId implements Parcelable, Comparable, Databas return from(null, identifier); } + public static @NonNull RecipientId from(@NonNull GroupId groupId) { + RecipientId recipientId = RecipientIdCache.INSTANCE.get(groupId); + if (recipientId == null) { + recipientId = SignalDatabase.recipients().getOrInsertFromPossiblyMigratedGroupId(groupId); + if (groupId.isV2()) { + RecipientIdCache.INSTANCE.put(groupId, recipientId); + } + } + return recipientId; + } /** * Used for when you have a string that could be either a UUID or an e164. This was primarily * created for interacting with protocol stores. diff --git a/app/src/main/java/org/thoughtcrime/securesms/recipients/RecipientIdCache.java b/app/src/main/java/org/thoughtcrime/securesms/recipients/RecipientIdCache.java index 6e44715528..341eb830b5 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/recipients/RecipientIdCache.java +++ b/app/src/main/java/org/thoughtcrime/securesms/recipients/RecipientIdCache.java @@ -4,6 +4,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import org.signal.core.util.logging.Log; +import org.thoughtcrime.securesms.groups.GroupId; import org.whispersystems.signalservice.api.push.ServiceId; import java.util.LinkedHashMap; @@ -50,6 +51,14 @@ final class RecipientIdCache { put(recipientId, e164.orElse(null), serviceId.orElse(null)); } + synchronized @Nullable RecipientId get(@NonNull GroupId groupId) { + return ids.get(groupId); + } + + synchronized void put(@NonNull GroupId groupId, @NonNull RecipientId recipientId) { + ids.put(groupId, recipientId); + } + synchronized @Nullable RecipientId get(@Nullable ServiceId serviceId, @Nullable String e164) { if (serviceId != null && e164 != null) { RecipientId recipientIdByAci = ids.get(serviceId);