Simplify contact splitting when reading from storage service.

This commit is contained in:
Greyson Parrelli
2024-01-26 12:48:23 -05:00
committed by Nicholas Tinsley
parent 9146f2fb30
commit d4488c72fb
4 changed files with 45 additions and 40 deletions

View File

@@ -113,6 +113,7 @@ import java.util.LinkedList
import java.util.Objects
import java.util.Optional
import java.util.concurrent.TimeUnit
import kotlin.jvm.optionals.getOrNull
import kotlin.math.max
open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTable(context, databaseHelper) {
@@ -2246,13 +2247,16 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
* Removes the target recipient's E164+PNI, then creates a new recipient with that E164+PNI.
* Done so we can match a split contact during storage sync.
*/
fun splitForStorageSync(storageId: ByteArray) {
val record = getByStorageId(storageId)!!
if (record.aci == null || record.pni == null) {
Log.w(TAG, "Invalid state for split, ignoring.")
fun splitForStorageSyncIfNecessary(aci: ACI) {
val recipientId = getByAci(aci).getOrNull() ?: return
val record = getRecord(recipientId)
if (record.pni == null && record.e164 == null) {
return
}
Log.i(TAG, "Splitting $recipientId for storage sync", true)
writableDatabase
.update(TABLE_NAME)
.values(

View File

@@ -56,17 +56,13 @@ public class ContactRecordProcessor extends DefaultStorageRecordProcessor<Signal
/**
* For contact records specifically, we have some extra work that needs to be done before we process all of the records.
*
* We have to look and see if there is an unregistered ACI-only record and another E164/PNI-only record that points to the
* same local contact row.
*
* If so, we actually want to mimic the split and turn them into two separate contact rows locally. The reasons are nuanced,
* but the TL;DR is that we want to split unregistered users into separate rows so that a user could re-register and get a
* different ACI.
* We have to find all unregistered ACI-only records and split them into two separate contact rows locally, if necessary.
* The reasons are nuanced, but the TL;DR is that we want to split unregistered users into separate rows so that a user
* could re-register and get a different ACI.
*/
@Override
public void process(@NonNull Collection<SignalContactRecord> remoteRecords, @NonNull StorageKeyGenerator keyGenerator) throws IOException {
List<SignalContactRecord> unregisteredAciOnly = new ArrayList<>();
List<SignalContactRecord> pniE164Only = new ArrayList<>();
for (SignalContactRecord remoteRecord : remoteRecords) {
if (isInvalid(remoteRecord)) {
@@ -75,39 +71,15 @@ public class ContactRecordProcessor extends DefaultStorageRecordProcessor<Signal
if (remoteRecord.getUnregisteredTimestamp() > 0 && remoteRecord.getAci().isPresent() && remoteRecord.getPni().isEmpty() && remoteRecord.getNumber().isEmpty()) {
unregisteredAciOnly.add(remoteRecord);
} else if (remoteRecord.getAci().isEmpty()) {
pniE164Only.add(remoteRecord);
}
}
if (unregisteredAciOnly.isEmpty() || pniE164Only.isEmpty()) {
super.process(remoteRecords, keyGenerator);
return;
}
Log.i(TAG, "We have some unregistered ACI-only contacts as well as some PNI-only contacts. Need to do an intersection to detect any possible required splits.");
TreeSet<SignalContactRecord> localMatches = new TreeSet<>(this);
for (SignalContactRecord aciOnly : unregisteredAciOnly) {
Optional<SignalContactRecord> localMatch = getMatching(aciOnly, keyGenerator);
if (localMatch.isPresent()) {
localMatches.add(localMatch.get());
if (unregisteredAciOnly.size() > 0) {
for (SignalContactRecord aciOnly : unregisteredAciOnly) {
SignalDatabase.recipients().splitForStorageSyncIfNecessary(aciOnly.getAci().get());
}
}
for (SignalContactRecord pniOnly : pniE164Only) {
Optional<SignalContactRecord> localMatch = getMatching(pniOnly, keyGenerator);
if (localMatch.isPresent() && localMatches.contains(localMatch.get())) {
Log.w(TAG, "Found a situation where we need to split our local record in two in order to match the remote state.");
SignalDatabase.recipients().splitForStorageSync(localMatch.get().getId().getRaw());
}
}
super.process(remoteRecords, keyGenerator);
}