Fix story sync message behaviour between iOS and Android.

main
Alex Hart 2022-11-30 16:34:59 -04:00 zatwierdzone przez Cody Henthorne
rodzic e5d196c642
commit 7945b3c971
6 zmienionych plików z 30 dodań i 39 usunięć

Wyświetl plik

@ -230,16 +230,6 @@ class StorySendTable(context: Context, databaseHelper: SignalDatabase) : Databas
return getLocalManifest(sentTimestamp) return getLocalManifest(sentTimestamp)
} }
/**
* Gets the manifest after a change to the available distribution lists occurs.
*/
fun getSentStorySyncManifestForUpdate(sentTimestamp: Long): SentStorySyncManifest {
val localManifest: SentStorySyncManifest = getLocalManifest(sentTimestamp)
val entries: List<SentStorySyncManifest.Entry> = localManifest.entries
return SentStorySyncManifest(entries)
}
/** /**
* Applies the given manifest to the local database. This method will: * Applies the given manifest to the local database. This method will:
* *
@ -331,34 +321,34 @@ class StorySendTable(context: Context, databaseHelper: SignalDatabase) : Databas
} }
} }
private fun getLocalManifest(sentTimestamp: Long): SentStorySyncManifest { fun getLocalManifest(sentTimestamp: Long): SentStorySyncManifest {
val entries = readableDatabase.rawQuery( val entries = readableDatabase.rawQuery(
// language=sql // language=sql
""" """
SELECT SELECT
$RECIPIENT_ID, $RECIPIENT_ID,
$ALLOWS_REPLIES, $ALLOWS_REPLIES,
$DISTRIBUTION_ID $DISTRIBUTION_ID,
${MmsTable.REMOTE_DELETED}
FROM $TABLE_NAME FROM $TABLE_NAME
WHERE $TABLE_NAME.$SENT_TIMESTAMP = ? AND ( INNER JOIN ${MmsTable.TABLE_NAME} ON ${MmsTable.TABLE_NAME}.${MmsTable.ID} = $TABLE_NAME.$MESSAGE_ID
SELECT ${MmsTable.REMOTE_DELETED} WHERE $TABLE_NAME.$SENT_TIMESTAMP = ?
FROM ${MmsTable.TABLE_NAME}
WHERE ${MmsTable.ID} = $TABLE_NAME.$MESSAGE_ID
) = 0
""".trimIndent(), """.trimIndent(),
arrayOf(sentTimestamp) arrayOf(sentTimestamp)
).use { cursor -> ).use { cursor ->
val results: MutableMap<RecipientId, SentStorySyncManifest.Entry> = mutableMapOf() val results: MutableMap<RecipientId, SentStorySyncManifest.Entry> = mutableMapOf()
while (cursor.moveToNext()) { while (cursor.moveToNext()) {
val isRemoteDeleted = CursorUtil.requireBoolean(cursor, MmsTable.REMOTE_DELETED)
val recipientId = RecipientId.from(CursorUtil.requireLong(cursor, RECIPIENT_ID)) val recipientId = RecipientId.from(CursorUtil.requireLong(cursor, RECIPIENT_ID))
val distributionId = DistributionId.from(CursorUtil.requireString(cursor, DISTRIBUTION_ID)) val distributionId = DistributionId.from(CursorUtil.requireString(cursor, DISTRIBUTION_ID))
val distributionIdList: List<DistributionId> = if (isRemoteDeleted) emptyList() else listOf(distributionId)
val allowsReplies = CursorUtil.requireBoolean(cursor, ALLOWS_REPLIES) val allowsReplies = CursorUtil.requireBoolean(cursor, ALLOWS_REPLIES)
val entry = results[recipientId]?.let { val entry = results[recipientId]?.let {
it.copy( it.copy(
allowedToReply = it.allowedToReply or allowsReplies, allowedToReply = it.allowedToReply or allowsReplies,
distributionLists = it.distributionLists + distributionId distributionLists = it.distributionLists + distributionIdList
) )
} ?: SentStorySyncManifest.Entry(recipientId, canReply(recipientId, sentTimestamp), listOf(distributionId)) } ?: SentStorySyncManifest.Entry(recipientId, canReply(recipientId, sentTimestamp), distributionIdList)
results[recipientId] = entry results[recipientId] = entry
} }

Wyświetl plik

@ -17,8 +17,7 @@ import java.util.concurrent.TimeUnit
/** /**
* Transmits a sent sync transcript to linked devices containing the story sync manifest for the given sent timestamp. * Transmits a sent sync transcript to linked devices containing the story sync manifest for the given sent timestamp.
* The transmitted message is sent as a recipient update, and will only contain affected recipients that still have a * The transmitted message will contain all current recipients of a given story.
* live story for the given timestamp.
*/ */
class MultiDeviceStorySendSyncJob private constructor(parameters: Parameters, private val sentTimestamp: Long, private val deletedMessageId: Long) : BaseJob(parameters) { class MultiDeviceStorySendSyncJob private constructor(parameters: Parameters, private val sentTimestamp: Long, private val deletedMessageId: Long) : BaseJob(parameters) {
@ -55,20 +54,18 @@ class MultiDeviceStorySendSyncJob private constructor(parameters: Parameters, pr
override fun getFactoryKey(): String = KEY override fun getFactoryKey(): String = KEY
override fun onRun() { override fun onRun() {
val updateManifest = SignalDatabase.storySends.getSentStorySyncManifestForUpdate(sentTimestamp) val updateManifest = SignalDatabase.storySends.getLocalManifest(sentTimestamp)
val recipientsSet: Set<SignalServiceStoryMessageRecipient> = updateManifest.toRecipientsSet()
if (updateManifest.entries.isEmpty()) {
Log.i(TAG, "No entries in updated manifest. Dropping.")
return
}
val recipientsSet = updateManifest.toRecipientsSet()
val transcriptMessage: SignalServiceSyncMessage = SignalServiceSyncMessage.forSentTranscript(buildSentTranscript(recipientsSet)) val transcriptMessage: SignalServiceSyncMessage = SignalServiceSyncMessage.forSentTranscript(buildSentTranscript(recipientsSet))
val sendMessageResult = ApplicationDependencies.getSignalServiceMessageSender().sendSyncMessage(transcriptMessage, Optional.empty()) val sendMessageResult = ApplicationDependencies.getSignalServiceMessageSender().sendSyncMessage(transcriptMessage, Optional.empty())
Log.i(TAG, "Sent transcript message with ${recipientsSet.size} recipients")
if (!sendMessageResult.isSuccess) { if (!sendMessageResult.isSuccess) {
throw RetryableException() throw RetryableException()
} }
SignalDatabase.mms.deleteRemotelyDeletedStory(deletedMessageId)
} }
override fun onShouldRetry(e: Exception): Boolean { override fun onShouldRetry(e: Exception): Boolean {

Wyświetl plik

@ -138,8 +138,8 @@ public final class PushDistributionListSendJob extends PushSendJob {
public void onPushSend() public void onPushSend()
throws IOException, MmsException, NoSuchMessageException, RetryLaterException throws IOException, MmsException, NoSuchMessageException, RetryLaterException
{ {
MessageTable database = SignalDatabase.mms(); MessageTable database = SignalDatabase.mms();
OutgoingMediaMessage message = database.getOutgoingMessage(messageId); OutgoingMediaMessage message = database.getOutgoingMessage(messageId);
Set<NetworkFailure> existingNetworkFailures = message.getNetworkFailures(); Set<NetworkFailure> existingNetworkFailures = message.getNetworkFailures();
Set<IdentityKeyMismatch> existingIdentityMismatches = message.getIdentityKeyMismatches(); Set<IdentityKeyMismatch> existingIdentityMismatches = message.getIdentityKeyMismatches();
@ -213,6 +213,8 @@ public final class PushDistributionListSendJob extends PushSendJob {
SentStorySyncManifest manifest = SignalDatabase.storySends().getFullSentStorySyncManifest(messageId, message.getSentTimeMillis()); SentStorySyncManifest manifest = SignalDatabase.storySends().getFullSentStorySyncManifest(messageId, message.getSentTimeMillis());
Set<SignalServiceStoryMessageRecipient> manifestCollection = manifest != null ? manifest.toRecipientsSet() : Collections.emptySet(); Set<SignalServiceStoryMessageRecipient> manifestCollection = manifest != null ? manifest.toRecipientsSet() : Collections.emptySet();
Log.d(TAG, "[" + messageId + "] Sending a story message with a manifest of size " + manifestCollection.size());
return GroupSendUtil.sendStoryMessage(context, message.getRecipient().requireDistributionListId(), destinations, isRecipientUpdate, new MessageId(messageId, true), message.getSentTimeMillis(), storyMessage, manifestCollection); return GroupSendUtil.sendStoryMessage(context, message.getRecipient().requireDistributionListId(), destinations, isRecipientUpdate, new MessageId(messageId, true), message.getSentTimeMillis(), storyMessage, manifestCollection);
} catch (ServerRejectedException e) { } catch (ServerRejectedException e) {
throw new UndeliverableMessageException(e); throw new UndeliverableMessageException(e);

Wyświetl plik

@ -189,10 +189,6 @@ public class RemoteDeleteSendJob extends BaseJob {
if (recipients.isEmpty()) { if (recipients.isEmpty()) {
db.markAsSent(messageId, true); db.markAsSent(messageId, true);
if (MessageRecordUtil.isStory(message)) {
db.deleteRemotelyDeletedStory(messageId);
}
} else { } else {
Log.w(TAG, "Still need to send to " + recipients.size() + " recipients. Retrying."); Log.w(TAG, "Still need to send to " + recipients.size() + " recipients. Retrying.");
throw new RetryLaterException(); throw new RetryLaterException();

Wyświetl plik

@ -278,6 +278,11 @@ public class SignalServiceMessageSender {
Set<SignalServiceStoryMessageRecipient> manifest) Set<SignalServiceStoryMessageRecipient> manifest)
throws IOException, UntrustedIdentityException throws IOException, UntrustedIdentityException
{ {
if (manifest.isEmpty()) {
Log.w(TAG, "Refusing to send sync message for empty manifest.");
return;
}
SignalServiceSyncMessage syncMessage = createSelfSendSyncMessageForStory(message, timestamp, isRecipientUpdate, manifest); SignalServiceSyncMessage syncMessage = createSelfSendSyncMessageForStory(message, timestamp, isRecipientUpdate, manifest);
sendSyncMessage(syncMessage, Optional.empty()); sendSyncMessage(syncMessage, Optional.empty());
} }
@ -300,8 +305,7 @@ public class SignalServiceMessageSender {
List<SendMessageResult> sendMessageResults = sendGroupMessage(distributionId, recipients, unidentifiedAccess, timestamp, content, ContentHint.IMPLICIT, groupId, false, SenderKeyGroupEvents.EMPTY, false, true); List<SendMessageResult> sendMessageResults = sendGroupMessage(distributionId, recipients, unidentifiedAccess, timestamp, content, ContentHint.IMPLICIT, groupId, false, SenderKeyGroupEvents.EMPTY, false, true);
if (aciStore.isMultiDevice()) { if (aciStore.isMultiDevice()) {
SignalServiceSyncMessage syncMessage = createSelfSendSyncMessageForStory(message, timestamp, isRecipientUpdate, manifest); sendStorySyncMessage(message, timestamp, isRecipientUpdate, manifest);
sendSyncMessage(syncMessage, Optional.empty());
} }
return sendMessageResults; return sendMessageResults;

Wyświetl plik

@ -763,8 +763,10 @@ public final class SignalServiceContent {
if (!address.isPresent() && if (!address.isPresent() &&
!dataMessage.flatMap(SignalServiceDataMessage::getGroupContext).isPresent() && !dataMessage.flatMap(SignalServiceDataMessage::getGroupContext).isPresent() &&
!storyMessage.flatMap(SignalServiceStoryMessage::getGroupContext).isPresent()) { !storyMessage.flatMap(SignalServiceStoryMessage::getGroupContext).isPresent() &&
throw new InvalidMessageStructureException("SyncMessage missing both destination and group ID!"); recipientManifest.isEmpty())
{
throw new InvalidMessageStructureException("SyncMessage missing destination, group ID, and recipient manifest!");
} }
for (SignalServiceProtos.SyncMessage.Sent.UnidentifiedDeliveryStatus status : sentContent.getUnidentifiedStatusList()) { for (SignalServiceProtos.SyncMessage.Sent.UnidentifiedDeliveryStatus status : sentContent.getUnidentifiedStatusList()) {