Don't send group update messages when member labels are changed.

This commit is contained in:
jeffrey-signal
2026-02-03 12:20:33 -05:00
committed by GitHub
parent 0cd93986bd
commit ff726ec4d2
8 changed files with 189 additions and 148 deletions

View File

@@ -7,6 +7,10 @@ import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.annotation.WorkerThread;
import org.signal.core.models.ServiceId;
import org.signal.core.models.ServiceId.ACI;
import org.signal.core.models.ServiceId.PNI;
import org.signal.core.util.UuidUtil;
import org.signal.core.util.logging.Log;
import org.signal.libsignal.zkgroup.InvalidInputException;
import org.signal.libsignal.zkgroup.VerificationFailedException;
@@ -17,9 +21,9 @@ import org.signal.libsignal.zkgroup.groups.UuidCiphertext;
import org.signal.libsignal.zkgroup.profiles.ExpiringProfileKeyCredential;
import org.signal.libsignal.zkgroup.profiles.ProfileKey;
import org.signal.storageservice.storage.protos.groups.AccessControl;
import org.signal.storageservice.storage.protos.groups.ExternalGroupCredential;
import org.signal.storageservice.storage.protos.groups.GroupChange;
import org.signal.storageservice.storage.protos.groups.GroupChangeResponse;
import org.signal.storageservice.storage.protos.groups.ExternalGroupCredential;
import org.signal.storageservice.storage.protos.groups.Member;
import org.signal.storageservice.storage.protos.groups.local.DecryptedGroup;
import org.signal.storageservice.storage.protos.groups.local.DecryptedGroupChange;
@@ -51,6 +55,7 @@ import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.sms.MessageSender;
import org.thoughtcrime.securesms.util.ProfileUtil;
import org.whispersystems.signalservice.api.groupsv2.DecryptChangeVerificationMode;
import org.whispersystems.signalservice.api.groupsv2.DecryptedGroupExtensions;
import org.whispersystems.signalservice.api.groupsv2.DecryptedGroupResponse;
import org.whispersystems.signalservice.api.groupsv2.DecryptedGroupUtil;
import org.whispersystems.signalservice.api.groupsv2.GroupCandidate;
@@ -62,13 +67,9 @@ import org.whispersystems.signalservice.api.groupsv2.GroupsV2Operations;
import org.whispersystems.signalservice.api.groupsv2.InvalidGroupStateException;
import org.whispersystems.signalservice.api.groupsv2.NotAbleToApplyGroupV2ChangeException;
import org.whispersystems.signalservice.api.groupsv2.ReceivedGroupSendEndorsements;
import org.signal.core.models.ServiceId;
import org.signal.core.models.ServiceId.ACI;
import org.signal.core.models.ServiceId.PNI;
import org.whispersystems.signalservice.api.push.ServiceIds;
import org.whispersystems.signalservice.api.push.exceptions.AuthorizationFailedException;
import org.whispersystems.signalservice.api.push.exceptions.ConflictException;
import org.signal.core.util.UuidUtil;
import org.whispersystems.signalservice.internal.push.exceptions.GroupExistsException;
import org.whispersystems.signalservice.internal.push.exceptions.GroupPatchNotAcceptedException;
import org.whispersystems.signalservice.internal.push.exceptions.NotInGroupException;
@@ -1296,7 +1297,7 @@ final class GroupManagerV2 {
DecryptedGroupChange plainGroupChange = groupMutation.getGroupChange();
if (plainGroupChange != null && DecryptedGroupUtil.changeIsSilent(plainGroupChange)) {
if (plainGroupChange != null && DecryptedGroupExtensions.isSilent(plainGroupChange)) {
if (sendToMembers) {
AppDependencies.getJobManager().add(PushGroupSilentUpdateSendJob.create(context, groupId, groupMutation.getNewGroupState(), outgoingMessage));
}

View File

@@ -7,6 +7,7 @@ import org.signal.core.util.logging.Log;
import org.signal.storageservice.storage.protos.groups.local.DecryptedGroup;
import org.signal.storageservice.storage.protos.groups.local.DecryptedGroupChange;
import org.whispersystems.signalservice.api.groupsv2.DecryptedGroupChangeLog;
import org.whispersystems.signalservice.api.groupsv2.DecryptedGroupExtensions;
import org.whispersystems.signalservice.api.groupsv2.DecryptedGroupUtil;
import org.whispersystems.signalservice.api.groupsv2.GroupChangeReconstruct;
import org.whispersystems.signalservice.api.groupsv2.GroupChangeUtil;
@@ -144,7 +145,7 @@ final class GroupStatePatcher {
}
},
(groupB, groupA) -> GroupChangeReconstruct.reconstructGroupChange(groupA, groupB),
(groupA, groupB) -> groupA.revision == groupB.revision && DecryptedGroupUtil.changeIsEmpty(GroupChangeReconstruct.reconstructGroupChange(groupA, groupB))
(groupA, groupB) -> groupA.revision == groupB.revision && DecryptedGroupExtensions.getChangedFields(GroupChangeReconstruct.reconstructGroupChange(groupA, groupB)).isEmpty()
);
}
}

