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)
}
/**
* 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:
*
@ -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(
// language=sql
"""
SELECT
$RECIPIENT_ID,
$ALLOWS_REPLIES,
$DISTRIBUTION_ID
$DISTRIBUTION_ID,
${MmsTable.REMOTE_DELETED}
FROM $TABLE_NAME
WHERE $TABLE_NAME.$SENT_TIMESTAMP = ? AND (
SELECT ${MmsTable.REMOTE_DELETED}
FROM ${MmsTable.TABLE_NAME}
WHERE ${MmsTable.ID} = $TABLE_NAME.$MESSAGE_ID
) = 0
INNER JOIN ${MmsTable.TABLE_NAME} ON ${MmsTable.TABLE_NAME}.${MmsTable.ID} = $TABLE_NAME.$MESSAGE_ID
WHERE $TABLE_NAME.$SENT_TIMESTAMP = ?
""".trimIndent(),
arrayOf(sentTimestamp)
).use { cursor ->
val results: MutableMap<RecipientId, SentStorySyncManifest.Entry> = mutableMapOf()
while (cursor.moveToNext()) {
val isRemoteDeleted = CursorUtil.requireBoolean(cursor, MmsTable.REMOTE_DELETED)
val recipientId = RecipientId.from(CursorUtil.requireLong(cursor, RECIPIENT_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 entry = results[recipientId]?.let {
it.copy(
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
}

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.
* The transmitted message is sent as a recipient update, and will only contain affected recipients that still have a
* live story for the given timestamp.
* The transmitted message will contain all current recipients of a given story.
*/
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 onRun() {
val updateManifest = SignalDatabase.storySends.getSentStorySyncManifestForUpdate(sentTimestamp)
if (updateManifest.entries.isEmpty()) {
Log.i(TAG, "No entries in updated manifest. Dropping.")
return
}
val recipientsSet = updateManifest.toRecipientsSet()
val updateManifest = SignalDatabase.storySends.getLocalManifest(sentTimestamp)
val recipientsSet: Set<SignalServiceStoryMessageRecipient> = updateManifest.toRecipientsSet()
val transcriptMessage: SignalServiceSyncMessage = SignalServiceSyncMessage.forSentTranscript(buildSentTranscript(recipientsSet))
val sendMessageResult = ApplicationDependencies.getSignalServiceMessageSender().sendSyncMessage(transcriptMessage, Optional.empty())
Log.i(TAG, "Sent transcript message with ${recipientsSet.size} recipients")
if (!sendMessageResult.isSuccess) {
throw RetryableException()
}
SignalDatabase.mms.deleteRemotelyDeletedStory(deletedMessageId)
}
override fun onShouldRetry(e: Exception): Boolean {

Wyświetl plik

@ -138,8 +138,8 @@ public final class PushDistributionListSendJob extends PushSendJob {
public void onPushSend()
throws IOException, MmsException, NoSuchMessageException, RetryLaterException
{
MessageTable database = SignalDatabase.mms();
OutgoingMediaMessage message = database.getOutgoingMessage(messageId);
MessageTable database = SignalDatabase.mms();
OutgoingMediaMessage message = database.getOutgoingMessage(messageId);
Set<NetworkFailure> existingNetworkFailures = message.getNetworkFailures();
Set<IdentityKeyMismatch> existingIdentityMismatches = message.getIdentityKeyMismatches();
@ -213,6 +213,8 @@ public final class PushDistributionListSendJob extends PushSendJob {
SentStorySyncManifest manifest = SignalDatabase.storySends().getFullSentStorySyncManifest(messageId, message.getSentTimeMillis());
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);
} catch (ServerRejectedException e) {
throw new UndeliverableMessageException(e);

Wyświetl plik

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

Wyświetl plik

@ -278,6 +278,11 @@ public class SignalServiceMessageSender {
Set<SignalServiceStoryMessageRecipient> manifest)
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);
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);
if (aciStore.isMultiDevice()) {
SignalServiceSyncMessage syncMessage = createSelfSendSyncMessageForStory(message, timestamp, isRecipientUpdate, manifest);
sendSyncMessage(syncMessage, Optional.empty());
sendStorySyncMessage(message, timestamp, isRecipientUpdate, manifest);
}
return sendMessageResults;

Wyświetl plik

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