Make ACI's optional on ContactRecords.
This commit is contained in:
parent
2492b8de34
commit
4b6b87d632
5 changed files with 61 additions and 35 deletions
|
@ -795,19 +795,19 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
|
|||
if (id < 0) {
|
||||
Log.w(TAG, "[applyStorageSyncContactInsert] Failed to insert. Possibly merging.")
|
||||
if (FeatureFlags.phoneNumberPrivacy()) {
|
||||
recipientId = getAndPossiblyMergePnpVerified(if (insert.aci.isValid) insert.aci else null, insert.pni.orElse(null), insert.number.orElse(null))
|
||||
recipientId = getAndPossiblyMergePnpVerified(insert.aci.orNull(), insert.pni.orNull(), insert.number.orNull())
|
||||
} else {
|
||||
recipientId = getAndPossiblyMerge(if (insert.aci.isValid) insert.aci else null, insert.number.orElse(null))
|
||||
recipientId = getAndPossiblyMerge(insert.aci.orNull(), insert.number.orNull())
|
||||
}
|
||||
db.update(TABLE_NAME, values, ID_WHERE, SqlUtil.buildArgs(recipientId))
|
||||
} else {
|
||||
recipientId = RecipientId.from(id)
|
||||
}
|
||||
|
||||
if (insert.identityKey.isPresent && insert.aci.isValid) {
|
||||
if (insert.identityKey.isPresent && insert.aci.isPresent) {
|
||||
try {
|
||||
val identityKey = IdentityKey(insert.identityKey.get(), 0)
|
||||
identities.updateIdentityAfterSync(insert.aci.toString(), recipientId, identityKey, StorageSyncModels.remoteToLocalIdentityStatus(insert.identityState))
|
||||
identities.updateIdentityAfterSync(insert.aci.get().toString(), recipientId, identityKey, StorageSyncModels.remoteToLocalIdentityStatus(insert.identityState))
|
||||
} catch (e: InvalidKeyException) {
|
||||
Log.w(TAG, "Failed to process identity key during insert! Skipping.", e)
|
||||
}
|
||||
|
@ -836,9 +836,9 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
|
|||
|
||||
Log.w(TAG, "[applyStorageSyncContactUpdate] Found user $recipientId. Possibly merging.")
|
||||
if (FeatureFlags.phoneNumberPrivacy()) {
|
||||
recipientId = getAndPossiblyMergePnpVerified(if (update.new.aci.isValid) update.new.aci else null, update.new.pni.orElse(null), update.new.number.orElse(null))
|
||||
recipientId = getAndPossiblyMergePnpVerified(update.new.aci.orElse(null), update.new.pni.orElse(null), update.new.number.orElse(null))
|
||||
} else {
|
||||
recipientId = getAndPossiblyMerge(if (update.new.aci.isValid) update.new.aci else null, update.new.number.orElse(null))
|
||||
recipientId = getAndPossiblyMerge(update.new.aci.orElse(null), update.new.number.orElse(null))
|
||||
}
|
||||
|
||||
Log.w(TAG, "[applyStorageSyncContactUpdate] Merged into $recipientId")
|
||||
|
@ -855,9 +855,9 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
|
|||
|
||||
try {
|
||||
val oldIdentityRecord = identityStore.getIdentityRecord(recipientId)
|
||||
if (update.new.identityKey.isPresent && update.new.aci.isValid) {
|
||||
if (update.new.identityKey.isPresent && update.new.aci.isPresent) {
|
||||
val identityKey = IdentityKey(update.new.identityKey.get(), 0)
|
||||
identities.updateIdentityAfterSync(update.new.aci.toString(), recipientId, identityKey, StorageSyncModels.remoteToLocalIdentityStatus(update.new.identityState))
|
||||
identities.updateIdentityAfterSync(update.new.aci.get().toString(), recipientId, identityKey, StorageSyncModels.remoteToLocalIdentityStatus(update.new.identityState))
|
||||
}
|
||||
|
||||
val newIdentityRecord = identityStore.getIdentityRecord(recipientId)
|
||||
|
@ -3872,9 +3872,7 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
|
|||
val systemName = ProfileName.fromParts(contact.systemGivenName.orElse(null), contact.systemFamilyName.orElse(null))
|
||||
val username = contact.username.orElse(null)
|
||||
|
||||
if (contact.aci.isValid) {
|
||||
put(ACI_COLUMN, contact.aci.toString())
|
||||
}
|
||||
put(ACI_COLUMN, contact.aci.orElse(null)?.toString())
|
||||
|
||||
if (FeatureFlags.phoneNumberPrivacy()) {
|
||||
put(PNI_COLUMN, contact.pni.orElse(null)?.toString())
|
||||
|
@ -3905,14 +3903,14 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
|
|||
put(UNREGISTERED_TIMESTAMP, contact.unregisteredTimestamp)
|
||||
if (contact.unregisteredTimestamp > 0L) {
|
||||
put(REGISTERED, RegisteredState.NOT_REGISTERED.id)
|
||||
} else if (contact.aci.isValid) {
|
||||
} else if (contact.aci.isPresent) {
|
||||
put(REGISTERED, RegisteredState.REGISTERED.id)
|
||||
} else {
|
||||
Log.w(TAG, "Contact is marked as registered, but has no serviceId! Can't locally mark registered. (Phone: ${contact.number.orElse("null")}, Username: ${username?.isNotEmpty()})")
|
||||
}
|
||||
|
||||
if (isInsert) {
|
||||
put(AVATAR_COLOR, AvatarColorHash.forAddress(contact.aci.toString(), contact.number.orNull()).serialize())
|
||||
put(AVATAR_COLOR, AvatarColorHash.forAddress(contact.aci.map { it.toString() }.or(contact.pni.map { it.toString() }).orNull(), contact.number.orNull()).serialize())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -80,9 +80,9 @@ public class ContactRecordProcessor extends DefaultStorageRecordProcessor<Signal
|
|||
continue;
|
||||
}
|
||||
|
||||
if (remoteRecord.getUnregisteredTimestamp() > 0 && remoteRecord.getAci() != null && !remoteRecord.getPni().isPresent() && !remoteRecord.getNumber().isPresent()) {
|
||||
if (remoteRecord.getUnregisteredTimestamp() > 0 && remoteRecord.getAci().isPresent() && remoteRecord.getPni().isEmpty() && remoteRecord.getNumber().isEmpty()) {
|
||||
unregisteredAciOnly.add(remoteRecord);
|
||||
} else if (remoteRecord.getAci() != null && remoteRecord.getAci().equals(remoteRecord.getPni().orElse(null))) {
|
||||
} else if (remoteRecord.getAci().isEmpty()) {
|
||||
pniE164Only.add(remoteRecord);
|
||||
}
|
||||
}
|
||||
|
@ -120,20 +120,20 @@ public class ContactRecordProcessor extends DefaultStorageRecordProcessor<Signal
|
|||
|
||||
/**
|
||||
* Error cases:
|
||||
* - You can't have a contact record without an address component.
|
||||
* - You can't have a contact record without an ACI or PNI.
|
||||
* - You can't have a contact record for yourself. That should be an account record.
|
||||
*
|
||||
* Note: This method could be written more succinctly, but the logs are useful :)
|
||||
*/
|
||||
@Override
|
||||
boolean isInvalid(@NonNull SignalContactRecord remote) {
|
||||
if (remote.getAci() == null) {
|
||||
Log.w(TAG, "No address on the ContentRecord -- marking as invalid.");
|
||||
boolean hasAci = remote.getAci().isPresent() && remote.getAci().get().isValid();
|
||||
boolean hasPni = remote.getPni().isPresent() && remote.getPni().get().isValid();
|
||||
|
||||
if (!hasAci && !hasPni) {
|
||||
Log.w(TAG, "Found a ContactRecord with neither an ACI nor a PNI -- marking as invalid.");
|
||||
return true;
|
||||
} else if (remote.getAci().isUnknown()) {
|
||||
Log.w(TAG, "Found a ContactRecord without a UUID -- marking as invalid.");
|
||||
return true;
|
||||
} else if (remote.getAci().equals(selfAci) ||
|
||||
} else if (selfAci != null && selfAci.equals(remote.getAci().orElse(null)) ||
|
||||
(selfPni != null && selfPni.equals(remote.getPni().orElse(null))) ||
|
||||
(selfE164 != null && remote.getNumber().isPresent() && remote.getNumber().get().equals(selfE164)))
|
||||
{
|
||||
|
@ -153,7 +153,7 @@ public class ContactRecordProcessor extends DefaultStorageRecordProcessor<Signal
|
|||
remote = remote.withoutPni();
|
||||
}
|
||||
|
||||
Optional<RecipientId> found = recipientTable.getByAci(remote.getAci());
|
||||
Optional<RecipientId> found = remote.getAci().isPresent() ? recipientTable.getByAci(remote.getAci().get()) : Optional.empty();
|
||||
|
||||
if (found.isEmpty() && remote.getNumber().isPresent()) {
|
||||
found = recipientTable.getByE164(remote.getNumber().get());
|
||||
|
@ -200,7 +200,7 @@ public class ContactRecordProcessor extends DefaultStorageRecordProcessor<Signal
|
|||
byte[] identityKey;
|
||||
|
||||
if ((remote.getIdentityState() != local.getIdentityState() && remote.getIdentityKey().isPresent()) ||
|
||||
(remote.getIdentityKey().isPresent() && !local.getIdentityKey().isPresent()))
|
||||
(remote.getIdentityKey().isPresent() && local.getIdentityKey().isEmpty()))
|
||||
{
|
||||
identityState = remote.getIdentityState();
|
||||
identityKey = remote.getIdentityKey().get();
|
||||
|
@ -209,9 +209,9 @@ public class ContactRecordProcessor extends DefaultStorageRecordProcessor<Signal
|
|||
identityKey = local.getIdentityKey().orElse(null);
|
||||
}
|
||||
|
||||
if (identityKey != null && remote.getIdentityKey().isPresent() && !Arrays.equals(identityKey, remote.getIdentityKey().get())) {
|
||||
Log.w(TAG, "The local and remote identity keys do not match for " + local.getAci() + ". Enqueueing a profile fetch.");
|
||||
RetrieveProfileJob.enqueue(Recipient.trustedPush(local.getAci(), local.getPni().orElse(null), local.getNumber().orElse(null)).getId());
|
||||
if (local.getAci().isPresent() && identityKey != null && remote.getIdentityKey().isPresent() && !Arrays.equals(identityKey, remote.getIdentityKey().get())) {
|
||||
Log.w(TAG, "The local and remote identity keys do not match for " + local.getAci().orElse(null) + ". Enqueueing a profile fetch.");
|
||||
RetrieveProfileJob.enqueue(Recipient.trustedPush(local.getAci().get(), local.getPni().orElse(null), local.getNumber().orElse(null)).getId());
|
||||
}
|
||||
|
||||
PNI pni;
|
||||
|
@ -250,7 +250,7 @@ public class ContactRecordProcessor extends DefaultStorageRecordProcessor<Signal
|
|||
}
|
||||
|
||||
byte[] unknownFields = remote.serializeUnknownFields();
|
||||
ACI aci = local.getAci() == ACI.UNKNOWN ? remote.getAci() : local.getAci();
|
||||
ACI aci = local.getAci().isEmpty() ? remote.getAci().orElse(null) : local.getAci().get();
|
||||
byte[] profileKey = OptionalUtil.or(remote.getProfileKey(), local.getProfileKey()).orElse(null);
|
||||
String username = OptionalUtil.or(remote.getUsername(), local.getUsername()).orElse("");
|
||||
boolean blocked = remote.isBlocked();
|
||||
|
@ -324,7 +324,7 @@ public class ContactRecordProcessor extends DefaultStorageRecordProcessor<Signal
|
|||
|
||||
private static boolean doParamsMatch(@NonNull SignalContactRecord contact,
|
||||
@Nullable byte[] unknownFields,
|
||||
@NonNull ServiceId serviceId,
|
||||
@NonNull ACI aci,
|
||||
@Nullable PNI pni,
|
||||
@Nullable String e164,
|
||||
@NonNull String profileGivenName,
|
||||
|
@ -346,7 +346,7 @@ public class ContactRecordProcessor extends DefaultStorageRecordProcessor<Signal
|
|||
boolean hidden)
|
||||
{
|
||||
return Arrays.equals(contact.serializeUnknownFields(), unknownFields) &&
|
||||
Objects.equals(contact.getAci(), serviceId) &&
|
||||
Objects.equals(contact.getAci().orElse(null), aci) &&
|
||||
Objects.equals(contact.getPni().orElse(null), pni) &&
|
||||
Objects.equals(contact.getNumber().orElse(null), e164) &&
|
||||
Objects.equals(contact.getProfileGivenName().orElse(""), profileGivenName) &&
|
||||
|
|
|
@ -164,7 +164,7 @@ public final class StorageSyncValidations {
|
|||
if (insert.getContact().isPresent()) {
|
||||
SignalContactRecord contact = insert.getContact().get();
|
||||
|
||||
if (self.requireAci().equals(contact.getAci()) ||
|
||||
if (self.requireAci().equals(contact.getAci().orElse(null)) ||
|
||||
self.requirePni().equals(contact.getPni().orElse(null)) ||
|
||||
self.requireE164().equals(contact.getNumber().orElse("")))
|
||||
{
|
||||
|
|
|
@ -69,7 +69,7 @@ class ContactRecordProcessorTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
fun `isInvalid, missing serviceId, true`() {
|
||||
fun `isInvalid, missing ACI and PNI, true`() {
|
||||
// GIVEN
|
||||
val subject = ContactRecordProcessor(ACI_A, PNI_A, E164_A, recipientTable)
|
||||
|
||||
|
@ -84,6 +84,24 @@ class ContactRecordProcessorTest {
|
|||
assertTrue(result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `isInvalid, unknown ACI and PNI, true`() {
|
||||
// GIVEN
|
||||
val subject = ContactRecordProcessor(ACI_A, PNI_A, E164_A, recipientTable)
|
||||
|
||||
val record = buildRecord {
|
||||
setAci(ACI.UNKNOWN.toString())
|
||||
setPni(PNI.UNKNOWN.toString())
|
||||
setE164(E164_B)
|
||||
}
|
||||
|
||||
// WHEN
|
||||
val result = subject.isInvalid(record)
|
||||
|
||||
// THEN
|
||||
assertTrue(result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `isInvalid, e164 matches self, true`() {
|
||||
// GIVEN
|
||||
|
|
|
@ -26,7 +26,7 @@ public final class SignalContactRecord implements SignalRecord {
|
|||
private final ContactRecord proto;
|
||||
private final boolean hasUnknownFields;
|
||||
|
||||
private final ACI aci;
|
||||
private final Optional<ACI> aci;
|
||||
private final Optional<PNI> pni;
|
||||
private final Optional<String> e164;
|
||||
private final Optional<String> profileGivenName;
|
||||
|
@ -42,7 +42,7 @@ public final class SignalContactRecord implements SignalRecord {
|
|||
this.id = id;
|
||||
this.proto = proto;
|
||||
this.hasUnknownFields = ProtoUtil.hasUnknownFields(proto);
|
||||
this.aci = ACI.parseOrUnknown(proto.getAci());
|
||||
this.aci = OptionalUtil.absentIfEmpty(proto.getAci()).map(ACI::parseOrNull);
|
||||
this.pni = OptionalUtil.absentIfEmpty(proto.getPni()).map(PNI::parseOrNull);
|
||||
this.e164 = OptionalUtil.absentIfEmpty(proto.getE164());
|
||||
this.profileGivenName = OptionalUtil.absentIfEmpty(proto.getGivenName());
|
||||
|
@ -173,7 +173,7 @@ public final class SignalContactRecord implements SignalRecord {
|
|||
return hasUnknownFields ? proto.toByteArray() : null;
|
||||
}
|
||||
|
||||
public ACI getAci() {
|
||||
public Optional<ACI> getAci() {
|
||||
return aci;
|
||||
}
|
||||
|
||||
|
@ -181,6 +181,16 @@ public final class SignalContactRecord implements SignalRecord {
|
|||
return pni;
|
||||
}
|
||||
|
||||
public Optional<? extends ServiceId> getServiceId() {
|
||||
if (aci.isPresent()) {
|
||||
return aci;
|
||||
} else if (pni.isPresent()) {
|
||||
return pni;
|
||||
} else {
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
public Optional<String> getNumber() {
|
||||
return e164;
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue