Save receipt timestamps on sms/mms database.

fork-5.53.8
Lucio Maciel 2021-08-26 16:33:13 -03:00 zatwierdzone przez Greyson Parrelli
rodzic 3dc1ce3353
commit 0273d0f285
5 zmienionych plików z 74 dodań i 35 usunięć

Wyświetl plik

@ -13,33 +13,53 @@ public class EarlyReceiptCache {
private static final String TAG = Log.tag(EarlyReceiptCache.class); private static final String TAG = Log.tag(EarlyReceiptCache.class);
private final LRUCache<Long, Map<RecipientId, Long>> cache = new LRUCache<>(100); private final LRUCache<Long, Map<RecipientId, Receipt>> cache = new LRUCache<>(100);
private final String name; private final String name;
public EarlyReceiptCache(@NonNull String name) { public EarlyReceiptCache(@NonNull String name) {
this.name = name; this.name = name;
} }
public synchronized void increment(long timestamp, @NonNull RecipientId origin) { public synchronized void increment(long timestamp, @NonNull RecipientId origin, long receiptTimestamp) {
Map<RecipientId, Long> receipts = cache.get(timestamp); Map<RecipientId, Receipt> receipts = cache.get(timestamp);
if (receipts == null) { if (receipts == null) {
receipts = new HashMap<>(); receipts = new HashMap<>();
} }
Long count = receipts.get(origin); Receipt receipt = receipts.get(origin);
if (count != null) { if (receipt != null) {
receipts.put(origin, ++count); receipt.count++;
receipt.timestamp = receiptTimestamp;
} else { } else {
receipts.put(origin, 1L); receipt = new Receipt(1, receiptTimestamp);
} }
receipts.put(origin, receipt);
cache.put(timestamp, receipts); cache.put(timestamp, receipts);
} }
public synchronized Map<RecipientId, Long> remove(long timestamp) { public synchronized Map<RecipientId, Receipt> remove(long timestamp) {
Map<RecipientId, Long> receipts = cache.remove(timestamp); Map<RecipientId, Receipt> receipts = cache.remove(timestamp);
return receipts != null ? receipts : new HashMap<>(); return receipts != null ? receipts : new HashMap<>();
} }
public class Receipt {
private long count;
private long timestamp;
private Receipt(long count, long timestamp) {
this.count = count;
this.timestamp = timestamp;
}
public long getCount() {
return count;
}
public long getTimestamp() {
return timestamp;
}
}
} }

Wyświetl plik

