Use strongly-typed ACIs and PNIs everywhere.

This commit is contained in:
Greyson Parrelli
2023-07-28 12:58:04 -04:00
parent 7ff4a82755
commit 82906aee58
146 changed files with 1416 additions and 1194 deletions

View File

@@ -34,7 +34,7 @@ import org.thoughtcrime.securesms.jobs.CallSyncEventJob
import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.recipients.RecipientId
import org.thoughtcrime.securesms.service.webrtc.links.CallLinkRoomId
import org.whispersystems.signalservice.api.push.ServiceId
import org.whispersystems.signalservice.api.push.ServiceId.ACI
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.CallEvent
import java.util.UUID
import java.util.concurrent.TimeUnit
@@ -605,11 +605,11 @@ class CallTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTabl
fun insertOrUpdateGroupCallFromRingState(
ringId: Long,
groupRecipientId: RecipientId,
ringerUUID: UUID,
ringerAci: ACI,
dateReceived: Long,
ringState: RingUpdate
) {
val ringerRecipient = Recipient.externalPush(ServiceId.from(ringerUUID))
val ringerRecipient = Recipient.externalPush(ringerAci)
handleGroupRingState(ringId, groupRecipientId, ringerRecipient.id, dateReceived, ringState)
}

View File

@@ -55,6 +55,7 @@ import org.whispersystems.signalservice.api.groupsv2.GroupChangeReconstruct
import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentPointer
import org.whispersystems.signalservice.api.push.DistributionId
import org.whispersystems.signalservice.api.push.ServiceId
import org.whispersystems.signalservice.api.push.ServiceId.ACI
import org.whispersystems.signalservice.api.util.UuidUtil
import java.io.Closeable
import java.security.SecureRandom
@@ -1215,7 +1216,7 @@ class GroupTable(context: Context?, databaseHelper: SignalDatabase?) : DatabaseT
val serviceId = recipient.serviceId
return if (serviceId.isPresent) {
DecryptedGroupUtil.findMemberByUuid(decryptedGroup.membersList, serviceId.get().uuid())
DecryptedGroupUtil.findMemberByUuid(decryptedGroup.membersList, serviceId.get().rawUuid)
.map { it.role == Member.Role.ADMINISTRATOR }
.orElse(false)
} else {
@@ -1232,7 +1233,7 @@ class GroupTable(context: Context?, databaseHelper: SignalDatabase?) : DatabaseT
return MemberLevel.NOT_A_MEMBER
}
var memberLevel: Optional<MemberLevel> = DecryptedGroupUtil.findMemberByUuid(decryptedGroup.membersList, serviceId.get().uuid())
var memberLevel: Optional<MemberLevel> = DecryptedGroupUtil.findMemberByUuid(decryptedGroup.membersList, serviceId.get().rawUuid)
.map { member ->
if (member.role == Member.Role.ADMINISTRATOR) {
MemberLevel.ADMINISTRATOR
@@ -1242,12 +1243,12 @@ class GroupTable(context: Context?, databaseHelper: SignalDatabase?) : DatabaseT
}
if (memberLevel.isAbsent()) {
memberLevel = DecryptedGroupUtil.findPendingByUuid(decryptedGroup.pendingMembersList, serviceId.get().uuid())
memberLevel = DecryptedGroupUtil.findPendingByUuid(decryptedGroup.pendingMembersList, serviceId.get().rawUuid)
.map { MemberLevel.PENDING_MEMBER }
}
if (memberLevel.isAbsent()) {
memberLevel = DecryptedGroupUtil.findRequestingByUuid(decryptedGroup.requestingMembersList, serviceId.get().uuid())
memberLevel = DecryptedGroupUtil.findRequestingByUuid(decryptedGroup.requestingMembersList, serviceId.get().rawUuid)
.map { _ -> MemberLevel.REQUESTING_MEMBER }
}
@@ -1264,7 +1265,7 @@ class GroupTable(context: Context?, databaseHelper: SignalDatabase?) : DatabaseT
fun getMemberRecipientIds(memberSet: MemberSet): List<RecipientId> {
val includeSelf = memberSet.includeSelf
val selfUuid = SignalStore.account().requireAci().uuid()
val selfAciUuid = SignalStore.account().requireAci().rawUuid
val recipients: MutableList<RecipientId> = ArrayList(decryptedGroup.membersCount + decryptedGroup.pendingMembersCount)
var unknownMembers = 0
@@ -1273,8 +1274,8 @@ class GroupTable(context: Context?, databaseHelper: SignalDatabase?) : DatabaseT
for (uuid in DecryptedGroupUtil.toUuidList(decryptedGroup.membersList)) {
if (UuidUtil.UNKNOWN_UUID == uuid) {
unknownMembers++
} else if (includeSelf || selfUuid != uuid) {
recipients += RecipientId.from(ServiceId.from(uuid))
} else if (includeSelf || selfAciUuid != uuid) {
recipients += RecipientId.from(ACI.from(uuid))
}
}
@@ -1282,8 +1283,8 @@ class GroupTable(context: Context?, databaseHelper: SignalDatabase?) : DatabaseT
for (uuid in DecryptedGroupUtil.pendingToUuidList(decryptedGroup.pendingMembersList)) {
if (UuidUtil.UNKNOWN_UUID == uuid) {
unknownPending++
} else if (includeSelf || selfUuid != uuid) {
recipients += RecipientId.from(ServiceId.from(uuid))
} else if (includeSelf || selfAciUuid != uuid) {
recipients += RecipientId.from(ACI.from(uuid))
}
}
}
@@ -1301,7 +1302,7 @@ class GroupTable(context: Context?, databaseHelper: SignalDatabase?) : DatabaseT
.asSequence()
.map { UuidUtil.fromByteStringOrNull(it.uuid) }
.filterNotNull()
.map { ServiceId.from(it) }
.map { ACI.from(it) }
.sortedBy { it.toString() }
.toList()
}
@@ -1376,8 +1377,8 @@ class GroupTable(context: Context?, databaseHelper: SignalDatabase?) : DatabaseT
private fun gv2GroupActive(decryptedGroup: DecryptedGroup): Boolean {
val aci = SignalStore.account().requireAci()
return DecryptedGroupUtil.findMemberByUuid(decryptedGroup.membersList, aci.uuid()).isPresent ||
DecryptedGroupUtil.findPendingByUuid(decryptedGroup.pendingMembersList, aci.uuid()).isPresent
return DecryptedGroupUtil.findMemberByUuid(decryptedGroup.membersList, aci.rawUuid).isPresent ||
DecryptedGroupUtil.findPendingByUuid(decryptedGroup.pendingMembersList, aci.rawUuid).isPresent
}
private fun List<UUID>.toRecipientIds(): MutableList<RecipientId> {
@@ -1405,7 +1406,7 @@ class GroupTable(context: Context?, databaseHelper: SignalDatabase?) : DatabaseT
Log.w(TAG, "Saw an unknown UUID when mapping to RecipientIds!")
null
} else {
val id = RecipientId.from(ServiceId.from(uuid))
val id = RecipientId.from(ACI.from(uuid))
val remapped = RemappedRecords.getInstance().getRecipient(id)
if (remapped.isPresent) {
Log.w(TAG, "Saw that $id remapped to $remapped. Using the mapping.")

View File

@@ -70,6 +70,14 @@ class IdentityTable internal constructor(context: Context?, databaseHelper: Sign
"""
}
fun getIdentityStoreRecord(serviceId: ServiceId?): IdentityStoreRecord? {
return if (serviceId != null) {
getIdentityStoreRecord(serviceId.toString())
} else {
null
}
}
fun getIdentityStoreRecord(addressName: String): IdentityStoreRecord? {
readableDatabase
.select()

View File

@@ -96,7 +96,7 @@ public final class MentionUtil {
BodyRangeList.Builder builder = BodyRangeList.newBuilder();
for (Mention mention : mentions) {
String uuid = Recipient.resolved(mention.getRecipientId()).requireServiceId().toString();
String uuid = Recipient.resolved(mention.getRecipientId()).requireAci().toString();
builder.addRanges(BodyRangeList.BodyRange.newBuilder()
.setMentionUuid(uuid)
.setStart(mention.getStart())

View File

@@ -142,6 +142,7 @@ import org.thoughtcrime.securesms.util.Util
import org.thoughtcrime.securesms.util.isStory
import org.whispersystems.signalservice.api.messages.multidevice.ReadMessage
import org.whispersystems.signalservice.api.push.ServiceId
import org.whispersystems.signalservice.api.push.ServiceId.ACI
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage
import java.io.Closeable
import java.io.IOException
@@ -845,7 +846,7 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
val threadId = threads.getOrCreateThreadIdFor(recipient)
val messageId: MessageId = writableDatabase.withinTransaction { db ->
val self = Recipient.self()
val markRead = joinedUuids.contains(self.requireServiceId().uuid()) || self.id == sender
val markRead = joinedUuids.contains(self.requireServiceId().rawUuid) || self.id == sender
val updateDetails: ByteArray = GroupCallUpdateDetails.newBuilder()
.setEraId(eraId)
.setStartedCallUuid(Recipient.resolved(sender).requireServiceId().toString())
@@ -919,7 +920,7 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
}
val updateDetail = GroupCallUpdateDetailsUtil.parse(message.body)
val containsSelf = joinedUuids.contains(SignalStore.account().requireAci().uuid())
val containsSelf = joinedUuids.contains(SignalStore.account().requireAci().rawUuid)
val sameEraId = updateDetail.eraId == eraId && !Util.isEmpty(eraId)
val inCallUuids = if (sameEraId) joinedUuids.map { it.toString() } else emptyList()
val contentValues = contentValuesOf(
@@ -954,7 +955,7 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
MmsReader(cursor).use { reader ->
val record = reader.getNext() ?: return@withinTransaction false
val groupCallUpdateDetails = GroupCallUpdateDetailsUtil.parse(record.body)
val containsSelf = peekJoinedUuids.contains(SignalStore.account().requireAci().uuid())
val containsSelf = peekJoinedUuids.contains(SignalStore.account().requireAci().rawUuid)
val sameEraId = groupCallUpdateDetails.eraId == peekGroupCallEraId && !Util.isEmpty(peekGroupCallEraId)
val inCallUuids = if (sameEraId) {
@@ -3088,9 +3089,10 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
val members: MutableSet<RecipientId> = mutableSetOf()
if (message.isGroupUpdate && message.isV2Group) {
// TODO [greyson][ServiceId] pending members could be ACI's or PNI's
members += message.requireGroupV2Properties().allActivePendingAndRemovedMembers
.distinct()
.map { uuid -> RecipientId.from(ServiceId.from(uuid)) }
.map { uuid -> RecipientId.from(ACI.from(uuid)) }
.toList()
members -= Recipient.self().id

View File

@@ -3,8 +3,9 @@ package org.thoughtcrime.securesms.database
import app.cash.exhaustive.Exhaustive
import org.thoughtcrime.securesms.database.model.RecipientRecord
import org.thoughtcrime.securesms.recipients.RecipientId
import org.whispersystems.signalservice.api.push.ACI
import org.whispersystems.signalservice.api.push.PNI
import org.whispersystems.signalservice.api.push.ServiceId
import org.whispersystems.signalservice.api.push.ServiceId.ACI
import org.whispersystems.signalservice.api.push.ServiceId.PNI
/**
* Encapsulates data around processing a tuple of user data into a user entry in [RecipientTable].
@@ -15,18 +16,20 @@ data class PnpDataSet(
val pni: PNI?,
val aci: ACI?,
val byE164: RecipientId?,
val byPniSid: RecipientId?,
val byPniOnly: RecipientId?,
val byAciSid: RecipientId?,
val byPni: RecipientId?,
val byAci: RecipientId?,
val e164Record: RecipientRecord? = null,
val pniSidRecord: RecipientRecord? = null,
val aciSidRecord: RecipientRecord? = null
val pniRecord: RecipientRecord? = null,
val aciRecord: RecipientRecord? = null
) {
/**
* @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.
*/
val commonId: RecipientId? = findCommonId(listOf(byE164, byPniSid, byPniOnly, byAciSid))
val commonId: RecipientId? = findCommonId(listOf(byE164, byPni, byAci))
/** The ID that would be used to contact this user. */
val serviceId: ServiceId? = aci ?: pni
fun MutableSet<RecipientRecord>.replace(recipientId: RecipientId, update: (RecipientRecord) -> RecipientRecord) {
val toUpdate = this.first { it.id == recipientId }
@@ -43,7 +46,7 @@ data class PnpDataSet(
return this
}
val records: MutableSet<RecipientRecord> = listOfNotNull(e164Record, pniSidRecord, aciSidRecord).toMutableSet()
val records: MutableSet<RecipientRecord> = listOfNotNull(e164Record, pniRecord, aciRecord).toMutableSet()
for (operation in operations) {
@Exhaustive
@@ -55,16 +58,12 @@ data class PnpDataSet(
records.replace(operation.recipientId) { record ->
record.copy(
pni = null,
serviceId = if (record.sidIsPni()) {
null
} else {
record.serviceId
}
aci = record.aci
)
}
}
is PnpOperation.SetAci -> {
records.replace(operation.recipientId) { it.copy(serviceId = operation.aci) }
records.replace(operation.recipientId) { it.copy(aci = operation.aci) }
}
is PnpOperation.SetE164 -> {
records.replace(operation.recipientId) { it.copy(e164 = operation.e164) }
@@ -72,12 +71,7 @@ data class PnpDataSet(
is PnpOperation.SetPni -> {
records.replace(operation.recipientId) { record ->
record.copy(
pni = operation.pni,
serviceId = if (record.sidIsPni()) {
operation.pni
} else {
record.serviceId ?: operation.pni
}
pni = operation.pni
)
}
}
@@ -89,7 +83,7 @@ data class PnpDataSet(
primary.copy(
e164 = primary.e164 ?: secondary.e164,
pni = primary.pni ?: secondary.pni,
serviceId = primary.serviceId ?: secondary.serviceId
aci = primary.aci ?: secondary.aci
)
}
@@ -101,20 +95,16 @@ data class PnpDataSet(
}
val newE164Record = if (e164 != null) records.firstOrNull { it.e164 == e164 } else null
val newPniSidRecord = if (pni != null) records.firstOrNull { it.serviceId == pni } else null
val newAciSidRecord = if (aci != null) records.firstOrNull { it.serviceId == aci } else null
val newPniRecord = if (pni != null) records.firstOrNull { it.pni == pni } else null
val newAciRecord = if (aci != null) records.firstOrNull { it.aci == aci } else null
return PnpDataSet(
e164 = e164,
pni = pni,
aci = aci,
return this.copy(
byE164 = newE164Record?.id,
byPniSid = newPniSidRecord?.id,
byPniOnly = byPniOnly,
byAciSid = newAciSidRecord?.id,
byPni = newPniRecord?.id,
byAci = newAciRecord?.id,
e164Record = newE164Record,
pniSidRecord = newPniSidRecord,
aciSidRecord = newAciSidRecord
pniRecord = newPniRecord,
aciRecord = newAciRecord
)
}

View File

@@ -116,12 +116,20 @@ class SenderKeySharedTable internal constructor(context: Context?, databaseHelpe
fun deleteAllFor(recipientId: RecipientId) {
val recipient = Recipient.resolved(recipientId)
if (recipient.hasServiceId()) {
writableDatabase
.delete(TABLE_NAME)
.where("$ADDRESS = ?", recipient.requireServiceId().toString())
.run()
if (recipient.hasAci()) {
writableDatabase
.delete(TABLE_NAME)
.where("$ADDRESS = ?", recipient.requireAci().toString())
.run()
}
if (recipient.hasPni()) {
writableDatabase
.delete(TABLE_NAME)
.where("$ADDRESS = ?", recipient.requirePni().toString())
.run()
}
} else {
Log.w(TAG, "Recipient doesn't have a UUID! $recipientId")
Log.w(TAG, "Recipient doesn't have a ServiceId! $recipientId")
}
}

View File

@@ -63,7 +63,6 @@ import org.thoughtcrime.securesms.util.JsonUtils.SaneJSONObject
import org.thoughtcrime.securesms.util.LRUCache
import org.thoughtcrime.securesms.util.TextSecurePreferences
import org.thoughtcrime.securesms.util.isScheduled
import org.whispersystems.signalservice.api.push.ServiceId
import org.whispersystems.signalservice.api.storage.SignalAccountRecord
import org.whispersystems.signalservice.api.storage.SignalAccountRecord.PinnedConversation
import org.whispersystems.signalservice.api.storage.SignalContactRecord
@@ -1700,7 +1699,7 @@ class ThreadTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTa
if (threadRecipient.isPushV2Group) {
val inviteAddState = record.gv2AddInviteState
if (inviteAddState != null) {
val from = RecipientId.from(ServiceId.from(inviteAddState.addedOrInvitedBy))
val from = RecipientId.from(inviteAddState.addedOrInvitedBy)
return if (inviteAddState.isInvited) {
Log.i(TAG, "GV2 invite message request from $from")
Extra.forGroupV2invite(from, authorId)

View File

@@ -55,6 +55,7 @@ import org.thoughtcrime.securesms.database.helpers.migration.V196_BackCallLinksW
import org.thoughtcrime.securesms.database.helpers.migration.V197_DropAvatarColorFromCallLinks
import org.thoughtcrime.securesms.database.helpers.migration.V198_AddMacDigestColumn
import org.thoughtcrime.securesms.database.helpers.migration.V199_AddThreadActiveColumn
import org.thoughtcrime.securesms.database.helpers.migration.V200_ResetPniColumn
/**
* Contains all of the database migrations for [SignalDatabase]. Broken into a separate file for cleanliness.
@@ -63,7 +64,7 @@ object SignalDatabaseMigrations {
val TAG: String = Log.tag(SignalDatabaseMigrations.javaClass)
const val DATABASE_VERSION = 199
const val DATABASE_VERSION = 200
@JvmStatic
fun migrate(context: Application, db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
@@ -270,6 +271,10 @@ object SignalDatabaseMigrations {
if (oldVersion < 199) {
V199_AddThreadActiveColumn.migrate(context, db, oldVersion, newVersion)
}
if (oldVersion < 200) {
V200_ResetPniColumn.migrate(context, db, oldVersion, newVersion)
}
}
@JvmStatic

View File

@@ -47,8 +47,8 @@ import org.thoughtcrime.securesms.util.FileUtils
import org.thoughtcrime.securesms.util.ServiceUtil
import org.thoughtcrime.securesms.util.Triple
import org.thoughtcrime.securesms.util.Util
import org.whispersystems.signalservice.api.push.ACI
import org.whispersystems.signalservice.api.push.DistributionId
import org.whispersystems.signalservice.api.push.ServiceId.ACI
import java.io.File
import java.util.LinkedList
import java.util.Locale

View File

@@ -16,7 +16,7 @@ import org.signal.core.util.requireString
import org.thoughtcrime.securesms.database.KeyValueDatabase
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
import org.thoughtcrime.securesms.recipients.RecipientId
import org.whispersystems.signalservice.api.push.ACI
import org.whispersystems.signalservice.api.push.ServiceId.ACI
/**
* This is a combination of the edit message and message recipient migrations (would have been V185 and v186), but as they

View File

@@ -16,7 +16,7 @@ import org.signal.core.util.requireString
import org.thoughtcrime.securesms.database.KeyValueDatabase
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
import org.thoughtcrime.securesms.recipients.RecipientId
import org.whispersystems.signalservice.api.push.ACI
import org.whispersystems.signalservice.api.push.ServiceId.ACI
/**
* This is a fix for a bad situation that could happen during [V185_MessageRecipientsAndEditMessageMigration].

View File

@@ -0,0 +1,20 @@
/*
* Copyright 2023 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.thoughtcrime.securesms.database.helpers.migration
import android.app.Application
import net.zetetic.database.sqlcipher.SQLiteDatabase
/**
* This updates the PNI column to have the proper serialized format.
*/
@Suppress("ClassName")
object V200_ResetPniColumn : SignalDatabaseMigration {
override fun migrate(context: Application, db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
db.execSQL("UPDATE recipient SET pni = 'PNI:' || pni WHERE pni NOT NULL")
db.execSQL("ALTER TABLE recipient RENAME COLUMN uuid to aci")
}
}

View File

@@ -12,7 +12,7 @@ import org.thoughtcrime.securesms.keyvalue.SignalStore;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.util.DateUtils;
import org.whispersystems.signalservice.api.push.ACI;
import org.whispersystems.signalservice.api.push.ServiceId.ACI;
import org.whispersystems.signalservice.api.push.ServiceId;
import java.util.ArrayList;
@@ -31,7 +31,7 @@ public class GroupCallUpdateMessageFactory implements UpdateDescription.Spannabl
private final ACI selfAci;
public GroupCallUpdateMessageFactory(@NonNull Context context,
@NonNull List<ServiceId> joinedMembers,
@NonNull List<ACI> joinedMembers,
boolean withTime,
@NonNull GroupCallUpdateDetails groupCallUpdateDetails)
{

View File

@@ -176,7 +176,7 @@ class GroupRecord(
if (isV2Group) {
val serviceId = recipient.serviceId
if (serviceId.isPresent) {
return DecryptedGroupUtil.findPendingByUuid(requireV2GroupProperties().decryptedGroup.pendingMembersList, serviceId.get().uuid())
return DecryptedGroupUtil.findPendingByUuid(requireV2GroupProperties().decryptedGroup.pendingMembersList, serviceId.get().rawUuid)
.isPresent
}
}

View File

@@ -33,7 +33,7 @@ import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.util.ExpirationUtil;
import org.thoughtcrime.securesms.util.SpanUtil;
import org.whispersystems.signalservice.api.groupsv2.DecryptedGroupUtil;
import org.whispersystems.signalservice.api.push.ServiceId;
import org.whispersystems.signalservice.api.push.ServiceId.ACI;
import org.whispersystems.signalservice.api.push.ServiceIds;
import org.whispersystems.signalservice.api.util.UuidUtil;
@@ -72,9 +72,9 @@ final class GroupsV2UpdateMessageProducer {
* When the revision of the group is 0, the change is very noisy and only the editor is useful.
*/
UpdateDescription describeNewGroup(@NonNull DecryptedGroup group, @NonNull DecryptedGroupChange decryptedGroupChange) {
Optional<DecryptedPendingMember> selfPending = DecryptedGroupUtil.findPendingByUuid(group.getPendingMembersList(), selfIds.getAci().uuid());
Optional<DecryptedPendingMember> selfPending = DecryptedGroupUtil.findPendingByUuid(group.getPendingMembersList(), selfIds.getAci().getRawUuid());
if (!selfPending.isPresent() && selfIds.getPni() != null) {
selfPending = DecryptedGroupUtil.findPendingByUuid(group.getPendingMembersList(), selfIds.getPni().uuid());
selfPending = DecryptedGroupUtil.findPendingByUuid(group.getPendingMembersList(), selfIds.getPni().getRawUuid());
}
if (selfPending.isPresent()) {
@@ -90,8 +90,8 @@ final class GroupsV2UpdateMessageProducer {
}
}
if (DecryptedGroupUtil.findMemberByUuid(group.getMembersList(), selfIds.getAci().uuid()).isPresent() ||
(selfIds.getPni() != null && DecryptedGroupUtil.findMemberByUuid(group.getMembersList(), selfIds.getPni().uuid()).isPresent()))
if (DecryptedGroupUtil.findMemberByUuid(group.getMembersList(), selfIds.getAci().getRawUuid()).isPresent() ||
(selfIds.getPni() != null && DecryptedGroupUtil.findMemberByUuid(group.getMembersList(), selfIds.getPni().getRawUuid()).isPresent()))
{
return updateDescription(context.getString(R.string.MessageRecord_you_joined_the_group), R.drawable.ic_update_group_add_16);
} else {
@@ -820,11 +820,11 @@ final class GroupsV2UpdateMessageProducer {
@NonNull ByteString uuid1Bytes,
@DrawableRes int iconResource)
{
ServiceId serviceId = ServiceId.fromByteStringOrUnknown(uuid1Bytes);
RecipientId recipientId = RecipientId.from(serviceId);
ACI aci = ACI.parseOrUnknown(uuid1Bytes);
RecipientId recipientId = RecipientId.from(aci);
return UpdateDescription.mentioning(
Collections.singletonList(serviceId),
Collections.singletonList(aci),
() -> {
List<RecipientId> recipientIdList = Collections.singletonList(recipientId);
String templateString = context.getString(stringRes, makePlaceholders(recipientIdList, null));
@@ -839,14 +839,14 @@ final class GroupsV2UpdateMessageProducer {
@NonNull ByteString uuid2Bytes,
@DrawableRes int iconResource)
{
ServiceId sid1 = ServiceId.fromByteStringOrUnknown(uuid1Bytes);
ServiceId sid2 = ServiceId.fromByteStringOrUnknown(uuid2Bytes);
ACI aci1 = ACI.parseOrUnknown(uuid1Bytes);
ACI aci2 = ACI.parseOrUnknown(uuid2Bytes);
RecipientId recipientId1 = RecipientId.from(sid1);
RecipientId recipientId2 = RecipientId.from(sid2);
RecipientId recipientId1 = RecipientId.from(aci1);
RecipientId recipientId2 = RecipientId.from(aci2);
return UpdateDescription.mentioning(
Arrays.asList(sid1, sid2),
Arrays.asList(aci1, aci2),
() -> {
List<RecipientId> recipientIdList = Arrays.asList(recipientId1, recipientId2);
String templateString = context.getString(stringRes, makePlaceholders(recipientIdList, null));
@@ -862,11 +862,11 @@ final class GroupsV2UpdateMessageProducer {
@NonNull Object formatArg,
@DrawableRes int iconResource)
{
ServiceId serviceId = ServiceId.fromByteStringOrUnknown(uuid1Bytes);
RecipientId recipientId = RecipientId.from(serviceId);
ACI aci = ACI.parseOrUnknown(uuid1Bytes);
RecipientId recipientId = RecipientId.from(aci);
return UpdateDescription.mentioning(
Collections.singletonList(serviceId),
Collections.singletonList(aci),
() -> {
List<RecipientId> recipientIdList = Collections.singletonList(recipientId);
String templateString = context.getString(stringRes, makePlaceholders(recipientIdList, Collections.singletonList(formatArg)));
@@ -883,11 +883,11 @@ final class GroupsV2UpdateMessageProducer {
@NonNull Object formatArg,
@DrawableRes int iconResource)
{
ServiceId serviceId = ServiceId.fromByteStringOrUnknown(uuid1Bytes);
RecipientId recipientId = RecipientId.from(serviceId);
ACI aci = ACI.parseOrUnknown(uuid1Bytes);
RecipientId recipientId = RecipientId.from(aci);
return UpdateDescription.mentioning(
Collections.singletonList(serviceId),
Collections.singletonList(aci),
() -> {
List<RecipientId> recipientIdList = Collections.singletonList(recipientId);
String templateString = context.getResources().getQuantityString(stringRes, quantity, makePlaceholders(recipientIdList, Collections.singletonList(formatArg)));

View File

@@ -64,6 +64,7 @@ import org.thoughtcrime.securesms.util.ExpirationUtil;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.thoughtcrime.securesms.util.Util;
import org.whispersystems.signalservice.api.groupsv2.DecryptedGroupUtil;
import org.whispersystems.signalservice.api.push.ServiceId.ACI;
import org.whispersystems.signalservice.api.push.ServiceId;
import org.whispersystems.signalservice.api.util.UuidUtil;
@@ -307,7 +308,7 @@ public abstract class MessageRecord extends DisplayRecord {
private static boolean selfCreatedGroup(@NonNull DecryptedGroupChange change) {
return change.getRevision() == 0 &&
change.getEditor().equals(UuidUtil.toByteString(SignalStore.account().requireAci().uuid()));
change.getEditor().equals(UuidUtil.toByteString(SignalStore.account().requireAci().getRawUuid()));
}
public static @NonNull UpdateDescription getGv2ChangeDescription(@NonNull Context context, @NonNull String body, @Nullable Consumer<RecipientId> recipientClickHandler) {
@@ -345,13 +346,13 @@ public abstract class MessageRecord extends DisplayRecord {
}
DecryptedGroup groupState = decryptedGroupV2Context.getGroupState();
boolean invited = DecryptedGroupUtil.findPendingByUuid(groupState.getPendingMembersList(), SignalStore.account().requireAci().uuid()).isPresent();
boolean invited = DecryptedGroupUtil.findPendingByUuid(groupState.getPendingMembersList(), SignalStore.account().requireAci().getRawUuid()).isPresent();
if (decryptedGroupV2Context.hasChange()) {
UUID changeEditor = UuidUtil.fromByteStringOrNull(decryptedGroupV2Context.getChange().getEditor());
if (changeEditor != null) {
return new InviteAddState(invited, changeEditor);
return new InviteAddState(invited, ACI.from(changeEditor));
}
}
@@ -367,7 +368,7 @@ public abstract class MessageRecord extends DisplayRecord {
@NonNull Function<Recipient, String> stringGenerator,
@DrawableRes int iconResource)
{
return UpdateDescription.mentioning(Collections.singletonList(recipient.getServiceId().orElse(ServiceId.UNKNOWN)),
return UpdateDescription.mentioning(Collections.singletonList(recipient.getAci().orElse(ACI.UNKNOWN)),
() -> new SpannableString(stringGenerator.apply(recipient.resolve())),
iconResource);
}
@@ -435,10 +436,10 @@ public abstract class MessageRecord extends DisplayRecord {
public static @NonNull UpdateDescription getGroupCallUpdateDescription(@NonNull Context context, @NonNull String body, boolean withTime) {
GroupCallUpdateDetails groupCallUpdateDetails = GroupCallUpdateDetailsUtil.parse(body);
List<ServiceId> joinedMembers = Stream.of(groupCallUpdateDetails.getInCallUuidsList())
List<ACI> joinedMembers = Stream.of(groupCallUpdateDetails.getInCallUuidsList())
.map(UuidUtil::parseOrNull)
.withoutNulls()
.map(ServiceId::from)
.map(ACI::from)
.toList();
UpdateDescription.SpannableFactory stringFactory = new GroupCallUpdateMessageFactory(context, joinedMembers, withTime, groupCallUpdateDetails);
@@ -467,7 +468,7 @@ public abstract class MessageRecord extends DisplayRecord {
return false;
}
return isGroupV2JoinRequest(UuidUtil.toByteString(serviceId.uuid()));
return isGroupV2JoinRequest(UuidUtil.toByteString(serviceId.getRawUuid()));
}
public boolean isGroupV2JoinRequest(@NonNull ByteString uuid) {
@@ -489,7 +490,7 @@ public abstract class MessageRecord extends DisplayRecord {
DecryptedGroupChange change = decryptedGroupV2Context.getChange();
return change.getNewRequestingMembersCount() > 0 &&
change.getDeleteRequestingMembersCount() > 0 &&
(serviceId == null || change.getEditor().equals(UuidUtil.toByteString(serviceId.uuid())));
(serviceId == null || change.getEditor().equals(UuidUtil.toByteString(serviceId.getRawUuid())));
}
return false;
}
@@ -748,14 +749,14 @@ public abstract class MessageRecord extends DisplayRecord {
public static final class InviteAddState {
private final boolean invited;
private final UUID addedOrInvitedBy;
private final ACI addedOrInvitedBy;
public InviteAddState(boolean invited, @NonNull UUID addedOrInvitedBy) {
public InviteAddState(boolean invited, @NonNull ACI addedOrInvitedBy) {
this.invited = invited;
this.addedOrInvitedBy = addedOrInvitedBy;
}
public @NonNull UUID getAddedOrInvitedBy() {
public @NonNull ACI getAddedOrInvitedBy() {
return addedOrInvitedBy;
}

View File

@@ -19,8 +19,9 @@ import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.recipients.RecipientId
import org.thoughtcrime.securesms.service.webrtc.links.CallLinkRoomId
import org.thoughtcrime.securesms.wallpaper.ChatWallpaper
import org.whispersystems.signalservice.api.push.PNI
import org.whispersystems.signalservice.api.push.ServiceId
import org.whispersystems.signalservice.api.push.ServiceId.ACI
import org.whispersystems.signalservice.api.push.ServiceId.PNI
import java.util.Optional
/**
@@ -28,7 +29,7 @@ import java.util.Optional
*/
data class RecipientRecord(
val id: RecipientId,
val serviceId: ServiceId?,
val aci: ACI?,
val pni: PNI?,
val username: String?,
val e164: String?,
@@ -89,21 +90,23 @@ data class RecipientRecord(
}
fun e164Only(): Boolean {
return this.e164 != null && this.serviceId == null
return this.e164 != null && this.aci == null
}
fun sidOnly(sid: ServiceId): Boolean {
return this.e164 == null && this.serviceId == sid && (this.pni == null || this.pni == sid)
fun pniOnly(): Boolean {
return this.e164 == null && this.aci == null && this.pni != null
}
fun sidIsPni(): Boolean {
return this.serviceId != null && this.pni != null && this.serviceId == this.pni
fun aciOnly(): Boolean {
return this.e164 == null && this.pni == null && this.aci != null
}
fun pniAndAci(): Boolean {
return this.serviceId != null && this.pni != null && this.serviceId != this.pni
return this.aci != null && this.pni != null
}
val serviceId: ServiceId? = this.aci ?: this.pni
/**
* A bundle of data that's only necessary when syncing to storage service, not for a
* [Recipient].

View File

@@ -11,6 +11,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.WorkerThread;
import org.whispersystems.signalservice.api.push.ServiceId.ACI;
import org.whispersystems.signalservice.api.push.ServiceId;
import java.util.Collection;
@@ -18,6 +19,7 @@ import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
/**
* Contains a list of people mentioned in an update message and a function to create the update message.
@@ -28,14 +30,14 @@ public final class UpdateDescription {
Spannable create();
}
private final Collection<ServiceId> mentioned;
private final SpannableFactory stringFactory;
private final Spannable staticString;
private final int lightIconResource;
private final int lightTint;
private final int darkTint;
private final Collection<ACI> mentioned;
private final SpannableFactory stringFactory;
private final Spannable staticString;
private final int lightIconResource;
private final int lightTint;
private final int darkTint;
private UpdateDescription(@NonNull Collection<ServiceId> mentioned,
private UpdateDescription(@NonNull Collection<ACI> mentioned,
@Nullable SpannableFactory stringFactory,
@Nullable Spannable staticString,
@DrawableRes int iconResource,
@@ -60,11 +62,11 @@ public final class UpdateDescription {
* @param mentioned UUIDs of recipients that are mentioned in the string.
* @param stringFactory The background method for generating the string.
*/
public static UpdateDescription mentioning(@NonNull Collection<ServiceId> mentioned,
public static UpdateDescription mentioning(@NonNull Collection<ACI> mentioned,
@NonNull SpannableFactory stringFactory,
@DrawableRes int iconResource)
{
return new UpdateDescription(ServiceId.filterKnown(mentioned),
return new UpdateDescription(mentioned.stream().filter(ACI::isValid).collect(Collectors.toList()),
stringFactory,
null,
iconResource,
@@ -125,7 +127,7 @@ public final class UpdateDescription {
}
@AnyThread
public @NonNull Collection<ServiceId> getMentioned() {
public @NonNull Collection<ACI> getMentioned() {
return mentioned;
}
@@ -156,7 +158,7 @@ public final class UpdateDescription {
);
}
Set<ServiceId> allMentioned = new HashSet<>();
Set<ACI> allMentioned = new HashSet<>();
for (UpdateDescription updateDescription : updateDescriptions) {
allMentioned.addAll(updateDescription.getMentioned());