Improve speed of sending single messages.

This commit is contained in:
Cody Henthorne
2021-07-29 14:07:39 -04:00
committed by GitHub
parent 25234496bf
commit 9398716848
12 changed files with 141 additions and 111 deletions

View File

@@ -29,7 +29,8 @@ import java.util.Set;
public abstract class Database {
protected static final String ID_WHERE = "_id = ?";
protected static final String ID_WHERE = "_id = ?";
protected static final String[] COUNT = new String[] { "COUNT(*)" };
protected SQLCipherOpenHelper databaseHelper;
protected final Context context;
@@ -49,22 +50,14 @@ public abstract class Database {
protected void notifyConversationListeners(long threadId) {
ApplicationDependencies.getDatabaseObserver().notifyConversationListeners(threadId);
context.getContentResolver().notifyChange(DatabaseContentProviders.Conversation.getUriForThread(threadId), null);
notifyVerboseConversationListeners(threadId);
}
protected void notifyVerboseConversationListeners(Set<Long> threadIds) {
ApplicationDependencies.getDatabaseObserver().notifyVerboseConversationListeners(threadIds);
for (long threadId : threadIds) {
notifyVerboseConversationListeners(threadId);
}
}
protected void notifyVerboseConversationListeners(long threadId) {
ApplicationDependencies.getDatabaseObserver().notifyVerboseConversationListeners(threadId);
context.getContentResolver().notifyChange(DatabaseContentProviders.Conversation.getVerboseUriForThread(threadId), null);
}
protected void notifyConversationListListeners() {
@@ -77,7 +70,6 @@ public abstract class Database {
protected void notifyStickerPackListeners() {
ApplicationDependencies.getDatabaseObserver().notifyStickerPackObservers();
context.getContentResolver().notifyChange(DatabaseContentProviders.StickerPack.CONTENT_URI, null);
}
@Deprecated

View File

@@ -103,22 +103,22 @@ public final class DatabaseObserver {
notifyMapped(conversationObservers, threadId);
notifyMapped(verboseConversationObservers, threadId);
}
});
for (long threadId : threadIds) {
application.getContentResolver().notifyChange(DatabaseContentProviders.Conversation.getUriForThread(threadId), null);
application.getContentResolver().notifyChange(DatabaseContentProviders.Conversation.getVerboseUriForThread(threadId), null);
}
for (long threadId : threadIds) {
application.getContentResolver().notifyChange(DatabaseContentProviders.Conversation.getUriForThread(threadId), null);
application.getContentResolver().notifyChange(DatabaseContentProviders.Conversation.getVerboseUriForThread(threadId), null);
}
});
}
public void notifyConversationListeners(long threadId) {
executor.execute(() -> {
notifyMapped(conversationObservers, threadId);
notifyMapped(verboseConversationObservers, threadId);
});
application.getContentResolver().notifyChange(DatabaseContentProviders.Conversation.getUriForThread(threadId), null);
application.getContentResolver().notifyChange(DatabaseContentProviders.Conversation.getVerboseUriForThread(threadId), null);
application.getContentResolver().notifyChange(DatabaseContentProviders.Conversation.getUriForThread(threadId), null);
application.getContentResolver().notifyChange(DatabaseContentProviders.Conversation.getVerboseUriForThread(threadId), null);
});
}
public void notifyVerboseConversationListeners(Set<Long> threadIds) {
@@ -126,19 +126,19 @@ public final class DatabaseObserver {
for (long threadId : threadIds) {
notifyMapped(verboseConversationObservers, threadId);
}
});
for (long threadId : threadIds) {
application.getContentResolver().notifyChange(DatabaseContentProviders.Conversation.getVerboseUriForThread(threadId), null);
}
for (long threadId : threadIds) {
application.getContentResolver().notifyChange(DatabaseContentProviders.Conversation.getVerboseUriForThread(threadId), null);
}
});
}
public void notifyVerboseConversationListeners(long threadId) {
executor.execute(() -> {
notifyMapped(verboseConversationObservers, threadId);
});
application.getContentResolver().notifyChange(DatabaseContentProviders.Conversation.getVerboseUriForThread(threadId), null);
application.getContentResolver().notifyChange(DatabaseContentProviders.Conversation.getVerboseUriForThread(threadId), null);
});
}
public void notifyConversationListListeners() {
@@ -172,6 +172,8 @@ public final class DatabaseObserver {
public void notifyStickerPackObservers() {
executor.execute(() -> {
notifySet(stickerPackObservers);
application.getContentResolver().notifyChange(DatabaseContentProviders.StickerPack.CONTENT_URI, null);
});
}

View File

@@ -60,6 +60,8 @@ public abstract class MessageDatabase extends Database implements MmsSmsColumns
private static final String TAG = Log.tag(MessageDatabase.class);
protected static final String THREAD_ID_WHERE = THREAD_ID + " = ?";
public MessageDatabase(Context context, SQLCipherOpenHelper databaseHelper) {
super(context, databaseHelper);
}

View File

@@ -572,11 +572,7 @@ public class MmsDatabase extends MessageDatabase {
public int getMessageCountForThread(long threadId) {
SQLiteDatabase db = databaseHelper.getReadableDatabase();
String[] cols = new String[] {"COUNT(*)"};
String query = THREAD_ID + " = ?";
String[] args = new String[]{String.valueOf(threadId)};
try (Cursor cursor = db.query(TABLE_NAME, cols, query, args, null, null, null)) {
try (Cursor cursor = db.query(TABLE_NAME, COUNT, THREAD_ID_WHERE, SqlUtil.buildArgs(threadId), null, null, null)) {
if (cursor != null && cursor.moveToFirst()) {
return cursor.getInt(0);
}
@@ -1560,9 +1556,13 @@ public class MmsDatabase extends MessageDatabase {
for (RecipientId recipientId : earlyDeliveryReceipts.keySet()) receiptDatabase.update(recipientId, messageId, GroupReceiptDatabase.STATUS_DELIVERED, -1);
}
DatabaseFactory.getThreadDatabase(context).setLastSeen(threadId);
DatabaseFactory.getThreadDatabase(context).setHasSent(threadId, true);
ApplicationDependencies.getJobManager().add(new TrimThreadJob(threadId));
DatabaseFactory.getThreadDatabase(context).setLastSeenSilently(threadId);
DatabaseFactory.getThreadDatabase(context).setHasSentSilently(threadId, true);
notifyConversationListeners(threadId);
notifyConversationListListeners();
TrimThreadJob.enqueueAsync(threadId);
return messageId;
}

View File

@@ -65,7 +65,6 @@ import org.whispersystems.libsignal.util.guava.Optional;
import java.io.Closeable;
import java.io.IOException;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@@ -148,8 +147,8 @@ public class SmsDatabase extends MessageDatabase {
REMOTE_DELETED, NOTIFIED_TIMESTAMP
};
private final String OUTGOING_INSECURE_MESSAGE_CLAUSE = "(" + TYPE + " & " + Types.BASE_TYPE_MASK + ") = " + Types.BASE_SENT_TYPE + " AND NOT (" + TYPE + " & " + Types.SECURE_MESSAGE_BIT + ")";
private final String OUTGOING_SECURE_MESSAGE_CLAUSE = "(" + TYPE + " & " + Types.BASE_TYPE_MASK + ") = " + Types.BASE_SENT_TYPE + " AND (" + TYPE + " & " + (Types.SECURE_MESSAGE_BIT | Types.PUSH_MESSAGE_BIT) + ")";
private static final long IGNORABLE_TYPESMASK_WHEN_COUNTING = Types.END_SESSION_BIT | Types.KEY_EXCHANGE_IDENTITY_UPDATE_BIT | Types.KEY_EXCHANGE_IDENTITY_VERIFIED_BIT;
private static final String[] THREAD_SUMMARY_COUNT_PROJECTION = new String[] { "SUM(1)", "SUM(CASE WHEN " + TYPE + " & " + IGNORABLE_TYPESMASK_WHEN_COUNTING + " OR " + TYPE + " = " + Types.PROFILE_CHANGE_TYPE + " THEN 1 ELSE 0 END)" };
private static final EarlyReceiptCache earlyDeliveryReceiptCache = new EarlyReceiptCache("SmsDelivery");
@@ -178,16 +177,15 @@ public class SmsDatabase extends MessageDatabase {
}
private void updateTypeBitmask(long id, long maskOff, long maskOn) {
Log.i(TAG, "Updating ID: " + id + " to base type: " + maskOn);
SQLiteDatabase db = databaseHelper.getWritableDatabase();
db.execSQL("UPDATE " + TABLE_NAME +
" SET " + TYPE + " = (" + TYPE + " & " + (Types.TOTAL_MASK - maskOff) + " | " + maskOn + " )" +
" WHERE " + ID + " = ?", new String[] {id+""});
" WHERE " + ID + " = ?", SqlUtil.buildArgs(id));
long threadId = getThreadIdForMessage(id);
DatabaseFactory.getThreadDatabase(context).updateSnippetTypeSilently(threadId);
DatabaseFactory.getThreadDatabase(context).update(threadId, false);
notifyConversationListListeners();
notifyConversationListeners(threadId);
}
@@ -231,17 +229,15 @@ public class SmsDatabase extends MessageDatabase {
}
@Override
public int getMessageCountForThreadSummary(long threadId) {
SQLiteDatabase db = databaseHelper.getReadableDatabase();
SqlUtil.Query query = buildMeaningfulMessagesQuery(threadId);
String[] cols = { "COUNT(*)" };
int getMessageCountForThreadSummary(long threadId) {
SQLiteDatabase db = databaseHelper.getReadableDatabase();
try (Cursor cursor = db.query(TABLE_NAME, cols, query.getWhere(), query.getWhereArgs(), null, null, null)) {
if (cursor != null && cursor.moveToFirst()) {
int count = cursor.getInt(0);
if (count > 0) {
return getMessageCountForThread(threadId);
}
try (Cursor cursor = db.query(TABLE_NAME, THREAD_SUMMARY_COUNT_PROJECTION, THREAD_ID_WHERE, SqlUtil.buildArgs(threadId), null, null, null)) {
if (cursor.moveToFirst()) {
int allMessagesCount = cursor.getInt(0);
int ignorableMessagesCount = cursor.getInt(1);
return allMessagesCount == ignorableMessagesCount ? 0 : allMessagesCount;
}
}
@@ -252,11 +248,7 @@ public class SmsDatabase extends MessageDatabase {
public int getMessageCountForThread(long threadId) {
SQLiteDatabase db = databaseHelper.getReadableDatabase();
String[] cols = new String[] {"COUNT(*)"};
String query = THREAD_ID + " = ?";
String[] args = new String[]{String.valueOf(threadId)};
try (Cursor cursor = db.query(TABLE_NAME, cols, query, args, null, null, null)) {
try (Cursor cursor = db.query(TABLE_NAME, COUNT, THREAD_ID_WHERE, SqlUtil.buildArgs(threadId), null, null, null)) {
if (cursor != null && cursor.moveToFirst()) {
return cursor.getInt(0);
}
@@ -294,8 +286,7 @@ public class SmsDatabase extends MessageDatabase {
private @NonNull SqlUtil.Query buildMeaningfulMessagesQuery(long threadId) {
String query = THREAD_ID + " = ? AND (NOT " + TYPE + " & ? AND TYPE != ?)";
long type = Types.END_SESSION_BIT | Types.KEY_EXCHANGE_IDENTITY_UPDATE_BIT | Types.KEY_EXCHANGE_IDENTITY_VERIFIED_BIT;
return SqlUtil.buildQuery(query, threadId, type, Types.PROFILE_CHANGE_TYPE);
return SqlUtil.buildQuery(query, threadId, IGNORABLE_TYPESMASK_WHEN_COUNTING, Types.PROFILE_CHANGE_TYPE);
}
@Override
@@ -1256,16 +1247,17 @@ public class SmsDatabase extends MessageDatabase {
}
if (!message.isIdentityVerified() && !message.isIdentityDefault()) {
DatabaseFactory.getThreadDatabase(context).update(threadId, true);
DatabaseFactory.getThreadDatabase(context).setLastSeen(threadId);
DatabaseFactory.getThreadDatabase(context).updateSilently(threadId, true);
DatabaseFactory.getThreadDatabase(context).setLastSeenSilently(threadId);
}
DatabaseFactory.getThreadDatabase(context).setHasSent(threadId, true);
DatabaseFactory.getThreadDatabase(context).setHasSentSilently(threadId, true);
notifyConversationListeners(threadId);
notifyConversationListListeners();
if (!message.isIdentityVerified() && !message.isIdentityDefault()) {
ApplicationDependencies.getJobManager().add(new TrimThreadJob(threadId));
TrimThreadJob.enqueueAsync(threadId);
}
return messageId;
@@ -1578,8 +1570,8 @@ public class SmsDatabase extends MessageDatabase {
return new Reader(cursor);
}
public static OutgoingMessageReader readerFor(OutgoingTextMessage message, long threadId) {
return new OutgoingMessageReader(message, threadId);
public static OutgoingMessageReader readerFor(OutgoingTextMessage message, long threadId, long messageId) {
return new OutgoingMessageReader(message, threadId, messageId);
}
public static class OutgoingMessageReader {
@@ -1588,10 +1580,10 @@ public class SmsDatabase extends MessageDatabase {
private final long id;
private final long threadId;
public OutgoingMessageReader(OutgoingTextMessage message, long threadId) {
public OutgoingMessageReader(OutgoingTextMessage message, long threadId, long messageId) {
this.message = message;
this.threadId = threadId;
this.id = new SecureRandom().nextLong();
this.id = messageId;
}
public MessageRecord getCurrent() {

View File

@@ -154,7 +154,7 @@ public class ThreadDatabase extends Database {
Stream.of(GroupDatabase.TYPED_GROUP_PROJECTION))
.toList();
private static final String ORDER_BY_DEFAULT = TABLE_NAME + "." + DATE + " DESC";
private static final String[] RECIPIENT_ID_PROJECTION = new String[] { RECIPIENT_ID };
public ThreadDatabase(Context context, SQLCipherOpenHelper databaseHelper) {
super(context, databaseHelper);
@@ -212,12 +212,9 @@ public class ThreadDatabase extends Database {
contentValues.put(READ_RECEIPT_COUNT, readReceiptCount);
contentValues.put(EXPIRES_IN, expiresIn);
if (count != getConversationMessageCount(threadId)) {
contentValues.put(LAST_SCROLLED, 0);
}
SQLiteDatabase db = databaseHelper.getWritableDatabase();
db.update(TABLE_NAME, contentValues, ID + " = ?", new String[] {threadId + ""});
db.execSQL("UPDATE " + TABLE_NAME + " SET " + LAST_SCROLLED + " = CASE WHEN " + MESSAGE_COUNT + " = ? THEN " + LAST_SCROLLED + " ELSE 0 END WHERE " + ID_WHERE, SqlUtil.buildArgs(count, threadId));
db.update(TABLE_NAME, contentValues, ID_WHERE, SqlUtil.buildArgs(threadId));
if (unarchive) {
ContentValues archiveValues = new ContentValues();
@@ -877,12 +874,16 @@ public class ThreadDatabase extends Database {
}
public void setLastSeen(long threadId) {
setLastSeenSilently(threadId);
notifyConversationListListeners();
}
void setLastSeenSilently(long threadId) {
SQLiteDatabase db = databaseHelper.getWritableDatabase();
ContentValues contentValues = new ContentValues(1);
contentValues.put(LAST_SEEN, System.currentTimeMillis());
db.update(TABLE_NAME, contentValues, ID_WHERE, new String[] {String.valueOf(threadId)});
notifyConversationListListeners();
}
public void setLastScrolled(long threadId, long lastScrolledTimestamp) {
@@ -1069,7 +1070,7 @@ public class ThreadDatabase extends Database {
public @Nullable RecipientId getRecipientIdForThreadId(long threadId) {
SQLiteDatabase db = databaseHelper.getReadableDatabase();
try (Cursor cursor = db.query(TABLE_NAME, null, ID + " = ?", new String[]{ threadId + "" }, null, null, null)) {
try (Cursor cursor = db.query(TABLE_NAME, RECIPIENT_ID_PROJECTION, ID_WHERE, SqlUtil.buildArgs(threadId), null, null, null)) {
if (cursor != null && cursor.moveToFirst()) {
return RecipientId.from(cursor.getLong(cursor.getColumnIndexOrThrow(RECIPIENT_ID)));
@@ -1103,14 +1104,12 @@ public class ThreadDatabase extends Database {
return getThreadIdIfExistsFor(recipientId) > -1;
}
public void setHasSent(long threadId, boolean hasSent) {
public void setHasSentSilently(long threadId, boolean hasSent) {
ContentValues contentValues = new ContentValues(1);
contentValues.put(HAS_SENT, hasSent ? 1 : 0);
databaseHelper.getWritableDatabase().update(TABLE_NAME, contentValues, ID_WHERE,
new String[] {String.valueOf(threadId)});
notifyConversationListeners(threadId);
}
void updateReadState(long threadId) {
@@ -1222,10 +1221,18 @@ public class ThreadDatabase extends Database {
}
public boolean update(long threadId, boolean unarchive) {
return update(threadId, unarchive, true);
return update(threadId, unarchive, true, true);
}
boolean updateSilently(long threadId, boolean unarchive) {
return update(threadId, unarchive, true, false);
}
public boolean update(long threadId, boolean unarchive, boolean allowDeletion) {
return update(threadId, unarchive, allowDeletion, true);
}
private boolean update(long threadId, boolean unarchive, boolean allowDeletion, boolean notifyListeners) {
MmsSmsDatabase mmsSmsDatabase = DatabaseFactory.getMmsSmsDatabase(context);
long count = mmsSmsDatabase.getConversationCountForThreadSummary(threadId);
@@ -1243,11 +1250,22 @@ public class ThreadDatabase extends Database {
MessageRecord record;
if (reader != null && (record = reader.getNext()) != null) {
updateThread(threadId, count, ThreadBodyUtil.getFormattedBodyFor(context, record), getAttachmentUriFor(record),
getContentTypeFor(record), getExtrasFor(record),
record.getTimestamp(), record.getDeliveryStatus(), record.getDeliveryReceiptCount(),
record.getType(), unarchive, record.getExpiresIn(), record.getReadReceiptCount());
notifyConversationListListeners();
updateThread(threadId,
count,
ThreadBodyUtil.getFormattedBodyFor(context, record),
getAttachmentUriFor(record),
getContentTypeFor(record),
getExtrasFor(record),
record.getTimestamp(),
record.getDeliveryStatus(),
record.getDeliveryReceiptCount(),
record.getType(),
unarchive,
record.getExpiresIn(),
record.getReadReceiptCount());
if (notifyListeners) {
notifyConversationListListeners();
}
return false;
} else {
deleteConversation(threadId);
@@ -1259,6 +1277,21 @@ public class ThreadDatabase extends Database {
}
}
public void updateSnippetTypeSilently(long threadId) {
MmsSmsDatabase mmsSmsDatabase = DatabaseFactory.getMmsSmsDatabase(context);
try (MmsSmsDatabase.Reader reader = MmsSmsDatabase.readerFor(mmsSmsDatabase.getConversationSnippet(threadId))) {
MessageRecord record = reader.getNext();
if (record != null) {
ContentValues contentValues = new ContentValues(1);
contentValues.put(SNIPPET_TYPE, record.getType());
databaseHelper.getWritableDatabase().update(TABLE_NAME, contentValues, ID_WHERE, SqlUtil.buildArgs(threadId));
}
}
}
public @NonNull ThreadRecord getThreadRecordFor(@NonNull Recipient recipient) {
return Objects.requireNonNull(getThreadRecord(getOrCreateThreadIdFor(recipient)));
}
@@ -1379,55 +1412,52 @@ public class ThreadDatabase extends Database {
}
private @Nullable Extra getExtrasFor(@NonNull MessageRecord record) {
boolean messageRequestAccepted = RecipientUtil.isMessageRequestAccepted(context, record.getThreadId());
RecipientId threadRecipientId = getRecipientIdForThreadId(record.getThreadId());
RecipientId individualRecipient = record.getIndividualRecipient().getId();
Recipient threadRecipient = record.isOutgoing() ? record.getRecipient() : getRecipientForThreadId(record.getThreadId());
boolean messageRequestAccepted = RecipientUtil.isMessageRequestAccepted(context, record.getThreadId(), threadRecipient);
RecipientId individualRecipientId = record.getIndividualRecipient().getId();
if (!messageRequestAccepted && threadRecipientId != null) {
Recipient resolved = Recipient.resolved(threadRecipientId);
if (resolved.isPushGroup()) {
if (resolved.isPushV2Group()) {
//noinspection ConstantConditions
if (!messageRequestAccepted && threadRecipient != null) {
if (threadRecipient.isPushGroup()) {
if (threadRecipient.isPushV2Group()) {
MessageRecord.InviteAddState inviteAddState = record.getGv2AddInviteState();
if (inviteAddState != null) {
RecipientId from = RecipientId.from(inviteAddState.getAddedOrInvitedBy(), null);
if (inviteAddState.isInvited()) {
Log.i(TAG, "GV2 invite message request from " + from);
return Extra.forGroupV2invite(from, individualRecipient);
return Extra.forGroupV2invite(from, individualRecipientId);
} else {
Log.i(TAG, "GV2 message request from " + from);
return Extra.forGroupMessageRequest(from, individualRecipient);
return Extra.forGroupMessageRequest(from, individualRecipientId);
}
}
Log.w(TAG, "Falling back to unknown message request state for GV2 message");
return Extra.forMessageRequest(individualRecipient);
return Extra.forMessageRequest(individualRecipientId);
} else {
RecipientId recipientId = DatabaseFactory.getMmsSmsDatabase(context).getGroupAddedBy(record.getThreadId());
if (recipientId != null) {
return Extra.forGroupMessageRequest(recipientId, individualRecipient);
return Extra.forGroupMessageRequest(recipientId, individualRecipientId);
}
}
}
return Extra.forMessageRequest(individualRecipient);
return Extra.forMessageRequest(individualRecipientId);
}
if (record.isRemoteDelete()) {
return Extra.forRemoteDelete(individualRecipient);
return Extra.forRemoteDelete(individualRecipientId);
} else if (record.isViewOnce()) {
return Extra.forViewOnce(individualRecipient);
return Extra.forViewOnce(individualRecipientId);
} else if (record.isMms() && ((MmsMessageRecord) record).getSlideDeck().getStickerSlide() != null) {
StickerSlide slide = Objects.requireNonNull(((MmsMessageRecord) record).getSlideDeck().getStickerSlide());
return Extra.forSticker(slide.getEmoji(), individualRecipient);
return Extra.forSticker(slide.getEmoji(), individualRecipientId);
} else if (record.isMms() && ((MmsMessageRecord) record).getSlideDeck().getSlides().size() > 1) {
return Extra.forAlbum(individualRecipient);
return Extra.forAlbum(individualRecipientId);
}
if (threadRecipientId != null) {
Recipient resolved = Recipient.resolved(threadRecipientId);
if (resolved.isGroup()) {
return Extra.forDefault(individualRecipient);
}
if (threadRecipient != null && threadRecipient.isGroup()) {
return Extra.forDefault(individualRecipientId);
}
return null;