Do not attempt to send to unregistered users when using CDS flag.
CDS is slow, and unregistered users will always trigger a CDS lookup on send (since we can't get their UUID). This starts skipping sends to unregistered users and shortens the time window to do a full CDS lookup from every 12 hours to every 6 hours.
This commit is contained in:
parent
1e37951701
commit
4714895c59
11 changed files with 149 additions and 75 deletions
|
@ -38,6 +38,7 @@ import org.thoughtcrime.securesms.logging.Log;
|
||||||
import org.thoughtcrime.securesms.notifications.NotificationChannels;
|
import org.thoughtcrime.securesms.notifications.NotificationChannels;
|
||||||
import org.thoughtcrime.securesms.permissions.Permissions;
|
import org.thoughtcrime.securesms.permissions.Permissions;
|
||||||
import org.thoughtcrime.securesms.phonenumbers.PhoneNumberFormatter;
|
import org.thoughtcrime.securesms.phonenumbers.PhoneNumberFormatter;
|
||||||
|
import org.thoughtcrime.securesms.recipients.RecipientDetails;
|
||||||
import org.thoughtcrime.securesms.registration.RegistrationUtil;
|
import org.thoughtcrime.securesms.registration.RegistrationUtil;
|
||||||
import org.thoughtcrime.securesms.storage.StorageSyncHelper;
|
import org.thoughtcrime.securesms.storage.StorageSyncHelper;
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
|
@ -92,61 +93,35 @@ public class DirectoryHelper {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Stopwatch stopwatch = new Stopwatch("full");
|
|
||||||
RecipientDatabase recipientDatabase = DatabaseFactory.getRecipientDatabase(context);
|
RecipientDatabase recipientDatabase = DatabaseFactory.getRecipientDatabase(context);
|
||||||
Set<String> databaseNumbers = sanitizeNumbers(recipientDatabase.getAllPhoneNumbers());
|
Set<String> databaseNumbers = sanitizeNumbers(recipientDatabase.getAllPhoneNumbers());
|
||||||
Set<String> systemNumbers = sanitizeNumbers(ContactAccessor.getInstance().getAllContactsWithNumbers(context));
|
Set<String> systemNumbers = sanitizeNumbers(ContactAccessor.getInstance().getAllContactsWithNumbers(context));
|
||||||
Set<String> allNumbers = SetUtil.union(databaseNumbers, systemNumbers);
|
|
||||||
|
|
||||||
DirectoryResult result;
|
refreshNumbers(context, databaseNumbers, systemNumbers, notifyOfNewUsers);
|
||||||
|
|
||||||
if (FeatureFlags.cds()) {
|
|
||||||
result = ContactDiscoveryV2.getDirectoryResult(context, databaseNumbers, systemNumbers);
|
|
||||||
} else {
|
|
||||||
result = ContactDiscoveryV1.getDirectoryResult(databaseNumbers, systemNumbers);
|
|
||||||
}
|
|
||||||
|
|
||||||
stopwatch.split("network");
|
|
||||||
|
|
||||||
if (result.getNumberRewrites().size() > 0) {
|
|
||||||
Log.i(TAG, "[getDirectoryResult] Need to rewrite some numbers.");
|
|
||||||
recipientDatabase.updatePhoneNumbers(result.getNumberRewrites());
|
|
||||||
}
|
|
||||||
|
|
||||||
Map<RecipientId, String> uuidMap = recipientDatabase.bulkProcessCdsResult(result.getRegisteredNumbers());
|
|
||||||
Set<String> activeNumbers = result.getRegisteredNumbers().keySet();
|
|
||||||
Set<RecipientId> activeIds = uuidMap.keySet();
|
|
||||||
Set<RecipientId> inactiveIds = Stream.of(allNumbers)
|
|
||||||
.filterNot(activeNumbers::contains)
|
|
||||||
.filterNot(n -> result.getNumberRewrites().containsKey(n))
|
|
||||||
.map(recipientDatabase::getOrInsertFromE164)
|
|
||||||
.collect(Collectors.toSet());
|
|
||||||
|
|
||||||
recipientDatabase.bulkUpdatedRegisteredStatus(uuidMap, inactiveIds);
|
|
||||||
|
|
||||||
updateContactsDatabase(context, activeIds, true, result.getNumberRewrites());
|
|
||||||
|
|
||||||
if (TextSecurePreferences.isMultiDevice(context)) {
|
|
||||||
ApplicationDependencies.getJobManager().add(new MultiDeviceContactUpdateJob());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (TextSecurePreferences.hasSuccessfullyRetrievedDirectory(context) && notifyOfNewUsers) {
|
|
||||||
Set<RecipientId> existingSignalIds = new HashSet<>(recipientDatabase.getRegistered());
|
|
||||||
Set<RecipientId> existingSystemIds = new HashSet<>(recipientDatabase.getSystemContacts());
|
|
||||||
Set<RecipientId> newlyActiveIds = new HashSet<>(activeIds);
|
|
||||||
|
|
||||||
newlyActiveIds.removeAll(existingSignalIds);
|
|
||||||
newlyActiveIds.retainAll(existingSystemIds);
|
|
||||||
|
|
||||||
notifyNewUsers(context, newlyActiveIds);
|
|
||||||
} else {
|
|
||||||
TextSecurePreferences.setHasSuccessfullyRetrievedDirectory(context, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
StorageSyncHelper.scheduleSyncForDataChange();
|
StorageSyncHelper.scheduleSyncForDataChange();
|
||||||
|
}
|
||||||
|
|
||||||
stopwatch.split("disk");
|
@WorkerThread
|
||||||
stopwatch.stop(TAG);
|
public static void refreshDirectoryFor(@NonNull Context context, @NonNull List<Recipient> recipients, boolean notifyOfNewUsers) throws IOException {
|
||||||
|
RecipientDatabase recipientDatabase = DatabaseFactory.getRecipientDatabase(context);
|
||||||
|
|
||||||
|
for (Recipient recipient : recipients) {
|
||||||
|
if (recipient.hasUuid() && !recipient.hasE164()) {
|
||||||
|
if (isUuidRegistered(context, recipient)) {
|
||||||
|
recipientDatabase.markRegistered(recipient.getId(), recipient.requireUuid());
|
||||||
|
} else {
|
||||||
|
recipientDatabase.markUnregistered(recipient.getId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Set<String> numbers = Stream.of(recipients)
|
||||||
|
.filter(Recipient::hasE164)
|
||||||
|
.map(Recipient::requireE164)
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
|
||||||
|
refreshNumbers(context, numbers, numbers, notifyOfNewUsers);
|
||||||
}
|
}
|
||||||
|
|
||||||
@WorkerThread
|
@WorkerThread
|
||||||
|
@ -231,6 +206,61 @@ public class DirectoryHelper {
|
||||||
return newRegisteredState;
|
return newRegisteredState;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@WorkerThread
|
||||||
|
private static void refreshNumbers(@NonNull Context context, @NonNull Set<String> databaseNumbers, @NonNull Set<String> systemNumbers, boolean notifyOfNewUsers) throws IOException {
|
||||||
|
RecipientDatabase recipientDatabase = DatabaseFactory.getRecipientDatabase(context);
|
||||||
|
Set<String> allNumbers = SetUtil.union(databaseNumbers, systemNumbers);
|
||||||
|
|
||||||
|
if (allNumbers.isEmpty()) {
|
||||||
|
Log.w(TAG, "No numbers to refresh!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
DirectoryResult result;
|
||||||
|
|
||||||
|
if (FeatureFlags.cds()) {
|
||||||
|
result = ContactDiscoveryV2.getDirectoryResult(context, databaseNumbers, systemNumbers);
|
||||||
|
} else {
|
||||||
|
result = ContactDiscoveryV1.getDirectoryResult(databaseNumbers, systemNumbers);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.getNumberRewrites().size() > 0) {
|
||||||
|
Log.i(TAG, "[getDirectoryResult] Need to rewrite some numbers.");
|
||||||
|
recipientDatabase.updatePhoneNumbers(result.getNumberRewrites());
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<RecipientId, String> uuidMap = recipientDatabase.bulkProcessCdsResult(result.getRegisteredNumbers());
|
||||||
|
Set<String> activeNumbers = result.getRegisteredNumbers().keySet();
|
||||||
|
Set<RecipientId> activeIds = uuidMap.keySet();
|
||||||
|
Set<RecipientId> inactiveIds = Stream.of(allNumbers)
|
||||||
|
.filterNot(activeNumbers::contains)
|
||||||
|
.filterNot(n -> result.getNumberRewrites().containsKey(n))
|
||||||
|
.map(recipientDatabase::getOrInsertFromE164)
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
|
||||||
|
recipientDatabase.bulkUpdatedRegisteredStatus(uuidMap, inactiveIds);
|
||||||
|
|
||||||
|
updateContactsDatabase(context, activeIds, true, result.getNumberRewrites());
|
||||||
|
|
||||||
|
if (TextSecurePreferences.isMultiDevice(context)) {
|
||||||
|
ApplicationDependencies.getJobManager().add(new MultiDeviceContactUpdateJob());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (TextSecurePreferences.hasSuccessfullyRetrievedDirectory(context) && notifyOfNewUsers) {
|
||||||
|
Set<RecipientId> existingSignalIds = new HashSet<>(recipientDatabase.getRegistered());
|
||||||
|
Set<RecipientId> existingSystemIds = new HashSet<>(recipientDatabase.getSystemContacts());
|
||||||
|
Set<RecipientId> newlyActiveIds = new HashSet<>(activeIds);
|
||||||
|
|
||||||
|
newlyActiveIds.removeAll(existingSignalIds);
|
||||||
|
newlyActiveIds.retainAll(existingSystemIds);
|
||||||
|
|
||||||
|
notifyNewUsers(context, newlyActiveIds);
|
||||||
|
} else {
|
||||||
|
TextSecurePreferences.setHasSuccessfullyRetrievedDirectory(context, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private static boolean isUuidRegistered(@NonNull Context context, @NonNull Recipient recipient) throws IOException {
|
private static boolean isUuidRegistered(@NonNull Context context, @NonNull Recipient recipient) throws IOException {
|
||||||
try {
|
try {
|
||||||
ProfileUtil.retrieveProfile(context, recipient, SignalServiceProfile.RequestType.PROFILE).get(10, TimeUnit.SECONDS);
|
ProfileUtil.retrieveProfile(context, recipient, SignalServiceProfile.RequestType.PROFILE).get(10, TimeUnit.SECONDS);
|
||||||
|
|
|
@ -56,7 +56,7 @@ public class ProfileKeySendJob extends BaseJob {
|
||||||
throw new AssertionError("Do not send profile keys directly for GV2");
|
throw new AssertionError("Do not send profile keys directly for GV2");
|
||||||
}
|
}
|
||||||
|
|
||||||
List<RecipientId> recipients = conversationRecipient.isGroup() ? Stream.of(conversationRecipient.getParticipants()).map(Recipient::getId).toList()
|
List<RecipientId> recipients = conversationRecipient.isGroup() ? Stream.of(RecipientUtil.getEligibleForSending(conversationRecipient.getParticipants())).map(Recipient::getId).toList()
|
||||||
: Stream.of(conversationRecipient.getId()).toList();
|
: Stream.of(conversationRecipient.getId()).toList();
|
||||||
|
|
||||||
recipients.remove(Recipient.self().getId());
|
recipients.remove(Recipient.self().getId());
|
||||||
|
|
|
@ -19,6 +19,7 @@ import org.thoughtcrime.securesms.database.GroupReceiptDatabase.GroupReceiptInfo
|
||||||
import org.thoughtcrime.securesms.database.MessageDatabase;
|
import org.thoughtcrime.securesms.database.MessageDatabase;
|
||||||
import org.thoughtcrime.securesms.database.MmsDatabase;
|
import org.thoughtcrime.securesms.database.MmsDatabase;
|
||||||
import org.thoughtcrime.securesms.database.NoSuchMessageException;
|
import org.thoughtcrime.securesms.database.NoSuchMessageException;
|
||||||
|
import org.thoughtcrime.securesms.database.RecipientDatabase;
|
||||||
import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatch;
|
import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatch;
|
||||||
import org.thoughtcrime.securesms.database.documents.NetworkFailure;
|
import org.thoughtcrime.securesms.database.documents.NetworkFailure;
|
||||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||||
|
@ -38,6 +39,7 @@ import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||||
import org.thoughtcrime.securesms.recipients.RecipientUtil;
|
import org.thoughtcrime.securesms.recipients.RecipientUtil;
|
||||||
import org.thoughtcrime.securesms.transport.RetryLaterException;
|
import org.thoughtcrime.securesms.transport.RetryLaterException;
|
||||||
import org.thoughtcrime.securesms.transport.UndeliverableMessageException;
|
import org.thoughtcrime.securesms.transport.UndeliverableMessageException;
|
||||||
|
import org.thoughtcrime.securesms.util.FeatureFlags;
|
||||||
import org.thoughtcrime.securesms.util.GroupUtil;
|
import org.thoughtcrime.securesms.util.GroupUtil;
|
||||||
import org.whispersystems.libsignal.util.Pair;
|
import org.whispersystems.libsignal.util.Pair;
|
||||||
import org.whispersystems.libsignal.util.guava.Optional;
|
import org.whispersystems.libsignal.util.guava.Optional;
|
||||||
|
@ -195,7 +197,13 @@ public final class PushGroupSendJob extends PushSendJob {
|
||||||
List<Pair<RecipientId, Boolean>> successUnidentifiedStatus = Stream.of(successes).map(result -> new Pair<>(findId(result.getAddress(), idByE164, idByUuid), result.getSuccess().isUnidentified())).toList();
|
List<Pair<RecipientId, Boolean>> successUnidentifiedStatus = Stream.of(successes).map(result -> new Pair<>(findId(result.getAddress(), idByE164, idByUuid), result.getSuccess().isUnidentified())).toList();
|
||||||
Set<RecipientId> successIds = Stream.of(successUnidentifiedStatus).map(Pair::first).collect(Collectors.toSet());
|
Set<RecipientId> successIds = Stream.of(successUnidentifiedStatus).map(Pair::first).collect(Collectors.toSet());
|
||||||
List<NetworkFailure> resolvedNetworkFailures = Stream.of(existingNetworkFailures).filter(failure -> successIds.contains(failure.getRecipientId(context))).toList();
|
List<NetworkFailure> resolvedNetworkFailures = Stream.of(existingNetworkFailures).filter(failure -> successIds.contains(failure.getRecipientId(context))).toList();
|
||||||
List<IdentityKeyMismatch> resolvedIdentityFailures = Stream.of(existingIdentityMismatches).filter(failure -> successIds.contains(failure.getRecipientId(context))).toList();
|
List<IdentityKeyMismatch> resolvedIdentityFailures = Stream.of(existingIdentityMismatches).filter(failure -> successIds.contains(failure.getRecipientId(context))).toList();
|
||||||
|
List<Recipient> unregisteredRecipients = Stream.of(results).filter(SendMessageResult::isUnregisteredFailure).map(result -> Recipient.externalPush(context, result.getAddress())).toList();
|
||||||
|
|
||||||
|
RecipientDatabase recipientDatabase = DatabaseFactory.getRecipientDatabase(context);
|
||||||
|
for (Recipient unregistered : unregisteredRecipients) {
|
||||||
|
recipientDatabase.markUnregistered(unregistered.getId());
|
||||||
|
}
|
||||||
|
|
||||||
for (NetworkFailure resolvedFailure : resolvedNetworkFailures) {
|
for (NetworkFailure resolvedFailure : resolvedNetworkFailures) {
|
||||||
database.removeFailure(messageId, resolvedFailure);
|
database.removeFailure(messageId, resolvedFailure);
|
||||||
|
@ -366,7 +374,10 @@ public final class PushGroupSendJob extends PushSendJob {
|
||||||
List<GroupReceiptInfo> destinations = DatabaseFactory.getGroupReceiptDatabase(context).getGroupReceiptInfo(messageId);
|
List<GroupReceiptInfo> destinations = DatabaseFactory.getGroupReceiptDatabase(context).getGroupReceiptInfo(messageId);
|
||||||
|
|
||||||
if (!destinations.isEmpty()) {
|
if (!destinations.isEmpty()) {
|
||||||
return Stream.of(destinations).map(GroupReceiptInfo::getRecipientId).map(Recipient::resolved).toList();
|
return RecipientUtil.getEligibleForSending(Stream.of(destinations)
|
||||||
|
.map(GroupReceiptInfo::getRecipientId)
|
||||||
|
.map(Recipient::resolved)
|
||||||
|
.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Recipient> members = Stream.of(DatabaseFactory.getGroupDatabase(context)
|
List<Recipient> members = Stream.of(DatabaseFactory.getGroupDatabase(context)
|
||||||
|
@ -377,8 +388,8 @@ public final class PushGroupSendJob extends PushSendJob {
|
||||||
if (members.size() > 0) {
|
if (members.size() > 0) {
|
||||||
Log.w(TAG, "No destinations found for group message " + groupId + " using current group membership");
|
Log.w(TAG, "No destinations found for group message " + groupId + " using current group membership");
|
||||||
}
|
}
|
||||||
|
|
||||||
return members;
|
return RecipientUtil.getEligibleForSending(members);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Factory implements Job.Factory<PushGroupSendJob> {
|
public static class Factory implements Job.Factory<PushGroupSendJob> {
|
||||||
|
|
|
@ -11,6 +11,7 @@ import com.google.protobuf.InvalidProtocolBufferException;
|
||||||
|
|
||||||
import org.signal.storageservice.protos.groups.local.DecryptedGroup;
|
import org.signal.storageservice.protos.groups.local.DecryptedGroup;
|
||||||
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
|
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
|
||||||
|
import org.thoughtcrime.securesms.database.RecipientDatabase;
|
||||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||||
import org.thoughtcrime.securesms.groups.GroupId;
|
import org.thoughtcrime.securesms.groups.GroupId;
|
||||||
import org.thoughtcrime.securesms.jobmanager.Data;
|
import org.thoughtcrime.securesms.jobmanager.Data;
|
||||||
|
@ -23,6 +24,7 @@ import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||||
import org.thoughtcrime.securesms.recipients.RecipientUtil;
|
import org.thoughtcrime.securesms.recipients.RecipientUtil;
|
||||||
import org.thoughtcrime.securesms.transport.RetryLaterException;
|
import org.thoughtcrime.securesms.transport.RetryLaterException;
|
||||||
import org.thoughtcrime.securesms.util.Base64;
|
import org.thoughtcrime.securesms.util.Base64;
|
||||||
|
import org.thoughtcrime.securesms.util.FeatureFlags;
|
||||||
import org.whispersystems.libsignal.util.guava.Optional;
|
import org.whispersystems.libsignal.util.guava.Optional;
|
||||||
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
|
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
|
||||||
import org.whispersystems.signalservice.api.crypto.UnidentifiedAccessPair;
|
import org.whispersystems.signalservice.api.crypto.UnidentifiedAccessPair;
|
||||||
|
@ -75,7 +77,15 @@ public final class PushGroupSilentUpdateSendJob extends BaseJob {
|
||||||
Set<RecipientId> recipients = Stream.concat(Stream.of(memberUuids), Stream.of(pendingUuids))
|
Set<RecipientId> recipients = Stream.concat(Stream.of(memberUuids), Stream.of(pendingUuids))
|
||||||
.filter(uuid -> !UuidUtil.UNKNOWN_UUID.equals(uuid))
|
.filter(uuid -> !UuidUtil.UNKNOWN_UUID.equals(uuid))
|
||||||
.filter(uuid -> !Recipient.self().getUuid().get().equals(uuid))
|
.filter(uuid -> !Recipient.self().getUuid().get().equals(uuid))
|
||||||
.map(uuid -> RecipientId.from(uuid, null))
|
.map(uuid -> Recipient.externalPush(context, uuid, null, false))
|
||||||
|
.filter(recipient -> {
|
||||||
|
if (FeatureFlags.cds()) {
|
||||||
|
return recipient.getRegistered() != RecipientDatabase.RegisteredState.NOT_REGISTERED;
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.map(Recipient::getId)
|
||||||
.collect(Collectors.toSet());
|
.collect(Collectors.toSet());
|
||||||
|
|
||||||
MessageGroupContext.GroupV2Properties properties = groupMessage.requireGroupV2Properties();
|
MessageGroupContext.GroupV2Properties properties = groupMessage.requireGroupV2Properties();
|
||||||
|
|
|
@ -75,7 +75,7 @@ public class ReactionSendJob extends BaseJob {
|
||||||
throw new AssertionError("We have a message, but couldn't find the thread!");
|
throw new AssertionError("We have a message, but couldn't find the thread!");
|
||||||
}
|
}
|
||||||
|
|
||||||
List<RecipientId> recipients = conversationRecipient.isGroup() ? Stream.of(conversationRecipient.getParticipants()).map(Recipient::getId).toList()
|
List<RecipientId> recipients = conversationRecipient.isGroup() ? Stream.of(RecipientUtil.getEligibleForSending(conversationRecipient.getParticipants())).map(Recipient::getId).toList()
|
||||||
: Stream.of(conversationRecipient.getId()).toList();
|
: Stream.of(conversationRecipient.getId()).toList();
|
||||||
|
|
||||||
recipients.remove(Recipient.self().getId());
|
recipients.remove(Recipient.self().getId());
|
||||||
|
|
|
@ -65,7 +65,7 @@ public class RemoteDeleteSendJob extends BaseJob {
|
||||||
throw new AssertionError("We have a message, but couldn't find the thread!");
|
throw new AssertionError("We have a message, but couldn't find the thread!");
|
||||||
}
|
}
|
||||||
|
|
||||||
List<RecipientId> recipients = conversationRecipient.isGroup() ? Stream.of(conversationRecipient.getParticipants()).map(Recipient::getId).toList()
|
List<RecipientId> recipients = conversationRecipient.isGroup() ? Stream.of(RecipientUtil.getEligibleForSending(conversationRecipient.getParticipants())).map(Recipient::getId).toList()
|
||||||
: Stream.of(conversationRecipient.getId()).toList();
|
: Stream.of(conversationRecipient.getId()).toList();
|
||||||
|
|
||||||
recipients.remove(Recipient.self().getId());
|
recipients.remove(Recipient.self().getId());
|
||||||
|
|
|
@ -9,6 +9,7 @@ import com.annimon.stream.Stream;
|
||||||
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
|
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||||
import org.thoughtcrime.securesms.database.GroupDatabase;
|
import org.thoughtcrime.securesms.database.GroupDatabase;
|
||||||
|
import org.thoughtcrime.securesms.database.RecipientDatabase;
|
||||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||||
import org.thoughtcrime.securesms.jobmanager.Data;
|
import org.thoughtcrime.securesms.jobmanager.Data;
|
||||||
import org.thoughtcrime.securesms.jobmanager.Job;
|
import org.thoughtcrime.securesms.jobmanager.Job;
|
||||||
|
@ -16,6 +17,7 @@ import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
|
||||||
import org.thoughtcrime.securesms.logging.Log;
|
import org.thoughtcrime.securesms.logging.Log;
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
import org.thoughtcrime.securesms.recipients.RecipientUtil;
|
import org.thoughtcrime.securesms.recipients.RecipientUtil;
|
||||||
|
import org.thoughtcrime.securesms.util.FeatureFlags;
|
||||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||||
import org.whispersystems.libsignal.util.guava.Optional;
|
import org.whispersystems.libsignal.util.guava.Optional;
|
||||||
import org.whispersystems.signalservice.api.CancelationException;
|
import org.whispersystems.signalservice.api.CancelationException;
|
||||||
|
@ -104,7 +106,9 @@ public class TypingSendJob extends BaseJob {
|
||||||
groupId = Optional.of(recipient.requireGroupId().getDecodedId());
|
groupId = Optional.of(recipient.requireGroupId().getDecodedId());
|
||||||
}
|
}
|
||||||
|
|
||||||
recipients = Stream.of(recipients).map(Recipient::resolve).toList();
|
recipients = RecipientUtil.getEligibleForSending(Stream.of(recipients)
|
||||||
|
.map(Recipient::resolve)
|
||||||
|
.toList());
|
||||||
|
|
||||||
SignalServiceMessageSender messageSender = ApplicationDependencies.getSignalServiceMessageSender();
|
SignalServiceMessageSender messageSender = ApplicationDependencies.getSignalServiceMessageSender();
|
||||||
List<SignalServiceAddress> addresses = RecipientUtil.toSignalServiceAddressesFromResolved(context, recipients);
|
List<SignalServiceAddress> addresses = RecipientUtil.toSignalServiceAddressesFromResolved(context, recipients);
|
||||||
|
|
|
@ -7,6 +7,7 @@ import androidx.annotation.Nullable;
|
||||||
import androidx.annotation.WorkerThread;
|
import androidx.annotation.WorkerThread;
|
||||||
|
|
||||||
import com.annimon.stream.Stream;
|
import com.annimon.stream.Stream;
|
||||||
|
import com.google.android.gms.common.Feature;
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.contacts.sync.DirectoryHelper;
|
import org.thoughtcrime.securesms.contacts.sync.DirectoryHelper;
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||||
|
@ -29,6 +30,7 @@ import org.whispersystems.signalservice.api.push.SignalServiceAddress;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class RecipientUtil {
|
public class RecipientUtil {
|
||||||
|
@ -38,7 +40,8 @@ public class RecipientUtil {
|
||||||
/**
|
/**
|
||||||
* This method will do it's best to craft a fully-populated {@link SignalServiceAddress} based on
|
* This method will do it's best to craft a fully-populated {@link SignalServiceAddress} based on
|
||||||
* the provided recipient. This includes performing a possible network request if no UUID is
|
* the provided recipient. This includes performing a possible network request if no UUID is
|
||||||
* available.
|
* available. If the request to get a UUID fails, the exception is swallowed an an E164-only
|
||||||
|
* recipient is returned.
|
||||||
*/
|
*/
|
||||||
@WorkerThread
|
@WorkerThread
|
||||||
public static @NonNull SignalServiceAddress toSignalServiceAddressBestEffort(@NonNull Context context, @NonNull Recipient recipient) {
|
public static @NonNull SignalServiceAddress toSignalServiceAddressBestEffort(@NonNull Context context, @NonNull Recipient recipient) {
|
||||||
|
@ -53,7 +56,7 @@ public class RecipientUtil {
|
||||||
/**
|
/**
|
||||||
* This method will do it's best to craft a fully-populated {@link SignalServiceAddress} based on
|
* This method will do it's best to craft a fully-populated {@link SignalServiceAddress} based on
|
||||||
* the provided recipient. This includes performing a possible network request if no UUID is
|
* the provided recipient. This includes performing a possible network request if no UUID is
|
||||||
* available.
|
* available. If the request to get a UUID fails, an IOException is thrown.
|
||||||
*/
|
*/
|
||||||
@WorkerThread
|
@WorkerThread
|
||||||
public static @NonNull SignalServiceAddress toSignalServiceAddress(@NonNull Context context, @NonNull Recipient recipient)
|
public static @NonNull SignalServiceAddress toSignalServiceAddress(@NonNull Context context, @NonNull Recipient recipient)
|
||||||
|
@ -79,25 +82,27 @@ public class RecipientUtil {
|
||||||
public static @NonNull List<SignalServiceAddress> toSignalServiceAddresses(@NonNull Context context, @NonNull List<RecipientId> recipients)
|
public static @NonNull List<SignalServiceAddress> toSignalServiceAddresses(@NonNull Context context, @NonNull List<RecipientId> recipients)
|
||||||
throws IOException
|
throws IOException
|
||||||
{
|
{
|
||||||
List<SignalServiceAddress> addresses = new ArrayList<>(recipients.size());
|
return toSignalServiceAddressesFromResolved(context, Recipient.resolvedList(recipients));
|
||||||
|
|
||||||
for (RecipientId id : recipients) {
|
|
||||||
addresses.add(toSignalServiceAddress(context, Recipient.resolved(id)));
|
|
||||||
}
|
|
||||||
|
|
||||||
return addresses;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static @NonNull List<SignalServiceAddress> toSignalServiceAddressesFromResolved(@NonNull Context context, @NonNull List<Recipient> recipients)
|
public static @NonNull List<SignalServiceAddress> toSignalServiceAddressesFromResolved(@NonNull Context context, @NonNull List<Recipient> recipients)
|
||||||
throws IOException
|
throws IOException
|
||||||
{
|
{
|
||||||
List<SignalServiceAddress> addresses = new ArrayList<>(recipients.size());
|
if (FeatureFlags.cds()) {
|
||||||
|
List<Recipient> recipientsWithoutUuids = Stream.of(recipients)
|
||||||
|
.map(Recipient::resolve)
|
||||||
|
.filterNot(Recipient::hasUuid)
|
||||||
|
.toList();
|
||||||
|
|
||||||
for (Recipient recipient : recipients) {
|
if (recipientsWithoutUuids.size() > 0) {
|
||||||
addresses.add(toSignalServiceAddress(context, recipient));
|
DirectoryHelper.refreshDirectoryFor(context, recipientsWithoutUuids, false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return addresses;
|
return Stream.of(recipients)
|
||||||
|
.map(Recipient::resolve)
|
||||||
|
.map(r -> new SignalServiceAddress(r.getUuid().orNull(), r.getE164().orNull()))
|
||||||
|
.toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isBlockable(@NonNull Recipient recipient) {
|
public static boolean isBlockable(@NonNull Recipient recipient) {
|
||||||
|
@ -105,6 +110,16 @@ public class RecipientUtil {
|
||||||
return resolved.isPushGroup() || resolved.hasServiceIdentifier();
|
return resolved.isPushGroup() || resolved.hasServiceIdentifier();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static List<Recipient> getEligibleForSending(@NonNull List<Recipient> recipients) {
|
||||||
|
if (FeatureFlags.cds()) {
|
||||||
|
return Stream.of(recipients)
|
||||||
|
.filter(r -> r.getRegistered() != RegisteredState.NOT_REGISTERED)
|
||||||
|
.toList();
|
||||||
|
} else {
|
||||||
|
return recipients;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* You can call this for non-groups and not have to handle any network errors.
|
* You can call this for non-groups and not have to handle any network errors.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -13,7 +13,7 @@ import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
public class DirectoryRefreshListener extends PersistentAlarmManagerListener {
|
public class DirectoryRefreshListener extends PersistentAlarmManagerListener {
|
||||||
|
|
||||||
private static final long INTERVAL = TimeUnit.HOURS.toMillis(12);
|
private static final long INTERVAL = TimeUnit.HOURS.toMillis(6);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected long getNextScheduledExecutionTime(Context context) {
|
protected long getNextScheduledExecutionTime(Context context) {
|
||||||
|
|
|
@ -59,7 +59,7 @@ public final class FeatureFlags {
|
||||||
private static final String GROUPS_V2_JOIN_VERSION = "android.groupsv2.joinVersion";
|
private static final String GROUPS_V2_JOIN_VERSION = "android.groupsv2.joinVersion";
|
||||||
private static final String GROUPS_V2_LINKS_VERSION = "android.groupsv2.manageGroupLinksVersion";
|
private static final String GROUPS_V2_LINKS_VERSION = "android.groupsv2.manageGroupLinksVersion";
|
||||||
private static final String GROUPS_V2_CAPACITY = "global.groupsv2.maxGroupSize";
|
private static final String GROUPS_V2_CAPACITY = "global.groupsv2.maxGroupSize";
|
||||||
private static final String CDS = "android.cds.4";
|
private static final String CDS_VERSION = "android.cdsVersion";
|
||||||
private static final String INTERNAL_USER = "android.internalUser";
|
private static final String INTERNAL_USER = "android.internalUser";
|
||||||
private static final String MENTIONS = "android.mentions";
|
private static final String MENTIONS = "android.mentions";
|
||||||
private static final String VERIFY_V2 = "android.verifyV2";
|
private static final String VERIFY_V2 = "android.verifyV2";
|
||||||
|
@ -77,7 +77,7 @@ public final class FeatureFlags {
|
||||||
GROUPS_V2_CAPACITY,
|
GROUPS_V2_CAPACITY,
|
||||||
GROUPS_V2_JOIN_VERSION,
|
GROUPS_V2_JOIN_VERSION,
|
||||||
GROUPS_V2_LINKS_VERSION,
|
GROUPS_V2_LINKS_VERSION,
|
||||||
CDS,
|
CDS_VERSION,
|
||||||
INTERNAL_USER,
|
INTERNAL_USER,
|
||||||
USERNAMES,
|
USERNAMES,
|
||||||
MENTIONS,
|
MENTIONS,
|
||||||
|
@ -106,7 +106,7 @@ public final class FeatureFlags {
|
||||||
GROUPS_V2_CREATE,
|
GROUPS_V2_CREATE,
|
||||||
GROUPS_V2_JOIN_VERSION,
|
GROUPS_V2_JOIN_VERSION,
|
||||||
VERIFY_V2,
|
VERIFY_V2,
|
||||||
CDS
|
CDS_VERSION
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -266,7 +266,7 @@ public final class FeatureFlags {
|
||||||
|
|
||||||
/** Whether or not to use the new contact discovery service endpoint, which supports UUIDs. */
|
/** Whether or not to use the new contact discovery service endpoint, which supports UUIDs. */
|
||||||
public static boolean cds() {
|
public static boolean cds() {
|
||||||
return getBoolean(CDS, false);
|
return getVersionFlag(CDS_VERSION) == VersionFlag.ON;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Whether or not we allow mentions send support in groups. */
|
/** Whether or not we allow mentions send support in groups. */
|
||||||
|
|
|
@ -368,6 +368,10 @@ public class SignalServiceAccountManager {
|
||||||
public Map<String, UUID> getRegisteredUsers(KeyStore iasKeyStore, Set<String> e164numbers, String mrenclave)
|
public Map<String, UUID> getRegisteredUsers(KeyStore iasKeyStore, Set<String> e164numbers, String mrenclave)
|
||||||
throws IOException, Quote.InvalidQuoteFormatException, UnauthenticatedQuoteException, SignatureException, UnauthenticatedResponseException
|
throws IOException, Quote.InvalidQuoteFormatException, UnauthenticatedQuoteException, SignatureException, UnauthenticatedResponseException
|
||||||
{
|
{
|
||||||
|
if (e164numbers.isEmpty()) {
|
||||||
|
return Collections.emptyMap();
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
String authorization = this.pushServiceSocket.getContactDiscoveryAuthorization();
|
String authorization = this.pushServiceSocket.getContactDiscoveryAuthorization();
|
||||||
Map<String, RemoteAttestation> attestations = RemoteAttestationUtil.getAndVerifyMultiRemoteAttestation(pushServiceSocket,
|
Map<String, RemoteAttestation> attestations = RemoteAttestationUtil.getAndVerifyMultiRemoteAttestation(pushServiceSocket,
|
||||||
|
|
Loading…
Add table
Reference in a new issue