mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-04-24 02:39:55 +01:00
Basic client usage of CDSHv2.
This provides a basic (read: useful-for-development-yet-broken) client usage of CDSHv2.
This commit is contained in:
@@ -27,6 +27,7 @@ import org.thoughtcrime.securesms.recipients.RecipientId
|
||||
import org.thoughtcrime.securesms.registration.RegistrationUtil
|
||||
import org.thoughtcrime.securesms.sms.IncomingJoinedMessage
|
||||
import org.thoughtcrime.securesms.storage.StorageSyncHelper
|
||||
import org.thoughtcrime.securesms.util.FeatureFlags
|
||||
import org.thoughtcrime.securesms.util.Stopwatch
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences
|
||||
import org.thoughtcrime.securesms.util.Util
|
||||
@@ -69,7 +70,11 @@ object ContactDiscovery {
|
||||
context = context,
|
||||
descriptor = "refresh-all",
|
||||
refresh = {
|
||||
ContactDiscoveryRefreshV1.refreshAll(context)
|
||||
if (FeatureFlags.usePnpCds()) {
|
||||
ContactDiscoveryRefreshV2.refreshAll(context)
|
||||
} else {
|
||||
ContactDiscoveryRefreshV1.refreshAll(context)
|
||||
}
|
||||
},
|
||||
removeSystemContactLinksIfMissing = true,
|
||||
notifyOfNewUsers = notifyOfNewUsers
|
||||
@@ -86,7 +91,11 @@ object ContactDiscovery {
|
||||
context = context,
|
||||
descriptor = "refresh-multiple",
|
||||
refresh = {
|
||||
ContactDiscoveryRefreshV1.refresh(context, recipients)
|
||||
if (FeatureFlags.usePnpCds()) {
|
||||
ContactDiscoveryRefreshV2.refresh(context, recipients)
|
||||
} else {
|
||||
ContactDiscoveryRefreshV1.refresh(context, recipients)
|
||||
}
|
||||
},
|
||||
removeSystemContactLinksIfMissing = false,
|
||||
notifyOfNewUsers = notifyOfNewUsers
|
||||
@@ -101,7 +110,11 @@ object ContactDiscovery {
|
||||
context = context,
|
||||
descriptor = "refresh-single",
|
||||
refresh = {
|
||||
ContactDiscoveryRefreshV1.refresh(context, listOf(recipient))
|
||||
if (FeatureFlags.usePnpCds()) {
|
||||
ContactDiscoveryRefreshV2.refresh(context, listOf(recipient))
|
||||
} else {
|
||||
ContactDiscoveryRefreshV1.refresh(context, listOf(recipient))
|
||||
}
|
||||
},
|
||||
removeSystemContactLinksIfMissing = false,
|
||||
notifyOfNewUsers = notifyOfNewUsers
|
||||
|
||||
@@ -0,0 +1,150 @@
|
||||
package org.thoughtcrime.securesms.contacts.sync
|
||||
|
||||
import android.content.Context
|
||||
import androidx.annotation.WorkerThread
|
||||
import org.signal.contacts.SystemContactsRepository
|
||||
import org.signal.core.util.logging.Log
|
||||
import org.signal.libsignal.zkgroup.profiles.ProfileKey
|
||||
import org.thoughtcrime.securesms.BuildConfig
|
||||
import org.thoughtcrime.securesms.database.RecipientDatabase
|
||||
import org.thoughtcrime.securesms.database.SignalDatabase
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||
import org.thoughtcrime.securesms.phonenumbers.PhoneNumberFormatter
|
||||
import org.thoughtcrime.securesms.recipients.Recipient
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId
|
||||
import org.thoughtcrime.securesms.util.Stopwatch
|
||||
import org.whispersystems.signalservice.api.push.ServiceId
|
||||
import org.whispersystems.signalservice.api.services.CdshV2Service
|
||||
import java.io.IOException
|
||||
import java.lang.NumberFormatException
|
||||
import java.util.Optional
|
||||
|
||||
/**
|
||||
* Performs the CDS refresh using the V2 interface (either CDSH or CDSI) that returns both PNIs and ACIs.
|
||||
*/
|
||||
object ContactDiscoveryRefreshV2 {
|
||||
|
||||
private val TAG = Log.tag(ContactDiscoveryRefreshV2::class.java)
|
||||
|
||||
/**
|
||||
* The maximum number items we will allow in a 'one-off' request.
|
||||
* One-off requests, while much faster, will always deduct the request size from our rate limit.
|
||||
* So we need to be careful about making it too large.
|
||||
* If a request size is over this limit, we will always fall back to a full sync.
|
||||
*/
|
||||
private const val MAXIMUM_ONE_OFF_REQUEST_SIZE = 3
|
||||
|
||||
@Throws(IOException::class)
|
||||
@WorkerThread
|
||||
@Synchronized
|
||||
@JvmStatic
|
||||
fun refreshAll(context: Context): ContactDiscovery.RefreshResult {
|
||||
val stopwatch = Stopwatch("refresh-all")
|
||||
|
||||
val previousE164s: Set<String> = SignalDatabase.cds.getAllE164s()
|
||||
stopwatch.split("previous")
|
||||
|
||||
val recipientE164s: Set<String> = SignalDatabase.recipients.getAllE164s().sanitize()
|
||||
val newRecipientE164s: Set<String> = recipientE164s - previousE164s
|
||||
stopwatch.split("recipient")
|
||||
|
||||
val systemE164s: Set<String> = SystemContactsRepository.getAllDisplayNumbers(context).toE164s(context).sanitize()
|
||||
val newSystemE164s: Set<String> = systemE164s - previousE164s
|
||||
stopwatch.split("system")
|
||||
|
||||
val newE164s: Set<String> = newRecipientE164s + newSystemE164s
|
||||
|
||||
val response: CdshV2Service.Response = makeRequest(
|
||||
previousE164s = previousE164s,
|
||||
newE164s = newE164s,
|
||||
serviceIds = SignalDatabase.recipients.getAllServiceIdProfileKeyPairs(),
|
||||
)
|
||||
stopwatch.split("network")
|
||||
|
||||
SignalStore.misc().cdsToken = response.token
|
||||
SignalDatabase.cds.updateAfterCdsQuery(newE164s, recipientE164s + systemE164s)
|
||||
stopwatch.split("cds-db")
|
||||
|
||||
val registeredIds: Set<RecipientId> = SignalDatabase.recipients.bulkProcessCdsV2Result(
|
||||
response.results
|
||||
.mapValues { entry -> RecipientDatabase.CdsV2Result(entry.value.pni, entry.value.aci.orElse(null)) }
|
||||
)
|
||||
stopwatch.split("recipient-db")
|
||||
|
||||
stopwatch.stop(TAG)
|
||||
|
||||
return ContactDiscovery.RefreshResult(registeredIds, emptyMap())
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
@WorkerThread
|
||||
@Synchronized
|
||||
@JvmStatic
|
||||
fun refresh(context: Context, inputRecipients: List<Recipient>): ContactDiscovery.RefreshResult {
|
||||
val stopwatch = Stopwatch("refresh-some")
|
||||
|
||||
val recipients = inputRecipients.map { it.resolve() }
|
||||
stopwatch.split("resolve")
|
||||
|
||||
val inputIds: Set<RecipientId> = recipients.map { it.id }.toSet()
|
||||
val inputE164s: Set<String> = recipients.mapNotNull { it.e164.orElse(null) }.toSet()
|
||||
|
||||
if (recipients.size > MAXIMUM_ONE_OFF_REQUEST_SIZE) {
|
||||
Log.i(TAG, "List of specific recipients to refresh is too large! (Size: ${recipients.size}). Doing a full refresh instead.")
|
||||
val fullResult: ContactDiscovery.RefreshResult = refreshAll(context)
|
||||
|
||||
return ContactDiscovery.RefreshResult(
|
||||
registeredIds = fullResult.registeredIds.intersect(inputIds),
|
||||
rewrites = fullResult.rewrites.filterKeys { inputE164s.contains(it) }
|
||||
)
|
||||
}
|
||||
|
||||
Log.i(TAG, "Doing a one-off request for ${recipients.size} recipients.")
|
||||
|
||||
val response: CdshV2Service.Response = makeRequest(
|
||||
previousE164s = emptySet(),
|
||||
newE164s = inputE164s,
|
||||
serviceIds = SignalDatabase.recipients.getAllServiceIdProfileKeyPairs()
|
||||
)
|
||||
stopwatch.split("network")
|
||||
|
||||
val registeredIds: Set<RecipientId> = SignalDatabase.recipients.bulkProcessCdsV2Result(
|
||||
response.results
|
||||
.mapValues { entry -> RecipientDatabase.CdsV2Result(entry.value.pni, entry.value.aci.orElse(null)) }
|
||||
)
|
||||
stopwatch.split("recipient-db")
|
||||
|
||||
stopwatch.stop(TAG)
|
||||
|
||||
return ContactDiscovery.RefreshResult(registeredIds, emptyMap())
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
private fun makeRequest(previousE164s: Set<String>, newE164s: Set<String>, serviceIds: Map<ServiceId, ProfileKey>): CdshV2Service.Response {
|
||||
return ApplicationDependencies.getSignalServiceAccountManager().getRegisteredUsersWithCdshV2(
|
||||
previousE164s,
|
||||
newE164s,
|
||||
serviceIds,
|
||||
Optional.ofNullable(SignalStore.misc().cdsToken),
|
||||
BuildConfig.CDSH_PUBLIC_KEY,
|
||||
BuildConfig.CDSH_CODE_HASH
|
||||
)
|
||||
}
|
||||
|
||||
private fun Set<String>.toE164s(context: Context): Set<String> {
|
||||
return this.map { PhoneNumberFormatter.get(context).format(it) }.toSet()
|
||||
}
|
||||
|
||||
private fun Set<String>.sanitize(): Set<String> {
|
||||
return this
|
||||
.filter {
|
||||
try {
|
||||
it.startsWith("+") && it.length > 1 && it[1] != '0' && it.toLong() > 0
|
||||
} catch (e: NumberFormatException) {
|
||||
false
|
||||
}
|
||||
}
|
||||
.toSet()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user