Switch MMS groups to use the group database infrastructure

Eliminate the concept of 'Recipients' (plural). There is now just
a 'Recipient', which contains an Address that is either an individual
or a group ID.

MMS groups now exist as part of the group database, just like push
groups.

// FREEBIE
This commit is contained in:
Moxie Marlinspike
2017-08-01 08:56:00 -07:00
parent 81682e0302
commit 375207f073
106 changed files with 1587 additions and 2192 deletions

View File

@@ -15,19 +15,21 @@ import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.AttachmentDatabase;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.GroupDatabase;
import org.thoughtcrime.securesms.database.ThreadDatabase;
import org.thoughtcrime.securesms.mms.OutgoingGroupMediaMessage;
import org.thoughtcrime.securesms.providers.SingleUseBlobProvider;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientFactory;
import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.sms.MessageSender;
import org.thoughtcrime.securesms.util.BitmapUtil;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.thoughtcrime.securesms.util.MediaUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.util.InvalidNumberException;
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext;
import java.io.IOException;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedList;
@@ -40,23 +42,93 @@ public class GroupManager {
@NonNull MasterSecret masterSecret,
@NonNull Set<Recipient> members,
@Nullable Bitmap avatar,
@Nullable String name)
throws InvalidNumberException
@Nullable String name,
boolean mms)
{
final byte[] avatarBytes = BitmapUtil.toByteArray(avatar);
final GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context);
final byte[] groupId = groupDatabase.allocateGroupId();
final Set<Address> memberAddresses = getMemberAddresses(context, members);
final String groupId = GroupUtil.getEncodedId(groupDatabase.allocateGroupId(), mms);
final Set<Address> memberAddresses = getMemberAddresses(members);
memberAddresses.add(Address.fromSerialized(TextSecurePreferences.getLocalNumber(context)));
groupDatabase.create(groupId, name, new LinkedList<>(memberAddresses), null, null);
groupDatabase.updateAvatar(groupId, avatarBytes);
return sendGroupUpdate(context, masterSecret, groupId, memberAddresses, name, avatarBytes);
if (!mms) {
groupDatabase.updateAvatar(groupId, avatarBytes);
return sendGroupUpdate(context, masterSecret, groupId, memberAddresses, name, avatarBytes);
} else {
Recipient groupRecipient = RecipientFactory.getRecipientFor(context, Address.fromSerialized(groupId), true);
long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(groupRecipient, ThreadDatabase.DistributionTypes.CONVERSATION);
return new GroupActionResult(groupRecipient, threadId);
}
}
private static Set<Address> getMemberAddresses(Context context, Collection<Recipient> recipients)
public static GroupActionResult updateGroup(@NonNull Context context,
@NonNull MasterSecret masterSecret,
@NonNull String groupId,
@NonNull Set<Recipient> members,
@Nullable Bitmap avatar,
@Nullable String name)
throws InvalidNumberException
{
final GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context);
final Set<Address> memberAddresses = getMemberAddresses(members);
final byte[] avatarBytes = BitmapUtil.toByteArray(avatar);
memberAddresses.add(Address.fromSerialized(TextSecurePreferences.getLocalNumber(context)));
groupDatabase.updateMembers(groupId, new LinkedList<>(memberAddresses));
groupDatabase.updateTitle(groupId, name);
groupDatabase.updateAvatar(groupId, avatarBytes);
if (!GroupUtil.isMmsGroup(groupId)) {
return sendGroupUpdate(context, masterSecret, groupId, memberAddresses, name, avatarBytes);
} else {
Recipient groupRecipient = RecipientFactory.getRecipientFor(context, Address.fromSerialized(groupId), true);
long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(groupRecipient);
return new GroupActionResult(groupRecipient, threadId);
}
}
private static GroupActionResult sendGroupUpdate(@NonNull Context context,
@NonNull MasterSecret masterSecret,
@NonNull String groupId,
@NonNull Set<Address> members,
@Nullable String groupName,
@Nullable byte[] avatar)
{
try {
Attachment avatarAttachment = null;
Address groupAddress = Address.fromSerialized(groupId);
Recipient groupRecipient = RecipientFactory.getRecipientFor(context, groupAddress, false);
List<String> numbers = new LinkedList<>();
for (Address member : members) {
numbers.add(member.serialize());
}
GroupContext.Builder groupContextBuilder = GroupContext.newBuilder()
.setId(ByteString.copyFrom(GroupUtil.getDecodedId(groupId)))
.setType(GroupContext.Type.UPDATE)
.addAllMembers(numbers);
if (groupName != null) groupContextBuilder.setName(groupName);
GroupContext groupContext = groupContextBuilder.build();
if (avatar != null) {
Uri avatarUri = SingleUseBlobProvider.getInstance().createUri(avatar);
avatarAttachment = new UriAttachment(avatarUri, MediaUtil.IMAGE_PNG, AttachmentDatabase.TRANSFER_PROGRESS_DONE, avatar.length, null, false);
}
OutgoingGroupMediaMessage outgoingMessage = new OutgoingGroupMediaMessage(groupRecipient, groupContext, avatarAttachment, System.currentTimeMillis(), 0);
long threadId = MessageSender.send(context, masterSecret, outgoingMessage, -1, false, null);
return new GroupActionResult(groupRecipient, threadId);
} catch (IOException e) {
throw new AssertionError(e);
}
}
private static Set<Address> getMemberAddresses(Collection<Recipient> recipients) {
final Set<Address> results = new HashSet<>();
for (Recipient recipient : recipients) {
results.add(recipient.getAddress());
@@ -65,71 +137,16 @@ public class GroupManager {
return results;
}
public static GroupActionResult updateGroup(@NonNull Context context,
@NonNull MasterSecret masterSecret,
@NonNull byte[] groupId,
@NonNull Set<Recipient> members,
@Nullable Bitmap avatar,
@Nullable String name)
throws InvalidNumberException
{
final GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context);
final Set<Address> memberAddresses = getMemberAddresses(context, members);
final byte[] avatarBytes = BitmapUtil.toByteArray(avatar);
memberAddresses.add(Address.fromSerialized(TextSecurePreferences.getLocalNumber(context)));
groupDatabase.updateMembers(groupId, new LinkedList<>(memberAddresses));
groupDatabase.updateTitle(groupId, name);
groupDatabase.updateAvatar(groupId, avatarBytes);
return sendGroupUpdate(context, masterSecret, groupId, memberAddresses, name, avatarBytes);
}
private static GroupActionResult sendGroupUpdate(@NonNull Context context,
@NonNull MasterSecret masterSecret,
@NonNull byte[] groupId,
@NonNull Set<Address> members,
@Nullable String groupName,
@Nullable byte[] avatar)
{
Attachment avatarAttachment = null;
Address groupAddress = Address.fromSerialized(GroupUtil.getEncodedId(groupId));
Recipients groupRecipient = RecipientFactory.getRecipientsFor(context, new Address[]{groupAddress}, false);
List<String> numbers = new LinkedList<>();
for (Address member : members) {
numbers.add(member.serialize());
}
GroupContext.Builder groupContextBuilder = GroupContext.newBuilder()
.setId(ByteString.copyFrom(groupId))
.setType(GroupContext.Type.UPDATE)
.addAllMembers(numbers);
if (groupName != null) groupContextBuilder.setName(groupName);
GroupContext groupContext = groupContextBuilder.build();
if (avatar != null) {
Uri avatarUri = SingleUseBlobProvider.getInstance().createUri(avatar);
avatarAttachment = new UriAttachment(avatarUri, MediaUtil.IMAGE_PNG, AttachmentDatabase.TRANSFER_PROGRESS_DONE, avatar.length, null, false);
}
OutgoingGroupMediaMessage outgoingMessage = new OutgoingGroupMediaMessage(groupRecipient, groupContext, avatarAttachment, System.currentTimeMillis(), 0);
long threadId = MessageSender.send(context, masterSecret, outgoingMessage, -1, false, null);
return new GroupActionResult(groupRecipient, threadId);
}
public static class GroupActionResult {
private Recipients groupRecipient;
private long threadId;
private Recipient groupRecipient;
private long threadId;
public GroupActionResult(Recipients groupRecipient, long threadId) {
public GroupActionResult(Recipient groupRecipient, long threadId) {
this.groupRecipient = groupRecipient;
this.threadId = threadId;
}
public Recipients getGroupRecipient() {
public Recipient getGroupRecipient() {
return groupRecipient;
}

View File

@@ -21,8 +21,8 @@ import org.thoughtcrime.securesms.jobs.PushGroupUpdateJob;
import org.thoughtcrime.securesms.mms.MmsException;
import org.thoughtcrime.securesms.mms.OutgoingGroupMediaMessage;
import org.thoughtcrime.securesms.notifications.MessageNotifier;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientFactory;
import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.sms.IncomingGroupMessage;
import org.thoughtcrime.securesms.sms.IncomingTextMessage;
import org.thoughtcrime.securesms.util.Base64;
@@ -60,7 +60,7 @@ public class GroupMessageProcessor {
GroupDatabase database = DatabaseFactory.getGroupDatabase(context);
SignalServiceGroup group = message.getGroupInfo().get();
byte[] id = group.getGroupId();
String id = GroupUtil.getEncodedId(group.getGroupId(), false);
GroupRecord record = database.getGroup(id);
if (record != null && group.getType() == Type.UPDATE) {
@@ -84,7 +84,7 @@ public class GroupMessageProcessor {
boolean outgoing)
{
GroupDatabase database = DatabaseFactory.getGroupDatabase(context);
byte[] id = group.getGroupId();
String id = GroupUtil.getEncodedId(group.getGroupId(), false);
GroupContext.Builder builder = createGroupContext(group);
builder.setType(GroupContext.Type.UPDATE);
@@ -113,7 +113,7 @@ public class GroupMessageProcessor {
{
GroupDatabase database = DatabaseFactory.getGroupDatabase(context);
byte[] id = group.getGroupId();
String id = GroupUtil.getEncodedId(group.getGroupId(), false);
Set<Address> recordMembers = new HashSet<>(groupRecord.getMembers());
Set<Address> messageMembers = new HashSet<>();
@@ -185,7 +185,7 @@ public class GroupMessageProcessor {
boolean outgoing)
{
GroupDatabase database = DatabaseFactory.getGroupDatabase(context);
byte[] id = group.getGroupId();
String id = GroupUtil.getEncodedId(group.getGroupId(), false);
List<Address> members = record.getMembers();
GroupContext.Builder builder = createGroupContext(group);
@@ -217,10 +217,10 @@ public class GroupMessageProcessor {
try {
if (outgoing) {
MmsDatabase mmsDatabase = DatabaseFactory.getMmsDatabase(context);
Address addres = Address.fromExternal(context, GroupUtil.getEncodedId(group.getGroupId()));
Recipients recipients = RecipientFactory.getRecipientsFor(context, new Address[] {addres}, false);
OutgoingGroupMediaMessage outgoingMessage = new OutgoingGroupMediaMessage(recipients, storage, null, envelope.getTimestamp(), 0);
long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipients);
Address addres = Address.fromExternal(context, GroupUtil.getEncodedId(group.getGroupId(), false));
Recipient recipient = RecipientFactory.getRecipientFor(context, addres, false);
OutgoingGroupMediaMessage outgoingMessage = new OutgoingGroupMediaMessage(recipient, storage, null, envelope.getTimestamp(), 0);
long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipient);
long messageId = mmsDatabase.insertMessageOutbox(masterSecret, outgoingMessage, threadId, false, null);
mmsDatabase.markAsSent(messageId, true);