diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/MediaDatabase.java b/app/src/main/java/org/thoughtcrime/securesms/database/MediaDatabase.java index e71feffb06..05bdb8804a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/MediaDatabase.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/MediaDatabase.java @@ -52,6 +52,7 @@ public class MediaDatabase extends Database { + MmsDatabase.TABLE_NAME + "." + MmsDatabase.MESSAGE_BOX + ", " + MmsDatabase.TABLE_NAME + "." + MmsDatabase.DATE_SENT + ", " + MmsDatabase.TABLE_NAME + "." + MmsDatabase.DATE_RECEIVED + ", " + + MmsDatabase.TABLE_NAME + "." + MmsDatabase.DATE_SERVER + ", " + MmsDatabase.TABLE_NAME + "." + MmsDatabase.THREAD_ID + ", " + MmsDatabase.TABLE_NAME + "." + MmsDatabase.RECIPIENT_ID + ", " + ThreadDatabase.TABLE_NAME + "." + ThreadDatabase.RECIPIENT_ID + " as " + THREAD_RECIPIENT_ID + " " diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/MmsDatabase.java b/app/src/main/java/org/thoughtcrime/securesms/database/MmsDatabase.java index ec37bc2b8b..5c71a00ee2 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/MmsDatabase.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/MmsDatabase.java @@ -118,6 +118,7 @@ public class MmsDatabase extends MessagingDatabase { THREAD_ID + " INTEGER, " + DATE_SENT + " INTEGER, " + DATE_RECEIVED + " INTEGER, " + + DATE_SERVER + " INTEGER DEFAULT -1, " + MESSAGE_BOX + " INTEGER, " + READ + " INTEGER DEFAULT 0, " + "m_id" + " TEXT, " + @@ -175,6 +176,7 @@ public class MmsDatabase extends MessagingDatabase { "CREATE INDEX IF NOT EXISTS mms_read_and_notified_and_thread_id_index ON " + TABLE_NAME + "(" + READ + "," + NOTIFIED + "," + THREAD_ID + ");", "CREATE INDEX IF NOT EXISTS mms_message_box_index ON " + TABLE_NAME + " (" + MESSAGE_BOX + ");", "CREATE INDEX IF NOT EXISTS mms_date_sent_index ON " + TABLE_NAME + " (" + DATE_SENT + ");", + "CREATE INDEX IF NOT EXISTS mms_date_server_index ON " + TABLE_NAME + " (" + DATE_SERVER + ");", "CREATE INDEX IF NOT EXISTS mms_thread_date_index ON " + TABLE_NAME + " (" + THREAD_ID + ", " + DATE_RECEIVED + ");", "CREATE INDEX IF NOT EXISTS mms_reactions_unread_index ON " + TABLE_NAME + " (" + REACTIONS_UNREAD + ");" }; @@ -183,6 +185,7 @@ public class MmsDatabase extends MessagingDatabase { MmsDatabase.TABLE_NAME + "." + ID + " AS " + ID, THREAD_ID, DATE_SENT + " AS " + NORMALIZED_DATE_SENT, DATE_RECEIVED + " AS " + NORMALIZED_DATE_RECEIVED, + DATE_SERVER, MESSAGE_BOX, READ, CONTENT_LOCATION, EXPIRY, MESSAGE_TYPE, MESSAGE_SIZE, STATUS, TRANSACTION_ID, @@ -902,6 +905,7 @@ public class MmsDatabase extends MessagingDatabase { ContentValues contentValues = new ContentValues(); contentValues.put(DATE_SENT, retrieved.getSentTimeMillis()); + contentValues.put(DATE_SERVER, retrieved.getServerTimeMillis()); contentValues.put(RECIPIENT_ID, retrieved.getFrom().serialize()); contentValues.put(MESSAGE_BOX, mailbox); @@ -991,7 +995,6 @@ public class MmsDatabase extends MessagingDatabase { Log.i(TAG, "Message received type: " + notification.getMessageType()); - contentBuilder.add(CONTENT_LOCATION, notification.getContentLocation()); contentBuilder.add(DATE_SENT, System.currentTimeMillis()); contentBuilder.add(EXPIRY, notification.getExpiry()); @@ -1474,13 +1477,20 @@ public class MmsDatabase extends MessagingDatabase { public MessageRecord getCurrent() { SlideDeck slideDeck = new SlideDeck(context, message.getAttachments()); - return new MediaMmsMessageRecord(id, message.getRecipient(), message.getRecipient(), - 1, System.currentTimeMillis(), System.currentTimeMillis(), - 0, threadId, message.getBody(), - slideDeck, slideDeck.getSlides().size(), + return new MediaMmsMessageRecord(id, + message.getRecipient(), + message.getRecipient(), + 1, + System.currentTimeMillis(), + System.currentTimeMillis(), + -1, + 0, + threadId, message.getBody(), + slideDeck, + slideDeck.getSlides().size(), message.isSecure() ? MmsSmsColumns.Types.getOutgoingEncryptedMessageType() : MmsSmsColumns.Types.getOutgoingSmsMessageType(), - new LinkedList(), - new LinkedList(), + new LinkedList<>(), + new LinkedList<>(), message.getSubscriptionId(), message.getExpiresIn(), System.currentTimeMillis(), @@ -1493,7 +1503,10 @@ public class MmsDatabase extends MessagingDatabase { message.getOutgoingQuote().isOriginalMissing(), new SlideDeck(context, message.getOutgoingQuote().getAttachments())) : null, - message.getSharedContacts(), message.getLinkPreviews(), false, Collections.emptyList()); + message.getSharedContacts(), + message.getLinkPreviews(), + false, + Collections.emptyList()); } } @@ -1565,22 +1578,23 @@ public class MmsDatabase extends MessagingDatabase { } private MediaMmsMessageRecord getMediaMmsMessageRecord(Cursor cursor) { - long id = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.ID )); - long dateSent = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.NORMALIZED_DATE_SENT )); + long id = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.ID)); + long dateSent = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.NORMALIZED_DATE_SENT)); long dateReceived = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.NORMALIZED_DATE_RECEIVED)); - long box = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.MESSAGE_BOX )); - long threadId = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.THREAD_ID )); - long recipientId = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.RECIPIENT_ID )); - int addressDeviceId = cursor.getInt(cursor.getColumnIndexOrThrow(MmsDatabase.ADDRESS_DEVICE_ID )); + long dateServer = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.DATE_SERVER)); + long box = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.MESSAGE_BOX)); + long threadId = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.THREAD_ID)); + long recipientId = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.RECIPIENT_ID)); + int addressDeviceId = cursor.getInt(cursor.getColumnIndexOrThrow(MmsDatabase.ADDRESS_DEVICE_ID)); int deliveryReceiptCount = cursor.getInt(cursor.getColumnIndexOrThrow(MmsDatabase.DELIVERY_RECEIPT_COUNT)); - int readReceiptCount = cursor.getInt(cursor.getColumnIndexOrThrow(MmsDatabase.READ_RECEIPT_COUNT )); - String body = cursor.getString(cursor.getColumnIndexOrThrow(MmsDatabase.BODY )); - int partCount = cursor.getInt(cursor.getColumnIndexOrThrow(MmsDatabase.PART_COUNT )); + int readReceiptCount = cursor.getInt(cursor.getColumnIndexOrThrow(MmsDatabase.READ_RECEIPT_COUNT)); + String body = cursor.getString(cursor.getColumnIndexOrThrow(MmsDatabase.BODY)); + int partCount = cursor.getInt(cursor.getColumnIndexOrThrow(MmsDatabase.PART_COUNT)); String mismatchDocument = cursor.getString(cursor.getColumnIndexOrThrow(MmsDatabase.MISMATCHED_IDENTITIES)); - String networkDocument = cursor.getString(cursor.getColumnIndexOrThrow(MmsDatabase.NETWORK_FAILURE )); - int subscriptionId = cursor.getInt(cursor.getColumnIndexOrThrow(MmsDatabase.SUBSCRIPTION_ID )); - long expiresIn = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.EXPIRES_IN )); - long expireStarted = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.EXPIRE_STARTED )); + String networkDocument = cursor.getString(cursor.getColumnIndexOrThrow(MmsDatabase.NETWORK_FAILURE)); + int subscriptionId = cursor.getInt(cursor.getColumnIndexOrThrow(MmsDatabase.SUBSCRIPTION_ID)); + long expiresIn = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.EXPIRES_IN)); + long expireStarted = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.EXPIRE_STARTED)); boolean unidentified = cursor.getInt(cursor.getColumnIndexOrThrow(MmsDatabase.UNIDENTIFIED)) == 1; boolean isViewOnce = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.VIEW_ONCE)) == 1; List reactions = parseReactions(cursor); @@ -1601,7 +1615,7 @@ public class MmsDatabase extends MessagingDatabase { Quote quote = getQuote(cursor); return new MediaMmsMessageRecord(id, recipient, recipient, - addressDeviceId, dateSent, dateReceived, deliveryReceiptCount, + addressDeviceId, dateSent, dateReceived, dateServer, deliveryReceiptCount, threadId, body, slideDeck, partCount, box, mismatches, networkFailures, subscriptionId, expiresIn, expireStarted, isViewOnce, readReceiptCount, quote, contacts, previews, unidentified, reactions); diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/MmsSmsColumns.java b/app/src/main/java/org/thoughtcrime/securesms/database/MmsSmsColumns.java index 52bea99579..e89be31264 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/MmsSmsColumns.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/MmsSmsColumns.java @@ -6,6 +6,7 @@ public interface MmsSmsColumns { public static final String ID = "_id"; public static final String NORMALIZED_DATE_SENT = "date_sent"; public static final String NORMALIZED_DATE_RECEIVED = "date_received"; + public static final String DATE_SERVER = "date_server"; public static final String THREAD_ID = "thread_id"; public static final String READ = "read"; public static final String BODY = "body"; diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/MmsSmsDatabase.java b/app/src/main/java/org/thoughtcrime/securesms/database/MmsSmsDatabase.java index 194da63628..10a2cce9d9 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/MmsSmsDatabase.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/MmsSmsDatabase.java @@ -44,19 +44,27 @@ public class MmsSmsDatabase extends Database { public static final String MMS_TRANSPORT = "mms"; public static final String SMS_TRANSPORT = "sms"; - private static final String[] PROJECTION = {MmsSmsColumns.ID, MmsSmsColumns.UNIQUE_ROW_ID, - SmsDatabase.BODY, SmsDatabase.TYPE, + private static final String[] PROJECTION = {MmsSmsColumns.ID, + MmsSmsColumns.UNIQUE_ROW_ID, + SmsDatabase.BODY, + SmsDatabase.TYPE, MmsSmsColumns.THREAD_ID, - SmsDatabase.RECIPIENT_ID, SmsDatabase.ADDRESS_DEVICE_ID, SmsDatabase.SUBJECT, + SmsDatabase.RECIPIENT_ID, + SmsDatabase.ADDRESS_DEVICE_ID, + SmsDatabase.SUBJECT, MmsSmsColumns.NORMALIZED_DATE_SENT, MmsSmsColumns.NORMALIZED_DATE_RECEIVED, - MmsDatabase.MESSAGE_TYPE, MmsDatabase.MESSAGE_BOX, + MmsSmsColumns.DATE_SERVER, + MmsDatabase.MESSAGE_TYPE, + MmsDatabase.MESSAGE_BOX, SmsDatabase.STATUS, MmsSmsColumns.UNIDENTIFIED, MmsSmsColumns.REACTIONS, MmsDatabase.PART_COUNT, - MmsDatabase.CONTENT_LOCATION, MmsDatabase.TRANSACTION_ID, - MmsDatabase.MESSAGE_SIZE, MmsDatabase.EXPIRY, + MmsDatabase.CONTENT_LOCATION, + MmsDatabase.TRANSACTION_ID, + MmsDatabase.MESSAGE_SIZE, + MmsDatabase.EXPIRY, MmsDatabase.STATUS, MmsSmsColumns.DELIVERY_RECEIPT_COUNT, MmsSmsColumns.READ_RECEIPT_COUNT, @@ -386,7 +394,8 @@ public class MmsSmsDatabase extends Database { MmsDatabase.VIEW_ONCE, MmsDatabase.REACTIONS, MmsSmsColumns.REACTIONS_UNREAD, - MmsSmsColumns.REACTIONS_LAST_SEEN}; + MmsSmsColumns.REACTIONS_LAST_SEEN, + MmsSmsColumns.DATE_SERVER }; String[] smsProjection = {SmsDatabase.DATE_SENT + " AS " + MmsSmsColumns.NORMALIZED_DATE_SENT, SmsDatabase.DATE_RECEIVED + " AS " + MmsSmsColumns.NORMALIZED_DATE_RECEIVED, @@ -416,7 +425,8 @@ public class MmsSmsDatabase extends Database { MmsDatabase.VIEW_ONCE, MmsDatabase.REACTIONS, MmsSmsColumns.REACTIONS_UNREAD, - MmsSmsColumns.REACTIONS_LAST_SEEN}; + MmsSmsColumns.REACTIONS_LAST_SEEN, + MmsSmsColumns.DATE_SERVER }; SQLiteQueryBuilder mmsQueryBuilder = new SQLiteQueryBuilder(); SQLiteQueryBuilder smsQueryBuilder = new SQLiteQueryBuilder(); @@ -447,6 +457,7 @@ public class MmsSmsDatabase extends Database { mmsColumnsPresent.add(MmsDatabase.MESSAGE_BOX); mmsColumnsPresent.add(MmsDatabase.DATE_SENT); mmsColumnsPresent.add(MmsDatabase.DATE_RECEIVED); + mmsColumnsPresent.add(MmsDatabase.DATE_SERVER); mmsColumnsPresent.add(MmsDatabase.PART_COUNT); mmsColumnsPresent.add(MmsDatabase.CONTENT_LOCATION); mmsColumnsPresent.add(MmsDatabase.TRANSACTION_ID); @@ -486,6 +497,7 @@ public class MmsSmsDatabase extends Database { smsColumnsPresent.add(SmsDatabase.SUBJECT); smsColumnsPresent.add(SmsDatabase.DATE_SENT); smsColumnsPresent.add(SmsDatabase.DATE_RECEIVED); + smsColumnsPresent.add(SmsDatabase.DATE_SERVER); smsColumnsPresent.add(SmsDatabase.STATUS); smsColumnsPresent.add(SmsDatabase.UNIDENTIFIED); smsColumnsPresent.add(SmsDatabase.REACTIONS); diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/SmsDatabase.java b/app/src/main/java/org/thoughtcrime/securesms/database/SmsDatabase.java index 98e75e74f8..3867fd428e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/SmsDatabase.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/SmsDatabase.java @@ -84,6 +84,7 @@ public class SmsDatabase extends MessagingDatabase { PERSON + " INTEGER, " + DATE_RECEIVED + " INTEGER, " + DATE_SENT + " INTEGER, " + + DATE_SERVER + " INTEGER DEFAULT -1, " + PROTOCOL + " INTEGER, " + READ + " INTEGER DEFAULT 0, " + STATUS + " INTEGER DEFAULT -1," + @@ -110,6 +111,7 @@ public class SmsDatabase extends MessagingDatabase { "CREATE INDEX IF NOT EXISTS sms_read_and_notified_and_thread_id_index ON " + TABLE_NAME + "(" + READ + "," + NOTIFIED + "," + THREAD_ID + ");", "CREATE INDEX IF NOT EXISTS sms_type_index ON " + TABLE_NAME + " (" + TYPE + ");", "CREATE INDEX IF NOT EXISTS sms_date_sent_index ON " + TABLE_NAME + " (" + DATE_SENT + ");", + "CREATE INDEX IF NOT EXISTS sms_date_server_index ON " + TABLE_NAME + " (" + DATE_SERVER + ");", "CREATE INDEX IF NOT EXISTS sms_thread_date_index ON " + TABLE_NAME + " (" + THREAD_ID + ", " + DATE_RECEIVED + ");", "CREATE INDEX IF NOT EXISTS sms_reactions_unread_index ON " + TABLE_NAME + " (" + REACTIONS_UNREAD + ");" }; @@ -118,6 +120,7 @@ public class SmsDatabase extends MessagingDatabase { ID, THREAD_ID, RECIPIENT_ID, ADDRESS_DEVICE_ID, PERSON, DATE_RECEIVED + " AS " + NORMALIZED_DATE_RECEIVED, DATE_SENT + " AS " + NORMALIZED_DATE_SENT, + DATE_SERVER, PROTOCOL, READ, STATUS, TYPE, REPLY_PATH_PRESENT, SUBJECT, BODY, SERVICE_CENTER, DELIVERY_RECEIPT_COUNT, MISMATCHED_IDENTITIES, SUBSCRIPTION_ID, EXPIRES_IN, EXPIRE_STARTED, @@ -534,36 +537,6 @@ public class SmsDatabase extends MessagingDatabase { return new Pair<>(messageId, threadId); } - public Pair copyMessageInbox(long messageId) { - try { - SmsMessageRecord record = getMessage(messageId); - - ContentValues contentValues = new ContentValues(); - contentValues.put(TYPE, (record.getType() & ~Types.BASE_TYPE_MASK) | Types.BASE_INBOX_TYPE); - contentValues.put(RECIPIENT_ID, record.getIndividualRecipient().getId().serialize()); - contentValues.put(ADDRESS_DEVICE_ID, record.getRecipientDeviceId()); - contentValues.put(DATE_RECEIVED, System.currentTimeMillis()); - contentValues.put(DATE_SENT, record.getDateSent()); - contentValues.put(PROTOCOL, 31337); - contentValues.put(READ, 0); - contentValues.put(BODY, record.getBody()); - contentValues.put(THREAD_ID, record.getThreadId()); - contentValues.put(EXPIRES_IN, record.getExpiresIn()); - - SQLiteDatabase db = databaseHelper.getWritableDatabase(); - long newMessageId = db.insert(TABLE_NAME, null, contentValues); - - DatabaseFactory.getThreadDatabase(context).update(record.getThreadId(), true); - notifyConversationListeners(record.getThreadId()); - - ApplicationDependencies.getJobManager().add(new TrimThreadJob(record.getThreadId())); - - return new Pair<>(newMessageId, record.getThreadId()); - } catch (NoSuchMessageException e) { - throw new AssertionError(e); - } - } - boolean hasReceivedAnyCallsSince(long threadId, long timestamp) { SQLiteDatabase db = databaseHelper.getReadableDatabase(); String[] projection = new String[]{SmsDatabase.TYPE}; @@ -665,6 +638,7 @@ public class SmsDatabase extends MessagingDatabase { values.put(ADDRESS_DEVICE_ID, message.getSenderDeviceId()); values.put(DATE_RECEIVED, System.currentTimeMillis()); values.put(DATE_SENT, message.getSentTimestampMillis()); + values.put(DATE_SERVER, message.getServerTimestampMillis()); values.put(PROTOCOL, message.getProtocol()); values.put(READ, unread ? 0 : 1); values.put(SUBSCRIPTION_ID, message.getSubscriptionId()); @@ -908,7 +882,7 @@ public class SmsDatabase extends MessagingDatabase { return new OutgoingMessageReader(message, threadId); } - public class OutgoingMessageReader { + public static class OutgoingMessageReader { private final OutgoingTextMessage message; private final long id; @@ -921,13 +895,25 @@ public class SmsDatabase extends MessagingDatabase { } public MessageRecord getCurrent() { - return new SmsMessageRecord(id, message.getMessageBody(), - message.getRecipient(), message.getRecipient(), - 1, System.currentTimeMillis(), System.currentTimeMillis(), - 0, message.isSecureMessage() ? MmsSmsColumns.Types.getOutgoingEncryptedMessageType() : MmsSmsColumns.Types.getOutgoingSmsMessageType(), - threadId, 0, new LinkedList(), - message.getSubscriptionId(), message.getExpiresIn(), - System.currentTimeMillis(), 0, false, Collections.emptyList()); + return new SmsMessageRecord(id, + message.getMessageBody(), + message.getRecipient(), + message.getRecipient(), + 1, + System.currentTimeMillis(), + System.currentTimeMillis(), + -1, + 0, + message.isSecureMessage() ? MmsSmsColumns.Types.getOutgoingEncryptedMessageType() : MmsSmsColumns.Types.getOutgoingSmsMessageType(), + threadId, + 0, + new LinkedList<>(), + message.getSubscriptionId(), + message.getExpiresIn(), + System.currentTimeMillis(), + 0, + false, + Collections.emptyList()); } } @@ -952,21 +938,22 @@ public class SmsDatabase extends MessagingDatabase { } public SmsMessageRecord getCurrent() { - long messageId = cursor.getLong(cursor.getColumnIndexOrThrow(SmsDatabase.ID )); - long recipientId = cursor.getLong(cursor.getColumnIndexOrThrow(SmsDatabase.RECIPIENT_ID )); - int addressDeviceId = cursor.getInt(cursor.getColumnIndexOrThrow(SmsDatabase.ADDRESS_DEVICE_ID )); - long type = cursor.getLong(cursor.getColumnIndexOrThrow(SmsDatabase.TYPE )); + long messageId = cursor.getLong(cursor.getColumnIndexOrThrow(SmsDatabase.ID)); + long recipientId = cursor.getLong(cursor.getColumnIndexOrThrow(SmsDatabase.RECIPIENT_ID)); + int addressDeviceId = cursor.getInt(cursor.getColumnIndexOrThrow(SmsDatabase.ADDRESS_DEVICE_ID)); + long type = cursor.getLong(cursor.getColumnIndexOrThrow(SmsDatabase.TYPE)); long dateReceived = cursor.getLong(cursor.getColumnIndexOrThrow(SmsDatabase.NORMALIZED_DATE_RECEIVED)); - long dateSent = cursor.getLong(cursor.getColumnIndexOrThrow(SmsDatabase.NORMALIZED_DATE_SENT )); - long threadId = cursor.getLong(cursor.getColumnIndexOrThrow(SmsDatabase.THREAD_ID )); - int status = cursor.getInt(cursor.getColumnIndexOrThrow(SmsDatabase.STATUS )); + long dateSent = cursor.getLong(cursor.getColumnIndexOrThrow(SmsDatabase.NORMALIZED_DATE_SENT)); + long dateServer = cursor.getLong(cursor.getColumnIndexOrThrow(SmsDatabase.DATE_SERVER)); + long threadId = cursor.getLong(cursor.getColumnIndexOrThrow(SmsDatabase.THREAD_ID)); + int status = cursor.getInt(cursor.getColumnIndexOrThrow(SmsDatabase.STATUS)); int deliveryReceiptCount = cursor.getInt(cursor.getColumnIndexOrThrow(SmsDatabase.DELIVERY_RECEIPT_COUNT)); - int readReceiptCount = cursor.getInt(cursor.getColumnIndexOrThrow(SmsDatabase.READ_RECEIPT_COUNT )); + int readReceiptCount = cursor.getInt(cursor.getColumnIndexOrThrow(SmsDatabase.READ_RECEIPT_COUNT)); String mismatchDocument = cursor.getString(cursor.getColumnIndexOrThrow(SmsDatabase.MISMATCHED_IDENTITIES)); - int subscriptionId = cursor.getInt(cursor.getColumnIndexOrThrow(SmsDatabase.SUBSCRIPTION_ID )); - long expiresIn = cursor.getLong(cursor.getColumnIndexOrThrow(SmsDatabase.EXPIRES_IN )); - long expireStarted = cursor.getLong(cursor.getColumnIndexOrThrow(SmsDatabase.EXPIRE_STARTED )); - String body = cursor.getString(cursor.getColumnIndexOrThrow(SmsDatabase.BODY )); + int subscriptionId = cursor.getInt(cursor.getColumnIndexOrThrow(SmsDatabase.SUBSCRIPTION_ID)); + long expiresIn = cursor.getLong(cursor.getColumnIndexOrThrow(SmsDatabase.EXPIRES_IN)); + long expireStarted = cursor.getLong(cursor.getColumnIndexOrThrow(SmsDatabase.EXPIRE_STARTED)); + String body = cursor.getString(cursor.getColumnIndexOrThrow(SmsDatabase.BODY)); boolean unidentified = cursor.getInt(cursor.getColumnIndexOrThrow(SmsDatabase.UNIDENTIFIED)) == 1; List reactions = parseReactions(cursor); @@ -980,7 +967,7 @@ public class SmsDatabase extends MessagingDatabase { return new SmsMessageRecord(messageId, body, recipient, recipient, addressDeviceId, - dateSent, dateReceived, deliveryReceiptCount, type, + dateSent, dateReceived, dateServer, deliveryReceiptCount, type, threadId, status, mismatches, subscriptionId, expiresIn, expireStarted, readReceiptCount, unidentified, reactions); diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java index 56c386ba2c..b51cb1810f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java @@ -128,8 +128,9 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper { private static final int ATTACHMENT_UPLOAD_TIMESTAMP = 56; private static final int ATTACHMENT_CDN_NUMBER = 57; private static final int JOB_INPUT_DATA = 58; + private static final int SERVER_TIMESTAMP = 59; - private static final int DATABASE_VERSION = 58; + private static final int DATABASE_VERSION = 59; private static final String DATABASE_NAME = "signal.db"; private final Context context; @@ -873,6 +874,14 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper { db.execSQL("ALTER TABLE job_spec ADD COLUMN serialized_input_data TEXT DEFAULT NULL"); } + if (oldVersion < SERVER_TIMESTAMP) { + db.execSQL("ALTER TABLE sms ADD COLUMN date_server INTEGER DEFAULT -1"); + db.execSQL("CREATE INDEX IF NOT EXISTS sms_date_server_index ON sms (date_server)"); + + db.execSQL("ALTER TABLE mms ADD COLUMN date_server INTEGER DEFAULT -1"); + db.execSQL("CREATE INDEX IF NOT EXISTS mms_date_server_index ON mms (date_server)"); + } + db.setTransactionSuccessful(); } finally { db.endTransaction(); diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/model/MediaMmsMessageRecord.java b/app/src/main/java/org/thoughtcrime/securesms/database/model/MediaMmsMessageRecord.java index 81342846ff..0b5c7ff104 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/model/MediaMmsMessageRecord.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/model/MediaMmsMessageRecord.java @@ -46,22 +46,34 @@ public class MediaMmsMessageRecord extends MmsMessageRecord { private final int partCount; - public MediaMmsMessageRecord(long id, Recipient conversationRecipient, - Recipient individualRecipient, int recipientDeviceId, - long dateSent, long dateReceived, int deliveryReceiptCount, - long threadId, String body, + public MediaMmsMessageRecord(long id, + Recipient conversationRecipient, + Recipient individualRecipient, + int recipientDeviceId, + long dateSent, + long dateReceived, + long dateServer, + int deliveryReceiptCount, + long threadId, + String body, @NonNull SlideDeck slideDeck, - int partCount, long mailbox, + int partCount, + long mailbox, List mismatches, - List failures, int subscriptionId, - long expiresIn, long expireStarted, - boolean viewOnce, int readReceiptCount, - @Nullable Quote quote, @NonNull List contacts, - @NonNull List linkPreviews, boolean unidentified, + List failures, + int subscriptionId, + long expiresIn, + long expireStarted, + boolean viewOnce, + int readReceiptCount, + @Nullable Quote quote, + @NonNull List contacts, + @NonNull List linkPreviews, + boolean unidentified, @NonNull List reactions) { super(id, body, conversationRecipient, individualRecipient, recipientDeviceId, dateSent, - dateReceived, threadId, Status.STATUS_NONE, deliveryReceiptCount, mailbox, mismatches, failures, + dateReceived, dateServer, threadId, Status.STATUS_NONE, deliveryReceiptCount, mailbox, mismatches, failures, subscriptionId, expiresIn, expireStarted, viewOnce, slideDeck, readReceiptCount, quote, contacts, linkPreviews, unidentified, reactions); this.partCount = partCount; diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/model/MessageRecord.java b/app/src/main/java/org/thoughtcrime/securesms/database/model/MessageRecord.java index c0b423f311..eca738c13b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/model/MessageRecord.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/model/MessageRecord.java @@ -54,10 +54,11 @@ public abstract class MessageRecord extends DisplayRecord { private final long expireStarted; private final boolean unidentified; private final List reactions; + private final long serverTimestamp; MessageRecord(long id, String body, Recipient conversationRecipient, Recipient individualRecipient, int recipientDeviceId, - long dateSent, long dateReceived, long threadId, + long dateSent, long dateReceived, long dateServer, long threadId, int deliveryStatus, int deliveryReceiptCount, long type, List mismatches, List networkFailures, @@ -77,6 +78,7 @@ public abstract class MessageRecord extends DisplayRecord { this.expireStarted = expireStarted; this.unidentified = unidentified; this.reactions = reactions; + this.serverTimestamp = dateServer; } public abstract boolean isMms(); @@ -145,6 +147,10 @@ public abstract class MessageRecord extends DisplayRecord { return getDateReceived(); } + public long getServerTimestamp() { + return serverTimestamp; + } + public boolean isForcedSms() { return SmsDatabase.Types.isForcedSms(type); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/model/MmsMessageRecord.java b/app/src/main/java/org/thoughtcrime/securesms/database/model/MmsMessageRecord.java index f595753a85..0ce7c4f5dd 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/model/MmsMessageRecord.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/model/MmsMessageRecord.java @@ -26,7 +26,7 @@ public abstract class MmsMessageRecord extends MessageRecord { MmsMessageRecord(long id, String body, Recipient conversationRecipient, Recipient individualRecipient, int recipientDeviceId, long dateSent, - long dateReceived, long threadId, int deliveryStatus, int deliveryReceiptCount, + long dateReceived, long dateServer, long threadId, int deliveryStatus, int deliveryReceiptCount, long type, List mismatches, List networkFailures, int subscriptionId, long expiresIn, long expireStarted, boolean viewOnce, @@ -35,7 +35,7 @@ public abstract class MmsMessageRecord extends MessageRecord { @NonNull List linkPreviews, boolean unidentified, @NonNull List reactions) { - super(id, body, conversationRecipient, individualRecipient, recipientDeviceId, dateSent, dateReceived, threadId, deliveryStatus, deliveryReceiptCount, type, mismatches, networkFailures, subscriptionId, expiresIn, expireStarted, readReceiptCount, unidentified, reactions); + super(id, body, conversationRecipient, individualRecipient, recipientDeviceId, dateSent, dateReceived, dateServer, threadId, deliveryStatus, deliveryReceiptCount, type, mismatches, networkFailures, subscriptionId, expiresIn, expireStarted, readReceiptCount, unidentified, reactions); this.slideDeck = slideDeck; this.quote = quote; diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/model/NotificationMmsMessageRecord.java b/app/src/main/java/org/thoughtcrime/securesms/database/model/NotificationMmsMessageRecord.java index d15e379569..596db81e06 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/model/NotificationMmsMessageRecord.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/model/NotificationMmsMessageRecord.java @@ -55,8 +55,8 @@ public class NotificationMmsMessageRecord extends MmsMessageRecord { int subscriptionId, SlideDeck slideDeck, int readReceiptCount) { super(id, "", conversationRecipient, individualRecipient, recipientDeviceId, - dateSent, dateReceived, threadId, Status.STATUS_NONE, deliveryReceiptCount, mailbox, - new LinkedList(), new LinkedList(), subscriptionId, + dateSent, dateReceived, -1, threadId, Status.STATUS_NONE, deliveryReceiptCount, mailbox, + new LinkedList<>(), new LinkedList<>(), subscriptionId, 0, 0, false, slideDeck, readReceiptCount, null, Collections.emptyList(), Collections.emptyList(), false, Collections.emptyList()); diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/model/SmsMessageRecord.java b/app/src/main/java/org/thoughtcrime/securesms/database/model/SmsMessageRecord.java index a194be387e..fe76e19375 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/model/SmsMessageRecord.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/model/SmsMessageRecord.java @@ -43,7 +43,7 @@ public class SmsMessageRecord extends MessageRecord { String body, Recipient recipient, Recipient individualRecipient, int recipientDeviceId, - long dateSent, long dateReceived, + long dateSent, long dateReceived, long dateServer, int deliveryReceiptCount, long type, long threadId, int status, List mismatches, @@ -52,7 +52,7 @@ public class SmsMessageRecord extends MessageRecord { @NonNull List reactions) { super(id, body, recipient, individualRecipient, recipientDeviceId, - dateSent, dateReceived, threadId, status, deliveryReceiptCount, type, + dateSent, dateReceived, dateServer, threadId, status, deliveryReceiptCount, type, mismatches, new LinkedList<>(), subscriptionId, expiresIn, expireStarted, readReceiptCount, unidentified, reactions); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/groups/GroupV1MessageProcessor.java b/app/src/main/java/org/thoughtcrime/securesms/groups/GroupV1MessageProcessor.java index 9d189ad01f..004172fbf9 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/groups/GroupV1MessageProcessor.java +++ b/app/src/main/java/org/thoughtcrime/securesms/groups/GroupV1MessageProcessor.java @@ -246,7 +246,7 @@ public final class GroupV1MessageProcessor { } else { SmsDatabase smsDatabase = DatabaseFactory.getSmsDatabase(context); String body = Base64.encodeBytes(storage.toByteArray()); - IncomingTextMessage incoming = new IncomingTextMessage(Recipient.externalPush(context, content.getSender()).getId(), content.getSenderDevice(), content.getTimestamp(), body, Optional.of(GroupId.v1(group.getGroupId())), 0, content.isNeedsReceipt()); + IncomingTextMessage incoming = new IncomingTextMessage(Recipient.externalPush(context, content.getSender()).getId(), content.getSenderDevice(), content.getTimestamp(), content.getServerTimestamp(), body, Optional.of(GroupId.v1(group.getGroupId())), 0, content.isNeedsReceipt()); IncomingGroupMessage groupMessage = new IncomingGroupMessage(incoming, storage, body); Optional insertResult = smsDatabase.insertMessageInbox(groupMessage); diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/MmsDownloadJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/MmsDownloadJob.java index 65eceaaefa..fc6fd73965 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/MmsDownloadJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/MmsDownloadJob.java @@ -235,7 +235,7 @@ public class MmsDownloadJob extends BaseJob { group = Optional.of(DatabaseFactory.getGroupDatabase(context).getOrCreateMmsGroupForMembers(recipients)); } - IncomingMediaMessage message = new IncomingMediaMessage(from, group, body, retrieved.getDate() * 1000L, attachments, subscriptionId, 0, false, false, false); + IncomingMediaMessage message = new IncomingMediaMessage(from, group, body, retrieved.getDate() * 1000L, -1, attachments, subscriptionId, 0, false, false, false); Optional insertResult = database.insertMessageInbox(message, contentLocation, threadId); if (insertResult.isPresent()) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/PushProcessMessageJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/PushProcessMessageJob.java index 080bd81ab7..648c9d79f0 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/PushProcessMessageJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/PushProcessMessageJob.java @@ -482,6 +482,7 @@ public final class PushProcessMessageJob extends BaseJob { IncomingTextMessage incomingTextMessage = new IncomingTextMessage(Recipient.externalPush(context, content.getSender()).getId(), content.getSenderDevice(), content.getTimestamp(), + content.getServerTimestamp(), "", Optional.absent(), 0, content.isNeedsReceipt()); @@ -572,8 +573,11 @@ public final class PushProcessMessageJob extends BaseJob { Recipient sender = Recipient.externalPush(context, content.getSender()); Recipient recipient = getMessageDestination(content, message); IncomingMediaMessage mediaMessage = new IncomingMediaMessage(sender.getId(), - message.getTimestamp(), -1, - message.getExpiresInSeconds() * 1000L, true, + message.getTimestamp(), + content.getServerTimestamp(), + -1, + message.getExpiresInSeconds() * 1000L, + true, false, content.isNeedsReceipt(), Optional.absent(), @@ -859,8 +863,11 @@ public final class PushProcessMessageJob extends BaseJob { Optional> linkPreviews = getLinkPreviews(message.getPreviews(), message.getBody().or("")); Optional sticker = getStickerAttachment(message.getSticker()); IncomingMediaMessage mediaMessage = new IncomingMediaMessage(Recipient.externalPush(context, content.getSender()).getId(), - message.getTimestamp(), -1, - message.getExpiresInSeconds() * 1000L, false, + message.getTimestamp(), + content.getServerTimestamp(), + -1, + message.getExpiresInSeconds() * 1000L, + false, message.isViewOnce(), content.isNeedsReceipt(), message.getBody(), @@ -1071,7 +1078,9 @@ public final class PushProcessMessageJob extends BaseJob { IncomingTextMessage textMessage = new IncomingTextMessage(Recipient.externalPush(context, content.getSender()).getId(), content.getSenderDevice(), - message.getTimestamp(), body, + message.getTimestamp(), + content.getServerTimestamp(), + body, groupId, message.getExpiresInSeconds() * 1000L, content.isNeedsReceipt()); @@ -1498,7 +1507,7 @@ public final class PushProcessMessageJob extends BaseJob { private Optional insertPlaceholder(@NonNull String sender, int senderDevice, long timestamp, Optional groupId) { SmsDatabase database = DatabaseFactory.getSmsDatabase(context); IncomingTextMessage textMessage = new IncomingTextMessage(Recipient.external(context, sender).getId(), - senderDevice, timestamp, "", + senderDevice, timestamp, -1, "", groupId, 0, false); textMessage = new IncomingEncryptedMessage(textMessage, ""); diff --git a/app/src/main/java/org/thoughtcrime/securesms/mms/IncomingMediaMessage.java b/app/src/main/java/org/thoughtcrime/securesms/mms/IncomingMediaMessage.java index de45750655..07f1b49fe7 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mms/IncomingMediaMessage.java +++ b/app/src/main/java/org/thoughtcrime/securesms/mms/IncomingMediaMessage.java @@ -24,6 +24,7 @@ public class IncomingMediaMessage { private final String body; private final boolean push; private final long sentTimeMillis; + private final long serverTimeMillis; private final int subscriptionId; private final long expiresIn; private final boolean expirationUpdate; @@ -39,6 +40,7 @@ public class IncomingMediaMessage { Optional groupId, String body, long sentTimeMillis, + long serverTimeMillis, List attachments, int subscriptionId, long expiresIn, @@ -49,6 +51,7 @@ public class IncomingMediaMessage { this.from = from; this.groupId = groupId.orNull(); this.sentTimeMillis = sentTimeMillis; + this.serverTimeMillis = serverTimeMillis; this.body = body; this.push = false; this.subscriptionId = subscriptionId; @@ -63,6 +66,7 @@ public class IncomingMediaMessage { public IncomingMediaMessage(@NonNull RecipientId from, long sentTimeMillis, + long serverTimeMillis, int subscriptionId, long expiresIn, boolean expirationUpdate, @@ -79,6 +83,7 @@ public class IncomingMediaMessage { this.push = true; this.from = from; this.sentTimeMillis = sentTimeMillis; + this.serverTimeMillis = serverTimeMillis; this.body = body.orNull(); this.subscriptionId = subscriptionId; this.expiresIn = expiresIn; @@ -131,6 +136,10 @@ public class IncomingMediaMessage { return sentTimeMillis; } + public long getServerTimeMillis() { + return serverTimeMillis; + } + public long getExpiresIn() { return expiresIn; } diff --git a/app/src/main/java/org/thoughtcrime/securesms/sms/IncomingJoinedMessage.java b/app/src/main/java/org/thoughtcrime/securesms/sms/IncomingJoinedMessage.java index c58c950410..22fb7aa8f7 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/sms/IncomingJoinedMessage.java +++ b/app/src/main/java/org/thoughtcrime/securesms/sms/IncomingJoinedMessage.java @@ -6,7 +6,7 @@ import org.whispersystems.libsignal.util.guava.Optional; public class IncomingJoinedMessage extends IncomingTextMessage { public IncomingJoinedMessage(RecipientId sender) { - super(sender, 1, System.currentTimeMillis(), null, Optional.absent(), 0, false); + super(sender, 1, System.currentTimeMillis(), -1, null, Optional.absent(), 0, false); } @Override diff --git a/app/src/main/java/org/thoughtcrime/securesms/sms/IncomingTextMessage.java b/app/src/main/java/org/thoughtcrime/securesms/sms/IncomingTextMessage.java index ebf3d6c3f0..9d470e2871 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/sms/IncomingTextMessage.java +++ b/app/src/main/java/org/thoughtcrime/securesms/sms/IncomingTextMessage.java @@ -37,6 +37,7 @@ public class IncomingTextMessage implements Parcelable { private final boolean replyPathPresent; private final String pseudoSubject; private final long sentTimestampMillis; + private final long serverTimestampMillis; @Nullable private final GroupId groupId; private final boolean push; private final int subscriptionId; @@ -44,70 +45,79 @@ public class IncomingTextMessage implements Parcelable { private final boolean unidentified; public IncomingTextMessage(@NonNull RecipientId sender, @NonNull SmsMessage message, int subscriptionId) { - this.message = message.getDisplayMessageBody(); - this.sender = sender; - this.senderDeviceId = SignalServiceAddress.DEFAULT_DEVICE_ID; - this.protocol = message.getProtocolIdentifier(); - this.serviceCenterAddress = message.getServiceCenterAddress(); - this.replyPathPresent = message.isReplyPathPresent(); - this.pseudoSubject = message.getPseudoSubject(); - this.sentTimestampMillis = message.getTimestampMillis(); - this.subscriptionId = subscriptionId; - this.expiresInMillis = 0; - this.groupId = null; - this.push = false; - this.unidentified = false; + this.message = message.getDisplayMessageBody(); + this.sender = sender; + this.senderDeviceId = SignalServiceAddress.DEFAULT_DEVICE_ID; + this.protocol = message.getProtocolIdentifier(); + this.serviceCenterAddress = message.getServiceCenterAddress(); + this.replyPathPresent = message.isReplyPathPresent(); + this.pseudoSubject = message.getPseudoSubject(); + this.sentTimestampMillis = message.getTimestampMillis(); + this.serverTimestampMillis = -1; + this.subscriptionId = subscriptionId; + this.expiresInMillis = 0; + this.groupId = null; + this.push = false; + this.unidentified = false; } - public IncomingTextMessage(@NonNull RecipientId sender, int senderDeviceId, long sentTimestampMillis, - String encodedBody, Optional groupId, - long expiresInMillis, boolean unidentified) + public IncomingTextMessage(@NonNull RecipientId sender, + int senderDeviceId, + long sentTimestampMillis, + long serverTimestampMillis, + String encodedBody, + Optional groupId, + long expiresInMillis, + boolean unidentified) { - this.message = encodedBody; - this.sender = sender; - this.senderDeviceId = senderDeviceId; - this.protocol = 31337; - this.serviceCenterAddress = "GCM"; - this.replyPathPresent = true; - this.pseudoSubject = ""; - this.sentTimestampMillis = sentTimestampMillis; - this.push = true; - this.subscriptionId = -1; - this.expiresInMillis = expiresInMillis; - this.unidentified = unidentified; - this.groupId = groupId.orNull(); + this.message = encodedBody; + this.sender = sender; + this.senderDeviceId = senderDeviceId; + this.protocol = 31337; + this.serviceCenterAddress = "GCM"; + this.replyPathPresent = true; + this.pseudoSubject = ""; + this.sentTimestampMillis = sentTimestampMillis; + this.serverTimestampMillis = serverTimestampMillis; + this.push = true; + this.subscriptionId = -1; + this.expiresInMillis = expiresInMillis; + this.unidentified = unidentified; + this.groupId = groupId.orNull(); } public IncomingTextMessage(Parcel in) { - this.message = in.readString(); - this.sender = in.readParcelable(IncomingTextMessage.class.getClassLoader()); - this.senderDeviceId = in.readInt(); - this.protocol = in.readInt(); - this.serviceCenterAddress = in.readString(); - this.replyPathPresent = (in.readInt() == 1); - this.pseudoSubject = in.readString(); - this.sentTimestampMillis = in.readLong(); - this.groupId = GroupId.parseNullable(in.readString()); - this.push = (in.readInt() == 1); - this.subscriptionId = in.readInt(); - this.expiresInMillis = in.readLong(); - this.unidentified = in.readInt() == 1; + this.message = in.readString(); + this.sender = in.readParcelable(IncomingTextMessage.class.getClassLoader()); + this.senderDeviceId = in.readInt(); + this.protocol = in.readInt(); + this.serviceCenterAddress = in.readString(); + this.replyPathPresent = (in.readInt() == 1); + this.pseudoSubject = in.readString(); + this.sentTimestampMillis = in.readLong(); + this.serverTimestampMillis = in.readLong(); + this.groupId = GroupId.parseNullable(in.readString()); + this.push = (in.readInt() == 1); + this.subscriptionId = in.readInt(); + this.expiresInMillis = in.readLong(); + this.unidentified = in.readInt() == 1; } public IncomingTextMessage(IncomingTextMessage base, String newBody) { - this.message = newBody; - this.sender = base.getSender(); - this.senderDeviceId = base.getSenderDeviceId(); - this.protocol = base.getProtocol(); - this.serviceCenterAddress = base.getServiceCenterAddress(); - this.replyPathPresent = base.isReplyPathPresent(); - this.pseudoSubject = base.getPseudoSubject(); - this.sentTimestampMillis = base.getSentTimestampMillis(); - this.groupId = base.getGroupId(); - this.push = base.isPush(); - this.subscriptionId = base.getSubscriptionId(); - this.expiresInMillis = base.getExpiresIn(); - this.unidentified = base.isUnidentified(); + this.message = newBody; + this.sender = base.getSender(); + this.senderDeviceId = base.getSenderDeviceId(); + this.protocol = base.getProtocol(); + this.serviceCenterAddress = base.getServiceCenterAddress(); + this.replyPathPresent = base.isReplyPathPresent(); + this.pseudoSubject = base.getPseudoSubject(); + this.sentTimestampMillis = base.getSentTimestampMillis(); + this.serverTimestampMillis = base.getServerTimestampMillis(); + this.groupId = base.getGroupId(); + this.push = base.isPush(); + this.subscriptionId = base.getSubscriptionId(); + this.expiresInMillis = base.getExpiresIn(); + this.unidentified = base.isUnidentified(); } public IncomingTextMessage(List fragments) { @@ -117,36 +127,38 @@ public class IncomingTextMessage implements Parcelable { body.append(message.getMessageBody()); } - this.message = body.toString(); - this.sender = fragments.get(0).getSender(); - this.senderDeviceId = fragments.get(0).getSenderDeviceId(); - this.protocol = fragments.get(0).getProtocol(); - this.serviceCenterAddress = fragments.get(0).getServiceCenterAddress(); - this.replyPathPresent = fragments.get(0).isReplyPathPresent(); - this.pseudoSubject = fragments.get(0).getPseudoSubject(); - this.sentTimestampMillis = fragments.get(0).getSentTimestampMillis(); - this.groupId = fragments.get(0).getGroupId(); - this.push = fragments.get(0).isPush(); - this.subscriptionId = fragments.get(0).getSubscriptionId(); - this.expiresInMillis = fragments.get(0).getExpiresIn(); - this.unidentified = fragments.get(0).isUnidentified(); + this.message = body.toString(); + this.sender = fragments.get(0).getSender(); + this.senderDeviceId = fragments.get(0).getSenderDeviceId(); + this.protocol = fragments.get(0).getProtocol(); + this.serviceCenterAddress = fragments.get(0).getServiceCenterAddress(); + this.replyPathPresent = fragments.get(0).isReplyPathPresent(); + this.pseudoSubject = fragments.get(0).getPseudoSubject(); + this.sentTimestampMillis = fragments.get(0).getSentTimestampMillis(); + this.serverTimestampMillis = fragments.get(0).getServerTimestampMillis(); + this.groupId = fragments.get(0).getGroupId(); + this.push = fragments.get(0).isPush(); + this.subscriptionId = fragments.get(0).getSubscriptionId(); + this.expiresInMillis = fragments.get(0).getExpiresIn(); + this.unidentified = fragments.get(0).isUnidentified(); } protected IncomingTextMessage(@NonNull RecipientId sender, @Nullable GroupId groupId) { - this.message = ""; - this.sender = sender; - this.senderDeviceId = SignalServiceAddress.DEFAULT_DEVICE_ID; - this.protocol = 31338; - this.serviceCenterAddress = "Outgoing"; - this.replyPathPresent = true; - this.pseudoSubject = ""; - this.sentTimestampMillis = System.currentTimeMillis(); - this.groupId = groupId; - this.push = true; - this.subscriptionId = -1; - this.expiresInMillis = 0; - this.unidentified = false; + this.message = ""; + this.sender = sender; + this.senderDeviceId = SignalServiceAddress.DEFAULT_DEVICE_ID; + this.protocol = 31338; + this.serviceCenterAddress = "Outgoing"; + this.replyPathPresent = true; + this.pseudoSubject = ""; + this.sentTimestampMillis = System.currentTimeMillis(); + this.serverTimestampMillis = sentTimestampMillis; + this.groupId = groupId; + this.push = true; + this.subscriptionId = -1; + this.expiresInMillis = 0; + this.unidentified = false; } public int getSubscriptionId() { @@ -161,6 +173,10 @@ public class IncomingTextMessage implements Parcelable { return sentTimestampMillis; } + public long getServerTimestampMillis() { + return serverTimestampMillis; + } + public String getPseudoSubject() { return pseudoSubject; } diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/IdentityUtil.java b/app/src/main/java/org/thoughtcrime/securesms/util/IdentityUtil.java index 7acfe7eaef..a16546858e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/IdentityUtil.java +++ b/app/src/main/java/org/thoughtcrime/securesms/util/IdentityUtil.java @@ -78,7 +78,7 @@ public class IdentityUtil { if (groupRecord.getMembers().contains(recipient.getId()) && groupRecord.isActive() && !groupRecord.isMms()) { if (remote) { - IncomingTextMessage incoming = new IncomingTextMessage(recipient.getId(), 1, time, null, Optional.of(groupRecord.getId()), 0, false); + IncomingTextMessage incoming = new IncomingTextMessage(recipient.getId(), 1, time, -1, null, Optional.of(groupRecord.getId()), 0, false); if (verified) incoming = new IncomingIdentityVerifiedMessage(incoming); else incoming = new IncomingIdentityDefaultMessage(incoming); @@ -100,7 +100,7 @@ public class IdentityUtil { } if (remote) { - IncomingTextMessage incoming = new IncomingTextMessage(recipient.getId(), 1, time, null, Optional.absent(), 0, false); + IncomingTextMessage incoming = new IncomingTextMessage(recipient.getId(), 1, time, -1, null, Optional.absent(), 0, false); if (verified) incoming = new IncomingIdentityVerifiedMessage(incoming); else incoming = new IncomingIdentityDefaultMessage(incoming); @@ -129,14 +129,14 @@ public class IdentityUtil { while ((groupRecord = reader.getNext()) != null) { if (groupRecord.getMembers().contains(recipient.getId()) && groupRecord.isActive()) { - IncomingTextMessage incoming = new IncomingTextMessage(recipient.getId(), 1, time, null, Optional.of(groupRecord.getId()), 0, false); + IncomingTextMessage incoming = new IncomingTextMessage(recipient.getId(), 1, time, time, null, Optional.of(groupRecord.getId()), 0, false); IncomingIdentityUpdateMessage groupUpdate = new IncomingIdentityUpdateMessage(incoming); smsDatabase.insertMessageInbox(groupUpdate); } } - IncomingTextMessage incoming = new IncomingTextMessage(recipient.getId(), 1, time, null, Optional.absent(), 0, false); + IncomingTextMessage incoming = new IncomingTextMessage(recipient.getId(), 1, time, -1, null, Optional.absent(), 0, false); IncomingIdentityUpdateMessage individualUpdate = new IncomingIdentityUpdateMessage(incoming); Optional insertResult = smsDatabase.insertMessageInbox(individualUpdate); diff --git a/libsignal/service/src/main/java/org/whispersystems/signalservice/api/crypto/SignalServiceCipher.java b/libsignal/service/src/main/java/org/whispersystems/signalservice/api/crypto/SignalServiceCipher.java index 01c40abd24..c74a0233d4 100644 --- a/libsignal/service/src/main/java/org/whispersystems/signalservice/api/crypto/SignalServiceCipher.java +++ b/libsignal/service/src/main/java/org/whispersystems/signalservice/api/crypto/SignalServiceCipher.java @@ -176,14 +176,14 @@ public class SignalServiceCipher { SessionCipher sessionCipher = new SessionCipher(signalProtocolStore, sourceAddress); paddedMessage = sessionCipher.decrypt(new PreKeySignalMessage(ciphertext)); - metadata = new SignalServiceMetadata(envelope.getSourceAddress(), envelope.getSourceDevice(), envelope.getTimestamp(), false); + metadata = new SignalServiceMetadata(envelope.getSourceAddress(), envelope.getSourceDevice(), envelope.getTimestamp(), envelope.getServerTimestamp(), false); sessionVersion = sessionCipher.getSessionVersion(); } else if (envelope.isSignalMessage()) { SignalProtocolAddress sourceAddress = getPreferredProtocolAddress(signalProtocolStore, envelope.getSourceAddress(), envelope.getSourceDevice()); SessionCipher sessionCipher = new SessionCipher(signalProtocolStore, sourceAddress); paddedMessage = sessionCipher.decrypt(new SignalMessage(ciphertext)); - metadata = new SignalServiceMetadata(envelope.getSourceAddress(), envelope.getSourceDevice(), envelope.getTimestamp(), false); + metadata = new SignalServiceMetadata(envelope.getSourceAddress(), envelope.getSourceDevice(), envelope.getTimestamp(), envelope.getServerTimestamp(), false); sessionVersion = sessionCipher.getSessionVersion(); } else if (envelope.isUnidentifiedSender()) { SealedSessionCipher sealedSessionCipher = new SealedSessionCipher(signalProtocolStore, localAddress.getUuid().orNull(), localAddress.getNumber().orNull(), 1); @@ -192,7 +192,7 @@ public class SignalServiceCipher { SignalProtocolAddress protocolAddress = getPreferredProtocolAddress(signalProtocolStore, resultAddress, result.getDeviceId()); paddedMessage = result.getPaddedMessage(); - metadata = new SignalServiceMetadata(resultAddress, result.getDeviceId(), envelope.getTimestamp(), true); + metadata = new SignalServiceMetadata(resultAddress, result.getDeviceId(), envelope.getTimestamp(), envelope.getServerTimestamp(), true); sessionVersion = sealedSessionCipher.getSessionVersion(protocolAddress); } else { throw new InvalidMetadataMessageException("Unknown type: " + envelope.getType()); diff --git a/libsignal/service/src/main/java/org/whispersystems/signalservice/api/messages/SignalServiceContent.java b/libsignal/service/src/main/java/org/whispersystems/signalservice/api/messages/SignalServiceContent.java index 91d3ab1024..dad39c3ed6 100644 --- a/libsignal/service/src/main/java/org/whispersystems/signalservice/api/messages/SignalServiceContent.java +++ b/libsignal/service/src/main/java/org/whispersystems/signalservice/api/messages/SignalServiceContent.java @@ -58,6 +58,7 @@ public final class SignalServiceContent { private final SignalServiceAddress sender; private final int senderDevice; private final long timestamp; + private final long serverTimestamp; private final boolean needsReceipt; private final SignalServiceContentProto serializedState; @@ -67,10 +68,11 @@ public final class SignalServiceContent { private final Optional readMessage; private final Optional typingMessage; - private SignalServiceContent(SignalServiceDataMessage message, SignalServiceAddress sender, int senderDevice, long timestamp, boolean needsReceipt, SignalServiceContentProto serializedState) { + private SignalServiceContent(SignalServiceDataMessage message, SignalServiceAddress sender, int senderDevice, long timestamp, long serverTimestamp, boolean needsReceipt, SignalServiceContentProto serializedState) { this.sender = sender; this.senderDevice = senderDevice; this.timestamp = timestamp; + this.serverTimestamp = serverTimestamp; this.needsReceipt = needsReceipt; this.serializedState = serializedState; @@ -81,10 +83,11 @@ public final class SignalServiceContent { this.typingMessage = Optional.absent(); } - private SignalServiceContent(SignalServiceSyncMessage synchronizeMessage, SignalServiceAddress sender, int senderDevice, long timestamp, boolean needsReceipt, SignalServiceContentProto serializedState) { + private SignalServiceContent(SignalServiceSyncMessage synchronizeMessage, SignalServiceAddress sender, int senderDevice, long timestamp, long serverTimestamp, boolean needsReceipt, SignalServiceContentProto serializedState) { this.sender = sender; this.senderDevice = senderDevice; this.timestamp = timestamp; + this.serverTimestamp = serverTimestamp; this.needsReceipt = needsReceipt; this.serializedState = serializedState; @@ -95,10 +98,11 @@ public final class SignalServiceContent { this.typingMessage = Optional.absent(); } - private SignalServiceContent(SignalServiceCallMessage callMessage, SignalServiceAddress sender, int senderDevice, long timestamp, boolean needsReceipt, SignalServiceContentProto serializedState) { + private SignalServiceContent(SignalServiceCallMessage callMessage, SignalServiceAddress sender, int senderDevice, long timestamp, long serverTimestamp, boolean needsReceipt, SignalServiceContentProto serializedState) { this.sender = sender; this.senderDevice = senderDevice; this.timestamp = timestamp; + this.serverTimestamp = serverTimestamp; this.needsReceipt = needsReceipt; this.serializedState = serializedState; @@ -109,10 +113,11 @@ public final class SignalServiceContent { this.typingMessage = Optional.absent(); } - private SignalServiceContent(SignalServiceReceiptMessage receiptMessage, SignalServiceAddress sender, int senderDevice, long timestamp, boolean needsReceipt, SignalServiceContentProto serializedState) { + private SignalServiceContent(SignalServiceReceiptMessage receiptMessage, SignalServiceAddress sender, int senderDevice, long timestamp, long serverTimestamp, boolean needsReceipt, SignalServiceContentProto serializedState) { this.sender = sender; this.senderDevice = senderDevice; this.timestamp = timestamp; + this.serverTimestamp = serverTimestamp; this.needsReceipt = needsReceipt; this.serializedState = serializedState; @@ -123,10 +128,11 @@ public final class SignalServiceContent { this.typingMessage = Optional.absent(); } - private SignalServiceContent(SignalServiceTypingMessage typingMessage, SignalServiceAddress sender, int senderDevice, long timestamp, boolean needsReceipt, SignalServiceContentProto serializedState) { + private SignalServiceContent(SignalServiceTypingMessage typingMessage, SignalServiceAddress sender, int senderDevice, long timestamp, long serverTimestamp, boolean needsReceipt, SignalServiceContentProto serializedState) { this.sender = sender; this.senderDevice = senderDevice; this.timestamp = timestamp; + this.serverTimestamp = serverTimestamp; this.needsReceipt = needsReceipt; this.serializedState = serializedState; @@ -169,6 +175,10 @@ public final class SignalServiceContent { return timestamp; } + public long getServerTimestamp() { + return serverTimestamp; + } + public boolean isNeedsReceipt() { return needsReceipt; } @@ -206,6 +216,7 @@ public final class SignalServiceContent { metadata.getSender(), metadata.getSenderDevice(), metadata.getTimestamp(), + metadata.getServerTimestamp(), metadata.isNeedsReceipt(), serviceContentProto); } else if (serviceContentProto.getDataCase() == SignalServiceContentProto.DataCase.CONTENT) { @@ -216,6 +227,7 @@ public final class SignalServiceContent { metadata.getSender(), metadata.getSenderDevice(), metadata.getTimestamp(), + metadata.getServerTimestamp(), metadata.isNeedsReceipt(), serviceContentProto); } else if (message.hasSyncMessage() && localAddress.matches(metadata.getSender())) { @@ -223,6 +235,7 @@ public final class SignalServiceContent { metadata.getSender(), metadata.getSenderDevice(), metadata.getTimestamp(), + metadata.getServerTimestamp(), metadata.isNeedsReceipt(), serviceContentProto); } else if (message.hasCallMessage()) { @@ -230,6 +243,7 @@ public final class SignalServiceContent { metadata.getSender(), metadata.getSenderDevice(), metadata.getTimestamp(), + metadata.getServerTimestamp(), metadata.isNeedsReceipt(), serviceContentProto); } else if (message.hasReceiptMessage()) { @@ -237,6 +251,7 @@ public final class SignalServiceContent { metadata.getSender(), metadata.getSenderDevice(), metadata.getTimestamp(), + metadata.getServerTimestamp(), metadata.isNeedsReceipt(), serviceContentProto); } else if (message.hasTypingMessage()) { @@ -244,6 +259,7 @@ public final class SignalServiceContent { metadata.getSender(), metadata.getSenderDevice(), metadata.getTimestamp(), + metadata.getServerTimestamp(), false, serviceContentProto); } diff --git a/libsignal/service/src/main/java/org/whispersystems/signalservice/api/messages/SignalServiceMetadata.java b/libsignal/service/src/main/java/org/whispersystems/signalservice/api/messages/SignalServiceMetadata.java index 15175ced9a..a2a9e18262 100644 --- a/libsignal/service/src/main/java/org/whispersystems/signalservice/api/messages/SignalServiceMetadata.java +++ b/libsignal/service/src/main/java/org/whispersystems/signalservice/api/messages/SignalServiceMetadata.java @@ -6,13 +6,15 @@ public final class SignalServiceMetadata { private final SignalServiceAddress sender; private final int senderDevice; private final long timestamp; + private final long serverTimestamp; private final boolean needsReceipt; - public SignalServiceMetadata(SignalServiceAddress sender, int senderDevice, long timestamp, boolean needsReceipt) { - this.sender = sender; - this.senderDevice = senderDevice; - this.timestamp = timestamp; - this.needsReceipt = needsReceipt; + public SignalServiceMetadata(SignalServiceAddress sender, int senderDevice, long timestamp, long serverTimestamp, boolean needsReceipt) { + this.sender = sender; + this.senderDevice = senderDevice; + this.timestamp = timestamp; + this.serverTimestamp = serverTimestamp; + this.needsReceipt = needsReceipt; } public SignalServiceAddress getSender() { @@ -27,6 +29,10 @@ public final class SignalServiceMetadata { return timestamp; } + public long getServerTimestamp() { + return serverTimestamp; + } + public boolean isNeedsReceipt() { return needsReceipt; } diff --git a/libsignal/service/src/main/java/org/whispersystems/signalservice/internal/serialize/SignalServiceMetadataProtobufSerializer.java b/libsignal/service/src/main/java/org/whispersystems/signalservice/internal/serialize/SignalServiceMetadataProtobufSerializer.java index 3cd7eefb49..99f6cff673 100644 --- a/libsignal/service/src/main/java/org/whispersystems/signalservice/internal/serialize/SignalServiceMetadataProtobufSerializer.java +++ b/libsignal/service/src/main/java/org/whispersystems/signalservice/internal/serialize/SignalServiceMetadataProtobufSerializer.java @@ -14,13 +14,15 @@ public final class SignalServiceMetadataProtobufSerializer { .setSenderDevice(metadata.getSenderDevice()) .setNeedsReceipt(metadata.isNeedsReceipt()) .setTimestamp(metadata.getTimestamp()) + .setServerTimestamp(metadata.getServerTimestamp()) .build(); } public static SignalServiceMetadata fromProtobuf(MetadataProto metadata) { return new SignalServiceMetadata(SignalServiceAddressProtobufSerializer.fromProtobuf(metadata.getAddress()), - metadata.getSenderDevice(), - metadata.getTimestamp(), - metadata.getNeedsReceipt()); + metadata.getSenderDevice(), + metadata.getTimestamp(), + metadata.getServerTimestamp(), + metadata.getNeedsReceipt()); } } diff --git a/libsignal/service/src/main/proto/InternalSerialization.proto b/libsignal/service/src/main/proto/InternalSerialization.proto index b85daa9e06..d045925ed6 100644 --- a/libsignal/service/src/main/proto/InternalSerialization.proto +++ b/libsignal/service/src/main/proto/InternalSerialization.proto @@ -22,10 +22,11 @@ message SignalServiceContentProto { } message MetadataProto { - optional AddressProto address = 1; - optional int32 senderDevice = 2; - optional int64 timestamp = 3; - optional bool needsReceipt = 4; + optional AddressProto address = 1; + optional int32 senderDevice = 2; + optional int64 timestamp = 3; + optional int64 serverTimestamp = 5; + optional bool needsReceipt = 4; } message AddressProto {