diff --git a/app/src/main/java/org/thoughtcrime/securesms/contacts/sync/ContactDiscovery.kt b/app/src/main/java/org/thoughtcrime/securesms/contacts/sync/ContactDiscovery.kt index 75483c7cf1..b006095e9a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/contacts/sync/ContactDiscovery.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/contacts/sync/ContactDiscovery.kt @@ -140,7 +140,7 @@ object ContactDiscovery { ): RefreshResult { val stopwatch = Stopwatch(descriptor) - val preExistingRegisteredIds: Set = SignalDatabase.recipients.getRegistered().toSet() + val preExistingRegisteredIds: Set = SignalDatabase.recipients.getRegistered() stopwatch.split("pre-existing") val result: RefreshResult = refresh() diff --git a/app/src/main/java/org/thoughtcrime/securesms/contacts/sync/ContactDiscoveryRefreshV2.kt b/app/src/main/java/org/thoughtcrime/securesms/contacts/sync/ContactDiscoveryRefreshV2.kt index 3a764a58e8..5ff5df595c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/contacts/sync/ContactDiscoveryRefreshV2.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/contacts/sync/ContactDiscoveryRefreshV2.kt @@ -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 = mutableSetOf() val rewrites: MutableMap = mutableMapOf() - if (useCompat && response.isCompatResponse()) { - val transformed: Map = response.results.mapValues { entry -> entry.value.aci.orElse(null) } - val fuzzyOutput: OutputResult = 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 = SignalDatabase.recipients.bulkProcessCdsResult(fuzzyOutput.numbers) - - registeredIds += aciMap.keys - rewrites += fuzzyOutput.rewrites - stopwatch.split("process-result") - - val existingIds: Set = SignalDatabase.recipients.getAllPossiblyRegisteredByE164(recipientE164s + rewrites.values) - stopwatch.split("get-ids") - - val inactiveIds: Set = (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 = response.results.mapValues { entry -> CdsV2Result(entry.value.pni, entry.value.aci.orElse(null)) } - val fuzzyOutput: OutputResult = 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 = SignalDatabase.recipients.getAllPossiblyRegisteredByE164(recipientE164s + rewrites.values) - stopwatch.split("get-ids") - - val inactiveIds: Set = (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 = response.results.mapValues { entry -> CdsV2Result(entry.value.pni, entry.value.aci.orElse(null)) } + val fuzzyOutput: OutputResult = 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 = SignalDatabase.recipients.getAllPossiblyRegisteredByE164(recipientE164s + rewrites.values) + stopwatch.split("get-ids") + + val inactiveIds: Set = (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. */ diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/RecipientTable.kt b/app/src/main/java/org/thoughtcrime/securesms/database/RecipientTable.kt index 8797c8bafc..88ba2551ff 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/RecipientTable.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/RecipientTable.kt @@ -2231,82 +2231,13 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da getAndPossiblyMerge(null, record.pni, record.e164) } - fun bulkUpdatedRegisteredStatus(registered: Map, unregistered: Collection) { - writableDatabase.withinTransaction { - val registeredWithServiceId: Set = getRegisteredWithServiceIds() - val needsMarkRegistered: Map = 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): Map { - val db = writableDatabase - val aciMap: MutableMap = 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): Set { + fun bulkProcessCdsResult(mapping: Map): Set { val ids: MutableSet = mutableSetOf() val db = writableDatabase @@ -2324,8 +2255,11 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da return ids } - fun bulkUpdatedRegisteredStatusV2(registered: Set, unregistered: Collection) { + fun bulkUpdatedRegisteredStatus(registered: Set, unregistered: Collection) { writableDatabase.withinTransaction { + val existingRegistered: Set = getRegistered() + val needsMarkRegistered: Set = 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 = 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 { - val results: MutableList = LinkedList() + fun getRegistered(): Set { + val results: MutableSet = 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 { - 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 { @@ -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) diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/RetrieveProfileJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/RetrieveProfileJob.java index e0bcb3d094..94e3c4678b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/RetrieveProfileJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/RetrieveProfileJob.java @@ -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 success = SetUtil.difference(recipientIds, operationState.retries); - Map newlyRegistered = Stream.of(operationState.profiles) - .map(Pair::first) - .filterNot(Recipient::isRegistered) - .collect(Collectors.toMap(Recipient::getId, - r -> r.getServiceId().orElse(null))); + Set newlyRegistered = Stream.of(operationState.profiles) + .map(Pair::first) + .filterNot(Recipient::isRegistered) + .map(Recipient::getId) + .collect(Collectors.toSet()); //noinspection SimplifyStreamApiCallChains diff --git a/app/src/main/java/org/thoughtcrime/securesms/migrations/UserNotificationMigrationJob.java b/app/src/main/java/org/thoughtcrime/securesms/migrations/UserNotificationMigrationJob.java index a76cc380b1..9caf693e09 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/migrations/UserNotificationMigrationJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/migrations/UserNotificationMigrationJob.java @@ -85,7 +85,7 @@ public class UserNotificationMigrationJob extends MigrationJob { return; } - List registered = SignalDatabase.recipients().getRegistered(); + Set registered = SignalDatabase.recipients().getRegistered(); List systemContacts = SignalDatabase.recipients().getSystemContacts(); Set registeredSystemContacts = SetUtil.intersection(registered, systemContacts); Set threadRecipients = threadTable.getAllThreadRecipients();