diff --git a/app/src/main/java/org/thoughtcrime/securesms/storage/ContactConflictMerger.java b/app/src/main/java/org/thoughtcrime/securesms/storage/ContactConflictMerger.java index d488bb04d..414f7c993 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/storage/ContactConflictMerger.java +++ b/app/src/main/java/org/thoughtcrime/securesms/storage/ContactConflictMerger.java @@ -7,6 +7,7 @@ import com.annimon.stream.Stream; import org.signal.core.util.logging.Log; import org.thoughtcrime.securesms.recipients.Recipient; +import org.thoughtcrime.securesms.util.Base64; import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.signalservice.api.push.SignalServiceAddress; import org.whispersystems.signalservice.api.storage.SignalContactRecord; @@ -15,9 +16,11 @@ import org.whispersystems.signalservice.internal.storage.protos.ContactRecord.Id import java.util.Arrays; import java.util.Collection; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Set; import java.util.UUID; class ContactConflictMerger implements StorageSyncHelper.ConflictMerger { @@ -52,11 +55,41 @@ class ContactConflictMerger implements StorageSyncHelper.ConflictMerger getInvalidEntries(@NonNull Collection remoteRecords) { - List invalid = Stream.of(remoteRecords) - .filter(r -> r.getAddress().getUuid().equals(self.getUuid()) || r.getAddress().getNumber().equals(self.getE164())) - .toList(); + Map> localIdToRemoteRecords = new HashMap<>(); + + for (SignalContactRecord remote : remoteRecords) { + Optional local = getMatching(remote); + + if (local.isPresent()) { + String serializedLocalId = Base64.encodeBytes(local.get().getId().getRaw()); + Set matches = localIdToRemoteRecords.get(serializedLocalId); + + if (matches == null) { + matches = new HashSet<>(); + } + + matches.add(remote); + localIdToRemoteRecords.put(serializedLocalId, matches); + } + } + + Set duplicates = new HashSet<>(); + for (Set matches : localIdToRemoteRecords.values()) { + if (matches.size() > 1) { + duplicates.addAll(matches); + } + } + + List selfRecords = Stream.of(remoteRecords) + .filter(r -> r.getAddress().getUuid().equals(self.getUuid()) || r.getAddress().getNumber().equals(self.getE164())) + .toList(); + + Set invalid = new HashSet<>(); + invalid.addAll(selfRecords); + invalid.addAll(duplicates); + if (invalid.size() > 0) { - Log.w(TAG, "Found invalid contact entries! Count: " + invalid.size()); + Log.w(TAG, "Found invalid contact entries! Self Records: " + selfRecords.size() + ", Duplicates: " + duplicates.size()); } return invalid; diff --git a/app/src/test/java/org/thoughtcrime/securesms/storage/ContactConflictMergerTest.java b/app/src/test/java/org/thoughtcrime/securesms/storage/ContactConflictMergerTest.java index e37890a03..5dfd65da4 100644 --- a/app/src/test/java/org/thoughtcrime/securesms/storage/ContactConflictMergerTest.java +++ b/app/src/test/java/org/thoughtcrime/securesms/storage/ContactConflictMergerTest.java @@ -150,6 +150,16 @@ public class ContactConflictMergerTest { assertEquals(local, merged); } + @Test + public void getInvalidEntries_nothingInvalid() { + SignalContactRecord a = new SignalContactRecord.Builder(byteArray(1), new SignalServiceAddress(UUID_A, E164_A)).build(); + SignalContactRecord b = new SignalContactRecord.Builder(byteArray(2), new SignalServiceAddress(UUID_B, E164_B)).build(); + + Collection invalid = new ContactConflictMerger(Collections.emptyList(), SELF).getInvalidEntries(setOf(a, b)); + + assertContentsEqual(setOf(), invalid); + } + @Test public void getInvalidEntries_selfIsInvalid() { SignalContactRecord a = new SignalContactRecord.Builder(byteArray(1), new SignalServiceAddress(UUID_A, E164_A)).build(); @@ -160,4 +170,17 @@ public class ContactConflictMergerTest { assertContentsEqual(setOf(self), invalid); } + + @Test + public void getInvalidEntries_duplicatesInvalid() { + SignalContactRecord aLocal = new SignalContactRecord.Builder(byteArray(1), new SignalServiceAddress(UUID_A, E164_A)).build(); + SignalContactRecord bRemote = new SignalContactRecord.Builder(byteArray(2), new SignalServiceAddress(UUID_B, E164_B)).build(); + SignalContactRecord aRemote1 = new SignalContactRecord.Builder(byteArray(3), new SignalServiceAddress(UUID_A, null)).build(); + SignalContactRecord aRemote2 = new SignalContactRecord.Builder(byteArray(4), new SignalServiceAddress(null, E164_A)).build(); + SignalContactRecord aRemote3 = new SignalContactRecord.Builder(byteArray(5), new SignalServiceAddress(UUID_A, E164_A)).build(); + + Collection invalid = new ContactConflictMerger(Collections.singleton(aLocal), SELF).getInvalidEntries(setOf(aRemote1, aRemote2, aRemote3, bRemote)); + + assertContentsEqual(setOf(aRemote1, aRemote2, aRemote3), invalid); + } }