Only write binary ids in staging.

This commit is contained in:
Michelle Tang
2025-11-06 10:35:48 -05:00
parent 27e6ecb2a0
commit 4c00337b1a
10 changed files with 101 additions and 47 deletions

View File

@@ -238,6 +238,7 @@ android {
buildConfigField("String", "STRIPE_PUBLISHABLE_KEY", "\"pk_live_6cmGZopuTsV8novGgJJW9JpC00vLIgtQ1D\"")
buildConfigField("boolean", "TRACING_ENABLED", "false")
buildConfigField("boolean", "LINK_DEVICE_UX_ENABLED", "false")
buildConfigField("boolean", "USE_STRING_ID", "true")
ndk {
abiFilters += listOf("armeabi-v7a", "arm64-v8a", "x86", "x86_64")
@@ -412,6 +413,7 @@ android {
buildConfigField("String", "RECAPTCHA_PROOF_URL", "\"https://signalcaptchas.org/staging/challenge/generate.html\"")
buildConfigField("org.signal.libsignal.net.Network.Environment", "LIBSIGNAL_NET_ENV", "org.signal.libsignal.net.Network.Environment.STAGING")
buildConfigField("int", "LIBSIGNAL_LOG_LEVEL", "org.signal.libsignal.protocol.logging.SignalProtocolLogger.DEBUG")
buildConfigField("boolean", "USE_STRING_ID", "false")
buildConfigField("String", "BUILD_ENVIRONMENT_TYPE", "\"Staging\"")
buildConfigField("String", "STRIPE_PUBLISHABLE_KEY", "\"pk_test_sngOd8FnXNkpce9nPXawKrJD00kIDngZkD\"")

View File

@@ -175,7 +175,8 @@ public class ApplicationDependencyProvider implements AppDependencies.Provider {
SignalExecutors.newCachedBoundedExecutor("signal-messages", ThreadUtil.PRIORITY_IMPORTANT_BACKGROUND_THREAD, 1, 16, 30),
RemoteConfig.maxEnvelopeSizeBytes(),
RemoteConfig::useMessageSendRestFallback,
RemoteConfig.useBinaryId());
RemoteConfig.useBinaryId(),
BuildConfig.USE_STRING_ID);
}
@Override

View File

@@ -13,6 +13,7 @@ import androidx.annotation.Nullable;
import org.signal.core.util.logging.Log;
import org.signal.libsignal.protocol.IdentityKey;
import org.signal.libsignal.zkgroup.profiles.ProfileKey;
import org.thoughtcrime.securesms.BuildConfig;
import org.thoughtcrime.securesms.conversation.colors.ChatColorsMapper;
import org.thoughtcrime.securesms.crypto.ProfileKeyUtil;
import org.thoughtcrime.securesms.database.RecipientTable;
@@ -145,7 +146,7 @@ public class MultiDeviceContactUpdateJob extends BaseJob {
Uri updateUri = null;
try {
DeviceContactsOutputStream out = new DeviceContactsOutputStream(writeDetails.outputStream, RemoteConfig.useBinaryId());
DeviceContactsOutputStream out = new DeviceContactsOutputStream(writeDetails.outputStream, RemoteConfig.useBinaryId(), BuildConfig.USE_STRING_ID);
Recipient recipient = Recipient.resolved(recipientId);
if (recipient.getRegistered() == RecipientTable.RegisteredState.NOT_REGISTERED) {
@@ -210,7 +211,7 @@ public class MultiDeviceContactUpdateJob extends BaseJob {
Uri updateUri = null;
try {
DeviceContactsOutputStream out = new DeviceContactsOutputStream(writeDetails.outputStream, RemoteConfig.useBinaryId());
DeviceContactsOutputStream out = new DeviceContactsOutputStream(writeDetails.outputStream, RemoteConfig.useBinaryId(), BuildConfig.USE_STRING_ID);
List<Recipient> recipients = SignalDatabase.recipients().getRecipientsForMultiDeviceSync();
Map<RecipientId, Integer> inboxPositions = SignalDatabase.threads().getInboxPositions();
Set<RecipientId> archived = SignalDatabase.threads().getArchivedRecipients();

View File

@@ -7,10 +7,12 @@ package org.thoughtcrime.securesms.jobs
import androidx.annotation.VisibleForTesting
import androidx.annotation.WorkerThread
import okio.ByteString
import okio.ByteString.Companion.toByteString
import org.signal.core.util.Base64
import org.signal.core.util.logging.Log
import org.signal.core.util.orNull
import org.thoughtcrime.securesms.BuildConfig
import org.thoughtcrime.securesms.attachments.DatabaseAttachment
import org.thoughtcrime.securesms.database.SignalDatabase
import org.thoughtcrime.securesms.database.ThreadTable
@@ -374,8 +376,16 @@ class MultiDeviceDeleteSyncJob private constructor(
private fun Recipient.toDeleteSyncConversationId(): ConversationIdentifier? {
return when {
isGroup -> ConversationIdentifier(threadGroupId = requireGroupId().decodedId.toByteString())
hasAci -> ConversationIdentifier(threadServiceId = requireAci().toString())
hasPni -> ConversationIdentifier(threadServiceId = requirePni().toString())
hasAci -> if (BuildConfig.USE_STRING_ID) {
ConversationIdentifier(threadServiceId = requireAci().toString())
} else {
ConversationIdentifier(threadServiceIdBinary = requireAci().toByteString())
}
hasPni -> if (BuildConfig.USE_STRING_ID) {
ConversationIdentifier(threadServiceId = requirePni().toString())
} else {
ConversationIdentifier(threadServiceIdBinary = requirePni().toByteString())
}
hasE164 -> ConversationIdentifier(threadE164 = requireE164())
else -> null
}
@@ -383,7 +393,12 @@ class MultiDeviceDeleteSyncJob private constructor(
private fun DeleteSyncJobData.AddressableMessage.toDeleteSyncMessage(): AddressableMessage? {
val author: Recipient = Recipient.resolved(RecipientId.from(authorRecipientId))
val authorServiceId: String? = author.aci.orNull()?.toString() ?: author.pni.orNull()?.toString()
val authorServiceId = if (BuildConfig.USE_STRING_ID) {
author.aci.orNull()?.toString() ?: author.pni.orNull()?.toString()
} else {
author.aci.orNull()?.toByteString() ?: author.pni.orNull()?.toByteString()
}
val authorE164: String? = if (authorServiceId == null) {
author.e164.orNull()
} else {
@@ -394,11 +409,19 @@ class MultiDeviceDeleteSyncJob private constructor(
Log.w(TAG, "Unable to send sync message without serviceId or e164 recipient: ${author.id}")
null
} else {
AddressableMessage(
authorServiceId = authorServiceId,
authorE164 = authorE164,
sentTimestamp = sentTimestamp
)
if (BuildConfig.USE_STRING_ID) {
AddressableMessage(
authorServiceId = authorServiceId as String?,
authorE164 = authorE164,
sentTimestamp = sentTimestamp
)
} else {
AddressableMessage(
authorServiceIdBinary = authorServiceId as ByteString?,
authorE164 = authorE164,
sentTimestamp = sentTimestamp
)
}
}
}

View File

@@ -6,6 +6,7 @@ import androidx.annotation.Nullable;
import org.signal.core.util.logging.Log;
import org.signal.libsignal.zkgroup.profiles.ProfileKey;
import org.thoughtcrime.securesms.BuildConfig;
import org.thoughtcrime.securesms.crypto.ProfileKeyUtil;
import org.thoughtcrime.securesms.dependencies.AppDependencies;
import org.thoughtcrime.securesms.jobmanager.Job;
@@ -72,7 +73,7 @@ public class MultiDeviceProfileKeyUpdateJob extends BaseJob {
}
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DeviceContactsOutputStream out = new DeviceContactsOutputStream(baos, RemoteConfig.useBinaryId());
DeviceContactsOutputStream out = new DeviceContactsOutputStream(baos, RemoteConfig.useBinaryId(), BuildConfig.USE_STRING_ID);
out.write(new DeviceContact(Optional.ofNullable(SignalStore.account().getAci()),
Optional.ofNullable(SignalStore.account().getE164()),

View File

@@ -7,6 +7,7 @@ import org.signal.core.util.isNullOrEmpty
import org.signal.core.util.logging.Log
import org.signal.libsignal.zkgroup.InvalidInputException
import org.signal.libsignal.zkgroup.groups.GroupMasterKey
import org.thoughtcrime.securesms.BuildConfig
import org.thoughtcrime.securesms.components.settings.app.chats.folders.ChatFolderRecord
import org.thoughtcrime.securesms.components.settings.app.usernamelinks.UsernameQrCodeColorScheme
import org.thoughtcrime.securesms.conversation.colors.AvatarColor
@@ -133,7 +134,7 @@ object StorageSyncModels {
RecipientType.INDIVIDUAL -> {
AccountRecord.PinnedConversation(
contact = AccountRecord.PinnedConversation.Contact(
serviceId = settings.serviceId?.toString() ?: "",
serviceId = settings.serviceId?.toString().takeIf { BuildConfig.USE_STRING_ID } ?: "",
e164 = settings.e164 ?: "",
serviceIdBinary = settings.serviceId?.toByteString().takeIf { RemoteConfig.useBinaryId } ?: ByteString.EMPTY
)
@@ -188,9 +189,9 @@ object StorageSyncModels {
}
return SignalContactRecord.newBuilder(recipient.syncExtras.storageProto).apply {
aci = recipient.aci?.toString() ?: ""
aci = recipient.aci?.toString().takeIf { BuildConfig.USE_STRING_ID } ?: ""
e164 = recipient.e164 ?: ""
pni = recipient.pni?.toStringWithoutPrefix() ?: ""
pni = recipient.pni?.toStringWithoutPrefix().takeIf { BuildConfig.USE_STRING_ID } ?: ""
profileKey = recipient.profileKey?.toByteString() ?: ByteString.EMPTY
givenName = recipient.signalProfileName.givenName
familyName = recipient.signalProfileName.familyName
@@ -294,10 +295,14 @@ object StorageSyncModels {
return SignalStoryDistributionListRecord.newBuilder(recipient.syncExtras.storageProto).apply {
identifier = UuidUtil.toByteArray(record.distributionId.asUuid()).toByteString()
name = record.name
recipientServiceIds = record.getMembersToSync()
.map { Recipient.resolved(it) }
.filter { it.hasServiceId }
.map { it.requireServiceId().toString() }
recipientServiceIds = if (BuildConfig.USE_STRING_ID) {
record.getMembersToSync()
.map { Recipient.resolved(it) }
.filter { it.hasServiceId }
.map { it.requireServiceId().toString() }
} else {
emptyList()
}
recipientServiceIdsBinary = if (RemoteConfig.useBinaryId) {
record.getMembersToSync()
.map { Recipient.resolved(it) }
@@ -507,7 +512,7 @@ object StorageSyncModels {
RecipientType.INDIVIDUAL -> {
RemoteRecipient(
contact = RemoteRecipient.Contact(
serviceId = recipient.serviceId?.toString() ?: "",
serviceId = recipient.serviceId?.toString().takeIf { BuildConfig.USE_STRING_ID } ?: "",
e164 = recipient.e164 ?: "",
serviceIdBinary = recipient.serviceId?.toByteString().takeIf { RemoteConfig.useBinaryId } ?: ByteString.EMPTY
)

View File

@@ -1186,7 +1186,7 @@ object RemoteConfig {
@get:JvmName("useBinaryId")
val useBinaryId: Boolean by remoteBoolean(
key = "android.useBinaryServiceId",
defaultValue = false,
defaultValue = Environment.IS_STAGING,
hotSwappable = false
)

View File

@@ -185,6 +185,7 @@ public class SignalServiceMessageSender {
private final long maxEnvelopeSize;
private final BooleanSupplier useRestFallback;
private final boolean useBinaryId;
private final boolean useStringId;
public SignalServiceMessageSender(PushServiceSocket pushServiceSocket,
SignalServiceDataStore store,
@@ -196,7 +197,8 @@ public class SignalServiceMessageSender {
ExecutorService executor,
long maxEnvelopeSize,
BooleanSupplier useRestFallback,
boolean useBinaryId)
boolean useBinaryId,
boolean useStringId)
{
CredentialsProvider credentialsProvider = pushServiceSocket.getCredentialsProvider();
@@ -215,6 +217,7 @@ public class SignalServiceMessageSender {
this.keysApi = keysApi;
this.useRestFallback = useRestFallback;
this.useBinaryId = useBinaryId;
this.useStringId = useStringId;
}
/**
@@ -1059,7 +1062,7 @@ public class SignalServiceMessageSender {
DataMessage.Quote.Builder quoteBuilder = new DataMessage.Quote.Builder()
.id(message.getQuote().get().getId())
.text(message.getQuote().get().getText())
.authorAci(message.getQuote().get().getAuthor().toString())
.authorAci(useStringId ? message.getQuote().get().getAuthor().toString() : null)
.authorAciBinary(useBinaryId ? message.getQuote().get().getAuthor().toByteString() : null)
.type(message.getQuote().get().getType().getProtoType());
@@ -1067,11 +1070,19 @@ public class SignalServiceMessageSender {
if (mentions != null && !mentions.isEmpty()) {
List<BodyRange> bodyRanges = new ArrayList<>(quoteBuilder.bodyRanges);
for (SignalServiceDataMessage.Mention mention : mentions) {
bodyRanges.add(new BodyRange.Builder()
.start(mention.getStart())
.length(mention.getLength())
.mentionAci(mention.getServiceId().toString())
.build());
if (useStringId) {
bodyRanges.add(new BodyRange.Builder()
.start(mention.getStart())
.length(mention.getLength())
.mentionAci(mention.getServiceId().toString())
.build());
} else {
bodyRanges.add(new BodyRange.Builder()
.start(mention.getStart())
.length(mention.getLength())
.mentionAciBinary(mention.getServiceId().toByteString())
.build());
}
}
quoteBuilder.bodyRanges(bodyRanges);
@@ -1128,11 +1139,19 @@ public class SignalServiceMessageSender {
if (message.getMentions().isPresent()) {
List<BodyRange> bodyRanges = new ArrayList<>(builder.bodyRanges);
for (SignalServiceDataMessage.Mention mention : message.getMentions().get()) {
bodyRanges.add(new BodyRange.Builder()
.start(mention.getStart())
.length(mention.getLength())
.mentionAci(mention.getServiceId().toString())
.build());
if (useStringId) {
bodyRanges.add(new BodyRange.Builder()
.start(mention.getStart())
.length(mention.getLength())
.mentionAci(mention.getServiceId().toString())
.build());
} else {
bodyRanges.add(new BodyRange.Builder()
.start(mention.getStart())
.length(mention.getLength())
.mentionAciBinary(mention.getServiceId().toByteString())
.build());
}
}
builder.bodyRanges(bodyRanges);
builder.requiredProtocolVersion(Math.max(DataMessage.ProtocolVersion.MENTIONS.getValue(), builder.requiredProtocolVersion));
@@ -1168,7 +1187,7 @@ public class SignalServiceMessageSender {
.emoji(message.getReaction().get().getEmoji())
.remove(message.getReaction().get().isRemove())
.targetSentTimestamp(message.getReaction().get().getTargetSentTimestamp())
.targetAuthorAci(message.getReaction().get().getTargetAuthor().toString())
.targetAuthorAci(useStringId ? message.getReaction().get().getTargetAuthor().toString() : null)
.targetAuthorAciBinary(useBinaryId ? message.getReaction().get().getTargetAuthor().toByteString() : null);
builder.reaction(reactionBuilder.build());
@@ -1213,7 +1232,7 @@ public class SignalServiceMessageSender {
SignalServiceDataMessage.StoryContext storyContext = message.getStoryContext().get();
builder.storyContext(new DataMessage.StoryContext.Builder()
.authorAci(storyContext.getAuthorServiceId().toString())
.authorAci(useStringId ? storyContext.getAuthorServiceId().toString() : null)
.authorAciBinary(useBinaryId ? storyContext.getAuthorServiceId().toByteString() : null)
.sentTimestamp(storyContext.getSentTimestamp())
.build());
@@ -1404,7 +1423,7 @@ public class SignalServiceMessageSender {
}
unidentifiedDeliveryStatuses.add(new SyncMessage.Sent.UnidentifiedDeliveryStatus.Builder()
.destinationServiceId(result.getAddress().getServiceId().toString())
.destinationServiceId(useStringId ? result.getAddress().getServiceId().toString() : null)
.destinationServiceIdBinary(useBinaryId ? result.getAddress().getServiceId().toByteString() : null)
.unidentified(false)
.destinationPniIdentityKey(identity)
@@ -1414,7 +1433,7 @@ public class SignalServiceMessageSender {
sentMessage.unidentifiedStatus(unidentifiedDeliveryStatuses);
if (recipient.isPresent()) {
sentMessage.destinationServiceId(recipient.get().getServiceId().toString());
sentMessage.destinationServiceId(useStringId ? recipient.get().getServiceId().toString() : null);
sentMessage.destinationServiceIdBinary(useBinaryId ? recipient.get().getServiceId().toByteString() : null);
if (recipient.get().getNumber().isPresent()) {
sentMessage.destinationE164(recipient.get().getNumber().get());
@@ -1454,7 +1473,7 @@ public class SignalServiceMessageSender {
private SyncMessage.Sent.StoryMessageRecipient createStoryMessageRecipient(SignalServiceStoryMessageRecipient storyMessageRecipient) {
return new SyncMessage.Sent.StoryMessageRecipient.Builder()
.distributionListIds(storyMessageRecipient.getDistributionListIds())
.destinationServiceId(storyMessageRecipient.getSignalServiceAddress().getIdentifier())
.destinationServiceId(useStringId ? storyMessageRecipient.getSignalServiceAddress().getIdentifier() : null)
.destinationServiceIdBinary(useBinaryId ? storyMessageRecipient.getSignalServiceAddress().getServiceId().toByteString() : null)
.isAllowedToReply(storyMessageRecipient.isAllowedToReply())
.build();
@@ -1468,7 +1487,7 @@ public class SignalServiceMessageSender {
readMessages.stream()
.map(readMessage -> new SyncMessage.Read.Builder()
.timestamp(readMessage.getTimestamp())
.senderAci(readMessage.getSenderAci().toString())
.senderAci(useStringId ? readMessage.getSenderAci().toString() : null)
.senderAciBinary(useBinaryId ? readMessage.getSenderAci().toByteString() : null)
.build())
.collect(Collectors.toList())
@@ -1485,7 +1504,7 @@ public class SignalServiceMessageSender {
readMessages.stream()
.map(readMessage -> new SyncMessage.Viewed.Builder()
.timestamp(readMessage.getTimestamp())
.senderAci(readMessage.getSender().toString())
.senderAci(useStringId ? readMessage.getSender().toString() : null)
.senderAciBinary(useBinaryId ? readMessage.getSender().toByteString() : null)
.build())
.collect(Collectors.toList())
@@ -1500,7 +1519,7 @@ public class SignalServiceMessageSender {
builder.viewOnceOpen(new SyncMessage.ViewOnceOpen.Builder()
.timestamp(readMessage.getTimestamp())
.senderAci(readMessage.getSender().toString())
.senderAci(useStringId ? readMessage.getSender().toString() : null)
.senderAciBinary(useBinaryId ? readMessage.getSender().toByteString() : null)
.build());
@@ -1512,7 +1531,7 @@ public class SignalServiceMessageSender {
SyncMessage.Builder syncMessage = createSyncMessageBuilder();
SyncMessage.Blocked.Builder blockedMessage = new SyncMessage.Blocked.Builder();
blockedMessage.acis(blocked.individuals.stream().filter(a -> a.getAci() != null).map(a -> a.getAci().toString()).collect(Collectors.toList()));
blockedMessage.acis(useStringId ? blocked.individuals.stream().filter(a -> a.getAci() != null).map(a -> a.getAci().toString()).collect(Collectors.toList()) : Collections.emptyList());
blockedMessage.acisBinary(useBinaryId ? blocked.individuals.stream().filter(a -> a.getAci() != null).map(a -> a.getAci().toByteString()).collect(Collectors.toList()) : Collections.emptyList());
blockedMessage.numbers(blocked.individuals.stream().filter(a -> a.getE164() != null).map(a -> a.getE164()).collect(Collectors.toList()));
blockedMessage.groupIds(blocked.groupIds.stream().map(ByteString::of).collect(Collectors.toList()));
@@ -1612,7 +1631,7 @@ public class SignalServiceMessageSender {
}
if (message.getPerson().isPresent()) {
responseMessage.threadAci(message.getPerson().get().toString());
responseMessage.threadAci(useStringId ? message.getPerson().get().toString() : null);
responseMessage.threadAciBinary(useBinaryId ? message.getPerson().get().toByteString() : null);
}
@@ -1710,7 +1729,7 @@ public class SignalServiceMessageSender {
verifiedMessageBuilder.nullMessage(ByteString.of(nullMessage));
verifiedMessageBuilder.identityKey(ByteString.of(verifiedMessage.getIdentityKey().serialize()));
verifiedMessageBuilder.destinationAci(verifiedMessage.getDestination().getServiceId().toString());
verifiedMessageBuilder.destinationAci(useStringId ? verifiedMessage.getDestination().getServiceId().toString() : null);
verifiedMessageBuilder.destinationAciBinary(useBinaryId ? verifiedMessage.getDestination().getServiceId().toByteString() : null);

View File

@@ -17,10 +17,12 @@ import okio.ByteString;
public class DeviceContactsOutputStream extends ChunkedOutputStream {
private final boolean useBinaryId;
private final boolean useStringId;
public DeviceContactsOutputStream(OutputStream out, boolean useBinaryId) {
public DeviceContactsOutputStream(OutputStream out, boolean useBinaryId, boolean useStringId) {
super(out);
this.useBinaryId = useBinaryId;
this.useStringId = useStringId;
}
public void write(DeviceContact contact) throws IOException {
@@ -42,7 +44,7 @@ public class DeviceContactsOutputStream extends ChunkedOutputStream {
ContactDetails.Builder contactDetails = new ContactDetails.Builder();
if (contact.getAci().isPresent()) {
contactDetails.aci(contact.getAci().get().toString());
contactDetails.aci(useStringId ? contact.getAci().get().toString() : null);
contactDetails.aciBinary(useBinaryId ? contact.getAci().get().toByteString() : null);
}

View File

@@ -17,7 +17,7 @@ public class DeviceContactsInputStreamTest {
@Test
public void read() throws IOException, InvalidInputException {
ByteArrayOutputStream byteArrayOut = new ByteArrayOutputStream();
DeviceContactsOutputStream output = new DeviceContactsOutputStream(byteArrayOut, true);
DeviceContactsOutputStream output = new DeviceContactsOutputStream(byteArrayOut, true, true);
Optional<ACI> aciFirst = Optional.of(ACI.from(UUID.randomUUID()));
Optional<String> e164First = Optional.of("+1404555555");
Optional<ACI> aciSecond = Optional.of(ACI.from(UUID.randomUUID()));