Improve mapping SignalServiceAddresses to Recipients.

This commit is contained in:
Greyson Parrelli 2021-07-02 17:27:19 -04:00 committed by Alex Hart
parent 7f0a0bef5a
commit 4677883838
10 changed files with 62 additions and 56 deletions

View file

@ -9,11 +9,11 @@ import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.recipients.RecipientId
import org.thoughtcrime.securesms.util.CursorUtil
import org.thoughtcrime.securesms.util.FeatureFlags
import org.thoughtcrime.securesms.util.RecipientAccessList
import org.thoughtcrime.securesms.util.SqlUtil
import org.whispersystems.signalservice.api.crypto.ContentHint
import org.whispersystems.signalservice.api.messages.SendMessageResult
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.
@ -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 {
if (!FeatureFlags.senderKey()) return -1
val recipientsByUuid: Map<UUID, Recipient> = possibleRecipients.filter(Recipient::hasUuid).associateBy(Recipient::requireUuid, { it })
val recipientsByE164: Map<String, Recipient> = possibleRecipients.filter(Recipient::hasE164).associateBy(Recipient::requireE164, { it })
val accessList = RecipientAccessList(possibleRecipients)
val recipientDevices: List<RecipientDevice> = results
.filter { it.isSuccess && it.success.content.isPresent }
.map { result ->
val recipient: Recipient =
if (result.address.uuid.isPresent) {
recipientsByUuid[result.address.uuid.get()]!!
} else {
recipientsByE164[result.address.number.get()]!!
}
val recipient: Recipient = accessList.requireByAddress(result.address)
RecipientDevice(recipient.id, result.success.devices)
}

View file

@ -166,7 +166,7 @@ public class GroupCallUpdateSendJob extends BaseJob {
ContentHint.DEFAULT,
dataMessage.build());
return GroupSendJobHelper.getCompletedSends(context, results);
return GroupSendJobHelper.getCompletedSends(destinations, results);
}
public static class Factory implements Job.Factory<GroupCallUpdateSendJob> {

View file

@ -6,6 +6,7 @@ import androidx.annotation.NonNull;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.RecipientAccessList;
import org.whispersystems.signalservice.api.messages.SendMessageResult;
import java.util.ArrayList;
@ -19,11 +20,12 @@ final class GroupSendJobHelper {
private GroupSendJobHelper() {
}
static List<Recipient> getCompletedSends(@NonNull Context context, @NonNull Collection<SendMessageResult> results) {
List<Recipient> completions = new ArrayList<>(results.size());
static List<Recipient> getCompletedSends(@NonNull List<Recipient> possibleRecipients, @NonNull Collection<SendMessageResult> results) {
RecipientAccessList accessList = new RecipientAccessList(possibleRecipients);
List<Recipient> completions = new ArrayList<>(results.size());
for (SendMessageResult sendMessageResult : results) {
Recipient recipient = Recipient.externalPush(context, sendMessageResult.getAddress());
Recipient recipient = accessList.requireByAddress(sendMessageResult.getAddress());
if (sendMessageResult.getIdentityFailure() != null) {
Log.w(TAG, "Identity failure for " + recipient.getId());

View file

@ -156,7 +156,7 @@ public class ProfileKeySendJob extends BaseJob {
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> {

View file

@ -41,6 +41,7 @@ import org.thoughtcrime.securesms.recipients.RecipientUtil;
import org.thoughtcrime.securesms.transport.RetryLaterException;
import org.thoughtcrime.securesms.transport.UndeliverableMessageException;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.thoughtcrime.securesms.util.RecipientAccessList;
import org.whispersystems.libsignal.util.Pair;
import org.whispersystems.libsignal.util.guava.Optional;
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 target = getGroupMessageRecipients(groupRecipient.requireGroupId(), messageId);
Map<String, Recipient> idByE164 = Stream.of(target).filter(Recipient::hasE164).collect(Collectors.toMap(Recipient::requireE164, r -> r));
Map<UUID, Recipient> idByUuid = Stream.of(target).filter(Recipient::hasUuid).collect(Collectors.toMap(Recipient::requireUuid, r -> r));
RecipientAccessList accessList = new RecipientAccessList(target);
List<SendMessageResult> results = deliver(message, groupRecipient, target);
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<IdentityKeyMismatch> identityMismatches = Stream.of(results).filter(result -> result.getIdentityFailure() != null).map(result -> new IdentityKeyMismatch(findId(result.getAddress(), idByE164, idByUuid), result.getIdentityFailure().getIdentityKey())).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(accessList.requireIdByAddress(result.getAddress()), result.getIdentityFailure().getIdentityKey())).toList();
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<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());
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();
@ -274,19 +274,6 @@ public final class PushGroupSendJob extends PushSendJob {
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)
throws IOException, UntrustedIdentityException, UndeliverableMessageException
{

View file

@ -169,7 +169,7 @@ public final class PushGroupSilentUpdateSendJob extends BaseJob {
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> {

View file

@ -242,7 +242,7 @@ public class ReactionSendJob extends BaseJob {
new MessageId(messageId, isMms),
dataMessage);
return GroupSendJobHelper.getCompletedSends(context, results);
return GroupSendJobHelper.getCompletedSends(destinations, results);
}
private static SignalServiceDataMessage.Reaction buildReaction(@NonNull Context context,

View file

@ -193,7 +193,7 @@ public class RemoteDeleteSendJob extends BaseJob {
new MessageId(messageId, isMms),
dataMessage);
return GroupSendJobHelper.getCompletedSends(context, results);
return GroupSendJobHelper.getCompletedSends(destinations, results);
}
public static class Factory implements Job.Factory<RemoteDeleteSendJob> {

View file

@ -19,6 +19,7 @@ import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.recipients.RecipientUtil;
import org.thoughtcrime.securesms.util.FeatureFlags;
import org.thoughtcrime.securesms.util.RecipientAccessList;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.libsignal.InvalidKeyException;
import org.whispersystems.libsignal.NoSessionException;
@ -410,23 +411,12 @@ public final class GroupSendUtil {
private final Map<RecipientId, Optional<UnidentifiedAccessPair>> accessById;
private final Map<RecipientId, SignalServiceAddress> addressById;
private final Map<UUID, RecipientId> idByUuid;
private final Map<String, RecipientId> idByE164;
private final RecipientAccessList accessList;
RecipientData(@NonNull Context context, @NonNull List<Recipient> recipients) throws IOException {
this.accessById = UnidentifiedAccessUtil.getAccessMapFor(context, recipients);
this.addressById = mapAddresses(context, recipients);
this.idByUuid = new HashMap<>(recipients.size());
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());
}
}
this.accessList = new RecipientAccessList(recipients);
}
@NonNull SignalServiceAddress getAddress(@NonNull RecipientId id) {
@ -442,13 +432,7 @@ public final class GroupSendUtil {
}
@NonNull RecipientId requireRecipientId(@NonNull SignalServiceAddress address) {
if (address.getUuid().isPresent() && idByUuid.containsKey(address.getUuid().get())) {
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();
}
return accessList.requireIdByAddress(address);
}
private static @NonNull Map<RecipientId, SignalServiceAddress> mapAddresses(@NonNull Context context, @NonNull List<Recipient> recipients) throws IOException {

View file

@ -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
}
}