diff --git a/app/src/main/java/org/thoughtcrime/securesms/groups/GroupManagerV1.java b/app/src/main/java/org/thoughtcrime/securesms/groups/GroupManagerV1.java index 6e7f3a1d4..0fb01d815 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/groups/GroupManagerV1.java +++ b/app/src/main/java/org/thoughtcrime/securesms/groups/GroupManagerV1.java @@ -36,6 +36,7 @@ import org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupC import java.io.ByteArrayInputStream; import java.io.IOException; import java.security.SecureRandom; +import java.util.ArrayList; import java.util.Collections; import java.util.LinkedList; import java.util.List; @@ -122,12 +123,15 @@ final class GroupManagerV1 { RecipientId groupRecipientId = DatabaseFactory.getRecipientDatabase(context).getOrInsertFromGroupId(groupId); Recipient groupRecipient = Recipient.resolved(groupRecipientId); - List uuidMembers = new LinkedList<>(); - List e164Members = new LinkedList<>(); + List uuidMembers = new ArrayList<>(members.size()); + List e164Members = new ArrayList<>(members.size()); for (RecipientId member : members) { Recipient recipient = Recipient.resolved(member); - uuidMembers.add(GroupV1MessageProcessor.createMember(RecipientUtil.toSignalServiceAddress(context, recipient))); + if (recipient.hasE164()) { + e164Members.add(recipient.requireE164()); + uuidMembers.add(GroupV1MessageProcessor.createMember(recipient.requireE164())); + } } GroupContext.Builder groupContextBuilder = GroupContext.newBuilder() @@ -135,7 +139,9 @@ final class GroupManagerV1 { .setType(GroupContext.Type.UPDATE) .addAllMembersE164(e164Members) .addAllMembers(uuidMembers); + if (groupName != null) groupContextBuilder.setName(groupName); + GroupContext groupContext = groupContextBuilder.build(); if (avatar != null) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/groups/GroupV1MessageProcessor.java b/app/src/main/java/org/thoughtcrime/securesms/groups/GroupV1MessageProcessor.java index 91225b7ec..253ff767d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/groups/GroupV1MessageProcessor.java +++ b/app/src/main/java/org/thoughtcrime/securesms/groups/GroupV1MessageProcessor.java @@ -101,7 +101,7 @@ public final class GroupV1MessageProcessor { if (group.getMembers().isPresent()) { for (SignalServiceAddress member : group.getMembers().get()) { - members.add(Recipient.externalPush(context, member).getId()); + members.add(Recipient.externalGV1Member(context, member).getId()); } } @@ -131,8 +131,10 @@ public final class GroupV1MessageProcessor { Set recordMembers = new HashSet<>(groupRecord.getMembers()); Set messageMembers = new HashSet<>(); - for (SignalServiceAddress messageMember : group.getMembers().get()) { - messageMembers.add(Recipient.externalPush(context, messageMember).getId()); + if (group.getMembers().isPresent()) { + for (SignalServiceAddress messageMember : group.getMembers().get()) { + messageMembers.add(Recipient.externalGV1Member(context, messageMember).getId()); + } } Set addedMembers = new HashSet<>(messageMembers); @@ -150,18 +152,19 @@ public final class GroupV1MessageProcessor { database.updateMembers(id, new LinkedList<>(unionMembers)); builder.clearMembers(); + builder.clearMembersE164(); for (RecipientId addedMember : addedMembers) { Recipient recipient = Recipient.resolved(addedMember); if (recipient.getE164().isPresent()) { - builder.addMembersE164(recipient.getE164().get()); + builder.addMembersE164(recipient.requireE164()); + builder.addMembers(createMember(recipient.requireE164())); } - - builder.addMembers(createMember(RecipientUtil.toSignalServiceAddress(context, recipient))); } } else { builder.clearMembers(); + builder.clearMembersE164(); } if (missingMembers.size() > 0) { @@ -287,6 +290,8 @@ public final class GroupV1MessageProcessor { .map(a -> a.getNumber().get()) .toList()); builder.addAllMembers(Stream.of(group.getMembers().get()) + .filter(address -> address.getNumber().isPresent()) + .map(address -> address.getNumber().get()) .map(GroupV1MessageProcessor::createMember) .toList()); } @@ -294,17 +299,9 @@ public final class GroupV1MessageProcessor { return builder; } - public static GroupContext.Member createMember(SignalServiceAddress address) { + public static GroupContext.Member createMember(@NonNull String e164) { GroupContext.Member.Builder member = GroupContext.Member.newBuilder(); - - if (address.getUuid().isPresent()) { - member.setUuid(address.getUuid().get().toString()); - } - - if (address.getNumber().isPresent()) { - member.setE164(address.getNumber().get()); - } - + member.setE164(e164); return member.build(); } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/PushGroupSendJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/PushGroupSendJob.java index 52a932648..ab47a2835 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/PushGroupSendJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/PushGroupSendJob.java @@ -323,8 +323,8 @@ public final class PushGroupSendJob extends PushSendJob { GroupContext groupContext = properties.getGroupContext(); SignalServiceAttachment avatar = attachmentPointers.isEmpty() ? null : attachmentPointers.get(0); SignalServiceGroup.Type type = properties.isQuit() ? SignalServiceGroup.Type.QUIT : SignalServiceGroup.Type.UPDATE; - List members = Stream.of(groupContext.getMembersList()) - .map(m -> new SignalServiceAddress(UuidUtil.parseOrNull(m.getUuid()), m.getE164())) + List members = Stream.of(groupContext.getMembersE164List()) + .map(e164 -> new SignalServiceAddress(null, e164)) .toList(); SignalServiceGroup group = new SignalServiceGroup(type, groupId.getDecodedId(), groupContext.getName(), members, avatar); SignalServiceDataMessage groupDataMessage = SignalServiceDataMessage.newBuilder() diff --git a/app/src/main/java/org/thoughtcrime/securesms/mms/MessageGroupContext.java b/app/src/main/java/org/thoughtcrime/securesms/mms/MessageGroupContext.java index ff93e6632..9083b7945 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mms/MessageGroupContext.java +++ b/app/src/main/java/org/thoughtcrime/securesms/mms/MessageGroupContext.java @@ -3,6 +3,8 @@ package org.thoughtcrime.securesms.mms; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import com.annimon.stream.Stream; + import org.signal.storageservice.protos.groups.local.DecryptedMember; import org.signal.zkgroup.InvalidInputException; import org.signal.zkgroup.groups.GroupMasterKey; @@ -11,6 +13,7 @@ import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.RecipientId; import org.thoughtcrime.securesms.util.Base64; import org.whispersystems.signalservice.api.groupsv2.DecryptedGroupUtil; +import org.whispersystems.signalservice.api.push.SignalServiceAddress; import org.whispersystems.signalservice.api.util.UuidUtil; import org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext; import org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContextV2; @@ -123,20 +126,13 @@ public final class MessageGroupContext { @Override public @NonNull List getMembersListExcludingSelf() { - List membersList = groupContext.getMembersList(); - if (membersList.isEmpty()) { - return Collections.emptyList(); - } else { - LinkedList members = new LinkedList<>(); + RecipientId selfId = Recipient.self().getId(); - for (GroupContext.Member member : membersList) { - RecipientId recipient = RecipientId.from(UuidUtil.parseOrNull(member.getUuid()), member.getE164()); - if (!Recipient.self().getId().equals(recipient)) { - members.add(recipient); - } - } - return members; - } + return Stream.of(groupContext.getMembersE164List()) + .map(e164 -> new SignalServiceAddress(null, e164)) + .map(RecipientId::from) + .filterNot(selfId::equals) + .toList(); } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/recipients/Recipient.java b/app/src/main/java/org/thoughtcrime/securesms/recipients/Recipient.java index 4eb70747b..fa894e2ba 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/recipients/Recipient.java +++ b/app/src/main/java/org/thoughtcrime/securesms/recipients/Recipient.java @@ -157,6 +157,20 @@ public class Recipient { return externalPush(context, signalServiceAddress.getUuid().orNull(), signalServiceAddress.getNumber().orNull(), false); } + /** + * Returns a fully-populated {@link Recipient} based off of a {@link SignalServiceAddress}, + * creating one in the database if necessary. We special-case GV1 members because we want to + * prioritize E164 addresses and not use the UUIDs if possible. + */ + @WorkerThread + public static @NonNull Recipient externalGV1Member(@NonNull Context context, @NonNull SignalServiceAddress address) { + if (address.getNumber().isPresent()) { + return externalPush(context, null, address.getNumber().get(), false); + } else { + return externalPush(context, address.getUuid().orNull(), null, false); + } + } + /** * Returns a fully-populated {@link Recipient} based off of a {@link SignalServiceAddress}, * creating one in the database if necessary. This should only used for high-trust sources, diff --git a/libsignal/service/src/main/java/org/whispersystems/signalservice/api/SignalServiceMessageSender.java b/libsignal/service/src/main/java/org/whispersystems/signalservice/api/SignalServiceMessageSender.java index 9b7d16fbb..e6ba2a904 100644 --- a/libsignal/service/src/main/java/org/whispersystems/signalservice/api/SignalServiceMessageSender.java +++ b/libsignal/service/src/main/java/org/whispersystems/signalservice/api/SignalServiceMessageSender.java @@ -1060,19 +1060,12 @@ public class SignalServiceMessageSender { for (SignalServiceAddress address : group.getMembers().get()) { if (address.getNumber().isPresent()) { builder.addMembersE164(address.getNumber().get()); - } - GroupContext.Member.Builder memberBuilder = GroupContext.Member.newBuilder(); - - if (address.getUuid().isPresent()) { - memberBuilder.setUuid(address.getUuid().get().toString()); - } - - if (address.getNumber().isPresent()) { + GroupContext.Member.Builder memberBuilder = GroupContext.Member.newBuilder(); memberBuilder.setE164(address.getNumber().get()); - } - builder.addMembers(memberBuilder.build()); + builder.addMembers(memberBuilder.build()); + } } } diff --git a/libsignal/service/src/main/java/org/whispersystems/signalservice/api/messages/SignalServiceContent.java b/libsignal/service/src/main/java/org/whispersystems/signalservice/api/messages/SignalServiceContent.java index 76783f96f..023db02f8 100644 --- a/libsignal/service/src/main/java/org/whispersystems/signalservice/api/messages/SignalServiceContent.java +++ b/libsignal/service/src/main/java/org/whispersystems/signalservice/api/messages/SignalServiceContent.java @@ -873,8 +873,8 @@ public final class SignalServiceContent { members = new ArrayList<>(content.getGroup().getMembersCount()); for (SignalServiceProtos.GroupContext.Member member : content.getGroup().getMembersList()) { - if (SignalServiceAddress.isValidAddress(member.getUuid(), member.getE164())) { - members.add(new SignalServiceAddress(UuidUtil.parseOrNull(member.getUuid()), member.getE164())); + if (SignalServiceAddress.isValidAddress(null, member.getE164())) { + members.add(new SignalServiceAddress(null, member.getE164())); } else { throw new ProtocolInvalidMessageException(new InvalidMessageException("GroupContext.Member had no address!"), null, 0); } diff --git a/libsignal/service/src/main/java/org/whispersystems/signalservice/api/messages/multidevice/DeviceGroupsInputStream.java b/libsignal/service/src/main/java/org/whispersystems/signalservice/api/messages/multidevice/DeviceGroupsInputStream.java index c106c215e..29a758d3d 100644 --- a/libsignal/service/src/main/java/org/whispersystems/signalservice/api/messages/multidevice/DeviceGroupsInputStream.java +++ b/libsignal/service/src/main/java/org/whispersystems/signalservice/api/messages/multidevice/DeviceGroupsInputStream.java @@ -61,8 +61,8 @@ public class DeviceGroupsInputStream extends ChunkedInputStream{ List addressMembers = new ArrayList<>(members.size()); for (GroupDetails.Member member : members) { - if (SignalServiceAddress.isValidAddress(member.getUuid(), member.getE164())) { - addressMembers.add(new SignalServiceAddress(UuidUtil.parseOrNull(member.getUuid()), member.getE164())); + if (SignalServiceAddress.isValidAddress(null, member.getE164())) { + addressMembers.add(new SignalServiceAddress(null, member.getE164())); } else { throw new IOException("Missing group member address!"); } diff --git a/libsignal/service/src/main/java/org/whispersystems/signalservice/api/messages/multidevice/DeviceGroupsOutputStream.java b/libsignal/service/src/main/java/org/whispersystems/signalservice/api/messages/multidevice/DeviceGroupsOutputStream.java index 3db85b642..848fd368d 100644 --- a/libsignal/service/src/main/java/org/whispersystems/signalservice/api/messages/multidevice/DeviceGroupsOutputStream.java +++ b/libsignal/service/src/main/java/org/whispersystems/signalservice/api/messages/multidevice/DeviceGroupsOutputStream.java @@ -65,18 +65,13 @@ public class DeviceGroupsOutputStream extends ChunkedOutputStream { List membersE164 = new ArrayList<>(group.getMembers().size()); for (SignalServiceAddress address : group.getMembers()) { - GroupDetails.Member.Builder builder = GroupDetails.Member.newBuilder(); - - if (address.getUuid().isPresent()) { - builder.setUuid(address.getUuid().get().toString()); - } - if (address.getNumber().isPresent()) { - builder.setE164(address.getNumber().get()); membersE164.add(address.getNumber().get()); - } - members.add(builder.build()); + GroupDetails.Member.Builder builder = GroupDetails.Member.newBuilder(); + builder.setE164(address.getNumber().get()); + members.add(builder.build()); + } } groupDetails.addAllMembers(members); diff --git a/libsignal/service/src/main/proto/SignalService.proto b/libsignal/service/src/main/proto/SignalService.proto index a2f6b0e2c..1e4649608 100644 --- a/libsignal/service/src/main/proto/SignalService.proto +++ b/libsignal/service/src/main/proto/SignalService.proto @@ -435,7 +435,7 @@ message GroupContext { } message Member { - optional string uuid = 1; + // 1 is reserved optional string e164 = 2; } @@ -479,7 +479,7 @@ message GroupDetails { } message Member { - optional string uuid = 1; + // 1 is reserved optional string e164 = 2; }