mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-04-22 18:00:02 +01:00
Add basic CDSv2 database writes and unit tests.
This commit is contained in:
committed by
Cody Henthorne
parent
307be5c75e
commit
dda5ce4809
@@ -121,8 +121,8 @@ open class RecipientDatabase(context: Context, databaseHelper: SignalDatabase) :
|
||||
const val TABLE_NAME = "recipient"
|
||||
|
||||
const val ID = "_id"
|
||||
private const val SERVICE_ID = "uuid"
|
||||
private const val PNI_COLUMN = "pni"
|
||||
const val SERVICE_ID = "uuid"
|
||||
const val PNI_COLUMN = "pni"
|
||||
private const val USERNAME = "username"
|
||||
const val PHONE = "phone"
|
||||
const val EMAIL = "email"
|
||||
@@ -403,6 +403,14 @@ open class RecipientDatabase(context: Context, databaseHelper: SignalDatabase) :
|
||||
return getByColumn(SERVICE_ID, serviceId.toString())
|
||||
}
|
||||
|
||||
/**
|
||||
* Will return a recipient matching the PNI, but only in the explicit [PNI_COLUMN]. This should only be checked in conjunction with [getByServiceId] as a way
|
||||
* to avoid creating a recipient we already merged.
|
||||
*/
|
||||
fun getByPni(pni: PNI): Optional<RecipientId> {
|
||||
return getByColumn(PNI_COLUMN, pni.toString())
|
||||
}
|
||||
|
||||
fun getByUsername(username: String): Optional<RecipientId> {
|
||||
return getByColumn(USERNAME, username)
|
||||
}
|
||||
@@ -2131,7 +2139,11 @@ open class RecipientDatabase(context: Context, databaseHelper: SignalDatabase) :
|
||||
}
|
||||
|
||||
/**
|
||||
* A dumb implementation of processing CDSv2 results. Suitable only for testing and not for actual use.
|
||||
* Processes CDSv2 results, merging recipients as necessary.
|
||||
*
|
||||
* 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> {
|
||||
val ids: MutableSet<RecipientId> = mutableSetOf()
|
||||
@@ -2140,7 +2152,7 @@ open class RecipientDatabase(context: Context, databaseHelper: SignalDatabase) :
|
||||
db.beginTransaction()
|
||||
try {
|
||||
for ((e164, result) in mapping) {
|
||||
ids += getAndPossiblyMerge(result.bestServiceId(), e164, true)
|
||||
ids += processCdsV2Result(e164, result.pni, result.aci)
|
||||
}
|
||||
|
||||
db.setTransactionSuccessful()
|
||||
@@ -2151,6 +2163,47 @@ open class RecipientDatabase(context: Context, databaseHelper: SignalDatabase) :
|
||||
return ids
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
fun processCdsV2Result(e164: String, pni: PNI, aci: ACI?): RecipientId {
|
||||
val byE164: RecipientId? = getByE164(e164).orElse(null)
|
||||
val byPni: RecipientId? = getByServiceId(pni).orElse(null)
|
||||
val byPniOnly: RecipientId? = getByPni(pni).orElse(null)
|
||||
val byAci: RecipientId? = aci?.let { getByServiceId(it).orElse(null) }
|
||||
|
||||
val commonId: RecipientId? = listOf(byE164, byPni, byPniOnly, byAci).commonId()
|
||||
val allRequiredDbFields: List<RecipientId?> = if (aci != null) listOf(byE164, byAci, byPniOnly) else listOf(byE164, byPni, byPniOnly)
|
||||
val allRequiredDbFieldPopulated: Boolean = allRequiredDbFields.all { it != null }
|
||||
|
||||
// All ID's agree and the database is up-to-date
|
||||
if (commonId != null && allRequiredDbFieldPopulated) {
|
||||
return commonId
|
||||
}
|
||||
|
||||
// All ID's agree but we need to update the database
|
||||
if (commonId != null && !allRequiredDbFieldPopulated) {
|
||||
writableDatabase
|
||||
.update(TABLE_NAME)
|
||||
.values(
|
||||
PHONE to e164,
|
||||
SERVICE_ID to (aci ?: pni).toString(),
|
||||
PNI_COLUMN to pni.toString(),
|
||||
REGISTERED to RegisteredState.REGISTERED.id,
|
||||
STORAGE_SERVICE_ID to Base64.encodeBytes(StorageSyncHelper.generateKey())
|
||||
)
|
||||
.where("$ID = ?", commonId)
|
||||
.run()
|
||||
return commonId
|
||||
}
|
||||
|
||||
// Nothing matches
|
||||
if (byE164 == null && byPni == null && byAci == null) {
|
||||
val id: Long = writableDatabase.insert(TABLE_NAME, null, buildContentValuesForCdsInsert(e164, pni, aci))
|
||||
return RecipientId.from(id)
|
||||
}
|
||||
|
||||
throw NotImplementedError("Handle cases where IDs map to different individuals")
|
||||
}
|
||||
|
||||
fun getUninvitedRecipientsForInsights(): List<RecipientId> {
|
||||
val results: MutableList<RecipientId> = LinkedList()
|
||||
val args = arrayOf((System.currentTimeMillis() - TimeUnit.DAYS.toMillis(31)).toString())
|
||||
@@ -2865,6 +2918,18 @@ open class RecipientDatabase(context: Context, databaseHelper: SignalDatabase) :
|
||||
return values
|
||||
}
|
||||
|
||||
private fun buildContentValuesForCdsInsert(e164: String, pni: PNI, aci: ACI?): ContentValues {
|
||||
val serviceId: ServiceId = aci ?: pni
|
||||
return ContentValues().apply {
|
||||
put(PHONE, e164)
|
||||
put(SERVICE_ID, serviceId.toString())
|
||||
put(PNI_COLUMN, pni.toString())
|
||||
put(REGISTERED, RegisteredState.REGISTERED.id)
|
||||
put(STORAGE_SERVICE_ID, Base64.encodeBytes(StorageSyncHelper.generateKey()))
|
||||
put(AVATAR_COLOR, AvatarColor.random().serialize())
|
||||
}
|
||||
}
|
||||
|
||||
private fun getValuesForStorageContact(contact: SignalContactRecord, isInsert: Boolean): ContentValues {
|
||||
return ContentValues().apply {
|
||||
val profileName = ProfileName.fromParts(contact.givenName.orElse(null), contact.familyName.orElse(null))
|
||||
@@ -2940,6 +3005,22 @@ open class RecipientDatabase(context: Context, databaseHelper: SignalDatabase) :
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The common id if all non-null ids are equal, or null if all are null or at least one non-null pair doesn't match.
|
||||
*/
|
||||
private fun Collection<RecipientId?>.commonId(): RecipientId? {
|
||||
val nonNull = this.filterNotNull()
|
||||
if (nonNull.isEmpty()) {
|
||||
return null
|
||||
}
|
||||
|
||||
return if (nonNull.all { it.equals(nonNull[0]) }) {
|
||||
nonNull[0]
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Should only be used for debugging! A very destructive action that clears all known serviceIds.
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user