Improve mapping SignalServiceAddresses to Recipients.
This commit is contained in:
parent
7f0a0bef5a
commit
4677883838
10 changed files with 62 additions and 56 deletions
|
@ -9,11 +9,11 @@ import org.thoughtcrime.securesms.recipients.Recipient
|
||||||
import org.thoughtcrime.securesms.recipients.RecipientId
|
import org.thoughtcrime.securesms.recipients.RecipientId
|
||||||
import org.thoughtcrime.securesms.util.CursorUtil
|
import org.thoughtcrime.securesms.util.CursorUtil
|
||||||
import org.thoughtcrime.securesms.util.FeatureFlags
|
import org.thoughtcrime.securesms.util.FeatureFlags
|
||||||
|
import org.thoughtcrime.securesms.util.RecipientAccessList
|
||||||
import org.thoughtcrime.securesms.util.SqlUtil
|
import org.thoughtcrime.securesms.util.SqlUtil
|
||||||
import org.whispersystems.signalservice.api.crypto.ContentHint
|
import org.whispersystems.signalservice.api.crypto.ContentHint
|
||||||
import org.whispersystems.signalservice.api.messages.SendMessageResult
|
import org.whispersystems.signalservice.api.messages.SendMessageResult
|
||||||
import org.whispersystems.signalservice.internal.push.SignalServiceProtos
|
import org.whispersystems.signalservice.internal.push.SignalServiceProtos
|
||||||
import java.util.UUID
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stores a 24-hr buffer of all outgoing messages. Used for the retry logic required for sender key.
|
* Stores a 24-hr buffer of all outgoing messages. Used for the retry logic required for sender key.
|
||||||
|
@ -176,19 +176,12 @@ class MessageSendLogDatabase constructor(context: Context?, databaseHelper: SQLC
|
||||||
fun insertIfPossible(sentTimestamp: Long, possibleRecipients: List<Recipient>, results: List<SendMessageResult>, contentHint: ContentHint, messageId: MessageId): Long {
|
fun insertIfPossible(sentTimestamp: Long, possibleRecipients: List<Recipient>, results: List<SendMessageResult>, contentHint: ContentHint, messageId: MessageId): Long {
|
||||||
if (!FeatureFlags.senderKey()) return -1
|
if (!FeatureFlags.senderKey()) return -1
|
||||||
|
|
||||||
val recipientsByUuid: Map<UUID, Recipient> = possibleRecipients.filter(Recipient::hasUuid).associateBy(Recipient::requireUuid, { it })
|
val accessList = RecipientAccessList(possibleRecipients)
|
||||||
val recipientsByE164: Map<String, Recipient> = possibleRecipients.filter(Recipient::hasE164).associateBy(Recipient::requireE164, { it })
|
|
||||||
|
|
||||||
val recipientDevices: List<RecipientDevice> = results
|
val recipientDevices: List<RecipientDevice> = results
|
||||||
.filter { it.isSuccess && it.success.content.isPresent }
|
.filter { it.isSuccess && it.success.content.isPresent }
|
||||||
.map { result ->
|
.map { result ->
|
||||||
val recipient: Recipient =
|
val recipient: Recipient = accessList.requireByAddress(result.address)
|
||||||
if (result.address.uuid.isPresent) {
|
|
||||||
recipientsByUuid[result.address.uuid.get()]!!
|
|
||||||
} else {
|
|
||||||
recipientsByE164[result.address.number.get()]!!
|
|
||||||
}
|
|
||||||
|
|
||||||
RecipientDevice(recipient.id, result.success.devices)
|
RecipientDevice(recipient.id, result.success.devices)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -166,7 +166,7 @@ public class GroupCallUpdateSendJob extends BaseJob {
|
||||||
ContentHint.DEFAULT,
|
ContentHint.DEFAULT,
|
||||||
dataMessage.build());
|
dataMessage.build());
|
||||||
|
|
||||||
return GroupSendJobHelper.getCompletedSends(context, results);
|
return GroupSendJobHelper.getCompletedSends(destinations, results);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Factory implements Job.Factory<GroupCallUpdateSendJob> {
|
public static class Factory implements Job.Factory<GroupCallUpdateSendJob> {
|
||||||
|
|
|
@ -6,6 +6,7 @@ import androidx.annotation.NonNull;
|
||||||
|
|
||||||
import org.signal.core.util.logging.Log;
|
import org.signal.core.util.logging.Log;
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
|
import org.thoughtcrime.securesms.util.RecipientAccessList;
|
||||||
import org.whispersystems.signalservice.api.messages.SendMessageResult;
|
import org.whispersystems.signalservice.api.messages.SendMessageResult;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
@ -19,11 +20,12 @@ final class GroupSendJobHelper {
|
||||||
private GroupSendJobHelper() {
|
private GroupSendJobHelper() {
|
||||||
}
|
}
|
||||||
|
|
||||||
static List<Recipient> getCompletedSends(@NonNull Context context, @NonNull Collection<SendMessageResult> results) {
|
static List<Recipient> getCompletedSends(@NonNull List<Recipient> possibleRecipients, @NonNull Collection<SendMessageResult> results) {
|
||||||
List<Recipient> completions = new ArrayList<>(results.size());
|
RecipientAccessList accessList = new RecipientAccessList(possibleRecipients);
|
||||||
|
List<Recipient> completions = new ArrayList<>(results.size());
|
||||||
|
|
||||||
for (SendMessageResult sendMessageResult : results) {
|
for (SendMessageResult sendMessageResult : results) {
|
||||||
Recipient recipient = Recipient.externalPush(context, sendMessageResult.getAddress());
|
Recipient recipient = accessList.requireByAddress(sendMessageResult.getAddress());
|
||||||
|
|
||||||
if (sendMessageResult.getIdentityFailure() != null) {
|
if (sendMessageResult.getIdentityFailure() != null) {
|
||||||
Log.w(TAG, "Identity failure for " + recipient.getId());
|
Log.w(TAG, "Identity failure for " + recipient.getId());
|
||||||
|
|
|
@ -156,7 +156,7 @@ public class ProfileKeySendJob extends BaseJob {
|
||||||
|
|
||||||
List<SendMessageResult> results = GroupSendUtil.sendUnresendableDataMessage(context, null, destinations, false, ContentHint.IMPLICIT, dataMessage.build());
|
List<SendMessageResult> results = GroupSendUtil.sendUnresendableDataMessage(context, null, destinations, false, ContentHint.IMPLICIT, dataMessage.build());
|
||||||
|
|
||||||
return GroupSendJobHelper.getCompletedSends(context, results);
|
return GroupSendJobHelper.getCompletedSends(destinations, results);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Factory implements Job.Factory<ProfileKeySendJob> {
|
public static class Factory implements Job.Factory<ProfileKeySendJob> {
|
||||||
|
|
|
@ -41,6 +41,7 @@ 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.GroupUtil;
|
import org.thoughtcrime.securesms.util.GroupUtil;
|
||||||
|
import org.thoughtcrime.securesms.util.RecipientAccessList;
|
||||||
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;
|
||||||
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
|
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
|
||||||
|
@ -191,17 +192,16 @@ public final class PushGroupSendJob extends PushSendJob {
|
||||||
else if (!existingNetworkFailures.isEmpty()) target = Stream.of(existingNetworkFailures).map(nf -> Recipient.resolved(nf.getRecipientId(context))).toList();
|
else if (!existingNetworkFailures.isEmpty()) target = Stream.of(existingNetworkFailures).map(nf -> Recipient.resolved(nf.getRecipientId(context))).toList();
|
||||||
else target = getGroupMessageRecipients(groupRecipient.requireGroupId(), messageId);
|
else target = getGroupMessageRecipients(groupRecipient.requireGroupId(), messageId);
|
||||||
|
|
||||||
Map<String, Recipient> idByE164 = Stream.of(target).filter(Recipient::hasE164).collect(Collectors.toMap(Recipient::requireE164, r -> r));
|
RecipientAccessList accessList = new RecipientAccessList(target);
|
||||||
Map<UUID, Recipient> idByUuid = Stream.of(target).filter(Recipient::hasUuid).collect(Collectors.toMap(Recipient::requireUuid, r -> r));
|
|
||||||
|
|
||||||
List<SendMessageResult> results = deliver(message, groupRecipient, target);
|
List<SendMessageResult> results = deliver(message, groupRecipient, target);
|
||||||
Log.i(TAG, JobLogger.format(this, "Finished send."));
|
Log.i(TAG, JobLogger.format(this, "Finished send."));
|
||||||
|
|
||||||
List<NetworkFailure> networkFailures = Stream.of(results).filter(SendMessageResult::isNetworkFailure).map(result -> new NetworkFailure(findId(result.getAddress(), idByE164, idByUuid))).toList();
|
List<NetworkFailure> networkFailures = Stream.of(results).filter(SendMessageResult::isNetworkFailure).map(result -> new NetworkFailure(accessList.requireIdByAddress(result.getAddress()))).toList();
|
||||||
List<IdentityKeyMismatch> identityMismatches = Stream.of(results).filter(result -> result.getIdentityFailure() != null).map(result -> new IdentityKeyMismatch(findId(result.getAddress(), idByE164, idByUuid), result.getIdentityFailure().getIdentityKey())).toList();
|
List<IdentityKeyMismatch> identityMismatches = Stream.of(results).filter(result -> result.getIdentityFailure() != null).map(result -> new IdentityKeyMismatch(accessList.requireIdByAddress(result.getAddress()), result.getIdentityFailure().getIdentityKey())).toList();
|
||||||
ProofRequiredException proofRequired = Stream.of(results).filter(r -> r.getProofRequiredFailure() != null).findLast().map(SendMessageResult::getProofRequiredFailure).orElse(null);
|
ProofRequiredException proofRequired = Stream.of(results).filter(r -> r.getProofRequiredFailure() != null).findLast().map(SendMessageResult::getProofRequiredFailure).orElse(null);
|
||||||
List<SendMessageResult> successes = Stream.of(results).filter(result -> result.getSuccess() != null).toList();
|
List<SendMessageResult> successes = Stream.of(results).filter(result -> result.getSuccess() != null).toList();
|
||||||
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<>(accessList.requireIdByAddress(result.getAddress()), 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();
|
||||||
|
@ -274,19 +274,6 @@ public final class PushGroupSendJob extends PushSendJob {
|
||||||
DatabaseFactory.getMmsDatabase(context).markAsSentFailed(messageId);
|
DatabaseFactory.getMmsDatabase(context).markAsSentFailed(messageId);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static @NonNull RecipientId findId(@NonNull SignalServiceAddress address,
|
|
||||||
@NonNull Map<String, Recipient> byE164,
|
|
||||||
@NonNull Map<UUID, Recipient> byUuid)
|
|
||||||
{
|
|
||||||
if (address.getNumber().isPresent() && byE164.containsKey(address.getNumber().get())) {
|
|
||||||
return Objects.requireNonNull(byE164.get(address.getNumber().get())).getId();
|
|
||||||
} else if (address.getUuid().isPresent() && byUuid.containsKey(address.getUuid().get())) {
|
|
||||||
return Objects.requireNonNull(byUuid.get(address.getUuid().get())).getId();
|
|
||||||
} else {
|
|
||||||
throw new IllegalStateException("Found an address that was never provided!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<SendMessageResult> deliver(OutgoingMediaMessage message, @NonNull Recipient groupRecipient, @NonNull List<Recipient> destinations)
|
private List<SendMessageResult> deliver(OutgoingMediaMessage message, @NonNull Recipient groupRecipient, @NonNull List<Recipient> destinations)
|
||||||
throws IOException, UntrustedIdentityException, UndeliverableMessageException
|
throws IOException, UntrustedIdentityException, UndeliverableMessageException
|
||||||
{
|
{
|
||||||
|
|
|
@ -169,7 +169,7 @@ public final class PushGroupSilentUpdateSendJob extends BaseJob {
|
||||||
|
|
||||||
List<SendMessageResult> results = GroupSendUtil.sendUnresendableDataMessage(context, groupId, destinations, false, ContentHint.IMPLICIT, groupDataMessage);
|
List<SendMessageResult> results = GroupSendUtil.sendUnresendableDataMessage(context, groupId, destinations, false, ContentHint.IMPLICIT, groupDataMessage);
|
||||||
|
|
||||||
return GroupSendJobHelper.getCompletedSends(context, results);
|
return GroupSendJobHelper.getCompletedSends(destinations, results);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Factory implements Job.Factory<PushGroupSilentUpdateSendJob> {
|
public static class Factory implements Job.Factory<PushGroupSilentUpdateSendJob> {
|
||||||
|
|
|
@ -242,7 +242,7 @@ public class ReactionSendJob extends BaseJob {
|
||||||
new MessageId(messageId, isMms),
|
new MessageId(messageId, isMms),
|
||||||
dataMessage);
|
dataMessage);
|
||||||
|
|
||||||
return GroupSendJobHelper.getCompletedSends(context, results);
|
return GroupSendJobHelper.getCompletedSends(destinations, results);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static SignalServiceDataMessage.Reaction buildReaction(@NonNull Context context,
|
private static SignalServiceDataMessage.Reaction buildReaction(@NonNull Context context,
|
||||||
|
|
|
@ -193,7 +193,7 @@ public class RemoteDeleteSendJob extends BaseJob {
|
||||||
new MessageId(messageId, isMms),
|
new MessageId(messageId, isMms),
|
||||||
dataMessage);
|
dataMessage);
|
||||||
|
|
||||||
return GroupSendJobHelper.getCompletedSends(context, results);
|
return GroupSendJobHelper.getCompletedSends(destinations, results);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Factory implements Job.Factory<RemoteDeleteSendJob> {
|
public static class Factory implements Job.Factory<RemoteDeleteSendJob> {
|
||||||
|
|
|
@ -19,6 +19,7 @@ import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||||
import org.thoughtcrime.securesms.recipients.RecipientUtil;
|
import org.thoughtcrime.securesms.recipients.RecipientUtil;
|
||||||
import org.thoughtcrime.securesms.util.FeatureFlags;
|
import org.thoughtcrime.securesms.util.FeatureFlags;
|
||||||
|
import org.thoughtcrime.securesms.util.RecipientAccessList;
|
||||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||||
import org.whispersystems.libsignal.InvalidKeyException;
|
import org.whispersystems.libsignal.InvalidKeyException;
|
||||||
import org.whispersystems.libsignal.NoSessionException;
|
import org.whispersystems.libsignal.NoSessionException;
|
||||||
|
@ -410,23 +411,12 @@ public final class GroupSendUtil {
|
||||||
|
|
||||||
private final Map<RecipientId, Optional<UnidentifiedAccessPair>> accessById;
|
private final Map<RecipientId, Optional<UnidentifiedAccessPair>> accessById;
|
||||||
private final Map<RecipientId, SignalServiceAddress> addressById;
|
private final Map<RecipientId, SignalServiceAddress> addressById;
|
||||||
private final Map<UUID, RecipientId> idByUuid;
|
private final RecipientAccessList accessList;
|
||||||
private final Map<String, RecipientId> idByE164;
|
|
||||||
|
|
||||||
RecipientData(@NonNull Context context, @NonNull List<Recipient> recipients) throws IOException {
|
RecipientData(@NonNull Context context, @NonNull List<Recipient> recipients) throws IOException {
|
||||||
this.accessById = UnidentifiedAccessUtil.getAccessMapFor(context, recipients);
|
this.accessById = UnidentifiedAccessUtil.getAccessMapFor(context, recipients);
|
||||||
this.addressById = mapAddresses(context, recipients);
|
this.addressById = mapAddresses(context, recipients);
|
||||||
this.idByUuid = new HashMap<>(recipients.size());
|
this.accessList = new RecipientAccessList(recipients);
|
||||||
this.idByE164 = new HashMap<>(recipients.size());
|
|
||||||
|
|
||||||
for (Recipient recipient : recipients) {
|
|
||||||
if (recipient.hasUuid()) {
|
|
||||||
idByUuid.put(recipient.requireUuid(), recipient.getId());
|
|
||||||
}
|
|
||||||
if (recipient.hasE164()) {
|
|
||||||
idByE164.put(recipient.requireE164(), recipient.getId());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull SignalServiceAddress getAddress(@NonNull RecipientId id) {
|
@NonNull SignalServiceAddress getAddress(@NonNull RecipientId id) {
|
||||||
|
@ -442,13 +432,7 @@ public final class GroupSendUtil {
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull RecipientId requireRecipientId(@NonNull SignalServiceAddress address) {
|
@NonNull RecipientId requireRecipientId(@NonNull SignalServiceAddress address) {
|
||||||
if (address.getUuid().isPresent() && idByUuid.containsKey(address.getUuid().get())) {
|
return accessList.requireIdByAddress(address);
|
||||||
return Objects.requireNonNull(idByUuid.get(address.getUuid().get()));
|
|
||||||
} else if (address.getNumber().isPresent() && idByE164.containsKey(address.getNumber().get())) {
|
|
||||||
return Objects.requireNonNull(idByE164.get(address.getNumber().get()));
|
|
||||||
} else {
|
|
||||||
throw new IllegalStateException();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static @NonNull Map<RecipientId, SignalServiceAddress> mapAddresses(@NonNull Context context, @NonNull List<Recipient> recipients) throws IOException {
|
private static @NonNull Map<RecipientId, SignalServiceAddress> mapAddresses(@NonNull Context context, @NonNull List<Recipient> recipients) throws IOException {
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
package org.thoughtcrime.securesms.util
|
||||||
|
|
||||||
|
import org.thoughtcrime.securesms.recipients.Recipient
|
||||||
|
import org.thoughtcrime.securesms.recipients.RecipientId
|
||||||
|
import org.whispersystems.signalservice.api.push.SignalServiceAddress
|
||||||
|
import java.lang.IllegalArgumentException
|
||||||
|
import java.util.UUID
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A list of Recipients, but with some helpful methods for retrieving them by various properties. Uses lazy properties to ensure that it will be as performant
|
||||||
|
* as a regular list if you don't call any of the extra methods.
|
||||||
|
*/
|
||||||
|
class RecipientAccessList(private val recipients: List<Recipient>) : List<Recipient> by recipients {
|
||||||
|
|
||||||
|
private val byUuid: Map<UUID, Recipient> by lazy {
|
||||||
|
recipients
|
||||||
|
.filter { it.hasUuid() }
|
||||||
|
.associateBy { it.requireUuid() }
|
||||||
|
}
|
||||||
|
|
||||||
|
private val byE164: Map<String, Recipient> by lazy {
|
||||||
|
recipients
|
||||||
|
.filter { it.hasE164() }
|
||||||
|
.associateBy { it.requireE164() }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun requireByAddress(address: SignalServiceAddress): Recipient {
|
||||||
|
if (address.uuid.isPresent && byUuid.containsKey(address.uuid.get())) {
|
||||||
|
return byUuid.get(address.uuid.get())!!
|
||||||
|
} else if (address.number.isPresent && byE164.containsKey(address.number.get())) {
|
||||||
|
return byE164.get(address.number.get())!!
|
||||||
|
} else {
|
||||||
|
throw IllegalArgumentException("Could not find a matching recipient!")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun requireIdByAddress(address: SignalServiceAddress): RecipientId {
|
||||||
|
return requireByAddress(address).id
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue