Job changes for GroupsV2 message receive and profile key updates.
parent
36c43ed2fa
commit
9ac1897880
|
@ -7,6 +7,7 @@ import androidx.annotation.NonNull;
|
|||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.WorkerThread;
|
||||
|
||||
import org.signal.zkgroup.groups.GroupMasterKey;
|
||||
import org.signal.zkgroup.groups.UuidCiphertext;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.database.GroupDatabase;
|
||||
|
@ -120,12 +121,13 @@ public final class GroupManager {
|
|||
|
||||
@WorkerThread
|
||||
public static void updateGroupFromServer(@NonNull Context context,
|
||||
@NonNull GroupId.V2 groupId,
|
||||
int version)
|
||||
@NonNull GroupMasterKey groupMasterKey,
|
||||
int version,
|
||||
long timestamp)
|
||||
throws GroupChangeBusyException, IOException, GroupNotAMemberException
|
||||
{
|
||||
try (GroupManagerV2.GroupEditor edit = new GroupManagerV2(context).edit(groupId)) {
|
||||
edit.updateLocalToServerVersion(version);
|
||||
try (GroupManagerV2.GroupUpdater updater = new GroupManagerV2(context).updater(groupMasterKey)) {
|
||||
updater.updateLocalToServerVersion(version, timestamp);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -89,6 +89,11 @@ final class GroupManagerV2 {
|
|||
return new GroupEditor(groupId, GroupsV2ProcessingLock.acquireGroupProcessingLock());
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
GroupUpdater updater(@NonNull GroupMasterKey groupId) throws GroupChangeBusyException {
|
||||
return new GroupUpdater(groupId, GroupsV2ProcessingLock.acquireGroupProcessingLock());
|
||||
}
|
||||
|
||||
class GroupCreator implements Closeable {
|
||||
|
||||
private final Closeable lock;
|
||||
|
@ -316,14 +321,6 @@ final class GroupManagerV2 {
|
|||
return commitChangeWithConflictResolution(groupOperations.createAcceptInviteChange(groupCandidate.getProfileKeyCredential().get()));
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
void updateLocalToServerVersion(int version)
|
||||
throws IOException, GroupNotAMemberException
|
||||
{
|
||||
new GroupsV2StateProcessor(context).forGroup(groupMasterKey)
|
||||
.updateLocalGroupToRevision(version, System.currentTimeMillis());
|
||||
}
|
||||
|
||||
private @NonNull GroupManager.GroupActionResult commitChangeWithConflictResolution(@NonNull GroupChange.Actions.Builder change)
|
||||
throws GroupChangeFailedException, GroupNotAMemberException, GroupInsufficientRightsException, IOException
|
||||
{
|
||||
|
@ -421,6 +418,30 @@ final class GroupManagerV2 {
|
|||
}
|
||||
}
|
||||
|
||||
class GroupUpdater implements Closeable {
|
||||
|
||||
private final Closeable lock;
|
||||
private final GroupMasterKey groupMasterKey;
|
||||
|
||||
GroupUpdater(@NonNull GroupMasterKey groupMasterKey, @NonNull Closeable lock) {
|
||||
this.lock = lock;
|
||||
this.groupMasterKey = groupMasterKey;
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
void updateLocalToServerVersion(int version, long timestamp)
|
||||
throws IOException, GroupNotAMemberException
|
||||
{
|
||||
new GroupsV2StateProcessor(context).forGroup(groupMasterKey)
|
||||
.updateLocalGroupToRevision(version, timestamp);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
lock.close();
|
||||
}
|
||||
}
|
||||
|
||||
private @NonNull GroupManager.GroupActionResult sendGroupUpdate(@NonNull GroupMasterKey masterKey,
|
||||
@NonNull DecryptedGroup decryptedGroup,
|
||||
@Nullable DecryptedGroupChange plainGroupChange)
|
||||
|
|
|
@ -0,0 +1,92 @@
|
|||
package org.thoughtcrime.securesms.jobs;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import org.thoughtcrime.securesms.groups.GroupChangeBusyException;
|
||||
import org.thoughtcrime.securesms.groups.GroupChangeFailedException;
|
||||
import org.thoughtcrime.securesms.groups.GroupId;
|
||||
import org.thoughtcrime.securesms.groups.GroupInsufficientRightsException;
|
||||
import org.thoughtcrime.securesms.groups.GroupManager;
|
||||
import org.thoughtcrime.securesms.groups.GroupNotAMemberException;
|
||||
import org.thoughtcrime.securesms.jobmanager.Data;
|
||||
import org.thoughtcrime.securesms.jobmanager.Job;
|
||||
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
import org.whispersystems.signalservice.api.groupsv2.NoCredentialForRedemptionTimeException;
|
||||
import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* When your profile key changes, this job can be used to update it on a single given group.
|
||||
* <p>
|
||||
* Your membership is confirmed first, so safe to run against any known {@link GroupId.V2}
|
||||
*/
|
||||
public final class GroupV2UpdateSelfProfileKeyJob extends BaseJob {
|
||||
|
||||
public static final String KEY = "GroupV2UpdateSelfProfileKeyJob";
|
||||
|
||||
private static final String QUEUE = "GroupV2UpdateSelfProfileKeyJob";
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static final String TAG = Log.tag(GroupV2UpdateSelfProfileKeyJob.class);
|
||||
|
||||
private static final String KEY_GROUP_ID = "group_id";
|
||||
|
||||
private final GroupId.V2 groupId;
|
||||
|
||||
GroupV2UpdateSelfProfileKeyJob(@NonNull GroupId.V2 groupId) {
|
||||
this(new Parameters.Builder()
|
||||
.addConstraint(NetworkConstraint.KEY)
|
||||
.setLifespan(TimeUnit.DAYS.toMillis(1))
|
||||
.setMaxAttempts(Parameters.UNLIMITED)
|
||||
.setQueue(QUEUE)
|
||||
.build(),
|
||||
groupId);
|
||||
}
|
||||
|
||||
private GroupV2UpdateSelfProfileKeyJob(@NonNull Parameters parameters, @NonNull GroupId.V2 groupId) {
|
||||
super(parameters);
|
||||
this.groupId = groupId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull Data serialize() {
|
||||
return new Data.Builder().putString(KEY_GROUP_ID, groupId.toString())
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull String getFactoryKey() {
|
||||
return KEY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRun()
|
||||
throws IOException, GroupNotAMemberException, GroupChangeFailedException, GroupInsufficientRightsException, GroupChangeBusyException
|
||||
{
|
||||
Log.i(TAG, "Updating profile key on group " + groupId);
|
||||
GroupManager.updateSelfProfileKeyInGroup(context, groupId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onShouldRetry(@NonNull Exception e) {
|
||||
return e instanceof PushNetworkException ||
|
||||
e instanceof NoCredentialForRedemptionTimeException||
|
||||
e instanceof GroupChangeBusyException;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure() {
|
||||
}
|
||||
|
||||
public static final class Factory implements Job.Factory<GroupV2UpdateSelfProfileKeyJob> {
|
||||
|
||||
@Override
|
||||
public @NonNull GroupV2UpdateSelfProfileKeyJob create(@NonNull Parameters parameters, @NonNull Data data) {
|
||||
return new GroupV2UpdateSelfProfileKeyJob(parameters,
|
||||
GroupId.parseOrThrow(data.getString(KEY_GROUP_ID)).requireV2());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -20,12 +20,12 @@ import org.thoughtcrime.securesms.jobmanager.migrations.RecipientIdFollowUpJobMi
|
|||
import org.thoughtcrime.securesms.jobmanager.migrations.RecipientIdJobMigration;
|
||||
import org.thoughtcrime.securesms.jobmanager.migrations.SendReadReceiptsJobMigration;
|
||||
import org.thoughtcrime.securesms.migrations.AvatarIdRemovalMigrationJob;
|
||||
import org.thoughtcrime.securesms.migrations.PassingMigrationJob;
|
||||
import org.thoughtcrime.securesms.migrations.AvatarMigrationJob;
|
||||
import org.thoughtcrime.securesms.migrations.CachedAttachmentsMigrationJob;
|
||||
import org.thoughtcrime.securesms.migrations.DatabaseMigrationJob;
|
||||
import org.thoughtcrime.securesms.migrations.LegacyMigrationJob;
|
||||
import org.thoughtcrime.securesms.migrations.MigrationCompleteJob;
|
||||
import org.thoughtcrime.securesms.migrations.PassingMigrationJob;
|
||||
import org.thoughtcrime.securesms.migrations.RecipientSearchMigrationJob;
|
||||
import org.thoughtcrime.securesms.migrations.RegistrationPinV2MigrationJob;
|
||||
import org.thoughtcrime.securesms.migrations.StickerAdditionMigrationJob;
|
||||
|
@ -91,6 +91,7 @@ public final class JobManagerFactories {
|
|||
put(ResumableUploadSpecJob.KEY, new ResumableUploadSpecJob.Factory());
|
||||
put(StorageAccountRestoreJob.KEY, new StorageAccountRestoreJob.Factory());
|
||||
put(RequestGroupV2InfoJob.KEY, new RequestGroupV2InfoJob.Factory());
|
||||
put(GroupV2UpdateSelfProfileKeyJob.KEY, new GroupV2UpdateSelfProfileKeyJob.Factory());
|
||||
put(RetrieveProfileAvatarJob.KEY, new RetrieveProfileAvatarJob.Factory());
|
||||
put(RetrieveProfileJob.KEY, new RetrieveProfileJob.Factory());
|
||||
put(RotateCertificateJob.KEY, new RotateCertificateJob.Factory());
|
||||
|
|
|
@ -12,6 +12,8 @@ import androidx.annotation.Nullable;
|
|||
import com.annimon.stream.Collectors;
|
||||
import com.annimon.stream.Stream;
|
||||
|
||||
import org.signal.zkgroup.VerificationFailedException;
|
||||
import org.signal.zkgroup.groups.GroupMasterKey;
|
||||
import org.signal.zkgroup.profiles.ProfileKey;
|
||||
import org.thoughtcrime.securesms.ApplicationContext;
|
||||
import org.thoughtcrime.securesms.attachments.Attachment;
|
||||
|
@ -44,7 +46,10 @@ import org.thoughtcrime.securesms.database.model.ReactionRecord;
|
|||
import org.thoughtcrime.securesms.database.model.StickerRecord;
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||
import org.thoughtcrime.securesms.groups.BadGroupIdException;
|
||||
import org.thoughtcrime.securesms.groups.GroupChangeBusyException;
|
||||
import org.thoughtcrime.securesms.groups.GroupId;
|
||||
import org.thoughtcrime.securesms.groups.GroupManager;
|
||||
import org.thoughtcrime.securesms.groups.GroupNotAMemberException;
|
||||
import org.thoughtcrime.securesms.groups.GroupV1MessageProcessor;
|
||||
import org.thoughtcrime.securesms.jobmanager.Data;
|
||||
import org.thoughtcrime.securesms.jobmanager.Job;
|
||||
|
@ -76,6 +81,7 @@ import org.thoughtcrime.securesms.sms.OutgoingTextMessage;
|
|||
import org.thoughtcrime.securesms.stickers.StickerLocator;
|
||||
import org.thoughtcrime.securesms.storage.StorageSyncHelper;
|
||||
import org.thoughtcrime.securesms.util.Base64;
|
||||
import org.thoughtcrime.securesms.util.FeatureFlags;
|
||||
import org.thoughtcrime.securesms.util.GroupUtil;
|
||||
import org.thoughtcrime.securesms.util.Hex;
|
||||
import org.thoughtcrime.securesms.util.IdentityUtil;
|
||||
|
@ -84,12 +90,15 @@ import org.thoughtcrime.securesms.util.RemoteDeleteUtil;
|
|||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
import org.whispersystems.libsignal.state.SessionStore;
|
||||
import org.whispersystems.libsignal.util.guava.Optional;
|
||||
import org.whispersystems.signalservice.api.groupsv2.InvalidGroupStateException;
|
||||
import org.whispersystems.signalservice.api.groupsv2.NoCredentialForRedemptionTimeException;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceAttachment;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceContent;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage.Preview;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceGroup;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceGroupContext;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceGroupV2;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceReceiptMessage;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceTypingMessage;
|
||||
import org.whispersystems.signalservice.api.messages.calls.AnswerMessage;
|
||||
|
@ -110,6 +119,7 @@ import org.whispersystems.signalservice.api.messages.multidevice.VerifiedMessage
|
|||
import org.whispersystems.signalservice.api.messages.multidevice.ViewOnceOpenMessage;
|
||||
import org.whispersystems.signalservice.api.messages.shared.SharedContact;
|
||||
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
|
||||
import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.SecureRandom;
|
||||
|
@ -180,6 +190,7 @@ public final class PushProcessMessageJob extends BaseJob {
|
|||
this(new Parameters.Builder()
|
||||
.setQueue(QUEUE)
|
||||
.setMaxAttempts(Parameters.UNLIMITED)
|
||||
// TODO [Alan] GV2 add network constraint and split queues.
|
||||
.build(),
|
||||
messageState,
|
||||
serializedPlaintextContent,
|
||||
|
@ -234,7 +245,7 @@ public final class PushProcessMessageJob extends BaseJob {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void onRun() {
|
||||
public void onRun() throws Exception {
|
||||
Optional<Long> optionalSmsMessageId = smsMessageId > 0 ? Optional.of(smsMessageId) : Optional.absent();
|
||||
|
||||
if (messageState == MessageState.DECRYPTED_OK) {
|
||||
|
@ -258,15 +269,19 @@ public final class PushProcessMessageJob extends BaseJob {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean onShouldRetry(@NonNull Exception exception) {
|
||||
return false;
|
||||
public boolean onShouldRetry(@NonNull Exception e) {
|
||||
return e instanceof PushNetworkException ||
|
||||
e instanceof NoCredentialForRedemptionTimeException ||
|
||||
e instanceof GroupChangeBusyException;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure() {
|
||||
}
|
||||
|
||||
private void handleMessage(@Nullable SignalServiceContent content, @NonNull Optional<Long> smsMessageId) {
|
||||
private void handleMessage(@Nullable SignalServiceContent content, @NonNull Optional<Long> smsMessageId)
|
||||
throws VerificationFailedException, IOException, InvalidGroupStateException, GroupChangeBusyException
|
||||
{
|
||||
try {
|
||||
GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context);
|
||||
|
||||
|
@ -282,14 +297,25 @@ public final class PushProcessMessageJob extends BaseJob {
|
|||
boolean isGv2Message = groupId.isPresent() && groupId.get().isV2();
|
||||
|
||||
if (isGv2Message) {
|
||||
Log.w(TAG, "Ignoring GV2 message.");
|
||||
return;
|
||||
GroupMasterKey groupMasterKey = message.getGroupContext().get().getGroupV2().get().getMasterKey();
|
||||
|
||||
if (!groupV2PreProcessMessage(content, groupMasterKey, message.getGroupContext().get().getGroupV2().get())) {
|
||||
Log.i(TAG, "Ignoring GV2 message for group we are not currently in " + groupId);
|
||||
return;
|
||||
}
|
||||
|
||||
GroupId.V2 groupIdV2 = groupId.get().requireV2();
|
||||
Recipient sender = Recipient.externalPush(context, content.getSender());
|
||||
if (!groupDatabase.isCurrentMember(groupIdV2, sender.getId())) {
|
||||
Log.i(TAG, "Ignoring GV2 message from member not in group " + groupId);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (isInvalidMessage(message)) handleInvalidMessage(content.getSender(), content.getSenderDevice(), groupId, content.getTimestamp(), smsMessageId);
|
||||
else if (message.isEndSession()) handleEndSessionMessage(content, smsMessageId);
|
||||
else if (message.isGroupV1Update()) handleGroupV1Message(content, message, smsMessageId);
|
||||
else if (message.isExpirationUpdate()) handleExpirationUpdate(content, message, smsMessageId);
|
||||
else if (message.isGroupV1Update()) handleGroupV1Message(content, message, smsMessageId, groupId.get().requireV1());
|
||||
else if (message.isExpirationUpdate()) handleExpirationUpdate(content, message, smsMessageId, groupId);
|
||||
else if (message.getReaction().isPresent()) handleReaction(content, message);
|
||||
else if (message.getRemoteDelete().isPresent()) handleRemoteDelete(content, message);
|
||||
else if (isMediaMessage) handleMediaMessage(content, message, smsMessageId);
|
||||
|
@ -351,6 +377,26 @@ public final class PushProcessMessageJob extends BaseJob {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to update the group to the version mentioned in the message.
|
||||
* If the local version is at least that it will not check the server.
|
||||
*
|
||||
* @return false iff self is not a current member of the group.
|
||||
*/
|
||||
private boolean groupV2PreProcessMessage(@NonNull SignalServiceContent content,
|
||||
@NonNull GroupMasterKey groupMasterKey,
|
||||
@NonNull SignalServiceGroupV2 groupV2)
|
||||
throws IOException, GroupChangeBusyException
|
||||
{
|
||||
try {
|
||||
GroupManager.updateGroupFromServer(context, groupMasterKey, groupV2.getRevision(), content.getTimestamp());
|
||||
return true;
|
||||
} catch (GroupNotAMemberException e) {
|
||||
Log.w(TAG, "Ignoring message for a group we're not in");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void handleExceptionMessage(@NonNull ExceptionMetadata e, @NonNull Optional<Long> smsMessageId) {
|
||||
switch (messageState) {
|
||||
|
||||
|
@ -551,13 +597,14 @@ public final class PushProcessMessageJob extends BaseJob {
|
|||
|
||||
private void handleGroupV1Message(@NonNull SignalServiceContent content,
|
||||
@NonNull SignalServiceDataMessage message,
|
||||
@NonNull Optional<Long> smsMessageId)
|
||||
@NonNull Optional<Long> smsMessageId,
|
||||
@NonNull GroupId.V1 groupId)
|
||||
throws StorageFailedException, BadGroupIdException
|
||||
{
|
||||
GroupV1MessageProcessor.process(context, content, message, false);
|
||||
|
||||
if (message.getExpiresInSeconds() != 0 && message.getExpiresInSeconds() != getMessageDestination(content, message).getExpireMessages()) {
|
||||
handleExpirationUpdate(content, message, Optional.absent());
|
||||
handleExpirationUpdate(content, message, Optional.absent(), Optional.of(groupId));
|
||||
}
|
||||
|
||||
if (smsMessageId.isPresent()) {
|
||||
|
@ -583,32 +630,46 @@ public final class PushProcessMessageJob extends BaseJob {
|
|||
|
||||
private void handleExpirationUpdate(@NonNull SignalServiceContent content,
|
||||
@NonNull SignalServiceDataMessage message,
|
||||
@NonNull Optional<Long> smsMessageId)
|
||||
@NonNull Optional<Long> smsMessageId,
|
||||
@NonNull Optional<GroupId> groupId)
|
||||
throws StorageFailedException, BadGroupIdException
|
||||
{
|
||||
if (groupId.isPresent() && groupId.get().isV2()) {
|
||||
Log.w(TAG, "Expiration update received for GV2. Ignoring.");
|
||||
return;
|
||||
}
|
||||
|
||||
int expiresInSeconds = message.getExpiresInSeconds();
|
||||
Optional<SignalServiceGroupContext> groupContext = message.getGroupContext();
|
||||
Recipient recipient = getMessageDestination(content, groupContext);
|
||||
|
||||
if (recipient.getExpireMessages() == expiresInSeconds) {
|
||||
Log.i(TAG, "No change in message expiry for group. Ignoring.");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
MmsDatabase database = DatabaseFactory.getMmsDatabase(context);
|
||||
Recipient sender = Recipient.externalPush(context, content.getSender());
|
||||
Recipient recipient = getMessageDestination(content, message);
|
||||
IncomingMediaMessage mediaMessage = new IncomingMediaMessage(sender.getId(),
|
||||
message.getTimestamp(),
|
||||
content.getTimestamp(),
|
||||
content.getServerTimestamp(),
|
||||
-1,
|
||||
message.getExpiresInSeconds() * 1000L,
|
||||
true,
|
||||
expiresInSeconds * 1000L,
|
||||
true,
|
||||
false,
|
||||
content.isNeedsReceipt(),
|
||||
Optional.absent(),
|
||||
message.getGroupContext(),
|
||||
groupContext,
|
||||
Optional.absent(),
|
||||
Optional.absent(),
|
||||
Optional.absent(),
|
||||
Optional.absent(),
|
||||
Optional.absent());
|
||||
|
||||
database.insertSecureDecryptedMessageInbox(mediaMessage, -1);
|
||||
database.insertSecureDecryptedMessageInbox(mediaMessage, -1);
|
||||
|
||||
DatabaseFactory.getRecipientDatabase(context).setExpireMessages(recipient.getId(), message.getExpiresInSeconds());
|
||||
DatabaseFactory.getRecipientDatabase(context).setExpireMessages(recipient.getId(), expiresInSeconds);
|
||||
|
||||
if (smsMessageId.isPresent()) {
|
||||
DatabaseFactory.getSmsDatabase(context).deleteMessage(smsMessageId.get());
|
||||
|
@ -772,7 +833,7 @@ public final class PushProcessMessageJob extends BaseJob {
|
|||
|
||||
private void handleSynchronizeSentMessage(@NonNull SignalServiceContent content,
|
||||
@NonNull SentTranscriptMessage message)
|
||||
throws StorageFailedException, BadGroupIdException
|
||||
throws StorageFailedException, BadGroupIdException, VerificationFailedException, IOException, InvalidGroupStateException
|
||||
{
|
||||
try {
|
||||
GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context);
|
||||
|
@ -799,8 +860,7 @@ public final class PushProcessMessageJob extends BaseJob {
|
|||
threadId = handleSynchronizeSentTextMessage(message);
|
||||
}
|
||||
|
||||
if (message.getMessage().getGroupContext().isPresent() && groupDatabase.isUnknownGroup(GroupUtil.idFromGroupContext(message.getMessage().getGroupContext().get()))) {
|
||||
handleUnknownGroupMessage(content, message.getMessage().getGroupContext().get());
|
||||
if (message.getMessage().getGroupContext().isPresent() && groupDatabase.isUnknownGroup(GroupUtil.idFromGroupContext(message.getMessage().getGroupContext().get()))) { handleUnknownGroupMessage(content, message.getMessage().getGroupContext().get());
|
||||
}
|
||||
|
||||
if (message.getMessage().getProfileKey().isPresent()) {
|
||||
|
@ -1112,7 +1172,7 @@ public final class PushProcessMessageJob extends BaseJob {
|
|||
Recipient recipient = getMessageDestination(content, message);
|
||||
|
||||
if (message.getExpiresInSeconds() != recipient.getExpireMessages()) {
|
||||
handleExpirationUpdate(content, message, Optional.absent());
|
||||
handleExpirationUpdate(content, message, Optional.absent(), groupId);
|
||||
}
|
||||
|
||||
Long threadId;
|
||||
|
@ -1573,16 +1633,21 @@ public final class PushProcessMessageJob extends BaseJob {
|
|||
private Recipient getSyncMessageDestination(@NonNull SentTranscriptMessage message)
|
||||
throws BadGroupIdException
|
||||
{
|
||||
return getGroupRecipient(message.getMessage().getGroupContext())
|
||||
.or(() -> Recipient.externalPush(context, message.getDestination().get()));
|
||||
return getGroupRecipient(message.getMessage().getGroupContext()).or(() -> Recipient.externalPush(context, message.getDestination().get()));
|
||||
}
|
||||
|
||||
private Recipient getMessageDestination(@NonNull SignalServiceContent content,
|
||||
@NonNull SignalServiceDataMessage message)
|
||||
throws BadGroupIdException
|
||||
{
|
||||
return getGroupRecipient(message.getGroupContext())
|
||||
.or(() -> Recipient.externalPush(context, content.getSender()));
|
||||
return getGroupRecipient(message.getGroupContext()).or(() -> Recipient.externalPush(context, content.getSender()));
|
||||
}
|
||||
|
||||
private Recipient getMessageDestination(@NonNull SignalServiceContent content,
|
||||
@NonNull Optional<SignalServiceGroupContext> groupContext)
|
||||
throws BadGroupIdException
|
||||
{
|
||||
return getGroupRecipient(groupContext).or(() -> Recipient.externalPush(context, content.getSender()));
|
||||
}
|
||||
|
||||
private Optional<Recipient> getGroupRecipient(Optional<SignalServiceGroupContext> message)
|
||||
|
@ -1631,11 +1696,18 @@ public final class PushProcessMessageJob extends BaseJob {
|
|||
boolean isTextMessage = message.getBody().isPresent();
|
||||
boolean isMediaMessage = message.getAttachments().isPresent() || message.getQuote().isPresent() || message.getSharedContacts().isPresent();
|
||||
boolean isExpireMessage = message.isExpirationUpdate();
|
||||
boolean isContentMessage = !message.isGroupV1Update() && !isExpireMessage && (isTextMessage || isMediaMessage);
|
||||
boolean isGv2Message = message.isGroupV2Message();
|
||||
boolean isGv2Update = message.isGroupV2Update();
|
||||
boolean isContentMessage = !message.isGroupV1Update() && !isGv2Update && !isExpireMessage && (isTextMessage || isMediaMessage);
|
||||
boolean isGroupActive = groupId.isPresent() && groupDatabase.isActive(groupId.get());
|
||||
boolean isLeaveMessage = message.getGroupContext().isPresent() && message.getGroupContext().get().getGroupV1Type() == SignalServiceGroup.Type.QUIT;
|
||||
|
||||
return (isContentMessage && !isGroupActive) || (sender.isBlocked() && !isLeaveMessage);
|
||||
if (isGv2Message && !FeatureFlags.groupsV2()) {
|
||||
Log.i(TAG, "Ignoring GV2 message by feature flag.");
|
||||
return true;
|
||||
}
|
||||
|
||||
return (isContentMessage && !isGroupActive) || (sender.isBlocked() && !isLeaveMessage && !isGv2Update);
|
||||
} else {
|
||||
return sender.isBlocked();
|
||||
}
|
||||
|
|
|
@ -81,7 +81,7 @@ public final class RequestGroupV2InfoJob extends BaseJob {
|
|||
return;
|
||||
}
|
||||
|
||||
GroupManager.updateGroupFromServer(context, groupId, toRevision);
|
||||
GroupManager.updateGroupFromServer(context, group.get().requireV2GroupProperties().getGroupMasterKey(), toRevision, System.currentTimeMillis());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -7,18 +7,18 @@ import org.thoughtcrime.securesms.crypto.ProfileKeyUtil;
|
|||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.database.RecipientDatabase;
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||
import org.thoughtcrime.securesms.groups.GroupId;
|
||||
import org.thoughtcrime.securesms.jobmanager.Data;
|
||||
import org.thoughtcrime.securesms.jobmanager.Job;
|
||||
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
|
||||
import org.thoughtcrime.securesms.profiles.AvatarHelper;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.util.FeatureFlags;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
import org.whispersystems.signalservice.api.SignalServiceAccountManager;
|
||||
import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException;
|
||||
import org.whispersystems.signalservice.api.util.StreamDetails;
|
||||
|
||||
import java.util.UUID;
|
||||
import java.util.List;
|
||||
|
||||
public class RotateProfileKeyJob extends BaseJob {
|
||||
|
||||
|
@ -55,6 +55,7 @@ public class RotateProfileKeyJob extends BaseJob {
|
|||
Recipient self = Recipient.self();
|
||||
|
||||
recipientDatabase.setProfileKey(self.getId(), profileKey);
|
||||
|
||||
try (StreamDetails avatarStream = AvatarHelper.getSelfProfileAvatarStream(context)) {
|
||||
if (FeatureFlags.VERSIONED_PROFILES) {
|
||||
accountManager.setVersionedProfile(self.getUuid().get(),
|
||||
|
@ -68,6 +69,16 @@ public class RotateProfileKeyJob extends BaseJob {
|
|||
}
|
||||
|
||||
ApplicationDependencies.getJobManager().add(new RefreshAttributesJob());
|
||||
|
||||
updateProfileKeyOnAllV2Groups();
|
||||
}
|
||||
|
||||
private void updateProfileKeyOnAllV2Groups() {
|
||||
List<GroupId.V2> allGv2Groups = DatabaseFactory.getGroupDatabase(context).getAllGroupV2Ids();
|
||||
|
||||
for (GroupId.V2 groupId : allGv2Groups) {
|
||||
ApplicationDependencies.getJobManager().add(new GroupV2UpdateSelfProfileKeyJob(groupId));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -60,6 +60,7 @@ public final class FeatureFlags {
|
|||
private static final String CALLING_PIP = "android.callingPip";
|
||||
private static final String NEW_GROUP_UI = "android.newGroupUI";
|
||||
private static final String REACT_WITH_ANY_EMOJI = "android.reactWithAnyEmoji";
|
||||
private static final String GROUPS_V2 = "android.groupsv2";
|
||||
|
||||
/**
|
||||
* We will only store remote values for flags in this set. If you want a flag to be controllable
|
||||
|
@ -109,7 +110,8 @@ public final class FeatureFlags {
|
|||
*/
|
||||
private static final Set<String> STICKY = Sets.newHashSet(
|
||||
PINS_FOR_ALL_LEGACY,
|
||||
PINS_FOR_ALL
|
||||
PINS_FOR_ALL,
|
||||
GROUPS_V2
|
||||
);
|
||||
|
||||
/**
|
||||
|
@ -255,6 +257,11 @@ public final class FeatureFlags {
|
|||
return getBoolean(REACT_WITH_ANY_EMOJI, false);
|
||||
}
|
||||
|
||||
/** Groups v2 send and receive. */
|
||||
public static boolean groupsV2() {
|
||||
return org.whispersystems.signalservice.FeatureFlags.ZK_GROUPS && getBoolean(GROUPS_V2, false);
|
||||
}
|
||||
|
||||
/** Only for rendering debug info. */
|
||||
public static synchronized @NonNull Map<String, Object> getMemoryValues() {
|
||||
return new TreeMap<>(REMOTE_VALUES);
|
||||
|
|
|
@ -144,6 +144,16 @@ public class SignalServiceDataMessage {
|
|||
group.get().getGroupV1().get().getType() != SignalServiceGroup.Type.DELIVER;
|
||||
}
|
||||
|
||||
public boolean isGroupV2Message() {
|
||||
return group.isPresent() &&
|
||||
group.get().getGroupV2().isPresent();
|
||||
}
|
||||
|
||||
public boolean isGroupV2Update() {
|
||||
return isGroupV2Message() &&
|
||||
!body.isPresent();
|
||||
}
|
||||
|
||||
public int getExpiresInSeconds() {
|
||||
return expiresInSeconds;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue