Fix storage sync issue related to duplicate remote contacts.

The theory is that if multiple remote keys map to the *same* local
entry, then when we go to update the local contact the second time, we
won't find the entry by StorageID, because we changed it during the
*first*  update, which will then lead to a crash.

This change makes it so dupes are considered invalid, so we'll delete
them and upload our own local copy.
fork-5.53.8
Greyson Parrelli 2021-02-01 18:04:52 -05:00
rodzic 904593c103
commit 857b945410
2 zmienionych plików z 60 dodań i 4 usunięć

Wyświetl plik

@ -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<SignalContactRecord> {
@ -52,11 +55,41 @@ class ContactConflictMerger implements StorageSyncHelper.ConflictMerger<SignalCo
@Override
public @NonNull Collection<SignalContactRecord> getInvalidEntries(@NonNull Collection<SignalContactRecord> remoteRecords) {
List<SignalContactRecord> invalid = Stream.of(remoteRecords)
.filter(r -> r.getAddress().getUuid().equals(self.getUuid()) || r.getAddress().getNumber().equals(self.getE164()))
.toList();
Map<String, Set<SignalContactRecord>> localIdToRemoteRecords = new HashMap<>();
for (SignalContactRecord remote : remoteRecords) {
Optional<SignalContactRecord> local = getMatching(remote);
if (local.isPresent()) {
String serializedLocalId = Base64.encodeBytes(local.get().getId().getRaw());
Set<SignalContactRecord> matches = localIdToRemoteRecords.get(serializedLocalId);
if (matches == null) {
matches = new HashSet<>();
}
matches.add(remote);
localIdToRemoteRecords.put(serializedLocalId, matches);
}
}
Set<SignalContactRecord> duplicates = new HashSet<>();
for (Set<SignalContactRecord> matches : localIdToRemoteRecords.values()) {
if (matches.size() > 1) {
duplicates.addAll(matches);
}
}
List<SignalContactRecord> selfRecords = Stream.of(remoteRecords)
.filter(r -> r.getAddress().getUuid().equals(self.getUuid()) || r.getAddress().getNumber().equals(self.getE164()))
.toList();
Set<SignalContactRecord> 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;

Wyświetl plik

@ -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<SignalContactRecord> 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<SignalContactRecord> invalid = new ContactConflictMerger(Collections.singleton(aLocal), SELF).getInvalidEntries(setOf(aRemote1, aRemote2, aRemote3, bRemote));
assertContentsEqual(setOf(aRemote1, aRemote2, aRemote3), invalid);
}
}