mirror of
https://github.com/signalapp/Signal-Android.git
synced 2025-12-24 04:58:45 +00:00
Save PNI's from CDSv2 for all users.
This commit is contained in:
committed by
Alex Hart
parent
13853c708e
commit
327cd93e3c
@@ -140,7 +140,7 @@ object ContactDiscovery {
|
||||
): RefreshResult {
|
||||
val stopwatch = Stopwatch(descriptor)
|
||||
|
||||
val preExistingRegisteredIds: Set<RecipientId> = SignalDatabase.recipients.getRegistered().toSet()
|
||||
val preExistingRegisteredIds: Set<RecipientId> = SignalDatabase.recipients.getRegistered()
|
||||
stopwatch.split("pre-existing")
|
||||
|
||||
val result: RefreshResult = refresh()
|
||||
|
||||
@@ -16,7 +16,6 @@ import org.thoughtcrime.securesms.phonenumbers.PhoneNumberFormatter
|
||||
import org.thoughtcrime.securesms.recipients.Recipient
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId
|
||||
import org.thoughtcrime.securesms.util.FeatureFlags
|
||||
import org.whispersystems.signalservice.api.push.ServiceId.ACI
|
||||
import org.whispersystems.signalservice.api.push.exceptions.CdsiInvalidTokenException
|
||||
import org.whispersystems.signalservice.api.push.exceptions.CdsiResourceExhaustedException
|
||||
import org.whispersystems.signalservice.api.services.CdsiV2Service
|
||||
@@ -26,7 +25,7 @@ import kotlin.math.roundToInt
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
|
||||
/**
|
||||
* Performs the CDS refresh using the V2 interface (either CDSH or CDSI) that returns both PNIs and ACIs.
|
||||
* Performs a CDS refresh using CDSv2.
|
||||
*/
|
||||
object ContactDiscoveryRefreshV2 {
|
||||
|
||||
@@ -166,56 +165,29 @@ object ContactDiscoveryRefreshV2 {
|
||||
val registeredIds: MutableSet<RecipientId> = mutableSetOf()
|
||||
val rewrites: MutableMap<String, String> = mutableMapOf()
|
||||
|
||||
if (useCompat && response.isCompatResponse()) {
|
||||
val transformed: Map<String, ACI?> = response.results.mapValues { entry -> entry.value.aci.orElse(null) }
|
||||
val fuzzyOutput: OutputResult<ACI> = FuzzyPhoneNumberHelper.generateOutput(transformed, fuzzyInput)
|
||||
|
||||
if (transformed.values.any { it == null }) {
|
||||
throw IOException("Unexpected null ACI!")
|
||||
}
|
||||
|
||||
SignalDatabase.recipients.rewritePhoneNumbers(fuzzyOutput.rewrites)
|
||||
stopwatch.split("rewrite-e164")
|
||||
|
||||
val aciMap: Map<RecipientId, ACI?> = SignalDatabase.recipients.bulkProcessCdsResult(fuzzyOutput.numbers)
|
||||
|
||||
registeredIds += aciMap.keys
|
||||
rewrites += fuzzyOutput.rewrites
|
||||
stopwatch.split("process-result")
|
||||
|
||||
val existingIds: Set<RecipientId> = SignalDatabase.recipients.getAllPossiblyRegisteredByE164(recipientE164s + rewrites.values)
|
||||
stopwatch.split("get-ids")
|
||||
|
||||
val inactiveIds: Set<RecipientId> = (existingIds - registeredIds).removePossiblyRegisteredButUnlisted()
|
||||
stopwatch.split("registered-but-unlisted")
|
||||
|
||||
SignalDatabase.recipients.bulkUpdatedRegisteredStatus(aciMap, inactiveIds)
|
||||
stopwatch.split("update-registered")
|
||||
} else {
|
||||
if (useCompat) {
|
||||
Log.w(TAG, "Was told to useCompat, but the server responded with a non-compat response! Assuming the server has shut off compat mode.")
|
||||
}
|
||||
|
||||
val transformed: Map<String, CdsV2Result> = response.results.mapValues { entry -> CdsV2Result(entry.value.pni, entry.value.aci.orElse(null)) }
|
||||
val fuzzyOutput: OutputResult<CdsV2Result> = FuzzyPhoneNumberHelper.generateOutput(transformed, fuzzyInput)
|
||||
|
||||
SignalDatabase.recipients.rewritePhoneNumbers(fuzzyOutput.rewrites)
|
||||
stopwatch.split("rewrite-e164")
|
||||
|
||||
registeredIds += SignalDatabase.recipients.bulkProcessCdsV2Result(fuzzyOutput.numbers)
|
||||
rewrites += fuzzyOutput.rewrites
|
||||
stopwatch.split("process-result")
|
||||
|
||||
val existingIds: Set<RecipientId> = SignalDatabase.recipients.getAllPossiblyRegisteredByE164(recipientE164s + rewrites.values)
|
||||
stopwatch.split("get-ids")
|
||||
|
||||
val inactiveIds: Set<RecipientId> = (existingIds - registeredIds).removePossiblyRegisteredButUnlisted()
|
||||
stopwatch.split("registered-but-unlisted")
|
||||
|
||||
SignalDatabase.recipients.bulkUpdatedRegisteredStatusV2(registeredIds, inactiveIds)
|
||||
stopwatch.split("update-registered")
|
||||
if (useCompat && !response.isCompatResponse()) {
|
||||
Log.w(TAG, "Was told to useCompat, but the server responded with a non-compat response! Assuming the server has shut off compat mode.")
|
||||
}
|
||||
|
||||
val transformed: Map<String, CdsV2Result> = response.results.mapValues { entry -> CdsV2Result(entry.value.pni, entry.value.aci.orElse(null)) }
|
||||
val fuzzyOutput: OutputResult<CdsV2Result> = FuzzyPhoneNumberHelper.generateOutput(transformed, fuzzyInput)
|
||||
|
||||
SignalDatabase.recipients.rewritePhoneNumbers(fuzzyOutput.rewrites)
|
||||
stopwatch.split("rewrite-e164")
|
||||
|
||||
registeredIds += SignalDatabase.recipients.bulkProcessCdsResult(fuzzyOutput.numbers)
|
||||
rewrites += fuzzyOutput.rewrites
|
||||
stopwatch.split("process-result")
|
||||
|
||||
val existingIds: Set<RecipientId> = SignalDatabase.recipients.getAllPossiblyRegisteredByE164(recipientE164s + rewrites.values)
|
||||
stopwatch.split("get-ids")
|
||||
|
||||
val inactiveIds: Set<RecipientId> = (existingIds - registeredIds).removePossiblyRegisteredButUnlisted()
|
||||
stopwatch.split("registered-but-unlisted")
|
||||
|
||||
SignalDatabase.recipients.bulkUpdatedRegisteredStatus(registeredIds, inactiveIds)
|
||||
stopwatch.split("update-registered")
|
||||
|
||||
stopwatch.stop(TAG)
|
||||
|
||||
return ContactDiscovery.RefreshResult(registeredIds, rewrites)
|
||||
@@ -228,7 +200,7 @@ object ContactDiscoveryRefreshV2 {
|
||||
|
||||
/**
|
||||
* If an account is unlisted, it won't come back in the CDS response. So just because we're missing a entry doesn't mean they've become unregistered.
|
||||
* This function removes people from the list that both have a serviceId and some history of communication. We consider this a good hueristic for
|
||||
* This function removes people from the list that both have a serviceId and some history of communication. We consider this a good heuristic for
|
||||
* "maybe this person just removed themselves from CDS". We'll rely on profile fetches that occur during chat opens to check registered status and clear
|
||||
* actually-unregistered users out.
|
||||
*/
|
||||
|
||||
@@ -2231,82 +2231,13 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
|
||||
getAndPossiblyMerge(null, record.pni, record.e164)
|
||||
}
|
||||
|
||||
fun bulkUpdatedRegisteredStatus(registered: Map<RecipientId, ServiceId?>, unregistered: Collection<RecipientId>) {
|
||||
writableDatabase.withinTransaction {
|
||||
val registeredWithServiceId: Set<RecipientId> = getRegisteredWithServiceIds()
|
||||
val needsMarkRegistered: Map<RecipientId, ServiceId?> = registered - registeredWithServiceId
|
||||
|
||||
for ((recipientId, serviceId) in needsMarkRegistered) {
|
||||
val values = ContentValues().apply {
|
||||
put(REGISTERED, RegisteredState.REGISTERED.id)
|
||||
put(UNREGISTERED_TIMESTAMP, 0)
|
||||
if (serviceId != null) {
|
||||
put(ACI_COLUMN, serviceId.toString().lowercase())
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
if (update(recipientId, values)) {
|
||||
setStorageIdIfNotSet(recipientId)
|
||||
ApplicationDependencies.getDatabaseObserver().notifyRecipientChanged(recipientId)
|
||||
}
|
||||
} catch (e: SQLiteConstraintException) {
|
||||
Log.w(TAG, "[bulkUpdateRegisteredStatus] Hit a conflict when trying to update $recipientId. Possibly merging.")
|
||||
val e164 = getRecord(recipientId).e164
|
||||
val newId = getAndPossiblyMerge(serviceId, e164)
|
||||
Log.w(TAG, "[bulkUpdateRegisteredStatus] Merged into $newId")
|
||||
}
|
||||
}
|
||||
|
||||
for (id in unregistered) {
|
||||
markUnregistered(id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles inserts the (e164, UUID) pairs, which could result in merges. Does not mark users as
|
||||
* registered.
|
||||
*
|
||||
* @return A mapping of (RecipientId, UUID)
|
||||
*/
|
||||
fun bulkProcessCdsResult(mapping: Map<String, ACI?>): Map<RecipientId, ACI?> {
|
||||
val db = writableDatabase
|
||||
val aciMap: MutableMap<RecipientId, ACI?> = mutableMapOf()
|
||||
|
||||
db.beginTransaction()
|
||||
try {
|
||||
for ((e164, aci) in mapping) {
|
||||
var aciEntry = if (aci != null) getByAci(aci) else Optional.empty()
|
||||
|
||||
if (aciEntry.isPresent) {
|
||||
val idChanged = setPhoneNumber(aciEntry.get(), e164)
|
||||
if (idChanged) {
|
||||
aciEntry = getByAci(aci!!)
|
||||
}
|
||||
}
|
||||
|
||||
val id = if (aciEntry.isPresent) aciEntry.get() else getOrInsertFromE164(e164)
|
||||
aciMap[id] = aci
|
||||
}
|
||||
|
||||
db.setTransactionSuccessful()
|
||||
} finally {
|
||||
db.endTransaction()
|
||||
}
|
||||
|
||||
return aciMap
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes CDSv2 results, merging recipients as necessary. Does not mark users as
|
||||
* registered.
|
||||
*
|
||||
* Important: This is under active development and is not suitable for actual use.
|
||||
*
|
||||
* @return A set of [RecipientId]s that were updated/inserted.
|
||||
*/
|
||||
fun bulkProcessCdsV2Result(mapping: Map<String, CdsV2Result>): Set<RecipientId> {
|
||||
fun bulkProcessCdsResult(mapping: Map<String, CdsV2Result>): Set<RecipientId> {
|
||||
val ids: MutableSet<RecipientId> = mutableSetOf()
|
||||
val db = writableDatabase
|
||||
|
||||
@@ -2324,8 +2255,11 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
|
||||
return ids
|
||||
}
|
||||
|
||||
fun bulkUpdatedRegisteredStatusV2(registered: Set<RecipientId>, unregistered: Collection<RecipientId>) {
|
||||
fun bulkUpdatedRegisteredStatus(registered: Set<RecipientId>, unregistered: Collection<RecipientId>) {
|
||||
writableDatabase.withinTransaction {
|
||||
val existingRegistered: Set<RecipientId> = getRegistered()
|
||||
val needsMarkRegistered: Set<RecipientId> = registered - existingRegistered
|
||||
|
||||
val registeredValues = contentValuesOf(
|
||||
REGISTERED to RegisteredState.REGISTERED.id,
|
||||
UNREGISTERED_TIMESTAMP to 0
|
||||
@@ -2333,7 +2267,7 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
|
||||
|
||||
val newlyRegistered: MutableSet<RecipientId> = mutableSetOf()
|
||||
|
||||
for (id in registered) {
|
||||
for (id in needsMarkRegistered) {
|
||||
if (update(id, registeredValues)) {
|
||||
newlyRegistered += id
|
||||
setStorageIdIfNotSet(id)
|
||||
@@ -3034,26 +2968,16 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
|
||||
return operations
|
||||
}
|
||||
|
||||
fun getRegistered(): List<RecipientId> {
|
||||
val results: MutableList<RecipientId> = LinkedList()
|
||||
fun getRegistered(): Set<RecipientId> {
|
||||
val results: MutableSet<RecipientId> = mutableSetOf()
|
||||
|
||||
readableDatabase.query(TABLE_NAME, ID_PROJECTION, "$REGISTERED = ? and $HIDDEN = ?", arrayOf("1", "${Recipient.HiddenState.NOT_HIDDEN.serialize()}"), null, null, null).use { cursor ->
|
||||
while (cursor != null && cursor.moveToNext()) {
|
||||
results.add(RecipientId.from(cursor.getLong(cursor.getColumnIndexOrThrow(ID))))
|
||||
results += RecipientId.from(cursor.getLong(cursor.getColumnIndexOrThrow(ID)))
|
||||
}
|
||||
}
|
||||
return results
|
||||
}
|
||||
|
||||
fun getRegisteredWithServiceIds(): Set<RecipientId> {
|
||||
return readableDatabase
|
||||
.select(ID)
|
||||
.from(TABLE_NAME)
|
||||
.where("$REGISTERED = ? and $HIDDEN = ? AND $ACI_COLUMN NOT NULL", 1, Recipient.HiddenState.NOT_HIDDEN.serialize())
|
||||
.run()
|
||||
.readToSet { cursor ->
|
||||
RecipientId.from(cursor.requireLong(ID))
|
||||
}
|
||||
return results
|
||||
}
|
||||
|
||||
fun getSystemContacts(): List<RecipientId> {
|
||||
@@ -3082,7 +3006,7 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
|
||||
return readableDatabase
|
||||
.select(E164)
|
||||
.from(TABLE_NAME)
|
||||
.where("$REGISTERED = ? and $HIDDEN = ? AND $E164 NOT NULL", 1, Recipient.HiddenState.NOT_HIDDEN.serialize())
|
||||
.where("$REGISTERED = ? and $HIDDEN = ? AND $E164 NOT NULL", RegisteredState.REGISTERED.id, Recipient.HiddenState.NOT_HIDDEN.serialize())
|
||||
.run()
|
||||
.readToSet { cursor ->
|
||||
cursor.requireNonNullString(E164)
|
||||
|
||||
@@ -49,7 +49,6 @@ import org.whispersystems.signalservice.api.crypto.InvalidCiphertextException;
|
||||
import org.whispersystems.signalservice.api.crypto.ProfileCipher;
|
||||
import org.whispersystems.signalservice.api.profiles.ProfileAndCredential;
|
||||
import org.whispersystems.signalservice.api.profiles.SignalServiceProfile;
|
||||
import org.whispersystems.signalservice.api.push.ServiceId;
|
||||
import org.whispersystems.signalservice.api.services.ProfileService;
|
||||
import org.whispersystems.signalservice.api.util.ExpiringProfileCredentialUtil;
|
||||
import org.whispersystems.signalservice.internal.ServiceResponse;
|
||||
@@ -60,7 +59,6 @@ import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@@ -286,11 +284,11 @@ public class RetrieveProfileJob extends BaseJob {
|
||||
|
||||
Set<RecipientId> success = SetUtil.difference(recipientIds, operationState.retries);
|
||||
|
||||
Map<RecipientId, ServiceId> newlyRegistered = Stream.of(operationState.profiles)
|
||||
.map(Pair::first)
|
||||
.filterNot(Recipient::isRegistered)
|
||||
.collect(Collectors.toMap(Recipient::getId,
|
||||
r -> r.getServiceId().orElse(null)));
|
||||
Set<RecipientId> newlyRegistered = Stream.of(operationState.profiles)
|
||||
.map(Pair::first)
|
||||
.filterNot(Recipient::isRegistered)
|
||||
.map(Recipient::getId)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
|
||||
//noinspection SimplifyStreamApiCallChains
|
||||
|
||||
@@ -85,7 +85,7 @@ public class UserNotificationMigrationJob extends MigrationJob {
|
||||
return;
|
||||
}
|
||||
|
||||
List<RecipientId> registered = SignalDatabase.recipients().getRegistered();
|
||||
Set<RecipientId> registered = SignalDatabase.recipients().getRegistered();
|
||||
List<RecipientId> systemContacts = SignalDatabase.recipients().getSystemContacts();
|
||||
Set<RecipientId> registeredSystemContacts = SetUtil.intersection(registered, systemContacts);
|
||||
Set<RecipientId> threadRecipients = threadTable.getAllThreadRecipients();
|
||||
|
||||
Reference in New Issue
Block a user