View File

@@ -48,6 +48,8 @@ import org.whispersystems.signalservice.api.groupsv2.GroupHistoryPage
import org.whispersystems.signalservice.api.groupsv2.InvalidGroupStateException
import org.whispersystems.signalservice.api.groupsv2.NotAbleToApplyGroupV2ChangeException
import org.whispersystems.signalservice.api.groupsv2.ReceivedGroupSendEndorsements
import org.whispersystems.signalservice.api.groupsv2.getChangedFields
import org.whispersystems.signalservice.api.groupsv2.isSilent
import org.whispersystems.signalservice.api.push.ServiceIds
import org.whispersystems.signalservice.internal.push.exceptions.GroupNotFoundException
import org.whispersystems.signalservice.internal.push.exceptions.NotInGroupException
@@ -633,14 +635,19 @@ class GroupsV2StateProcessor private constructor(
var runningGroupState = previousGroupState
for (entry in processedLogEntries) {
if (entry.change != null && DecryptedGroupUtil.changeIsEmptyExceptForProfileKeyChanges(entry.change) && !DecryptedGroupUtil.changeIsEmpty(entry.change)) {
Log.d(TAG, "Skipping profile key changes only update message")
} else if (entry.change != null && DecryptedGroupUtil.changeIsEmptyExceptForBanChangesAndOptionalProfileKeyChanges(entry.change)) {
Log.d(TAG, "Skipping ban changes only update message")
} else {
if (entry.change != null && DecryptedGroupUtil.changeIsEmpty(entry.change) && runningGroupState != null) {
val changedFields = entry.change?.getChangedFields().orEmpty()
val changeSilently = entry.change?.isSilent(changedFields) == true
when {
entry.change != null && changeSilently && changedFields.isNotEmpty() -> {
Log.d(TAG, "Skipping silent changes: $changedFields")
}
entry.change != null && changedFields.isEmpty() && runningGroupState != null -> {
Log.w(TAG, "Empty group update message seen. Not inserting.")
} else {
}
else -> {
storeMessage(GroupProtoUtil.createDecryptedGroupV2Context(masterKey, GroupMutation(runningGroupState, entry.change, entry.group), null), runningTimestamp, serverGuid)
runningTimestamp++
}

View File

@@ -4,8 +4,8 @@ import androidx.annotation.NonNull;
import org.signal.storageservice.storage.protos.groups.local.DecryptedGroupChange;
import org.thoughtcrime.securesms.mms.MessageGroupContext;
import org.whispersystems.signalservice.api.groupsv2.DecryptedGroupUtil;
import org.signal.core.models.ServiceId;
import org.whispersystems.signalservice.api.groupsv2.DecryptedGroupExtensions;
import java.util.Collections;
import java.util.Optional;
@@ -44,7 +44,7 @@ public final class GroupV2UpdateMessageUtil {
DecryptedGroupChange withoutDeletedMembers = decryptedGroupChange.newBuilder()
.deleteMembers(Collections.emptyList())
.build();
return DecryptedGroupUtil.changeIsEmpty(withoutDeletedMembers);
return DecryptedGroupExtensions.getChangedFields(withoutDeletedMembers).isEmpty();
}
public static boolean isJoinRequestCancel(@NonNull MessageGroupContext groupContext) {