Separate thread updates into a job and other perf improvements.

This commit is contained in:
Greyson Parrelli 2021-08-24 11:11:48 -04:00 committed by Alex Hart
parent db7272730e
commit 665d9e31f6
19 changed files with 268 additions and 110 deletions

View file

@ -36,6 +36,9 @@ import org.thoughtcrime.securesms.migrations.LegacyMigrationJob;
import org.thoughtcrime.securesms.util.SqlUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import java.io.Closeable;
import java.io.IOException;
public class DatabaseFactory {
private static final Object lock = new Object();
@ -311,4 +314,17 @@ public class DatabaseFactory {
public boolean hasTable(String table) {
return SqlUtil.tableExists(databaseHelper.getRawReadableDatabase(), table);
}
public @NonNull Transaction transaction() {
getRawDatabase().beginTransaction();
return () -> {
getRawDatabase().setTransactionSuccessful();
getRawDatabase().endTransaction();
};
}
public interface Transaction extends Closeable {
@Override
void close();
}
}

View file

@ -102,7 +102,6 @@ public abstract class MessageDatabase extends Database implements MmsSmsColumns
public abstract void markExpireStarted(Collection<Long> messageId, long startTime);
public abstract void markAsEndSession(long id);
public abstract void markAsPreKeyBundle(long id);
public abstract void markAsInvalidVersionKeyExchange(long id);
public abstract void markAsSecure(long id);
public abstract void markAsInsecure(long id);
@ -111,7 +110,6 @@ public abstract class MessageDatabase extends Database implements MmsSmsColumns
public abstract void markAsRateLimited(long id);
public abstract void clearRateLimitStatus(Collection<Long> ids);
public abstract void markAsDecryptFailed(long id);
public abstract void markAsDecryptDuplicate(long id);
public abstract void markAsNoSession(long id);
public abstract void markAsUnsupportedProtocolVersion(long id);
public abstract void markAsInvalidMessage(long id);
@ -119,8 +117,8 @@ public abstract class MessageDatabase extends Database implements MmsSmsColumns
public abstract void markAsOutbox(long id);
public abstract void markAsPendingInsecureSmsFallback(long id);
public abstract void markAsSent(long messageId, boolean secure);
public abstract void markAsSentFailed(long id);
public abstract void markUnidentified(long messageId, boolean unidentified);
public abstract void markAsSentFailed(long id);
public abstract void markAsSending(long messageId);
public abstract void markAsRemoteDelete(long messageId);
public abstract void markAsMissedCall(long id, boolean isVideoOffer);

View file

@ -56,6 +56,7 @@ import org.thoughtcrime.securesms.database.model.SmsMessageRecord;
import org.thoughtcrime.securesms.database.model.databaseprotos.BodyRangeList;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.groups.GroupMigrationMembershipChange;
import org.thoughtcrime.securesms.jobs.ThreadUpdateJob;
import org.thoughtcrime.securesms.jobs.TrimThreadJob;
import org.thoughtcrime.securesms.linkpreview.LinkPreview;
import org.thoughtcrime.securesms.mms.IncomingMediaMessage;
@ -296,11 +297,6 @@ public class MmsDatabase extends MessageDatabase {
throw new UnsupportedOperationException();
}
@Override
public void markAsPreKeyBundle(long id) {
throw new UnsupportedOperationException();
}
@Override
public void markAsInvalidVersionKeyExchange(long id) {
throw new UnsupportedOperationException();
@ -321,11 +317,6 @@ public class MmsDatabase extends MessageDatabase {
throw new UnsupportedOperationException();
}
@Override
public void markAsDecryptDuplicate(long id) {
throw new UnsupportedOperationException();
}
@Override
public void markAsNoSession(long id) {
throw new UnsupportedOperationException();
@ -759,7 +750,7 @@ public class MmsDatabase extends MessageDatabase {
" WHERE " + ID + " = ?", new String[] {id + ""});
if (threadId.isPresent()) {
DatabaseFactory.getThreadDatabase(context).update(threadId.get(), false);
DatabaseFactory.getThreadDatabase(context).updateSnippetTypeSilently(threadId.get());
}
}
@ -1352,7 +1343,7 @@ public class MmsDatabase extends MessageDatabase {
if (!Types.isExpirationTimerUpdate(mailbox)) {
DatabaseFactory.getThreadDatabase(context).incrementUnread(threadId, 1);
DatabaseFactory.getThreadDatabase(context).update(threadId, true);
ThreadUpdateJob.enqueue(threadId);
}
notifyConversationListeners(threadId);
@ -1445,12 +1436,13 @@ public class MmsDatabase extends MessageDatabase {
@Override
public void markIncomingNotificationReceived(long threadId) {
notifyConversationListeners(threadId);
DatabaseFactory.getThreadDatabase(context).update(threadId, true);
if (org.thoughtcrime.securesms.util.Util.isDefaultSmsProvider(context)) {
DatabaseFactory.getThreadDatabase(context).incrementUnread(threadId, 1);
}
ThreadUpdateJob.enqueue(threadId);
TrimThreadJob.enqueueAsync(threadId);
}
@ -1658,9 +1650,11 @@ public class MmsDatabase extends MessageDatabase {
insertListener.onComplete();
}
notifyConversationListeners(contentValues.getAsLong(THREAD_ID));
DatabaseFactory.getThreadDatabase(context).setLastScrolled(contentValues.getAsLong(THREAD_ID), 0);
DatabaseFactory.getThreadDatabase(context).update(contentValues.getAsLong(THREAD_ID), true);
long contentValuesThreadId = contentValues.getAsLong(THREAD_ID);
notifyConversationListeners(contentValuesThreadId);
DatabaseFactory.getThreadDatabase(context).setLastScrolled(contentValuesThreadId, 0);
ThreadUpdateJob.enqueue(contentValuesThreadId);
}
}

View file

@ -44,6 +44,7 @@ import org.thoughtcrime.securesms.database.model.databaseprotos.GroupCallUpdateD
import org.thoughtcrime.securesms.database.model.databaseprotos.ProfileChangeDetails;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.groups.GroupMigrationMembershipChange;
import org.thoughtcrime.securesms.jobs.ThreadUpdateJob;
import org.thoughtcrime.securesms.jobs.TrimThreadJob;
import org.thoughtcrime.securesms.mms.IncomingMediaMessage;
import org.thoughtcrime.securesms.mms.MmsException;
@ -58,6 +59,7 @@ import org.thoughtcrime.securesms.util.Base64;
import org.thoughtcrime.securesms.util.CursorUtil;
import org.thoughtcrime.securesms.util.JsonUtils;
import org.thoughtcrime.securesms.util.SqlUtil;
import org.thoughtcrime.securesms.util.Stopwatch;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.Util;
import org.whispersystems.libsignal.util.Pair;
@ -177,14 +179,24 @@ public class SmsDatabase extends MessageDatabase {
private void updateTypeBitmask(long id, long maskOff, long maskOn) {
SQLiteDatabase db = databaseHelper.getSignalWritableDatabase();
db.execSQL("UPDATE " + TABLE_NAME +
" SET " + TYPE + " = (" + TYPE + " & " + (Types.TOTAL_MASK - maskOff) + " | " + maskOn + " )" +
" WHERE " + ID + " = ?", SqlUtil.buildArgs(id));
long threadId = getThreadIdForMessage(id);
DatabaseFactory.getThreadDatabase(context).updateSnippetTypeSilently(threadId);
long threadId;
db.beginTransaction();
try {
db.execSQL("UPDATE " + TABLE_NAME +
" SET " + TYPE + " = (" + TYPE + " & " + (Types.TOTAL_MASK - maskOff) + " | " + maskOn + " )" +
" WHERE " + ID + " = ?", SqlUtil.buildArgs(id));
threadId = getThreadIdForMessage(id);
DatabaseFactory.getThreadDatabase(context).updateSnippetTypeSilently(threadId);
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
notifyConversationListListeners();
notifyConversationListeners(threadId);
}
@ -267,11 +279,6 @@ public class SmsDatabase extends MessageDatabase {
updateTypeBitmask(id, Types.KEY_EXCHANGE_MASK, Types.END_SESSION_BIT);
}
@Override
public void markAsPreKeyBundle(long id) {
updateTypeBitmask(id, Types.KEY_EXCHANGE_MASK, Types.KEY_EXCHANGE_BIT | Types.KEY_EXCHANGE_BUNDLE_BIT);
}
@Override
public void markAsInvalidVersionKeyExchange(long id) {
updateTypeBitmask(id, 0, Types.KEY_EXCHANGE_INVALID_VERSION_BIT);
@ -323,11 +330,6 @@ public class SmsDatabase extends MessageDatabase {
updateTypeBitmask(id, Types.ENCRYPTION_MASK, Types.ENCRYPTION_REMOTE_FAILED_BIT);
}
@Override
public void markAsDecryptDuplicate(long id) {
updateTypeBitmask(id, Types.ENCRYPTION_MASK, Types.ENCRYPTION_REMOTE_DUPLICATE_BIT);
}
@Override
public void markAsNoSession(long id) {
updateTypeBitmask(id, Types.ENCRYPTION_MASK, Types.ENCRYPTION_REMOTE_NO_SESSION_BIT);
@ -656,9 +658,8 @@ public class SmsDatabase extends MessageDatabase {
long threadId = getThreadIdForMessage(messageId);
DatabaseFactory.getThreadDatabase(context).update(threadId, true);
ThreadUpdateJob.enqueue(threadId);
notifyConversationListeners(threadId);
notifyConversationListListeners();
return new InsertResult(messageId, threadId);
}
@ -741,7 +742,7 @@ public class SmsDatabase extends MessageDatabase {
DatabaseFactory.getThreadDatabase(context).incrementUnread(threadId, 1);
}
DatabaseFactory.getThreadDatabase(context).update(threadId, true);
ThreadUpdateJob.enqueue(threadId);
db.setTransactionSuccessful();
} finally {
@ -818,7 +819,7 @@ public class SmsDatabase extends MessageDatabase {
DatabaseFactory.getThreadDatabase(context).incrementUnread(threadId, 1);
}
DatabaseFactory.getThreadDatabase(context).update(threadId, true);
ThreadUpdateJob.enqueue(threadId);
db.setTransactionSuccessful();
} finally {
@ -883,14 +884,15 @@ public class SmsDatabase extends MessageDatabase {
values.put(TYPE, type);
values.put(THREAD_ID, threadId);
SQLiteDatabase db = databaseHelper.getSignalWritableDatabase();
long messageId = db.insert(TABLE_NAME, null, values);
SQLiteDatabase db = databaseHelper.getSignalWritableDatabase();
long messageId = db.insert(TABLE_NAME, null, values);
DatabaseFactory.getThreadDatabase(context).update(threadId, true);
if (unread) {
DatabaseFactory.getThreadDatabase(context).incrementUnread(threadId, 1);
}
ThreadUpdateJob.enqueue(threadId);
notifyConversationListeners(threadId);
TrimThreadJob.enqueueAsync(threadId);
@ -1109,7 +1111,7 @@ public class SmsDatabase extends MessageDatabase {
}
if (!silent) {
DatabaseFactory.getThreadDatabase(context).update(threadId, true);
ThreadUpdateJob.enqueue(threadId);
}
if (message.getSubscriptionId() != -1) {
@ -1152,7 +1154,7 @@ public class SmsDatabase extends MessageDatabase {
long messageId = db.insert(TABLE_NAME, null, values);
DatabaseFactory.getThreadDatabase(context).incrementUnread(threadId, 1);
DatabaseFactory.getThreadDatabase(context).update(threadId, true);
ThreadUpdateJob.enqueue(threadId);
notifyConversationListeners(threadId);
@ -1176,7 +1178,7 @@ public class SmsDatabase extends MessageDatabase {
databaseHelper.getSignalWritableDatabase().insert(TABLE_NAME, null, values);
DatabaseFactory.getThreadDatabase(context).incrementUnread(threadId, 1);
DatabaseFactory.getThreadDatabase(context).update(threadId, true);
ThreadUpdateJob.enqueue(threadId);
notifyConversationListeners(threadId);
@ -1226,7 +1228,6 @@ public class SmsDatabase extends MessageDatabase {
if (!message.isIdentityVerified() && !message.isIdentityDefault()) {
DatabaseFactory.getThreadDatabase(context).setLastScrolled(threadId, 0);
DatabaseFactory.getThreadDatabase(context).updateSilently(threadId, true);
DatabaseFactory.getThreadDatabase(context).setLastSeenSilently(threadId);
}
@ -1237,7 +1238,6 @@ public class SmsDatabase extends MessageDatabase {
}
notifyConversationListeners(threadId);
notifyConversationListListeners();
if (!message.isIdentityVerified() && !message.isIdentityDefault()) {
TrimThreadJob.enqueueAsync(threadId);

View file

@ -224,8 +224,6 @@ public class ThreadDatabase extends Database {
StorageSyncHelper.scheduleSyncForDataChange();
}
}
notifyConversationListListeners();
}
public void updateSnippet(long threadId, String snippet, @Nullable Uri attachment, long date, long type, boolean unarchive) {

View file

@ -13,6 +13,7 @@ import androidx.annotation.RequiresApi;
import com.annimon.stream.Collectors;
import com.annimon.stream.Stream;
import org.signal.core.util.concurrent.SignalExecutors;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
@ -33,32 +34,34 @@ public final class JobSchedulerScheduler implements Scheduler {
@RequiresApi(26)
@Override
public void schedule(long delay, @NonNull List<Constraint> constraints) {
JobScheduler jobScheduler = application.getSystemService(JobScheduler.class);
SignalExecutors.BOUNDED.execute(() -> {
JobScheduler jobScheduler = application.getSystemService(JobScheduler.class);
String constraintNames = constraints.isEmpty() ? ""
: Stream.of(constraints)
.map(Constraint::getJobSchedulerKeyPart)
.withoutNulls()
.sorted()
.collect(Collectors.joining("-"));
String constraintNames = constraints.isEmpty() ? ""
: Stream.of(constraints)
.map(Constraint::getJobSchedulerKeyPart)
.withoutNulls()
.sorted()
.collect(Collectors.joining("-"));
int jobId = constraintNames.hashCode();
int jobId = constraintNames.hashCode();
if (jobScheduler.getPendingJob(jobId) != null) {
return;
}
if (jobScheduler.getPendingJob(jobId) != null) {
return;
}
Log.i(TAG, String.format(Locale.US, "JobScheduler enqueue of %s (%d)", constraintNames, jobId));
Log.i(TAG, String.format(Locale.US, "JobScheduler enqueue of %s (%d)", constraintNames, jobId));
JobInfo.Builder jobInfoBuilder = new JobInfo.Builder(jobId, new ComponentName(application, SystemService.class))
.setMinimumLatency(delay)
.setPersisted(true);
JobInfo.Builder jobInfoBuilder = new JobInfo.Builder(jobId, new ComponentName(application, SystemService.class))
.setMinimumLatency(delay)
.setPersisted(true);
for (Constraint constraint : constraints) {
constraint.applyToJobInfo(jobInfoBuilder);
}
for (Constraint constraint : constraints) {
constraint.applyToJobInfo(jobInfoBuilder);
}
jobScheduler.schedule(jobInfoBuilder.build());
jobScheduler.schedule(jobInfoBuilder.build());
});
}
@RequiresApi(api = 26)

View file

@ -162,6 +162,7 @@ public final class JobManagerFactories {
put(StorageForcePushJob.KEY, new StorageForcePushJob.Factory());
put(StorageSyncJob.KEY, new StorageSyncJob.Factory());
put(SubmitRateLimitPushChallengeJob.KEY, new SubmitRateLimitPushChallengeJob.Factory());
put(ThreadUpdateJob.KEY, new ThreadUpdateJob.Factory());
put(TrimThreadJob.KEY, new TrimThreadJob.Factory());
put(TypingSendJob.KEY, new TypingSendJob.Factory());
put(UpdateApkJob.KEY, new UpdateApkJob.Factory());

View file

@ -16,6 +16,7 @@ import org.thoughtcrime.securesms.recipients.RecipientUtil;
import org.thoughtcrime.securesms.transport.RetryLaterException;
import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
import org.whispersystems.signalservice.api.SignalServiceMessageSender.IndividualSendEvents;
import org.whispersystems.signalservice.api.crypto.ContentHint;
import org.whispersystems.signalservice.api.crypto.UnidentifiedAccessPair;
import org.whispersystems.signalservice.api.messages.SendMessageResult;
@ -109,7 +110,7 @@ public final class PaymentNotificationSendJob extends BaseJob {
.withPayment(new SignalServiceDataMessage.Payment(new SignalServiceDataMessage.PaymentNotification(payment.getReceipt(), payment.getNote())))
.build();
SendMessageResult sendMessageResult = messageSender.sendDataMessage(address, unidentifiedAccess, ContentHint.DEFAULT, dataMessage);
SendMessageResult sendMessageResult = messageSender.sendDataMessage(address, unidentifiedAccess, ContentHint.DEFAULT, dataMessage, IndividualSendEvents.EMPTY);
if (sendMessageResult.getIdentityFailure() != null) {
Log.w(TAG, "Identity failure for " + recipient.getId());

View file

@ -20,6 +20,7 @@ import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.recipients.RecipientUtil;
import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
import org.whispersystems.signalservice.api.SignalServiceMessageSender.IndividualSendEvents;
import org.whispersystems.signalservice.api.crypto.ContentHint;
import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException;
import org.whispersystems.signalservice.api.messages.SignalServiceAttachment;
@ -137,7 +138,8 @@ public class PushGroupUpdateJob extends BaseJob {
messageSender.sendDataMessage(RecipientUtil.toSignalServiceAddress(context, sourceRecipient),
UnidentifiedAccessUtil.getAccessFor(context, sourceRecipient),
ContentHint.DEFAULT,
message);
message,
IndividualSendEvents.EMPTY);
}
@Override

View file

@ -33,6 +33,7 @@ import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.Util;
import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
import org.whispersystems.signalservice.api.SignalServiceMessageSender.IndividualSendEvents;
import org.whispersystems.signalservice.api.crypto.ContentHint;
import org.whispersystems.signalservice.api.crypto.UnidentifiedAccessPair;
import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException;
@ -233,7 +234,7 @@ public class PushMediaSendJob extends PushSendJob {
DatabaseFactory.getMessageLogDatabase(context).insertIfPossible(messageRecipient.getId(), message.getSentTimeMillis(), result, ContentHint.RESENDABLE, new MessageId(messageId, true));
return syncAccess.isPresent();
} else {
SendMessageResult result = messageSender.sendDataMessage(address, UnidentifiedAccessUtil.getAccessFor(context, messageRecipient), ContentHint.RESENDABLE, mediaMessage);
SendMessageResult result = messageSender.sendDataMessage(address, UnidentifiedAccessUtil.getAccessFor(context, messageRecipient), ContentHint.RESENDABLE, mediaMessage, IndividualSendEvents.EMPTY);
DatabaseFactory.getMessageLogDatabase(context).insertIfPossible(messageRecipient.getId(), message.getSentTimeMillis(), result, ContentHint.RESENDABLE, new MessageId(messageId, true));
return result.getSuccess().isUnidentified();
}

View file

@ -47,7 +47,7 @@ public class PushTextSendJob extends PushSendJob {
private static final String KEY_MESSAGE_ID = "message_id";
private long messageId;
private final long messageId;
public PushTextSendJob(long messageId, @NonNull Recipient recipient) {
this(constructParameters(recipient, false), messageId);
@ -91,35 +91,37 @@ public class PushTextSendJob extends PushSendJob {
RecipientUtil.shareProfileIfFirstSecureMessage(context, record.getRecipient());
Recipient recipient = record.getRecipient().fresh();
Recipient recipient = record.getRecipient().resolve();
byte[] profileKey = recipient.getProfileKey();
UnidentifiedAccessMode accessMode = recipient.getUnidentifiedAccessMode();
boolean unidentified = deliver(record);
database.markAsSent(messageId, true);
database.markUnidentified(messageId, unidentified);
try (DatabaseFactory.Transaction unused = DatabaseFactory.getInstance(context).transaction()) {
database.markAsSent(messageId, true);
database.markUnidentified(messageId, unidentified);
if (recipient.isSelf()) {
SyncMessageId id = new SyncMessageId(recipient.getId(), record.getDateSent());
DatabaseFactory.getMmsSmsDatabase(context).incrementDeliveryReceiptCount(id, System.currentTimeMillis());
DatabaseFactory.getMmsSmsDatabase(context).incrementReadReceiptCount(id, System.currentTimeMillis());
}
if (recipient.isSelf()) {
SyncMessageId id = new SyncMessageId(recipient.getId(), record.getDateSent());
DatabaseFactory.getMmsSmsDatabase(context).incrementDeliveryReceiptCount(id, System.currentTimeMillis());
DatabaseFactory.getMmsSmsDatabase(context).incrementReadReceiptCount(id, System.currentTimeMillis());
}
if (unidentified && accessMode == UnidentifiedAccessMode.UNKNOWN && profileKey == null) {
log(TAG, String.valueOf(record.getDateSent()), "Marking recipient as UD-unrestricted following a UD send.");
DatabaseFactory.getRecipientDatabase(context).setUnidentifiedAccessMode(recipient.getId(), UnidentifiedAccessMode.UNRESTRICTED);
} else if (unidentified && accessMode == UnidentifiedAccessMode.UNKNOWN) {
log(TAG, String.valueOf(record.getDateSent()), "Marking recipient as UD-enabled following a UD send.");
DatabaseFactory.getRecipientDatabase(context).setUnidentifiedAccessMode(recipient.getId(), UnidentifiedAccessMode.ENABLED);
} else if (!unidentified && accessMode != UnidentifiedAccessMode.DISABLED) {
log(TAG, String.valueOf(record.getDateSent()), "Marking recipient as UD-disabled following a non-UD send.");
DatabaseFactory.getRecipientDatabase(context).setUnidentifiedAccessMode(recipient.getId(), UnidentifiedAccessMode.DISABLED);
}
if (unidentified && accessMode == UnidentifiedAccessMode.UNKNOWN && profileKey == null) {
log(TAG, String.valueOf(record.getDateSent()), "Marking recipient as UD-unrestricted following a UD send.");
DatabaseFactory.getRecipientDatabase(context).setUnidentifiedAccessMode(recipient.getId(), UnidentifiedAccessMode.UNRESTRICTED);
} else if (unidentified && accessMode == UnidentifiedAccessMode.UNKNOWN) {
log(TAG, String.valueOf(record.getDateSent()), "Marking recipient as UD-enabled following a UD send.");
DatabaseFactory.getRecipientDatabase(context).setUnidentifiedAccessMode(recipient.getId(), UnidentifiedAccessMode.ENABLED);
} else if (!unidentified && accessMode != UnidentifiedAccessMode.DISABLED) {
log(TAG, String.valueOf(record.getDateSent()), "Marking recipient as UD-disabled following a non-UD send.");
DatabaseFactory.getRecipientDatabase(context).setUnidentifiedAccessMode(recipient.getId(), UnidentifiedAccessMode.DISABLED);
}
if (record.getExpiresIn() > 0) {
database.markExpireStarted(messageId);
expirationManager.scheduleDeletion(record.getId(), record.isMms(), record.getExpiresIn());
if (record.getExpiresIn() > 0) {
database.markExpireStarted(messageId);
expirationManager.scheduleDeletion(record.getId(), record.isMms(), record.getExpiresIn());
}
}
log(TAG, String.valueOf(record.getDateSent()), "Sent message: " + messageId);
@ -167,7 +169,7 @@ public class PushTextSendJob extends PushSendJob {
try {
rotateSenderCertificateIfNecessary();
Recipient messageRecipient = message.getIndividualRecipient().fresh();
Recipient messageRecipient = message.getIndividualRecipient().resolve();
if (messageRecipient.isUnregistered()) {
throw new UndeliverableMessageException(messageRecipient.getId() + " not registered!");
@ -192,16 +194,14 @@ public class PushTextSendJob extends PushSendJob {
Optional<UnidentifiedAccessPair> syncAccess = UnidentifiedAccessUtil.getAccessForSync(context);
SignalServiceSyncMessage syncMessage = buildSelfSendSyncMessage(context, textSecureMessage, syncAccess);
SignalLocalMetrics.IndividualMessageSend.onNetworkStarted(messageId);
SignalLocalMetrics.IndividualMessageSend.onDeliveryStarted(messageId);
SendMessageResult result = messageSender.sendSyncMessage(syncMessage, syncAccess);
SignalLocalMetrics.IndividualMessageSend.onNetworkFinished(messageId);
DatabaseFactory.getMessageLogDatabase(context).insertIfPossible(messageRecipient.getId(), message.getDateSent(), result, ContentHint.RESENDABLE, new MessageId(messageId, false));
return syncAccess.isPresent();
} else {
SignalLocalMetrics.IndividualMessageSend.onNetworkStarted(messageId);
SendMessageResult result = messageSender.sendDataMessage(address, unidentifiedAccess, ContentHint.RESENDABLE, textSecureMessage);
SignalLocalMetrics.IndividualMessageSend.onNetworkFinished(messageId);
SignalLocalMetrics.IndividualMessageSend.onDeliveryStarted(messageId);
SendMessageResult result = messageSender.sendDataMessage(address, unidentifiedAccess, ContentHint.RESENDABLE, textSecureMessage, new MetricEventListener(messageId));
DatabaseFactory.getMessageLogDatabase(context).insertIfPossible(messageRecipient.getId(), message.getDateSent(), result, ContentHint.RESENDABLE, new MessageId(messageId, false));
return result.getSuccess().isUnidentified();
@ -218,6 +218,29 @@ public class PushTextSendJob extends PushSendJob {
return data.getLong(KEY_MESSAGE_ID);
}
private static class MetricEventListener implements SignalServiceMessageSender.IndividualSendEvents {
private final long messageId;
private MetricEventListener(long messageId) {
this.messageId = messageId;
}
@Override
public void onMessageEncrypted() {
SignalLocalMetrics.IndividualMessageSend.onMessageEncrypted(messageId);
}
@Override
public void onMessageSent() {
SignalLocalMetrics.IndividualMessageSend.onMessageSent(messageId);
}
@Override
public void onSyncMessageSent() {
SignalLocalMetrics.IndividualMessageSend.onSyncMessageSent(messageId);
}
}
public static class Factory implements Job.Factory<PushTextSendJob> {
@Override
public @NonNull PushTextSendJob create(@NonNull Parameters parameters, @NonNull Data data) {

View file

@ -14,6 +14,7 @@ import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.recipients.RecipientUtil;
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
import org.whispersystems.signalservice.api.SignalServiceMessageSender.IndividualSendEvents;
import org.whispersystems.signalservice.api.crypto.ContentHint;
import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException;
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage;
@ -94,7 +95,8 @@ public class RequestGroupInfoJob extends BaseJob {
messageSender.sendDataMessage(RecipientUtil.toSignalServiceAddress(context, recipient),
UnidentifiedAccessUtil.getAccessFor(context, recipient),
ContentHint.IMPLICIT,
message);
message,
IndividualSendEvents.EMPTY);
}
@Override

View file

@ -0,0 +1,67 @@
package org.thoughtcrime.securesms.jobs;
import androidx.annotation.NonNull;
import org.signal.core.util.ThreadUtil;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.jobmanager.Data;
import org.thoughtcrime.securesms.jobmanager.Job;
public final class ThreadUpdateJob extends BaseJob {
public static final String KEY = "ThreadUpdateJob";
private static final String KEY_THREAD_ID = "thread_id";
private final long threadId;
private ThreadUpdateJob(long threadId) {
this(new Parameters.Builder()
.setQueue("ThreadUpdateJob_" + threadId)
.setMaxInstancesForQueue(2)
.build(),
threadId);
}
private ThreadUpdateJob(@NonNull Parameters parameters, long threadId) {
super(parameters);
this.threadId = threadId;
}
public static void enqueue(long threadId) {
ApplicationDependencies.getJobManager().add(new ThreadUpdateJob(threadId));
}
@Override
public @NonNull Data serialize() {
return new Data.Builder().putLong(KEY_THREAD_ID, threadId).build();
}
@Override
public @NonNull String getFactoryKey() {
return KEY;
}
@Override
protected void onRun() throws Exception {
DatabaseFactory.getThreadDatabase(context).update(threadId, true);
ThreadUtil.sleep(1000);
}
@Override
protected boolean onShouldRetry(@NonNull Exception e) {
return false;
}
@Override
public void onFailure() {
}
public static final class Factory implements Job.Factory<ThreadUpdateJob> {
@Override
public @NonNull ThreadUpdateJob create(@NonNull Parameters parameters, @NonNull Data data) {
return new ThreadUpdateJob(parameters, data.getLong(KEY_THREAD_ID));
}
}
}

View file

@ -82,6 +82,7 @@ import org.thoughtcrime.securesms.jobs.RetrieveProfileJob;
import org.thoughtcrime.securesms.jobs.SendDeliveryReceiptJob;
import org.thoughtcrime.securesms.jobs.SenderKeyDistributionSendJob;
import org.thoughtcrime.securesms.jobs.StickerPackDownloadJob;
import org.thoughtcrime.securesms.jobs.ThreadUpdateJob;
import org.thoughtcrime.securesms.jobs.TrimThreadJob;
import org.thoughtcrime.securesms.keyvalue.SignalStore;
import org.thoughtcrime.securesms.linkpreview.LinkPreview;
@ -721,10 +722,13 @@ public final class MessageContentProcessor {
SecurityEvent.broadcastSecurityUpdateEvent(context);
long messageId = database.insertMessageOutbox(threadId, outgoingEndSessionMessage,
false, message.getTimestamp(),
null);
long messageId = database.insertMessageOutbox(threadId,
outgoingEndSessionMessage,
false,
message.getTimestamp(),
null);
database.markAsSent(messageId, true);
ThreadUpdateJob.enqueue(threadId);
}
return threadId;
@ -1555,6 +1559,7 @@ public final class MessageContentProcessor {
messageId = DatabaseFactory.getSmsDatabase(context).insertMessageOutbox(threadId, outgoingTextMessage, false, message.getTimestamp(), null);
database = DatabaseFactory.getSmsDatabase(context);
database.markUnidentified(messageId, isUnidentified(message, recipient));
ThreadUpdateJob.enqueue(threadId);
}
database.markAsSent(messageId, true);

View file

@ -245,6 +245,10 @@ public class RecipientUtil {
@WorkerThread
public static void shareProfileIfFirstSecureMessage(@NonNull Context context, @NonNull Recipient recipient) {
if (recipient.isProfileSharing()) {
return;
}
long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdIfExistsFor(recipient.getId());
if (isPreMessageRequestThread(context, threadId)) {

View file

@ -16,6 +16,7 @@
*/
package org.thoughtcrime.securesms.sms;
import android.app.Application;
import android.content.Context;
import android.os.Parcel;
import android.os.Parcelable;
@ -61,6 +62,7 @@ import org.thoughtcrime.securesms.jobs.ReactionSendJob;
import org.thoughtcrime.securesms.jobs.RemoteDeleteSendJob;
import org.thoughtcrime.securesms.jobs.ResumableUploadSpecJob;
import org.thoughtcrime.securesms.jobs.SmsSendJob;
import org.thoughtcrime.securesms.jobs.ThreadUpdateJob;
import org.thoughtcrime.securesms.keyvalue.SignalStore;
import org.thoughtcrime.securesms.linkpreview.LinkPreview;
import org.thoughtcrime.securesms.mms.MmsException;
@ -120,6 +122,7 @@ public class MessageSender {
sendTextMessage(context, recipient, forceSms, keyExchange, messageId);
onMessageSent();
ThreadUpdateJob.enqueue(threadId);
return allocatedThreadId;
}

View file

@ -19,6 +19,7 @@ import org.thoughtcrime.securesms.database.IdentityDatabase.IdentityRecord;
import org.thoughtcrime.securesms.database.MessageDatabase;
import org.thoughtcrime.securesms.database.MessageDatabase.InsertResult;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.jobs.ThreadUpdateJob;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.sms.IncomingIdentityDefaultMessage;
@ -90,6 +91,7 @@ public final class IdentityUtil {
else outgoing = new OutgoingIdentityDefaultMessage(recipient);
DatabaseFactory.getSmsDatabase(context).insertMessageOutbox(threadId, outgoing, false, time, null);
ThreadUpdateJob.enqueue(threadId);
}
}
}
@ -112,6 +114,7 @@ public final class IdentityUtil {
Log.i(TAG, "Inserting verified outbox...");
DatabaseFactory.getSmsDatabase(context).insertMessageOutbox(threadId, outgoing, false, time, null);
ThreadUpdateJob.enqueue(threadId);
}
}

View file

@ -99,7 +99,9 @@ public final class SignalLocalMetrics {
private static final String SPLIT_DB_INSERT = "db-insert";
private static final String SPLIT_JOB_ENQUEUE = "job-enqueue";
private static final String SPLIT_JOB_PRE_NETWORK = "job-pre-network";
private static final String SPLIT_NETWORK = "network";
private static final String SPLIT_ENCRYPT = "encrypt";
private static final String SPLIT_NETWORK_MAIN = "network-main";
private static final String SPLIT_NETWORK_SYNC = "network-sync";
private static final String SPLIT_JOB_POST_NETWORK = "job-post-network";
private static final String SPLIT_UI_UPDATE = "ui-update";
@ -123,14 +125,24 @@ public final class SignalLocalMetrics {
LocalMetrics.getInstance().split(requireId(messageId), SPLIT_JOB_ENQUEUE);
}
public static void onNetworkStarted(long messageId) {
public static void onDeliveryStarted(long messageId) {
if (!ID_MAP.containsKey(messageId)) return;
LocalMetrics.getInstance().split(requireId(messageId), SPLIT_JOB_PRE_NETWORK);
}
public static void onNetworkFinished(long messageId) {
public static void onMessageEncrypted(long messageId) {
if (!ID_MAP.containsKey(messageId)) return;
LocalMetrics.getInstance().split(requireId(messageId), SPLIT_NETWORK);
LocalMetrics.getInstance().split(requireId(messageId), SPLIT_ENCRYPT);
}
public static void onMessageSent(long messageId) {
if (!ID_MAP.containsKey(messageId)) return;
LocalMetrics.getInstance().split(requireId(messageId), SPLIT_NETWORK_MAIN);
}
public static void onSyncMessageSent(long messageId) {
if (!ID_MAP.containsKey(messageId)) return;
LocalMetrics.getInstance().split(requireId(messageId), SPLIT_NETWORK_SYNC);
}
public static void onJobFinished(long messageId) {

View file

@ -319,15 +319,21 @@ public class SignalServiceMessageSender {
public SendMessageResult sendDataMessage(SignalServiceAddress recipient,
Optional<UnidentifiedAccessPair> unidentifiedAccess,
ContentHint contentHint,
SignalServiceDataMessage message)
SignalServiceDataMessage message,
IndividualSendEvents sendEvents)
throws UntrustedIdentityException, IOException
{
Log.d(TAG, "[" + message.getTimestamp() + "] Sending a data message.");
Content content = createMessageContent(message);
EnvelopeContent envelopeContent = EnvelopeContent.encrypted(content, contentHint, message.getGroupId());
long timestamp = message.getTimestamp();
SendMessageResult result = sendMessage(recipient, getTargetUnidentifiedAccess(unidentifiedAccess), timestamp, envelopeContent, false, null);
sendEvents.onMessageEncrypted();
long timestamp = message.getTimestamp();
SendMessageResult result = sendMessage(recipient, getTargetUnidentifiedAccess(unidentifiedAccess), timestamp, envelopeContent, false, null);
sendEvents.onMessageSent();
if (result.getSuccess() != null && result.getSuccess().isNeedsSync()) {
Content syncMessage = createMultiDeviceSentTranscriptContent(content, Optional.of(recipient), timestamp, Collections.singletonList(result), false);
@ -336,6 +342,8 @@ public class SignalServiceMessageSender {
sendMessage(localAddress, Optional.absent(), timestamp, syncMessageContent, false, null);
}
sendEvents.onSyncMessageSent();
// TODO [greyson][session] Delete this when we delete the button
if (message.isEndSession()) {
store.deleteAllSessions(recipient.getUuid().toString());
@ -2077,4 +2085,21 @@ public class SignalServiceMessageSender {
public interface EventListener {
void onSecurityEvent(SignalServiceAddress address);
}
public interface IndividualSendEvents {
IndividualSendEvents EMPTY = new IndividualSendEvents() {
@Override
public void onMessageEncrypted() { }
@Override
public void onMessageSent() { }
@Override
public void onSyncMessageSent() { }
};
void onMessageEncrypted();
void onMessageSent();
void onSyncMessageSent();
}
}