From dc04c8ed98eb565b9c3e43522a42d3463c2634d2 Mon Sep 17 00:00:00 2001 From: Cody Henthorne Date: Mon, 1 Aug 2022 13:06:51 -0400 Subject: [PATCH] Add urgency flag to message sends. --- .../database/MessageSendLogDatabase.kt | 28 +++--- .../securesms/database/PushDatabase.java | 6 +- .../helpers/SignalDatabaseMigrations.kt | 8 +- .../migration/SignalDatabaseMigration.kt | 11 +++ .../migration/UrgentMslFlagMigration.kt | 14 +++ .../database/model/MessageLogEntry.kt | 2 + .../securesms/groups/GroupManagerV2.java | 11 +-- .../v2/processing/GroupsV2StateProcessor.java | 16 +--- .../jobs/GroupCallUpdateSendJob.java | 9 +- .../jobs/PaymentNotificationSendJob.java | 2 +- .../securesms/jobs/ProfileKeySendJob.java | 2 +- .../securesms/jobs/PushGroupSendJob.java | 5 +- .../jobs/PushGroupSilentUpdateSendJob.java | 2 +- .../securesms/jobs/PushMediaSendJob.java | 6 +- .../securesms/jobs/PushTextSendJob.java | 6 +- .../securesms/jobs/ReactionSendJob.java | 3 +- .../securesms/jobs/RemoteDeleteSendJob.java | 3 +- .../securesms/jobs/ResendMessageJob.java | 10 ++- .../jobs/SendDeliveryReceiptJob.java | 2 +- .../securesms/jobs/SendReadReceiptJob.java | 2 +- .../securesms/jobs/SendViewedReceiptJob.java | 2 +- .../jobs/SenderKeyDistributionSendJob.java | 2 +- .../securesms/messages/GroupSendUtil.java | 61 +++++++++---- .../messages/MessageContentProcessor.java | 2 + .../mms/OutgoingExpirationUpdateMessage.java | 4 + .../mms/OutgoingGroupUpdateMessage.java | 16 ++-- .../securesms/mms/OutgoingMediaMessage.java | 4 + .../api/SignalServiceMessageReceiver.java | 6 +- .../api/SignalServiceMessageSender.java | 87 +++++++++++-------- .../api/messages/SignalServiceEnvelope.java | 21 +++-- .../calls/SignalServiceCallMessage.java | 6 ++ .../messages/multidevice/RequestMessage.java | 4 + .../api/services/MessagingService.java | 4 +- .../push/OutgoingPushMessageList.java | 11 ++- .../internal/push/PushServiceSocket.java | 6 +- .../push/SignalServiceEnvelopeEntity.java | 7 ++ .../main/proto/InternalSerialization.proto | 1 + .../src/main/proto/SignalService.proto | 3 +- 38 files changed, 256 insertions(+), 139 deletions(-) create mode 100644 app/src/main/java/org/thoughtcrime/securesms/database/helpers/migration/SignalDatabaseMigration.kt create mode 100644 app/src/main/java/org/thoughtcrime/securesms/database/helpers/migration/UrgentMslFlagMigration.kt diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/MessageSendLogDatabase.kt b/app/src/main/java/org/thoughtcrime/securesms/database/MessageSendLogDatabase.kt index 4ca2d053d..bec7d8fc3 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/MessageSendLogDatabase.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/MessageSendLogDatabase.kt @@ -6,6 +6,8 @@ import net.zetetic.database.sqlcipher.SQLiteConstraintException import org.signal.core.util.CursorUtil import org.signal.core.util.SqlUtil import org.signal.core.util.logging.Log +import org.signal.core.util.requireBoolean +import org.signal.core.util.toInt import org.thoughtcrime.securesms.database.model.MessageId import org.thoughtcrime.securesms.database.model.MessageLogEntry import org.thoughtcrime.securesms.recipients.Recipient @@ -67,13 +69,15 @@ class MessageSendLogDatabase constructor(context: Context?, databaseHelper: Sign const val DATE_SENT = "date_sent" const val CONTENT = "content" const val CONTENT_HINT = "content_hint" + const val URGENT = "urgent" const val CREATE_TABLE = """ CREATE TABLE $TABLE_NAME ( $ID INTEGER PRIMARY KEY, $DATE_SENT INTEGER NOT NULL, $CONTENT BLOB NOT NULL, - $CONTENT_HINT INTEGER NOT NULL + $CONTENT_HINT INTEGER NOT NULL, + $URGENT INTEGER NOT NULL DEFAULT 1 ) """ @@ -152,31 +156,31 @@ class MessageSendLogDatabase constructor(context: Context?, databaseHelper: Sign } /** @return The ID of the inserted entry, or -1 if none was inserted. Can be used with [addRecipientToExistingEntryIfPossible] */ - fun insertIfPossible(recipientId: RecipientId, sentTimestamp: Long, sendMessageResult: SendMessageResult, contentHint: ContentHint, messageId: MessageId): Long { + fun insertIfPossible(recipientId: RecipientId, sentTimestamp: Long, sendMessageResult: SendMessageResult, contentHint: ContentHint, messageId: MessageId, urgent: Boolean): Long { if (!FeatureFlags.retryReceipts()) return -1 if (sendMessageResult.isSuccess && sendMessageResult.success.content.isPresent) { val recipientDevice = listOf(RecipientDevice(recipientId, sendMessageResult.success.devices)) - return insert(recipientDevice, sentTimestamp, sendMessageResult.success.content.get(), contentHint, listOf(messageId)) + return insert(recipientDevice, sentTimestamp, sendMessageResult.success.content.get(), contentHint, listOf(messageId), urgent) } return -1 } /** @return The ID of the inserted entry, or -1 if none was inserted. Can be used with [addRecipientToExistingEntryIfPossible] */ - fun insertIfPossible(recipientId: RecipientId, sentTimestamp: Long, sendMessageResult: SendMessageResult, contentHint: ContentHint, messageIds: List): Long { + fun insertIfPossible(recipientId: RecipientId, sentTimestamp: Long, sendMessageResult: SendMessageResult, contentHint: ContentHint, messageIds: List, urgent: Boolean): Long { if (!FeatureFlags.retryReceipts()) return -1 if (sendMessageResult.isSuccess && sendMessageResult.success.content.isPresent) { val recipientDevice = listOf(RecipientDevice(recipientId, sendMessageResult.success.devices)) - return insert(recipientDevice, sentTimestamp, sendMessageResult.success.content.get(), contentHint, messageIds) + return insert(recipientDevice, sentTimestamp, sendMessageResult.success.content.get(), contentHint, messageIds, urgent) } return -1 } /** @return The ID of the inserted entry, or -1 if none was inserted. Can be used with [addRecipientToExistingEntryIfPossible] */ - fun insertIfPossible(sentTimestamp: Long, possibleRecipients: List, results: List, contentHint: ContentHint, messageId: MessageId): Long { + fun insertIfPossible(sentTimestamp: Long, possibleRecipients: List, results: List, contentHint: ContentHint, messageId: MessageId, urgent: Boolean): Long { if (!FeatureFlags.retryReceipts()) return -1 val accessList = RecipientAccessList(possibleRecipients) @@ -194,10 +198,10 @@ class MessageSendLogDatabase constructor(context: Context?, databaseHelper: Sign val content: SignalServiceProtos.Content = results.first { it.isSuccess && it.success.content.isPresent }.success.content.get() - return insert(recipientDevices, sentTimestamp, content, contentHint, listOf(messageId)) + return insert(recipientDevices, sentTimestamp, content, contentHint, listOf(messageId), urgent) } - fun addRecipientToExistingEntryIfPossible(payloadId: Long, recipientId: RecipientId, sentTimestamp: Long, sendMessageResult: SendMessageResult, contentHint: ContentHint, messageId: MessageId): Long { + fun addRecipientToExistingEntryIfPossible(payloadId: Long, recipientId: RecipientId, sentTimestamp: Long, sendMessageResult: SendMessageResult, contentHint: ContentHint, messageId: MessageId, urgent: Boolean): Long { if (!FeatureFlags.retryReceipts()) return payloadId if (sendMessageResult.isSuccess && sendMessageResult.success.content.isPresent) { @@ -218,9 +222,9 @@ class MessageSendLogDatabase constructor(context: Context?, databaseHelper: Sign db.setTransactionSuccessful() } catch (e: SQLiteConstraintException) { Log.w(TAG, "Failed to append to existing entry. Creating a new one.") - val payloadId = insertIfPossible(recipientId, sentTimestamp, sendMessageResult, contentHint, messageId) + val newPayloadId = insertIfPossible(recipientId, sentTimestamp, sendMessageResult, contentHint, messageId, urgent) db.setTransactionSuccessful() - return payloadId + return newPayloadId } finally { db.endTransaction() } @@ -229,7 +233,7 @@ class MessageSendLogDatabase constructor(context: Context?, databaseHelper: Sign return payloadId } - private fun insert(recipients: List, dateSent: Long, content: SignalServiceProtos.Content, contentHint: ContentHint, messageIds: List): Long { + private fun insert(recipients: List, dateSent: Long, content: SignalServiceProtos.Content, contentHint: ContentHint, messageIds: List, urgent: Boolean): Long { val db = databaseHelper.signalWritableDatabase db.beginTransaction() @@ -238,6 +242,7 @@ class MessageSendLogDatabase constructor(context: Context?, databaseHelper: Sign put(PayloadTable.DATE_SENT, dateSent) put(PayloadTable.CONTENT, content.toByteArray()) put(PayloadTable.CONTENT_HINT, contentHint.type) + put(PayloadTable.URGENT, urgent.toInt()) } val payloadId: Long = db.insert(PayloadTable.TABLE_NAME, null, payloadValues) @@ -304,6 +309,7 @@ class MessageSendLogDatabase constructor(context: Context?, databaseHelper: Sign dateSent = CursorUtil.requireLong(entryCursor, PayloadTable.DATE_SENT), content = SignalServiceProtos.Content.parseFrom(CursorUtil.requireBlob(entryCursor, PayloadTable.CONTENT)), contentHint = ContentHint.fromType(CursorUtil.requireInt(entryCursor, PayloadTable.CONTENT_HINT)), + urgent = entryCursor.requireBoolean(PayloadTable.URGENT), relatedMessages = messageIds ) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/PushDatabase.java b/app/src/main/java/org/thoughtcrime/securesms/database/PushDatabase.java index e1ace1b0b..39ef4cb5d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/PushDatabase.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/PushDatabase.java @@ -94,7 +94,8 @@ public class PushDatabase extends Database { cursor.getLong(cursor.getColumnIndexOrThrow(SERVER_RECEIVED_TIMESTAMP)), cursor.getLong(cursor.getColumnIndexOrThrow(SERVER_DELIVERED_TIMESTAMP)), cursor.getString(cursor.getColumnIndexOrThrow(SERVER_GUID)), - ""); + "", + true); } } catch (IOException e) { Log.w(TAG, e); @@ -180,7 +181,8 @@ public class PushDatabase extends Database { serverReceivedTimestamp, serverDeliveredTimestamp, serverGuid, - ""); + "", + true); } catch (IOException e) { throw new AssertionError(e); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SignalDatabaseMigrations.kt b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SignalDatabaseMigrations.kt index 78cf5b7af..81f99aa19 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SignalDatabaseMigrations.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SignalDatabaseMigrations.kt @@ -26,6 +26,7 @@ import org.thoughtcrime.securesms.conversation.colors.ChatColors import org.thoughtcrime.securesms.conversation.colors.ChatColorsMapper.entrySet import org.thoughtcrime.securesms.database.KeyValueDatabase import org.thoughtcrime.securesms.database.RecipientDatabase +import org.thoughtcrime.securesms.database.helpers.migration.UrgentMslFlagMigration import org.thoughtcrime.securesms.database.model.databaseprotos.ReactionList import org.thoughtcrime.securesms.dependencies.ApplicationDependencies import org.thoughtcrime.securesms.groups.GroupId @@ -203,8 +204,9 @@ object SignalDatabaseMigrations { private const val QUOTE_INDEX = 147 private const val MY_STORY_PRIVACY_MODE = 148 private const val EXPIRING_PROFILE_CREDENTIALS = 149 + private const val URGENT_FLAG = 150 - const val DATABASE_VERSION = 149 + const val DATABASE_VERSION = 150 @JvmStatic fun migrate(context: Application, db: SQLiteDatabase, oldVersion: Int, newVersion: Int) { @@ -2663,6 +2665,10 @@ object SignalDatabaseMigrations { if (oldVersion < EXPIRING_PROFILE_CREDENTIALS) { db.execSQL("UPDATE recipient SET profile_key_credential = NULL") } + + if (oldVersion < URGENT_FLAG) { + UrgentMslFlagMigration.migrate(context, db, oldVersion, newVersion) + } } @JvmStatic diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/migration/SignalDatabaseMigration.kt b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/migration/SignalDatabaseMigration.kt new file mode 100644 index 000000000..d6b23ff37 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/migration/SignalDatabaseMigration.kt @@ -0,0 +1,11 @@ +package org.thoughtcrime.securesms.database.helpers.migration + +import android.app.Application +import net.zetetic.database.sqlcipher.SQLiteDatabase + +/** + * Simple interface for allowing database migrations to live outside of [org.thoughtcrime.securesms.database.helpers.SignalDatabaseMigrations]. + */ +interface SignalDatabaseMigration { + fun migrate(context: Application, db: SQLiteDatabase, oldVersion: Int, newVersion: Int) +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/migration/UrgentMslFlagMigration.kt b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/migration/UrgentMslFlagMigration.kt new file mode 100644 index 000000000..cae72a73f --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/migration/UrgentMslFlagMigration.kt @@ -0,0 +1,14 @@ +package org.thoughtcrime.securesms.database.helpers.migration + +import android.app.Application +import net.zetetic.database.sqlcipher.SQLiteDatabase + +/** + * Adding an urgent flag to message envelopes to help with notifications. Need to track flag in + * MSL table so can be resent with the correct urgency. + */ +object UrgentMslFlagMigration : SignalDatabaseMigration { + override fun migrate(context: Application, db: SQLiteDatabase, oldVersion: Int, newVersion: Int) { + db.execSQL("ALTER TABLE msl_payload ADD COLUMN urgent INTEGER NOT NULL DEFAULT 1") + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/model/MessageLogEntry.kt b/app/src/main/java/org/thoughtcrime/securesms/database/model/MessageLogEntry.kt index 1ab476654..429773611 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/model/MessageLogEntry.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/model/MessageLogEntry.kt @@ -12,6 +12,8 @@ data class MessageLogEntry( val dateSent: Long, val content: SignalServiceProtos.Content, val contentHint: ContentHint, + @get:JvmName("isUrgent") + val urgent: Boolean, val relatedMessages: List ) { val hasRelatedMessage: Boolean diff --git a/app/src/main/java/org/thoughtcrime/securesms/groups/GroupManagerV2.java b/app/src/main/java/org/thoughtcrime/securesms/groups/GroupManagerV2.java index 61d7e06e5..980a28733 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/groups/GroupManagerV2.java +++ b/app/src/main/java/org/thoughtcrime/securesms/groups/GroupManagerV2.java @@ -1273,16 +1273,7 @@ final class GroupManagerV2 { GroupId.V2 groupId = GroupId.v2(masterKey); Recipient groupRecipient = Recipient.externalGroupExact(groupId); DecryptedGroupV2Context decryptedGroupV2Context = GroupProtoUtil.createDecryptedGroupV2Context(masterKey, groupMutation, signedGroupChange); - OutgoingGroupUpdateMessage outgoingMessage = new OutgoingGroupUpdateMessage(groupRecipient, - decryptedGroupV2Context, - null, - System.currentTimeMillis(), - 0, - false, - null, - Collections.emptyList(), - Collections.emptyList(), - Collections.emptyList()); + OutgoingGroupUpdateMessage outgoingMessage = new OutgoingGroupUpdateMessage(groupRecipient, decryptedGroupV2Context, System.currentTimeMillis()); DecryptedGroupChange plainGroupChange = groupMutation.getGroupChange(); diff --git a/app/src/main/java/org/thoughtcrime/securesms/groups/v2/processing/GroupsV2StateProcessor.java b/app/src/main/java/org/thoughtcrime/securesms/groups/v2/processing/GroupsV2StateProcessor.java index a0d8dc4c8..156755bda 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/groups/v2/processing/GroupsV2StateProcessor.java +++ b/app/src/main/java/org/thoughtcrime/securesms/groups/v2/processing/GroupsV2StateProcessor.java @@ -464,18 +464,8 @@ public class GroupsV2StateProcessor { .addDeleteMembers(UuidUtil.toByteString(selfUuid)) .build(); - DecryptedGroupV2Context decryptedGroupV2Context = GroupProtoUtil.createDecryptedGroupV2Context(masterKey, new GroupMutation(decryptedGroup, simulatedGroupChange, simulatedGroupState), null); - - OutgoingGroupUpdateMessage leaveMessage = new OutgoingGroupUpdateMessage(groupRecipient, - decryptedGroupV2Context, - null, - System.currentTimeMillis(), - 0, - false, - null, - Collections.emptyList(), - Collections.emptyList(), - Collections.emptyList()); + DecryptedGroupV2Context decryptedGroupV2Context = GroupProtoUtil.createDecryptedGroupV2Context(masterKey, new GroupMutation(decryptedGroup, simulatedGroupChange, simulatedGroupState), null); + OutgoingGroupUpdateMessage leaveMessage = new OutgoingGroupUpdateMessage(groupRecipient, decryptedGroupV2Context, System.currentTimeMillis()); try { MessageDatabase mmsDatabase = SignalDatabase.mms(); @@ -703,7 +693,7 @@ public class GroupsV2StateProcessor { ThreadDatabase threadDatabase = SignalDatabase.threads(); RecipientId recipientId = recipientDatabase.getOrInsertFromGroupId(groupId); Recipient recipient = Recipient.resolved(recipientId); - OutgoingGroupUpdateMessage outgoingMessage = new OutgoingGroupUpdateMessage(recipient, decryptedGroupV2Context, null, timestamp, 0, false, null, Collections.emptyList(), Collections.emptyList(), Collections.emptyList()); + OutgoingGroupUpdateMessage outgoingMessage = new OutgoingGroupUpdateMessage(recipient, decryptedGroupV2Context, timestamp); long threadId = threadDatabase.getOrCreateThreadIdFor(recipient); long messageId = mmsDatabase.insertMessageOutbox(outgoingMessage, threadId, false, null); diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/GroupCallUpdateSendJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/GroupCallUpdateSendJob.java index c924c4841..d07a2611b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/GroupCallUpdateSendJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/GroupCallUpdateSendJob.java @@ -57,9 +57,9 @@ public class GroupCallUpdateSendJob extends BaseJob { } List recipientIds = Stream.of(RecipientUtil.getEligibleForSending(Recipient.resolvedList(conversationRecipient.getParticipantIds()))) - .filterNot(Recipient::isSelf) - .map(Recipient::getId) - .toList(); + .filterNot(Recipient::isSelf) + .map(Recipient::getId) + .toList(); return new GroupCallUpdateSendJob(recipientId, eraId, @@ -161,7 +161,8 @@ public class GroupCallUpdateSendJob extends BaseJob { nonSelfDestinations, false, ContentHint.DEFAULT, - dataMessage); + dataMessage, + false); if (includesSelf) { results.add(ApplicationDependencies.getSignalServiceMessageSender().sendSyncMessage(dataMessage)); diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/PaymentNotificationSendJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/PaymentNotificationSendJob.java index e14901ee1..5a97dc046 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/PaymentNotificationSendJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/PaymentNotificationSendJob.java @@ -110,7 +110,7 @@ public final class PaymentNotificationSendJob extends BaseJob { .withPayment(new SignalServiceDataMessage.Payment(new SignalServiceDataMessage.PaymentNotification(payment.getReceipt(), payment.getNote()))) .build(); - SendMessageResult sendMessageResult = messageSender.sendDataMessage(address, unidentifiedAccess, ContentHint.DEFAULT, dataMessage, IndividualSendEvents.EMPTY); + SendMessageResult sendMessageResult = messageSender.sendDataMessage(address, unidentifiedAccess, ContentHint.DEFAULT, dataMessage, IndividualSendEvents.EMPTY, false); if (sendMessageResult.getIdentityFailure() != null) { Log.w(TAG, "Identity failure for " + recipient.getId()); diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/ProfileKeySendJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/ProfileKeySendJob.java index 54bf69470..9ccc708f8 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/ProfileKeySendJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/ProfileKeySendJob.java @@ -153,7 +153,7 @@ public class ProfileKeySendJob extends BaseJob { .withTimestamp(System.currentTimeMillis()) .withProfileKey(Recipient.self().resolve().getProfileKey()); - List results = GroupSendUtil.sendUnresendableDataMessage(context, null, destinations, false, ContentHint.IMPLICIT, dataMessage.build()); + List results = GroupSendUtil.sendUnresendableDataMessage(context, null, destinations, false, ContentHint.IMPLICIT, dataMessage.build(), false); return GroupSendJobHelper.getCompletedSends(destinations, results).completed; } diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/PushGroupSendJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/PushGroupSendJob.java index 049c2a74b..32786349a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/PushGroupSendJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/PushGroupSendJob.java @@ -292,7 +292,7 @@ public final class PushGroupSendJob extends PushSendJob { .withExpiration(groupRecipient.getExpiresInSeconds()) .asGroupMessage(group) .build(); - return GroupSendUtil.sendResendableDataMessage(context, groupRecipient.requireGroupId().requireV2(), destinations, isRecipientUpdate, ContentHint.IMPLICIT, new MessageId(messageId, true), groupDataMessage); + return GroupSendUtil.sendResendableDataMessage(context, groupRecipient.requireGroupId().requireV2(), destinations, isRecipientUpdate, ContentHint.IMPLICIT, new MessageId(messageId, true), groupDataMessage, message.isUrgent()); } else { throw new UndeliverableMessageException("Messages can no longer be sent to V1 groups!"); } @@ -351,7 +351,8 @@ public final class PushGroupSendJob extends PushSendJob { isRecipientUpdate, ContentHint.RESENDABLE, new MessageId(messageId, true), - groupMessageBuilder.build()); + groupMessageBuilder.build(), + message.isUrgent()); } } catch (ServerRejectedException e) { throw new UndeliverableMessageException(e); diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/PushGroupSilentUpdateSendJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/PushGroupSilentUpdateSendJob.java index 52b4045dd..e1497ad4d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/PushGroupSilentUpdateSendJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/PushGroupSilentUpdateSendJob.java @@ -175,7 +175,7 @@ public final class PushGroupSilentUpdateSendJob extends BaseJob { .asGroupMessage(group) .build(); - List results = GroupSendUtil.sendUnresendableDataMessage(context, groupId, destinations, false, ContentHint.IMPLICIT, groupDataMessage); + List results = GroupSendUtil.sendUnresendableDataMessage(context, groupId, destinations, false, ContentHint.IMPLICIT, groupDataMessage, false); return GroupSendJobHelper.getCompletedSends(destinations, results).completed; } diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/PushMediaSendJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/PushMediaSendJob.java index 4c3913366..016e6bffb 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/PushMediaSendJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/PushMediaSendJob.java @@ -254,11 +254,11 @@ public class PushMediaSendJob extends PushSendJob { if (Util.equals(SignalStore.account().getAci(), address.getServiceId())) { Optional syncAccess = UnidentifiedAccessUtil.getAccessForSync(context); SendMessageResult result = messageSender.sendSyncMessage(mediaMessage); - SignalDatabase.messageLog().insertIfPossible(messageRecipient.getId(), message.getSentTimeMillis(), result, ContentHint.RESENDABLE, new MessageId(messageId, true)); + SignalDatabase.messageLog().insertIfPossible(messageRecipient.getId(), message.getSentTimeMillis(), result, ContentHint.RESENDABLE, new MessageId(messageId, true), false); return syncAccess.isPresent(); } else { - SendMessageResult result = messageSender.sendDataMessage(address, UnidentifiedAccessUtil.getAccessFor(context, messageRecipient), ContentHint.RESENDABLE, mediaMessage, IndividualSendEvents.EMPTY); - SignalDatabase.messageLog().insertIfPossible(messageRecipient.getId(), message.getSentTimeMillis(), result, ContentHint.RESENDABLE, new MessageId(messageId, true)); + SendMessageResult result = messageSender.sendDataMessage(address, UnidentifiedAccessUtil.getAccessFor(context, messageRecipient), ContentHint.RESENDABLE, mediaMessage, IndividualSendEvents.EMPTY, message.isUrgent()); + SignalDatabase.messageLog().insertIfPossible(messageRecipient.getId(), message.getSentTimeMillis(), result, ContentHint.RESENDABLE, new MessageId(messageId, true), message.isUrgent()); return result.getSuccess().isUnidentified(); } } catch (UnregisteredUserException e) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/PushTextSendJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/PushTextSendJob.java index 44c85a9dd..58ee5c4de 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/PushTextSendJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/PushTextSendJob.java @@ -194,13 +194,13 @@ public class PushTextSendJob extends PushSendJob { SignalLocalMetrics.IndividualMessageSend.onDeliveryStarted(messageId); SendMessageResult result = messageSender.sendSyncMessage(textSecureMessage); - SignalDatabase.messageLog().insertIfPossible(messageRecipient.getId(), message.getDateSent(), result, ContentHint.RESENDABLE, new MessageId(messageId, false)); + SignalDatabase.messageLog().insertIfPossible(messageRecipient.getId(), message.getDateSent(), result, ContentHint.RESENDABLE, new MessageId(messageId, false), false); return syncAccess.isPresent(); } else { SignalLocalMetrics.IndividualMessageSend.onDeliveryStarted(messageId); - SendMessageResult result = messageSender.sendDataMessage(address, unidentifiedAccess, ContentHint.RESENDABLE, textSecureMessage, new MetricEventListener(messageId)); + SendMessageResult result = messageSender.sendDataMessage(address, unidentifiedAccess, ContentHint.RESENDABLE, textSecureMessage, new MetricEventListener(messageId), true); - SignalDatabase.messageLog().insertIfPossible(messageRecipient.getId(), message.getDateSent(), result, ContentHint.RESENDABLE, new MessageId(messageId, false)); + SignalDatabase.messageLog().insertIfPossible(messageRecipient.getId(), message.getDateSent(), result, ContentHint.RESENDABLE, new MessageId(messageId, false), true); return result.getSuccess().isUnidentified(); } } catch (UnregisteredUserException e) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/ReactionSendJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/ReactionSendJob.java index 94a4888d8..bf3ba72e8 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/ReactionSendJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/ReactionSendJob.java @@ -242,7 +242,8 @@ public class ReactionSendJob extends BaseJob { false, ContentHint.RESENDABLE, messageId, - dataMessage); + dataMessage, + true); if (includesSelf) { results.add(ApplicationDependencies.getSignalServiceMessageSender().sendSyncMessage(dataMessage)); diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/RemoteDeleteSendJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/RemoteDeleteSendJob.java index 55b2e1c9b..04b285d9e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/RemoteDeleteSendJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/RemoteDeleteSendJob.java @@ -214,7 +214,8 @@ public class RemoteDeleteSendJob extends BaseJob { false, ContentHint.RESENDABLE, new MessageId(messageId, isMms), - dataMessage); + dataMessage, + true); return GroupSendJobHelper.getCompletedSends(destinations, results); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/ResendMessageJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/ResendMessageJob.java index 6e9ec40ee..d52a2de6f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/ResendMessageJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/ResendMessageJob.java @@ -51,6 +51,7 @@ public class ResendMessageJob extends BaseJob { private final long sentTimestamp; private final Content content; private final ContentHint contentHint; + private final boolean urgent; private final GroupId.V2 groupId; private final DistributionId distributionId; @@ -58,6 +59,7 @@ public class ResendMessageJob extends BaseJob { private static final String KEY_SENT_TIMESTAMP = "sent_timestamp"; private static final String KEY_CONTENT = "content"; private static final String KEY_CONTENT_HINT = "content_hint"; + private static final String KEY_URGENT = "urgent"; private static final String KEY_GROUP_ID = "group_id"; private static final String KEY_DISTRIBUTION_ID = "distribution_id"; @@ -65,6 +67,7 @@ public class ResendMessageJob extends BaseJob { long sentTimestamp, @NonNull Content content, @NonNull ContentHint contentHint, + boolean urgent, @Nullable GroupId.V2 groupId, @Nullable DistributionId distributionId) { @@ -72,6 +75,7 @@ public class ResendMessageJob extends BaseJob { sentTimestamp, content, contentHint, + urgent, groupId, distributionId, new Parameters.Builder().setQueue(recipientId.toQueueKey()) @@ -85,6 +89,7 @@ public class ResendMessageJob extends BaseJob { long sentTimestamp, @NonNull Content content, @NonNull ContentHint contentHint, + boolean urgent, @Nullable GroupId.V2 groupId, @Nullable DistributionId distributionId, @NonNull Parameters parameters) @@ -95,6 +100,7 @@ public class ResendMessageJob extends BaseJob { this.sentTimestamp = sentTimestamp; this.content = content; this.contentHint = contentHint; + this.urgent = urgent; this.groupId = groupId; this.distributionId = distributionId; } @@ -106,6 +112,7 @@ public class ResendMessageJob extends BaseJob { .putLong(KEY_SENT_TIMESTAMP, sentTimestamp) .putBlobAsString(KEY_CONTENT, content.toByteArray()) .putInt(KEY_CONTENT_HINT, contentHint.getType()) + .putBoolean(KEY_URGENT, urgent) .putBlobAsString(KEY_GROUP_ID, groupId != null ? groupId.getDecodedId() : null) .putString(KEY_DISTRIBUTION_ID, distributionId != null ? distributionId.toString() : null) .build(); @@ -152,7 +159,7 @@ public class ResendMessageJob extends BaseJob { contentToSend = contentToSend.toBuilder().setSenderKeyDistributionMessage(distributionBytes).build(); } - SendMessageResult result = messageSender.resendContent(address, access, sentTimestamp, contentToSend, contentHint, Optional.ofNullable(groupId).map(GroupId::getDecodedId)); + SendMessageResult result = messageSender.resendContent(address, access, sentTimestamp, contentToSend, contentHint, Optional.ofNullable(groupId).map(GroupId::getDecodedId), urgent); if (result.isSuccess() && distributionId != null) { List addresses = result.getSuccess() @@ -195,6 +202,7 @@ public class ResendMessageJob extends BaseJob { data.getLong(KEY_SENT_TIMESTAMP), content, ContentHint.fromType(data.getInt(KEY_CONTENT_HINT)), + data.getBooleanOrDefault(KEY_URGENT, true), groupId, distributionId, parameters); diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/SendDeliveryReceiptJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/SendDeliveryReceiptJob.java index dfd4dff31..86fe7fe74 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/SendDeliveryReceiptJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/SendDeliveryReceiptJob.java @@ -122,7 +122,7 @@ public class SendDeliveryReceiptJob extends BaseJob { receiptMessage); if (messageId != null) { - SignalDatabase.messageLog().insertIfPossible(recipientId, timestamp, result, ContentHint.IMPLICIT, messageId); + SignalDatabase.messageLog().insertIfPossible(recipientId, timestamp, result, ContentHint.IMPLICIT, messageId, false); } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/SendReadReceiptJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/SendReadReceiptJob.java index 469a99b6c..039a65fbf 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/SendReadReceiptJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/SendReadReceiptJob.java @@ -186,7 +186,7 @@ public class SendReadReceiptJob extends BaseJob { receiptMessage); if (Util.hasItems(messageIds)) { - SignalDatabase.messageLog().insertIfPossible(recipientId, timestamp, result, ContentHint.IMPLICIT, messageIds); + SignalDatabase.messageLog().insertIfPossible(recipientId, timestamp, result, ContentHint.IMPLICIT, messageIds, false); } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/SendViewedReceiptJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/SendViewedReceiptJob.java index 5466c37d6..6c5375de4 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/SendViewedReceiptJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/SendViewedReceiptJob.java @@ -182,7 +182,7 @@ public class SendViewedReceiptJob extends BaseJob { receiptMessage); if (Util.hasItems(messageIds)) { - SignalDatabase.messageLog().insertIfPossible(recipientId, timestamp, result, ContentHint.IMPLICIT, messageIds); + SignalDatabase.messageLog().insertIfPossible(recipientId, timestamp, result, ContentHint.IMPLICIT, messageIds, false); } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/SenderKeyDistributionSendJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/SenderKeyDistributionSendJob.java index e997a2b6b..35893d3f8 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/SenderKeyDistributionSendJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/SenderKeyDistributionSendJob.java @@ -101,7 +101,7 @@ public final class SenderKeyDistributionSendJob extends BaseJob { SenderKeyDistributionMessage message = messageSender.getOrCreateNewGroupSession(distributionId); List> access = UnidentifiedAccessUtil.getAccessFor(context, Collections.singletonList(recipient)); - SendMessageResult result = messageSender.sendSenderKeyDistributionMessage(distributionId, address, access, message, Optional.of(groupId.getDecodedId())).get(0); + SendMessageResult result = messageSender.sendSenderKeyDistributionMessage(distributionId, address, access, message, Optional.of(groupId.getDecodedId()), false).get(0); if (result.isSuccess()) { List addresses = result.getSuccess() diff --git a/app/src/main/java/org/thoughtcrime/securesms/messages/GroupSendUtil.java b/app/src/main/java/org/thoughtcrime/securesms/messages/GroupSendUtil.java index df3235d56..a1f09acf3 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/messages/GroupSendUtil.java +++ b/app/src/main/java/org/thoughtcrime/securesms/messages/GroupSendUtil.java @@ -88,10 +88,11 @@ public final class GroupSendUtil { boolean isRecipientUpdate, ContentHint contentHint, @NonNull MessageId messageId, - @NonNull SignalServiceDataMessage message) + @NonNull SignalServiceDataMessage message, + boolean urgent) throws IOException, UntrustedIdentityException { - return sendMessage(context, groupId, getDistributionId(groupId), messageId, allTargets, isRecipientUpdate, DataSendOperation.resendable(message, contentHint, messageId), null); + return sendMessage(context, groupId, getDistributionId(groupId), messageId, allTargets, isRecipientUpdate, DataSendOperation.resendable(message, contentHint, messageId, urgent), null); } /** @@ -109,10 +110,11 @@ public final class GroupSendUtil { @NonNull List allTargets, boolean isRecipientUpdate, ContentHint contentHint, - @NonNull SignalServiceDataMessage message) + @NonNull SignalServiceDataMessage message, + boolean urgent) throws IOException, UntrustedIdentityException { - return sendMessage(context, groupId, getDistributionId(groupId), null, allTargets, isRecipientUpdate, DataSendOperation.unresendable(message, contentHint), null); + return sendMessage(context, groupId, getDistributionId(groupId), null, allTargets, isRecipientUpdate, DataSendOperation.unresendable(message, contentHint, urgent), null); } /** @@ -295,7 +297,7 @@ public final class GroupSendUtil { Log.d(TAG, "Successfully sent using sender key to " + successCount + "/" + targets.size() + " sender key targets."); if (sendOperation.shouldIncludeInMessageLog()) { - SignalDatabase.messageLog().insertIfPossible(sendOperation.getSentTimestamp(), senderKeyTargets, results, sendOperation.getContentHint(), sendOperation.getRelatedMessageId()); + SignalDatabase.messageLog().insertIfPossible(sendOperation.getSentTimestamp(), senderKeyTargets, results, sendOperation.getContentHint(), sendOperation.getRelatedMessageId(), sendOperation.isUrgent()); } if (relatedMessageId != null) { @@ -353,9 +355,9 @@ public final class GroupSendUtil { synchronized (entryId) { if (entryId.get() == -1) { - entryId.set(messageLogDatabase.insertIfPossible(recipients.requireRecipientId(result.getAddress()), sendOperation.getSentTimestamp(), result, sendOperation.getContentHint(), sendOperation.getRelatedMessageId())); + entryId.set(messageLogDatabase.insertIfPossible(recipients.requireRecipientId(result.getAddress()), sendOperation.getSentTimestamp(), result, sendOperation.getContentHint(), sendOperation.getRelatedMessageId(), sendOperation.isUrgent())); } else { - entryId.set(messageLogDatabase.addRecipientToExistingEntryIfPossible(entryId.get(), recipients.requireRecipientId(result.getAddress()), sendOperation.getSentTimestamp(), result, sendOperation.getContentHint(), sendOperation.getRelatedMessageId())); + entryId.set(messageLogDatabase.addRecipientToExistingEntryIfPossible(entryId.get(), recipients.requireRecipientId(result.getAddress()), sendOperation.getSentTimestamp(), result, sendOperation.getContentHint(), sendOperation.getRelatedMessageId(), sendOperation.isUrgent())); } } }, cancelationSignal); @@ -424,6 +426,7 @@ public final class GroupSendUtil { long getSentTimestamp(); boolean shouldIncludeInMessageLog(); @NonNull MessageId getRelatedMessageId(); + boolean isUrgent(); } private static class DataSendOperation implements SendOperation { @@ -431,20 +434,22 @@ public final class GroupSendUtil { private final ContentHint contentHint; private final MessageId relatedMessageId; private final boolean resendable; + private final boolean urgent; - public static DataSendOperation resendable(@NonNull SignalServiceDataMessage message, @NonNull ContentHint contentHint, @NonNull MessageId relatedMessageId) { - return new DataSendOperation(message, contentHint, true, relatedMessageId); + public static DataSendOperation resendable(@NonNull SignalServiceDataMessage message, @NonNull ContentHint contentHint, @NonNull MessageId relatedMessageId, boolean urgent) { + return new DataSendOperation(message, contentHint, true, relatedMessageId, urgent); } - public static DataSendOperation unresendable(@NonNull SignalServiceDataMessage message, @NonNull ContentHint contentHint) { - return new DataSendOperation(message, contentHint, false, null); + public static DataSendOperation unresendable(@NonNull SignalServiceDataMessage message, @NonNull ContentHint contentHint, boolean urgent) { + return new DataSendOperation(message, contentHint, false, null, urgent); } - private DataSendOperation(@NonNull SignalServiceDataMessage message, @NonNull ContentHint contentHint, boolean resendable, @Nullable MessageId relatedMessageId) { - this.message = message; - this.contentHint = contentHint; - this.resendable = resendable; - this.relatedMessageId = relatedMessageId; + private DataSendOperation(@NonNull SignalServiceDataMessage message, @NonNull ContentHint contentHint, boolean resendable, @Nullable MessageId relatedMessageId, boolean urgent) { + this.message = message; + this.contentHint = contentHint; + this.resendable = resendable; + this.relatedMessageId = relatedMessageId; + this.urgent = urgent; if (resendable && relatedMessageId == null) { throw new IllegalArgumentException("If a message is resendable, it must have a related message ID!"); @@ -460,7 +465,7 @@ public final class GroupSendUtil { throws NoSessionException, UntrustedIdentityException, InvalidKeyException, IOException, InvalidRegistrationIdException { SenderKeyGroupEvents listener = relatedMessageId != null ? new SenderKeyMetricEventListener(relatedMessageId.getId()) : SenderKeyGroupEvents.EMPTY; - return messageSender.sendGroupDataMessage(distributionId, targets, access, isRecipientUpdate, contentHint, message, listener); + return messageSender.sendGroupDataMessage(distributionId, targets, access, isRecipientUpdate, contentHint, message, listener, urgent); } @Override @@ -473,7 +478,7 @@ public final class GroupSendUtil { throws IOException, UntrustedIdentityException { LegacyGroupEvents listener = relatedMessageId != null ? new LegacyMetricEventListener(relatedMessageId.getId()) : LegacyGroupEvents.EMPTY; - return messageSender.sendDataMessage(targets, access, isRecipientUpdate, contentHint, message, listener, partialListener, cancelationSignal); + return messageSender.sendDataMessage(targets, access, isRecipientUpdate, contentHint, message, listener, partialListener, cancelationSignal, urgent); } @Override @@ -499,6 +504,11 @@ public final class GroupSendUtil { throw new UnsupportedOperationException(); } } + + @Override + public boolean isUrgent() { + return urgent; + } } private static class TypingSendOperation implements SendOperation { @@ -553,6 +563,11 @@ public final class GroupSendUtil { public @NonNull MessageId getRelatedMessageId() { throw new UnsupportedOperationException(); } + + @Override + public boolean isUrgent() { + return false; + } } private static class CallSendOperation implements SendOperation { @@ -605,6 +620,11 @@ public final class GroupSendUtil { public @NonNull MessageId getRelatedMessageId() { throw new UnsupportedOperationException(); } + + @Override + public boolean isUrgent() { + return message.isUrgent(); + } } public static class StorySendOperation implements SendOperation { @@ -670,6 +690,11 @@ public final class GroupSendUtil { public @NonNull MessageId getRelatedMessageId() { return relatedMessageId; } + + @Override + public boolean isUrgent() { + return false; + } } private static final class SenderKeyMetricEventListener implements SenderKeyGroupEvents { diff --git a/app/src/main/java/org/thoughtcrime/securesms/messages/MessageContentProcessor.java b/app/src/main/java/org/thoughtcrime/securesms/messages/MessageContentProcessor.java index a9c016c3a..658e91fa8 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/messages/MessageContentProcessor.java +++ b/app/src/main/java/org/thoughtcrime/securesms/messages/MessageContentProcessor.java @@ -2694,6 +2694,7 @@ public final class MessageContentProcessor { messageLogEntry.getDateSent(), messageLogEntry.getContent(), messageLogEntry.getContentHint(), + messageLogEntry.isUrgent(), groupId, distributionId)); } else { @@ -2735,6 +2736,7 @@ public final class MessageContentProcessor { messageLogEntry.getDateSent(), messageLogEntry.getContent(), messageLogEntry.getContentHint(), + messageLogEntry.isUrgent(), null, null)); } else if (archivedSession) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/mms/OutgoingExpirationUpdateMessage.java b/app/src/main/java/org/thoughtcrime/securesms/mms/OutgoingExpirationUpdateMessage.java index 05fe72951..fa8d595ac 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mms/OutgoingExpirationUpdateMessage.java +++ b/app/src/main/java/org/thoughtcrime/securesms/mms/OutgoingExpirationUpdateMessage.java @@ -32,4 +32,8 @@ public class OutgoingExpirationUpdateMessage extends OutgoingSecureMediaMessage return true; } + @Override + public boolean isUrgent() { + return false; + } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/mms/OutgoingGroupUpdateMessage.java b/app/src/main/java/org/thoughtcrime/securesms/mms/OutgoingGroupUpdateMessage.java index e4fdcec4e..16affa209 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mms/OutgoingGroupUpdateMessage.java +++ b/app/src/main/java/org/thoughtcrime/securesms/mms/OutgoingGroupUpdateMessage.java @@ -67,16 +67,9 @@ public final class OutgoingGroupUpdateMessage extends OutgoingSecureMediaMessage public OutgoingGroupUpdateMessage(@NonNull Recipient recipient, @NonNull DecryptedGroupV2Context group, - @Nullable final Attachment avatar, - long sentTimeMillis, - long expireIn, - boolean viewOnce, - @Nullable QuoteModel quote, - @NonNull List contacts, - @NonNull List previews, - @NonNull List mentions) + long sentTimeMillis) { - this(recipient, new MessageGroupContext(group), getAttachments(avatar), sentTimeMillis, expireIn, viewOnce, quote, contacts, previews, mentions); + this(recipient, new MessageGroupContext(group), Collections.emptyList(), sentTimeMillis, 0, false, null, Collections.emptyList(), Collections.emptyList(), Collections.emptyList()); } @Override @@ -100,6 +93,11 @@ public final class OutgoingGroupUpdateMessage extends OutgoingSecureMediaMessage return messageGroupContext.requireGroupV2Properties(); } + @Override + public boolean isUrgent() { + return false; + } + private static List getAttachments(@Nullable Attachment avatar) { return avatar == null ? Collections.emptyList() : Collections.singletonList(avatar); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/mms/OutgoingMediaMessage.java b/app/src/main/java/org/thoughtcrime/securesms/mms/OutgoingMediaMessage.java index 35991a85f..e9351e7c6 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mms/OutgoingMediaMessage.java +++ b/app/src/main/java/org/thoughtcrime/securesms/mms/OutgoingMediaMessage.java @@ -249,6 +249,10 @@ public class OutgoingMediaMessage { return giftBadge; } + public boolean isUrgent() { + return true; + } + private static String buildMessage(SlideDeck slideDeck, String message) { if (!TextUtils.isEmpty(message) && !TextUtils.isEmpty(slideDeck.getBody())) { return slideDeck.getBody() + "\n\n" + message; diff --git a/libsignal/service/src/main/java/org/whispersystems/signalservice/api/SignalServiceMessageReceiver.java b/libsignal/service/src/main/java/org/whispersystems/signalservice/api/SignalServiceMessageReceiver.java index 9e36a88dc..60002057c 100644 --- a/libsignal/service/src/main/java/org/whispersystems/signalservice/api/SignalServiceMessageReceiver.java +++ b/libsignal/service/src/main/java/org/whispersystems/signalservice/api/SignalServiceMessageReceiver.java @@ -225,7 +225,8 @@ public class SignalServiceMessageReceiver { entity.getServerTimestamp(), messageResult.getServerDeliveredTimestamp(), entity.getServerUuid(), - entity.getDestinationUuid()); + entity.getDestinationUuid(), + entity.isUrgent()); } else { envelope = new SignalServiceEnvelope(entity.getType(), entity.getTimestamp(), @@ -234,7 +235,8 @@ public class SignalServiceMessageReceiver { entity.getServerTimestamp(), messageResult.getServerDeliveredTimestamp(), entity.getServerUuid(), - entity.getDestinationUuid()); + entity.getDestinationUuid(), + entity.isUrgent()); } callback.onMessage(envelope); diff --git a/libsignal/service/src/main/java/org/whispersystems/signalservice/api/SignalServiceMessageSender.java b/libsignal/service/src/main/java/org/whispersystems/signalservice/api/SignalServiceMessageSender.java index a2d2718a4..db3b0bd77 100644 --- a/libsignal/service/src/main/java/org/whispersystems/signalservice/api/SignalServiceMessageSender.java +++ b/libsignal/service/src/main/java/org/whispersystems/signalservice/api/SignalServiceMessageSender.java @@ -203,7 +203,7 @@ public class SignalServiceMessageSender { Content content = createReceiptContent(message); EnvelopeContent envelopeContent = EnvelopeContent.encrypted(content, ContentHint.IMPLICIT, Optional.empty()); - return sendMessage(recipient, getTargetUnidentifiedAccess(unidentifiedAccess), message.getWhen(), envelopeContent, false, null); + return sendMessage(recipient, getTargetUnidentifiedAccess(unidentifiedAccess), message.getWhen(), envelopeContent, false, null, false); } /** @@ -219,7 +219,7 @@ public class SignalServiceMessageSender { PlaintextContent content = new PlaintextContent(errorMessage); EnvelopeContent envelopeContent = EnvelopeContent.plaintext(content, groupId); - sendMessage(recipient, getTargetUnidentifiedAccess(unidentifiedAccess), System.currentTimeMillis(), envelopeContent, false, null); + sendMessage(recipient, getTargetUnidentifiedAccess(unidentifiedAccess), System.currentTimeMillis(), envelopeContent, false, null, false); } /** @@ -234,7 +234,7 @@ public class SignalServiceMessageSender { Content content = createTypingContent(message); EnvelopeContent envelopeContent = EnvelopeContent.encrypted(content, ContentHint.IMPLICIT, Optional.empty()); - sendMessage(recipients, getTargetUnidentifiedAccess(unidentifiedAccess), message.getTimestamp(), envelopeContent, true, null, cancelationSignal); + sendMessage(recipients, getTargetUnidentifiedAccess(unidentifiedAccess), message.getTimestamp(), envelopeContent, true, null, cancelationSignal, false); } /** @@ -247,7 +247,7 @@ public class SignalServiceMessageSender { throws IOException, UntrustedIdentityException, InvalidKeyException, NoSessionException, InvalidRegistrationIdException { Content content = createTypingContent(message); - sendGroupMessage(distributionId, recipients, unidentifiedAccess, message.getTimestamp(), content, ContentHint.IMPLICIT, message.getGroupId(), true, SenderKeyGroupEvents.EMPTY); + sendGroupMessage(distributionId, recipients, unidentifiedAccess, message.getTimestamp(), content, ContentHint.IMPLICIT, message.getGroupId(), true, SenderKeyGroupEvents.EMPTY, false); } public List sendStory(List recipients, @@ -260,7 +260,7 @@ public class SignalServiceMessageSender { { Content content = createStoryContent(message); EnvelopeContent envelopeContent = EnvelopeContent.encrypted(content, ContentHint.RESENDABLE, Optional.empty()); - List sendMessageResults = sendMessage(recipients, getTargetUnidentifiedAccess(unidentifiedAccess), timestamp, envelopeContent, false, null, null); + List sendMessageResults = sendMessage(recipients, getTargetUnidentifiedAccess(unidentifiedAccess), timestamp, envelopeContent, false, null, null, false); if (store.isMultiDevice()) { SignalServiceSyncMessage syncMessage = createSelfSendSyncMessageForStory(message, timestamp, isRecipientUpdate, manifest); @@ -284,7 +284,7 @@ public class SignalServiceMessageSender { throws IOException, UntrustedIdentityException, InvalidKeyException, NoSessionException, InvalidRegistrationIdException { Content content = createStoryContent(message); - List sendMessageResults = sendGroupMessage(distributionId, recipients, unidentifiedAccess, timestamp, content, ContentHint.RESENDABLE, groupId, false, SenderKeyGroupEvents.EMPTY); + List sendMessageResults = sendGroupMessage(distributionId, recipients, unidentifiedAccess, timestamp, content, ContentHint.RESENDABLE, groupId, false, SenderKeyGroupEvents.EMPTY, false); if (store.isMultiDevice()) { SignalServiceSyncMessage syncMessage = createSelfSendSyncMessageForStory(message, timestamp, isRecipientUpdate, manifest); @@ -310,7 +310,7 @@ public class SignalServiceMessageSender { Content content = createCallContent(message); EnvelopeContent envelopeContent = EnvelopeContent.encrypted(content, ContentHint.DEFAULT, Optional.empty()); - sendMessage(recipient, getTargetUnidentifiedAccess(unidentifiedAccess), System.currentTimeMillis(), envelopeContent, false, null); + sendMessage(recipient, getTargetUnidentifiedAccess(unidentifiedAccess), System.currentTimeMillis(), envelopeContent, false, null, message.isUrgent()); } public List sendCallMessage(List recipients, @@ -321,7 +321,7 @@ public class SignalServiceMessageSender { Content content = createCallContent(message); EnvelopeContent envelopeContent = EnvelopeContent.encrypted(content, ContentHint.DEFAULT, Optional.empty()); - return sendMessage(recipients, getTargetUnidentifiedAccess(unidentifiedAccess), System.currentTimeMillis(), envelopeContent, false, null, null); + return sendMessage(recipients, getTargetUnidentifiedAccess(unidentifiedAccess), System.currentTimeMillis(), envelopeContent, false, null, null, message.isUrgent()); } public List sendCallMessage(DistributionId distributionId, @@ -331,7 +331,7 @@ public class SignalServiceMessageSender { throws IOException, UntrustedIdentityException, InvalidKeyException, NoSessionException, InvalidRegistrationIdException { Content content = createCallContent(message); - return sendGroupMessage(distributionId, recipients, unidentifiedAccess, message.getTimestamp().get(), content, ContentHint.IMPLICIT, message.getGroupId(), false, SenderKeyGroupEvents.EMPTY); + return sendGroupMessage(distributionId, recipients, unidentifiedAccess, message.getTimestamp().get(), content, ContentHint.IMPLICIT, message.getGroupId(), false, SenderKeyGroupEvents.EMPTY, message.isUrgent()); } /** @@ -360,7 +360,8 @@ public class SignalServiceMessageSender { Optional unidentifiedAccess, ContentHint contentHint, SignalServiceDataMessage message, - IndividualSendEvents sendEvents) + IndividualSendEvents sendEvents, + boolean urgent) throws UntrustedIdentityException, IOException { Log.d(TAG, "[" + message.getTimestamp() + "] Sending a data message."); @@ -371,7 +372,7 @@ public class SignalServiceMessageSender { sendEvents.onMessageEncrypted(); long timestamp = message.getTimestamp(); - SendMessageResult result = sendMessage(recipient, getTargetUnidentifiedAccess(unidentifiedAccess), timestamp, envelopeContent, false, null); + SendMessageResult result = sendMessage(recipient, getTargetUnidentifiedAccess(unidentifiedAccess), timestamp, envelopeContent, false, null, urgent); sendEvents.onMessageSent(); @@ -379,7 +380,7 @@ public class SignalServiceMessageSender { Content syncMessage = createMultiDeviceSentTranscriptContent(content, Optional.of(recipient), timestamp, Collections.singletonList(result), false, Collections.emptySet()); EnvelopeContent syncMessageContent = EnvelopeContent.encrypted(syncMessage, ContentHint.IMPLICIT, Optional.empty()); - sendMessage(localAddress, Optional.empty(), timestamp, syncMessageContent, false, null); + sendMessage(localAddress, Optional.empty(), timestamp, syncMessageContent, false, null, false); } sendEvents.onSyncMessageSent(); @@ -403,7 +404,8 @@ public class SignalServiceMessageSender { List recipients, List> unidentifiedAccess, SenderKeyDistributionMessage message, - Optional groupId) + Optional groupId, + boolean urgent) throws IOException { ByteString distributionBytes = ByteString.copyFrom(message.serialize()); @@ -412,7 +414,7 @@ public class SignalServiceMessageSender { long timestamp = System.currentTimeMillis(); Log.d(TAG, "[" + timestamp + "] Sending SKDM to " + recipients.size() + " recipients for DistributionId " + distributionId); - return sendMessage(recipients, getTargetUnidentifiedAccess(unidentifiedAccess), timestamp, envelopeContent, false, null, null); + return sendMessage(recipients, getTargetUnidentifiedAccess(unidentifiedAccess), timestamp, envelopeContent, false, null, null, urgent); } /** @@ -430,13 +432,14 @@ public class SignalServiceMessageSender { long timestamp, Content content, ContentHint contentHint, - Optional groupId) + Optional groupId, + boolean urgent) throws UntrustedIdentityException, IOException { EnvelopeContent envelopeContent = EnvelopeContent.encrypted(content, contentHint, groupId); Optional access = unidentifiedAccess.isPresent() ? unidentifiedAccess.get().getTargetUnidentifiedAccess() : Optional.empty(); - return sendMessage(address, access, timestamp, envelopeContent, false, null); + return sendMessage(address, access, timestamp, envelopeContent, false, null, urgent); } /** @@ -448,14 +451,15 @@ public class SignalServiceMessageSender { boolean isRecipientUpdate, ContentHint contentHint, SignalServiceDataMessage message, - SenderKeyGroupEvents sendEvents) + SenderKeyGroupEvents sendEvents, + boolean urgent) throws IOException, UntrustedIdentityException, NoSessionException, InvalidKeyException, InvalidRegistrationIdException { Log.d(TAG, "[" + message.getTimestamp() + "] Sending a group data message to " + recipients.size() + " recipients using DistributionId " + distributionId); Content content = createMessageContent(message); Optional groupId = message.getGroupId(); - List results = sendGroupMessage(distributionId, recipients, unidentifiedAccess, message.getTimestamp(), content, contentHint, groupId, false, sendEvents); + List results = sendGroupMessage(distributionId, recipients, unidentifiedAccess, message.getTimestamp(), content, contentHint, groupId, false, sendEvents, urgent); sendEvents.onMessageSent(); @@ -463,7 +467,7 @@ public class SignalServiceMessageSender { Content syncMessage = createMultiDeviceSentTranscriptContent(content, Optional.empty(), message.getTimestamp(), results, isRecipientUpdate, Collections.emptySet()); EnvelopeContent syncMessageContent = EnvelopeContent.encrypted(syncMessage, ContentHint.IMPLICIT, Optional.empty()); - sendMessage(localAddress, Optional.empty(), message.getTimestamp(), syncMessageContent, false, null); + sendMessage(localAddress, Optional.empty(), message.getTimestamp(), syncMessageContent, false, null, false); } sendEvents.onSyncMessageSent(); @@ -484,7 +488,8 @@ public class SignalServiceMessageSender { SignalServiceDataMessage message, LegacyGroupEvents sendEvents, PartialSendCompleteListener partialListener, - CancelationSignal cancelationSignal) + CancelationSignal cancelationSignal, + boolean urgent) throws IOException, UntrustedIdentityException { Log.d(TAG, "[" + message.getTimestamp() + "] Sending a data message to " + recipients.size() + " recipients."); @@ -492,7 +497,7 @@ public class SignalServiceMessageSender { Content content = createMessageContent(message); EnvelopeContent envelopeContent = EnvelopeContent.encrypted(content, contentHint, message.getGroupId()); long timestamp = message.getTimestamp(); - List results = sendMessage(recipients, getTargetUnidentifiedAccess(unidentifiedAccess), timestamp, envelopeContent, false, partialListener, cancelationSignal); + List results = sendMessage(recipients, getTargetUnidentifiedAccess(unidentifiedAccess), timestamp, envelopeContent, false, partialListener, cancelationSignal, urgent); boolean needsSyncInResults = false; sendEvents.onMessageSent(); @@ -513,7 +518,7 @@ public class SignalServiceMessageSender { Content syncMessage = createMultiDeviceSentTranscriptContent(content, recipient, timestamp, results, isRecipientUpdate, Collections.emptySet()); EnvelopeContent syncMessageContent = EnvelopeContent.encrypted(syncMessage, ContentHint.IMPLICIT, Optional.empty()); - sendMessage(localAddress, Optional.empty(), timestamp, syncMessageContent, false, null); + sendMessage(localAddress, Optional.empty(), timestamp, syncMessageContent, false, null, false); } sendEvents.onSyncMessageSent(); @@ -531,14 +536,15 @@ public class SignalServiceMessageSender { throws IOException, UntrustedIdentityException { Content content; + boolean urgent = false; if (message.getContacts().isPresent()) { - content = createMultiDeviceContactsContent(message.getContacts().get().getContactsStream().asStream(), - message.getContacts().get().isComplete()); + content = createMultiDeviceContactsContent(message.getContacts().get().getContactsStream().asStream(), message.getContacts().get().isComplete()); } else if (message.getGroups().isPresent()) { content = createMultiDeviceGroupsContent(message.getGroups().get().asStream()); } else if (message.getRead().isPresent()) { content = createMultiDeviceReadContent(message.getRead().get()); + urgent = true; } else if (message.getViewed().isPresent()) { content = createMultiDeviceViewedContent(message.getViewed().get()); } else if (message.getViewOnceOpen().isPresent()) { @@ -563,6 +569,7 @@ public class SignalServiceMessageSender { return sendVerifiedSyncMessage(message.getVerified().get()); } else if (message.getRequest().isPresent()) { content = createRequestContent(message.getRequest().get().getRequest()); + urgent = message.getRequest().get().isUrgent(); } else if (message.getPniIdentity().isPresent()) { content = createPniIdentityContent(message.getPniIdentity().get()); } else { @@ -574,7 +581,7 @@ public class SignalServiceMessageSender { EnvelopeContent envelopeContent = EnvelopeContent.encrypted(content, ContentHint.IMPLICIT, Optional.empty()); - return sendMessage(localAddress, Optional.empty(), timestamp, envelopeContent, false, null); + return sendMessage(localAddress, Optional.empty(), timestamp, envelopeContent, false, null, urgent); } public void setSoTimeoutMillis(long soTimeoutMillis) { @@ -711,13 +718,13 @@ public class SignalServiceMessageSender { EnvelopeContent envelopeContent = EnvelopeContent.encrypted(content, ContentHint.IMPLICIT, Optional.empty()); - SendMessageResult result = sendMessage(message.getDestination(), Optional.empty(), message.getTimestamp(), envelopeContent, false, null); + SendMessageResult result = sendMessage(message.getDestination(), Optional.empty(), message.getTimestamp(), envelopeContent, false, null, false); if (result.getSuccess().isNeedsSync()) { Content syncMessage = createMultiDeviceVerifiedContent(message, nullMessage.toByteArray()); EnvelopeContent syncMessageContent = EnvelopeContent.encrypted(syncMessage, ContentHint.IMPLICIT, Optional.empty()); - sendMessage(localAddress, Optional.empty(), message.getTimestamp(), syncMessageContent, false, null); + sendMessage(localAddress, Optional.empty(), message.getTimestamp(), syncMessageContent, false, null, false); } return result; @@ -741,7 +748,7 @@ public class SignalServiceMessageSender { EnvelopeContent envelopeContent = EnvelopeContent.encrypted(content, ContentHint.IMPLICIT, Optional.empty()); - return sendMessage(address, getTargetUnidentifiedAccess(unidentifiedAccess), System.currentTimeMillis(), envelopeContent, false, null); + return sendMessage(address, getTargetUnidentifiedAccess(unidentifiedAccess), System.currentTimeMillis(), envelopeContent, false, null, false); } private Content createTypingContent(SignalServiceTypingMessage message) { @@ -1616,7 +1623,8 @@ public class SignalServiceMessageSender { EnvelopeContent content, boolean online, PartialSendCompleteListener partialListener, - CancelationSignal cancelationSignal) + CancelationSignal cancelationSignal, + boolean urgent) throws IOException { Log.d(TAG, "[" + timestamp + "] Sending to " + recipients.size() + " recipients."); @@ -1631,7 +1639,7 @@ public class SignalServiceMessageSender { SignalServiceAddress recipient = recipientIterator.next(); Optional access = unidentifiedAccessIterator.next(); futureResults.add(executor.submit(() -> { - SendMessageResult result = sendMessage(recipient, access, timestamp, content, online, cancelationSignal); + SendMessageResult result = sendMessage(recipient, access, timestamp, content, online, cancelationSignal, urgent); if (partialListener != null) { partialListener.onPartialSendComplete(result); } @@ -1698,7 +1706,8 @@ public class SignalServiceMessageSender { long timestamp, EnvelopeContent content, boolean online, - CancelationSignal cancelationSignal) + CancelationSignal cancelationSignal, + boolean urgent) throws UntrustedIdentityException, IOException { enforceMaxContentSize(content); @@ -1711,7 +1720,7 @@ public class SignalServiceMessageSender { } try { - OutgoingPushMessageList messages = getEncryptedMessages(socket, recipient, unidentifiedAccess, timestamp, content, online); + OutgoingPushMessageList messages = getEncryptedMessages(socket, recipient, unidentifiedAccess, timestamp, content, online, urgent); if (content.getContent().isPresent() && content.getContent().get().getSyncMessage() != null && content.getContent().get().getSyncMessage().hasSent()) { Log.d(TAG, "[sendMessage][" + timestamp + "] Sending a sent sync message to devices: " + messages.getDevices()); @@ -1796,7 +1805,8 @@ public class SignalServiceMessageSender { ContentHint contentHint, Optional groupId, boolean online, - SenderKeyGroupEvents sendEvents) + SenderKeyGroupEvents sendEvents, + boolean urgent) throws IOException, UntrustedIdentityException, NoSessionException, InvalidKeyException, InvalidRegistrationIdException { if (recipients.isEmpty()) { @@ -1833,7 +1843,7 @@ public class SignalServiceMessageSender { }) .collect(Collectors.toList()); - List results = sendSenderKeyDistributionMessage(distributionId, needsSenderKey, access, message, groupId); + List results = sendSenderKeyDistributionMessage(distributionId, needsSenderKey, access, message, groupId, urgent); List successes = results.stream() .filter(SendMessageResult::isSuccess) @@ -1895,7 +1905,7 @@ public class SignalServiceMessageSender { try { try { - SendGroupMessageResponse response = new MessagingService.SendResponseProcessor<>(messagingService.sendToGroup(ciphertext, joinedUnidentifiedAccess, timestamp, online).blockingGet()).getResultOrThrow(); + SendGroupMessageResponse response = new MessagingService.SendResponseProcessor<>(messagingService.sendToGroup(ciphertext, joinedUnidentifiedAccess, timestamp, online, urgent).blockingGet()).getResultOrThrow(); return transformGroupResponseToMessageResults(targetInfo.devices, response, content); } catch (InvalidUnidentifiedAccessHeaderException | NotFoundException | GroupMismatchedDevicesException | GroupStaleDevicesException e) { // Non-technical failures shouldn't be retried with socket @@ -1906,7 +1916,7 @@ public class SignalServiceMessageSender { Log.w(TAG, "[sendGroupMessage][" + timestamp + "] Pipe failed, falling back... (" + e.getClass().getSimpleName() + ": " + e.getMessage() + ")"); } - SendGroupMessageResponse response = socket.sendGroupMessage(ciphertext, joinedUnidentifiedAccess, timestamp, online); + SendGroupMessageResponse response = socket.sendGroupMessage(ciphertext, joinedUnidentifiedAccess, timestamp, online, urgent); return transformGroupResponseToMessageResults(targetInfo.devices, response, content); } catch (GroupMismatchedDevicesException e) { Log.w(TAG, "[sendGroupMessage][" + timestamp + "] Handling mismatched devices. (" + e.getMessage() + ")"); @@ -2070,7 +2080,8 @@ public class SignalServiceMessageSender { Optional unidentifiedAccess, long timestamp, EnvelopeContent plaintext, - boolean online) + boolean online, + boolean urgent) throws IOException, InvalidKeyException, UntrustedIdentityException { List messages = new LinkedList<>(); @@ -2091,7 +2102,7 @@ public class SignalServiceMessageSender { } } - return new OutgoingPushMessageList(recipient.getIdentifier(), timestamp, messages, online); + return new OutgoingPushMessageList(recipient.getIdentifier(), timestamp, messages, online, urgent); } private OutgoingPushMessage getEncryptedMessage(PushServiceSocket socket, diff --git a/libsignal/service/src/main/java/org/whispersystems/signalservice/api/messages/SignalServiceEnvelope.java b/libsignal/service/src/main/java/org/whispersystems/signalservice/api/messages/SignalServiceEnvelope.java index 1cd2b69ae..00bc30a8b 100644 --- a/libsignal/service/src/main/java/org/whispersystems/signalservice/api/messages/SignalServiceEnvelope.java +++ b/libsignal/service/src/main/java/org/whispersystems/signalservice/api/messages/SignalServiceEnvelope.java @@ -65,14 +65,16 @@ public class SignalServiceEnvelope { long serverReceivedTimestamp, long serverDeliveredTimestamp, String uuid, - String destinationUuid) + String destinationUuid, + boolean urgent) { Envelope.Builder builder = Envelope.newBuilder() .setType(Envelope.Type.valueOf(type)) .setSourceDevice(senderDevice) .setTimestamp(timestamp) .setServerTimestamp(serverReceivedTimestamp) - .setDestinationUuid(destinationUuid); + .setDestinationUuid(destinationUuid) + .setUrgent(urgent); if (sender.isPresent()) { builder.setSourceUuid(sender.get().getServiceId().toString()); @@ -100,13 +102,15 @@ public class SignalServiceEnvelope { long serverReceivedTimestamp, long serverDeliveredTimestamp, String uuid, - String destinationUuid) + String destinationUuid, + boolean urgent) { Envelope.Builder builder = Envelope.newBuilder() .setType(Envelope.Type.valueOf(type)) .setTimestamp(timestamp) .setServerTimestamp(serverReceivedTimestamp) - .setDestinationUuid(destinationUuid); + .setDestinationUuid(destinationUuid) + .setUrgent(urgent); if (uuid != null) { builder.setServerGuid(uuid); @@ -273,6 +277,9 @@ public class SignalServiceEnvelope { return envelope.getDestinationUuid(); } + public boolean isUrgent() { + return envelope.getUrgent(); + } private SignalServiceEnvelopeProto.Builder serializeToProto() { SignalServiceEnvelopeProto.Builder builder = SignalServiceEnvelopeProto.newBuilder() @@ -280,7 +287,8 @@ public class SignalServiceEnvelope { .setDeviceId(getSourceDevice()) .setTimestamp(getTimestamp()) .setServerReceivedTimestamp(getServerReceivedTimestamp()) - .setServerDeliveredTimestamp(getServerDeliveredTimestamp()); + .setServerDeliveredTimestamp(getServerDeliveredTimestamp()) + .setUrgent(isUrgent()); if (getSourceUuid().isPresent()) { builder.setSourceUuid(getSourceUuid().get()); @@ -330,6 +338,7 @@ public class SignalServiceEnvelope { proto.getServerReceivedTimestamp(), proto.getServerDeliveredTimestamp(), proto.getServerGuid(), - proto.getDestinationUuid()); + proto.getDestinationUuid(), + proto.getUrgent()); } } diff --git a/libsignal/service/src/main/java/org/whispersystems/signalservice/api/messages/calls/SignalServiceCallMessage.java b/libsignal/service/src/main/java/org/whispersystems/signalservice/api/messages/calls/SignalServiceCallMessage.java index dd28ae4a3..84ab3d79e 100644 --- a/libsignal/service/src/main/java/org/whispersystems/signalservice/api/messages/calls/SignalServiceCallMessage.java +++ b/libsignal/service/src/main/java/org/whispersystems/signalservice/api/messages/calls/SignalServiceCallMessage.java @@ -198,4 +198,10 @@ public class SignalServiceCallMessage { public Optional getTimestamp() { return timestamp; } + + public boolean isUrgent() { + return offerMessage.isPresent() || + hangupMessage.isPresent() || + opaqueMessage.map(m -> m.getUrgency() == OpaqueMessage.Urgency.HANDLE_IMMEDIATELY).orElse(false); + } } diff --git a/libsignal/service/src/main/java/org/whispersystems/signalservice/api/messages/multidevice/RequestMessage.java b/libsignal/service/src/main/java/org/whispersystems/signalservice/api/messages/multidevice/RequestMessage.java index db3948932..2afb7e1a4 100644 --- a/libsignal/service/src/main/java/org/whispersystems/signalservice/api/messages/multidevice/RequestMessage.java +++ b/libsignal/service/src/main/java/org/whispersystems/signalservice/api/messages/multidevice/RequestMessage.java @@ -47,4 +47,8 @@ public class RequestMessage { public boolean isPniIdentityRequest() { return request.getType() == Request.Type.PNI_IDENTITY; } + + public boolean isUrgent() { + return isContactsRequest() || isKeysRequest() || isPniIdentityRequest(); + } } diff --git a/libsignal/service/src/main/java/org/whispersystems/signalservice/api/services/MessagingService.java b/libsignal/service/src/main/java/org/whispersystems/signalservice/api/services/MessagingService.java index 61dccff04..c52355c0f 100644 --- a/libsignal/service/src/main/java/org/whispersystems/signalservice/api/services/MessagingService.java +++ b/libsignal/service/src/main/java/org/whispersystems/signalservice/api/services/MessagingService.java @@ -72,13 +72,13 @@ public class MessagingService { .onErrorReturn(ServiceResponse::forUnknownError); } - public Single> sendToGroup(byte[] body, byte[] joinedUnidentifiedAccess, long timestamp, boolean online) { + public Single> sendToGroup(byte[] body, byte[] joinedUnidentifiedAccess, long timestamp, boolean online, boolean urgent) { List headers = new LinkedList() {{ add("content-type:application/vnd.signal-messenger.mrm"); add("Unidentified-Access-Key:" + Base64.encodeBytes(joinedUnidentifiedAccess)); }}; - String path = String.format(Locale.US, "/v1/messages/multi_recipient?ts=%s&online=%s", timestamp, online); + String path = String.format(Locale.US, "/v1/messages/multi_recipient?ts=%s&online=%s&urgent=%s", timestamp, online, urgent); WebSocketRequestMessage requestMessage = WebSocketRequestMessage.newBuilder() .setId(new SecureRandom().nextLong()) diff --git a/libsignal/service/src/main/java/org/whispersystems/signalservice/internal/push/OutgoingPushMessageList.java b/libsignal/service/src/main/java/org/whispersystems/signalservice/internal/push/OutgoingPushMessageList.java index 5c90b7902..2fb23e2a1 100644 --- a/libsignal/service/src/main/java/org/whispersystems/signalservice/internal/push/OutgoingPushMessageList.java +++ b/libsignal/service/src/main/java/org/whispersystems/signalservice/internal/push/OutgoingPushMessageList.java @@ -26,15 +26,20 @@ public class OutgoingPushMessageList { @JsonProperty private boolean online; + @JsonProperty + private boolean urgent; + public OutgoingPushMessageList(String destination, long timestamp, List messages, - boolean online) + boolean online, + boolean urgent) { this.timestamp = timestamp; this.destination = destination; this.messages = messages; this.online = online; + this.urgent = urgent; } public String getDestination() { @@ -53,6 +58,10 @@ public class OutgoingPushMessageList { return online; } + public boolean isUrgent() { + return urgent; + } + @JsonIgnore public List getDevices() { return messages.stream().map(OutgoingPushMessage::getDestinationDeviceId).collect(Collectors.toList()); diff --git a/libsignal/service/src/main/java/org/whispersystems/signalservice/internal/push/PushServiceSocket.java b/libsignal/service/src/main/java/org/whispersystems/signalservice/internal/push/PushServiceSocket.java index d3200d272..8a45b929d 100644 --- a/libsignal/service/src/main/java/org/whispersystems/signalservice/internal/push/PushServiceSocket.java +++ b/libsignal/service/src/main/java/org/whispersystems/signalservice/internal/push/PushServiceSocket.java @@ -212,7 +212,7 @@ public class PushServiceSocket { private static final String DIRECTORY_AUTH_PATH = "/v1/directory/auth"; private static final String MESSAGE_PATH = "/v1/messages/%s"; - private static final String GROUP_MESSAGE_PATH = "/v1/messages/multi_recipient?ts=%s&online=%s"; + private static final String GROUP_MESSAGE_PATH = "/v1/messages/multi_recipient?ts=%s&online=%s&urgent=%s"; private static final String SENDER_ACK_MESSAGE_PATH = "/v1/messages/%s/%d"; private static final String UUID_ACK_MESSAGE_PATH = "/v1/messages/uuid/%s"; private static final String ATTACHMENT_V2_PATH = "/v2/attachments/form/upload"; @@ -463,12 +463,12 @@ public class PushServiceSocket { return JsonUtil.fromJson(responseText, SenderCertificate.class).getCertificate(); } - public SendGroupMessageResponse sendGroupMessage(byte[] body, byte[] joinedUnidentifiedAccess, long timestamp, boolean online) + public SendGroupMessageResponse sendGroupMessage(byte[] body, byte[] joinedUnidentifiedAccess, long timestamp, boolean online, boolean urgent) throws IOException { ServiceConnectionHolder connectionHolder = (ServiceConnectionHolder) getRandom(serviceClients, random); - String path = String.format(Locale.US, GROUP_MESSAGE_PATH, timestamp, online); + String path = String.format(Locale.US, GROUP_MESSAGE_PATH, timestamp, online, urgent); Request.Builder requestBuilder = new Request.Builder(); requestBuilder.url(String.format("%s%s", connectionHolder.getUrl(), path)); diff --git a/libsignal/service/src/main/java/org/whispersystems/signalservice/internal/push/SignalServiceEnvelopeEntity.java b/libsignal/service/src/main/java/org/whispersystems/signalservice/internal/push/SignalServiceEnvelopeEntity.java index 0712905cb..d6180c63f 100644 --- a/libsignal/service/src/main/java/org/whispersystems/signalservice/internal/push/SignalServiceEnvelopeEntity.java +++ b/libsignal/service/src/main/java/org/whispersystems/signalservice/internal/push/SignalServiceEnvelopeEntity.java @@ -37,6 +37,9 @@ public class SignalServiceEnvelopeEntity { @JsonProperty private String guid; + @JsonProperty + private Boolean urgent; + public SignalServiceEnvelopeEntity() {} public int getType() { @@ -86,4 +89,8 @@ public class SignalServiceEnvelopeEntity { public String getDestinationUuid() { return destinationUuid; } + + public boolean isUrgent() { + return urgent == null || urgent; + } } diff --git a/libsignal/service/src/main/proto/InternalSerialization.proto b/libsignal/service/src/main/proto/InternalSerialization.proto index 37643222d..ef29b7f43 100644 --- a/libsignal/service/src/main/proto/InternalSerialization.proto +++ b/libsignal/service/src/main/proto/InternalSerialization.proto @@ -33,6 +33,7 @@ message SignalServiceEnvelopeProto { optional int64 serverDeliveredTimestamp = 9; optional string serverGuid = 10; optional string destinationUuid = 11; + optional bool urgent = 12 [default = true]; } message MetadataProto { diff --git a/libsignal/service/src/main/proto/SignalService.proto b/libsignal/service/src/main/proto/SignalService.proto index 058d74f45..7c617bea0 100644 --- a/libsignal/service/src/main/proto/SignalService.proto +++ b/libsignal/service/src/main/proto/SignalService.proto @@ -33,7 +33,8 @@ message Envelope { optional bytes content = 8; // Contains an encrypted Content optional string serverGuid = 9; optional uint64 serverTimestamp = 10; - // NEXT ID: 14 + optional bool urgent = 14 [default = true]; + // NEXT ID: 15 } message Content {