@ -170,7 +170,8 @@ public class MmsDatabase extends MessageDatabase {
MENTIONS_SELF + " INTEGER DEFAULT 0, " + MENTIONS_SELF + " INTEGER DEFAULT 0, " +
NOTIFIED_TIMESTAMP + " INTEGER DEFAULT 0, " + NOTIFIED_TIMESTAMP + " INTEGER DEFAULT 0, " +
VIEWED_RECEIPT_COUNT + " INTEGER DEFAULT 0, " + VIEWED_RECEIPT_COUNT + " INTEGER DEFAULT 0, " +
SERVER_GUID + " TEXT DEFAULT NULL);"; SERVER_GUID + " TEXT DEFAULT NULL, "+
RECEIPT_TIMESTAMP + " INTEGER DEFAULT -1);";
public static final String[] CREATE_INDEXS = { public static final String[] CREATE_INDEXS = {
"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_read_and_notified_and_thread_id_index ON " + TABLE_NAME + "(" + READ + "," + NOTIFIED + "," + THREAD_ID + ");",
@ -617,25 +618,29 @@ public class MmsDatabase extends MessageDatabase {
SQLiteDatabase database = databaseHelper.getSignalWritableDatabase(); SQLiteDatabase database = databaseHelper.getSignalWritableDatabase();
Set<ThreadUpdate> threadUpdates = new HashSet<>(); Set<ThreadUpdate> threadUpdates = new HashSet<>();
try (Cursor cursor = database.query(TABLE_NAME, new String[] {ID, THREAD_ID, MESSAGE_BOX, RECIPIENT_ID, receiptType.getColumnName()}, try (Cursor cursor = database.query(TABLE_NAME, new String[] {ID, THREAD_ID, MESSAGE_BOX, RECIPIENT_ID, receiptType.getColumnName(), RECEIPT_TIMESTAMP},
DATE_SENT + " = ?", new String[] {String.valueOf(messageId.getTimetamp())}, DATE_SENT + " = ?", new String[] {String.valueOf(messageId.getTimetamp())},
null, null, null, null)) null, null, null, null))
{ {
while (cursor.moveToNext()) { while (cursor.moveToNext()) {
if (Types.isOutgoingMessageType(cursor.getLong(cursor.getColumnIndexOrThrow(MESSAGE_BOX)))) { if (Types.isOutgoingMessageType(CursorUtil.requireLong(cursor, MESSAGE_BOX))) {
RecipientId theirRecipientId = RecipientId.from(cursor.getLong(cursor.getColumnIndexOrThrow(RECIPIENT_ID))); RecipientId theirRecipientId = RecipientId.from(CursorUtil.requireLong(cursor, RECIPIENT_ID));
RecipientId ourRecipientId = messageId.getRecipientId(); RecipientId ourRecipientId = messageId.getRecipientId();
String columnName = receiptType.getColumnName(); String columnName = receiptType.getColumnName();
if (ourRecipientId.equals(theirRecipientId) || Recipient.resolved(theirRecipientId).isGroup()) { if (ourRecipientId.equals(theirRecipientId) || Recipient.resolved(theirRecipientId).isGroup()) {
long id = cursor.getLong(cursor.getColumnIndexOrThrow(ID)); long id = CursorUtil.requireLong(cursor, ID);
long threadId = cursor.getLong(cursor.getColumnIndexOrThrow(THREAD_ID)); long threadId = CursorUtil.requireLong(cursor, THREAD_ID);
int status = receiptType.getGroupStatus(); int status = receiptType.getGroupStatus();
boolean isFirstIncrement = cursor.getLong(cursor.getColumnIndexOrThrow(columnName)) == 0; boolean isFirstIncrement = CursorUtil.requireLong(cursor, columnName) == 0;
long savedTimestamp = CursorUtil.requireLong(cursor, RECEIPT_TIMESTAMP);
long updatedTimestamp = isFirstIncrement ? Math.max(savedTimestamp, timestamp) : savedTimestamp;
database.execSQL("UPDATE " + TABLE_NAME + " SET " + database.execSQL("UPDATE " + TABLE_NAME + " SET " +
columnName + " = " + columnName + " + 1 WHERE " + ID + " = ?", columnName + " = " + columnName + " + 1, " +
new String[] {String.valueOf(id)}); RECEIPT_TIMESTAMP + " = ? WHERE " +
ID + " = ?",
SqlUtil.buildArgs(updatedTimestamp, id));
DatabaseFactory.getGroupReceiptDatabase(context).update(ourRecipientId, id, status, timestamp); DatabaseFactory.getGroupReceiptDatabase(context).update(ourRecipientId, id, status, timestamp);
@ -645,7 +650,7 @@ public class MmsDatabase extends MessageDatabase {
} }
if (threadUpdates.size() > 0 && receiptType == ReceiptType.DELIVERY) { if (threadUpdates.size() > 0 && receiptType == ReceiptType.DELIVERY) {
earlyDeliveryReceiptCache.increment(messageId.getTimetamp(), messageId.getRecipientId()); earlyDeliveryReceiptCache.increment(messageId.getTimetamp(), messageId.getRecipientId(), timestamp);
} }
return threadUpdates; return threadUpdates;
@ -1484,7 +1489,7 @@ public class MmsDatabase extends MessageDatabase {
type |= Types.EXPIRATION_TIMER_UPDATE_BIT; type |= Types.EXPIRATION_TIMER_UPDATE_BIT;
} }
Map<RecipientId, Long> earlyDeliveryReceipts = earlyDeliveryReceiptCache.remove(message.getSentTimeMillis()); Map<RecipientId, EarlyReceiptCache.Receipt> earlyDeliveryReceipts = earlyDeliveryReceiptCache.remove(message.getSentTimeMillis());
ContentValues contentValues = new ContentValues(); ContentValues contentValues = new ContentValues();
contentValues.put(DATE_SENT, message.getSentTimeMillis()); contentValues.put(DATE_SENT, message.getSentTimeMillis());
@ -1498,7 +1503,8 @@ public class MmsDatabase extends MessageDatabase {
contentValues.put(EXPIRES_IN, message.getExpiresIn()); contentValues.put(EXPIRES_IN, message.getExpiresIn());
contentValues.put(VIEW_ONCE, message.isViewOnce()); contentValues.put(VIEW_ONCE, message.isViewOnce());
contentValues.put(RECIPIENT_ID, message.getRecipient().getId().serialize()); contentValues.put(RECIPIENT_ID, message.getRecipient().getId().serialize());
contentValues.put(DELIVERY_RECEIPT_COUNT, Stream.of(earlyDeliveryReceipts.values()).mapToLong(Long::longValue).sum()); contentValues.put(DELIVERY_RECEIPT_COUNT, Stream.of(earlyDeliveryReceipts.values()).mapToLong(EarlyReceiptCache.Receipt::getCount).sum());
contentValues.put(RECEIPT_TIMESTAMP, Stream.of(earlyDeliveryReceipts.values()).mapToLong(EarlyReceiptCache.Receipt::getTimestamp).max().orElse(-1));
if (message.getRecipient().isSelf() && hasAudioAttachment(message.getAttachments())) { if (message.getRecipient().isSelf() && hasAudioAttachment(message.getAttachments())) {
contentValues.put(VIEWED_RECEIPT_COUNT, 1L); contentValues.put(VIEWED_RECEIPT_COUNT, 1L);

Wyświetl plik

@ -29,6 +29,7 @@ public interface MmsSmsColumns {
public static final String REACTIONS_LAST_SEEN = "reactions_last_seen"; public static final String REACTIONS_LAST_SEEN = "reactions_last_seen";
public static final String REMOTE_DELETED = "remote_deleted"; public static final String REMOTE_DELETED = "remote_deleted";
public static final String SERVER_GUID = "server_guid"; public static final String SERVER_GUID = "server_guid";
public static final String RECEIPT_TIMESTAMP = "receipt_timestamp";
/** /**
* For storage efficiency, all types are stored within a single 64-bit integer column in the * For storage efficiency, all types are stored within a single 64-bit integer column in the

Wyświetl plik

@ -126,7 +126,8 @@ public class SmsDatabase extends MessageDatabase {
REACTIONS_LAST_SEEN + " INTEGER DEFAULT -1, " + REACTIONS_LAST_SEEN + " INTEGER DEFAULT -1, " +
REMOTE_DELETED + " INTEGER DEFAULT 0, " + REMOTE_DELETED + " INTEGER DEFAULT 0, " +
NOTIFIED_TIMESTAMP + " INTEGER DEFAULT 0, " + NOTIFIED_TIMESTAMP + " INTEGER DEFAULT 0, " +
SERVER_GUID + " TEXT DEFAULT NULL);"; SERVER_GUID + " TEXT DEFAULT NULL, " +
RECEIPT_TIMESTAMP + " INTEGER DEFAULT -1);";
public static final String[] CREATE_INDEXS = { public static final String[] CREATE_INDEXS = {
"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_read_and_notified_and_thread_id_index ON " + TABLE_NAME + "(" + READ + "," + NOTIFIED + "," + THREAD_ID + ");",
@ -488,24 +489,28 @@ public class SmsDatabase extends MessageDatabase {
SQLiteDatabase database = databaseHelper.getSignalWritableDatabase(); SQLiteDatabase database = databaseHelper.getSignalWritableDatabase();
Set<ThreadUpdate> threadUpdates = new HashSet<>(); Set<ThreadUpdate> threadUpdates = new HashSet<>();
try (Cursor cursor = database.query(TABLE_NAME, new String[] {ID, THREAD_ID, RECIPIENT_ID, TYPE, DELIVERY_RECEIPT_COUNT, READ_RECEIPT_COUNT}, try (Cursor cursor = database.query(TABLE_NAME, new String[] {ID, THREAD_ID, RECIPIENT_ID, TYPE, DELIVERY_RECEIPT_COUNT, READ_RECEIPT_COUNT, RECEIPT_TIMESTAMP},
DATE_SENT + " = ?", new String[] {String.valueOf(messageId.getTimetamp())}, DATE_SENT + " = ?", new String[] {String.valueOf(messageId.getTimetamp())},
null, null, null, null)) { null, null, null, null)) {
while (cursor.moveToNext()) { while (cursor.moveToNext()) {
if (Types.isOutgoingMessageType(cursor.getLong(cursor.getColumnIndexOrThrow(TYPE)))) { if (Types.isOutgoingMessageType(CursorUtil.requireLong(cursor, TYPE))) {
RecipientId theirRecipientId = messageId.getRecipientId(); RecipientId theirRecipientId = messageId.getRecipientId();
RecipientId outRecipientId = RecipientId.from(cursor.getLong(cursor.getColumnIndexOrThrow(RECIPIENT_ID))); RecipientId outRecipientId = RecipientId.from(CursorUtil.requireLong(cursor, RECIPIENT_ID));
if (outRecipientId.equals(theirRecipientId)) { if (outRecipientId.equals(theirRecipientId)) {
long threadId = cursor.getLong(cursor.getColumnIndexOrThrow(THREAD_ID)); long id = CursorUtil.requireLong(cursor, ID);
long threadId = CursorUtil.requireLong(cursor, THREAD_ID);
String columnName = receiptType.getColumnName(); String columnName = receiptType.getColumnName();
boolean isFirstIncrement = cursor.getLong(cursor.getColumnIndexOrThrow(columnName)) == 0; boolean isFirstIncrement = CursorUtil.requireLong(cursor, columnName) == 0;
long savedTimestamp = CursorUtil.requireLong(cursor, RECEIPT_TIMESTAMP);
long updatedTimestamp = isFirstIncrement ? Math.max(savedTimestamp, timestamp) : savedTimestamp;
database.execSQL("UPDATE " + TABLE_NAME + database.execSQL("UPDATE " + TABLE_NAME +
" SET " + columnName + " = " + columnName + " + 1 WHERE " + " SET " + columnName + " = " + columnName + " + 1, " +
RECEIPT_TIMESTAMP + " = ? WHERE " +
ID + " = ?", ID + " = ?",
new String[] {String.valueOf(cursor.getLong(cursor.getColumnIndexOrThrow(ID)))}); SqlUtil.buildArgs(updatedTimestamp, id));
threadUpdates.add(new ThreadUpdate(threadId, !isFirstIncrement)); threadUpdates.add(new ThreadUpdate(threadId, !isFirstIncrement));
} }
@ -513,7 +518,7 @@ public class SmsDatabase extends MessageDatabase {
} }
if (threadUpdates.size() > 0 && receiptType == ReceiptType.DELIVERY) { if (threadUpdates.size() > 0 && receiptType == ReceiptType.DELIVERY) {
earlyDeliveryReceiptCache.increment(messageId.getTimetamp(), messageId.getRecipientId()); earlyDeliveryReceiptCache.increment(messageId.getTimetamp(), messageId.getRecipientId(), timestamp);
} }
return threadUpdates; return threadUpdates;
@ -1202,7 +1207,7 @@ public class SmsDatabase extends MessageDatabase {
else if (message.isIdentityDefault()) type |= Types.KEY_EXCHANGE_IDENTITY_DEFAULT_BIT; else if (message.isIdentityDefault()) type |= Types.KEY_EXCHANGE_IDENTITY_DEFAULT_BIT;
RecipientId recipientId = message.getRecipient().getId(); RecipientId recipientId = message.getRecipient().getId();
Map<RecipientId, Long> earlyDeliveryReceipts = earlyDeliveryReceiptCache.remove(date); Map<RecipientId, EarlyReceiptCache.Receipt> earlyDeliveryReceipts = earlyDeliveryReceiptCache.remove(date);
ContentValues contentValues = new ContentValues(6); ContentValues contentValues = new ContentValues(6);
contentValues.put(RECIPIENT_ID, recipientId.serialize()); contentValues.put(RECIPIENT_ID, recipientId.serialize());
@ -1214,7 +1219,8 @@ public class SmsDatabase extends MessageDatabase {
contentValues.put(TYPE, type); contentValues.put(TYPE, type);
contentValues.put(SUBSCRIPTION_ID, message.getSubscriptionId()); contentValues.put(SUBSCRIPTION_ID, message.getSubscriptionId());
contentValues.put(EXPIRES_IN, message.getExpiresIn()); contentValues.put(EXPIRES_IN, message.getExpiresIn());
contentValues.put(DELIVERY_RECEIPT_COUNT, Stream.of(earlyDeliveryReceipts.values()).mapToLong(Long::longValue).sum()); contentValues.put(DELIVERY_RECEIPT_COUNT, Stream.of(earlyDeliveryReceipts.values()).mapToLong(EarlyReceiptCache.Receipt::getCount).sum());
contentValues.put(RECEIPT_TIMESTAMP, Stream.of(earlyDeliveryReceipts.values()).mapToLong(EarlyReceiptCache.Receipt::getTimestamp).max().orElse(-1));
long messageId = db.insert(TABLE_NAME, null, contentValues); long messageId = db.insert(TABLE_NAME, null, contentValues);

Wyświetl plik

@ -216,8 +216,9 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper implements SignalDatab
private static final int IDENTITY_MIGRATION = 114; private static final int IDENTITY_MIGRATION = 114;
private static final int GROUP_CALL_RING_TABLE = 115; private static final int GROUP_CALL_RING_TABLE = 115;
private static final int CLEANUP_SESSION_MIGRATION = 116; private static final int CLEANUP_SESSION_MIGRATION = 116;
private static final int RECEIPT_TIMESTAMP = 117;
private static final int DATABASE_VERSION = 116; private static final int DATABASE_VERSION = 117;
private static final String DATABASE_NAME = "signal.db"; private static final String DATABASE_NAME = "signal.db";
private final Context context; private final Context context;
@ -2040,6 +2041,11 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper implements SignalDatab
Log.i(TAG, "Cleaned up " + storageCount + " storageIds."); Log.i(TAG, "Cleaned up " + storageCount + " storageIds.");
} }
if (oldVersion < RECEIPT_TIMESTAMP) {
db.execSQL("ALTER TABLE sms ADD COLUMN receipt_timestamp INTEGER DEFAULT -1");
db.execSQL("ALTER TABLE mms ADD COLUMN receipt_timestamp INTEGER DEFAULT -1");
}
db.setTransactionSuccessful(); db.setTransactionSuccessful();
} finally { } finally {
db.endTransaction(); db.endTransaction();