Ensure notifications are processed after receiving GCM message.

It's unreliable to run these tasks on WorkManager, as there's no
scheduling guarantees.
This commit is contained in:
Greyson Parrelli
2018-10-05 15:23:33 -07:00
parent 1c197ad93d
commit 1b736e9e04
11 changed files with 208 additions and 63 deletions

View File

@@ -54,7 +54,7 @@ public class PushContentReceiveJob extends PushReceivedJob {
String sessionKey = TextSecurePreferences.getSignalingKey(context);
SignalServiceEnvelope envelope = new SignalServiceEnvelope(data, sessionKey);
handle(envelope);
processEnvelope(envelope);
} catch (IOException | InvalidVersionException e) {
Log.w(TAG, e);
}

View File

@@ -119,6 +119,10 @@ public class PushDecryptJob extends ContextJob {
super(null, null);
}
public PushDecryptJob(Context context) {
this(context, -1);
}
public PushDecryptJob(Context context, long pushMessageId) {
this(context, pushMessageId, -1);
}
@@ -146,32 +150,20 @@ public class PushDecryptJob extends ContextJob {
@Override
public void onRun() throws NoSuchMessageException {
if (!IdentityKeyUtil.hasIdentityKey(context)) {
Log.w(TAG, "Skipping job, waiting for migration...");
return;
synchronized (PushReceivedJob.RECEIVE_LOCK) {
if (needsMigration()) {
Log.w(TAG, "Skipping, waiting for migration...");
postMigrationNotification();
return;
}
PushDatabase database = DatabaseFactory.getPushDatabase(context);
SignalServiceEnvelope envelope = database.get(messageId);
Optional<Long> optionalSmsMessageId = smsMessageId > 0 ? Optional.of(smsMessageId) : Optional.absent();
handleMessage(envelope, optionalSmsMessageId);
database.delete(messageId);
}
if (TextSecurePreferences.getNeedsSqlCipherMigration(context)) {
Log.w(TAG, "Skipping job, waiting for sqlcipher migration...");
NotificationManagerCompat.from(context).notify(494949,
new NotificationCompat.Builder(context, NotificationChannels.getMessagesChannel(context))
.setSmallIcon(R.drawable.icon_notification)
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setCategory(NotificationCompat.CATEGORY_MESSAGE)
.setContentTitle(context.getString(R.string.PushDecryptJob_new_locked_message))
.setContentText(context.getString(R.string.PushDecryptJob_unlock_to_view_pending_messages))
.setContentIntent(PendingIntent.getActivity(context, 0, new Intent(context, ConversationListActivity.class), 0))
.setDefaults(NotificationCompat.DEFAULT_SOUND | NotificationCompat.DEFAULT_VIBRATE)
.build());
return;
}
PushDatabase database = DatabaseFactory.getPushDatabase(context);
SignalServiceEnvelope envelope = database.get(messageId);
Optional<Long> optionalSmsMessageId = smsMessageId > 0 ? Optional.of(smsMessageId) : Optional.absent();
handleMessage(envelope, optionalSmsMessageId);
database.delete(messageId);
}
@Override
@@ -184,7 +176,38 @@ public class PushDecryptJob extends ContextJob {
}
private void handleMessage(SignalServiceEnvelope envelope, Optional<Long> smsMessageId) {
public void processMessage(@NonNull SignalServiceEnvelope envelope) {
synchronized (PushReceivedJob.RECEIVE_LOCK) {
if (needsMigration()) {
Log.w(TAG, "Skipping and storing envelope, waiting for migration...");
DatabaseFactory.getPushDatabase(context).insert(envelope);
postMigrationNotification();
return;
}
handleMessage(envelope, Optional.absent());
}
}
private boolean needsMigration() {
return !IdentityKeyUtil.hasIdentityKey(context) || TextSecurePreferences.getNeedsSqlCipherMigration(context);
}
private void postMigrationNotification() {
NotificationManagerCompat.from(context).notify(494949,
new NotificationCompat.Builder(context, NotificationChannels.getMessagesChannel(context))
.setSmallIcon(R.drawable.icon_notification)
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setCategory(NotificationCompat.CATEGORY_MESSAGE)
.setContentTitle(context.getString(R.string.PushDecryptJob_new_locked_message))
.setContentText(context.getString(R.string.PushDecryptJob_unlock_to_view_pending_messages))
.setContentIntent(PendingIntent.getActivity(context, 0, new Intent(context, ConversationListActivity.class), 0))
.setDefaults(NotificationCompat.DEFAULT_SOUND | NotificationCompat.DEFAULT_VIBRATE)
.build());
}
private void handleMessage(@NonNull SignalServiceEnvelope envelope, @NonNull Optional<Long> smsMessageId) {
try {
GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context);
SignalProtocolStore axolotlStore = new SignalProtocolStoreImpl(context);

View File

@@ -4,10 +4,13 @@ import android.content.Context;
import android.support.annotation.NonNull;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.database.NoSuchMessageException;
import org.thoughtcrime.securesms.dependencies.InjectableType;
import org.thoughtcrime.securesms.jobmanager.JobParameters;
import org.thoughtcrime.securesms.jobmanager.SafeData;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.SignalServiceMessageReceiver;
import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope;
import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException;
@@ -51,14 +54,19 @@ public class PushNotificationReceiveJob extends PushReceivedJob implements Injec
@Override
public void onRun() throws IOException {
receiver.retrieveMessages(new SignalServiceMessageReceiver.MessageReceivedCallback() {
@Override
public void onMessage(SignalServiceEnvelope envelope) {
handle(envelope);
}
});
pullAndProcessMessages(receiver, TAG, System.currentTimeMillis());
}
public void pullAndProcessMessages(SignalServiceMessageReceiver receiver, String tag, long startTime) throws IOException {
synchronized (PushReceivedJob.RECEIVE_LOCK) {
receiver.retrieveMessages(envelope -> {
Log.i(tag, "Retrieved an envelope." + timeSuffix(startTime));
processEnvelope(envelope);
Log.i(tag, "Successfully processed an envelope." + timeSuffix(startTime));
});
TextSecurePreferences.setNeedsMessagePull(context, false);
}
}
@Override
public boolean onShouldRetry(Exception e) {
Log.w(TAG, e);
@@ -70,4 +78,8 @@ public class PushNotificationReceiveJob extends PushReceivedJob implements Injec
Log.w(TAG, "***** Failed to download pending message!");
// MessageNotifier.notifyMessagesPending(getContext());
}
private static String timeSuffix(long startTime) {
return " (" + (System.currentTimeMillis() - startTime) + " ms elapsed)";
}
}

View File

@@ -8,7 +8,6 @@ import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.MessagingDatabase.SyncMessageId;
import org.thoughtcrime.securesms.database.RecipientDatabase;
import org.thoughtcrime.securesms.jobmanager.JobManager;
import org.thoughtcrime.securesms.jobmanager.JobParameters;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.recipients.Recipient;
@@ -18,33 +17,34 @@ public abstract class PushReceivedJob extends ContextJob {
private static final String TAG = PushReceivedJob.class.getSimpleName();
public static final Object RECEIVE_LOCK = new Object();
protected PushReceivedJob(Context context, JobParameters parameters) {
super(context, parameters);
}
public void handle(SignalServiceEnvelope envelope) {
Address source = Address.fromExternal(context, envelope.getSource());
Recipient recipient = Recipient.from(context, source, false);
public void processEnvelope(@NonNull SignalServiceEnvelope envelope) {
synchronized (RECEIVE_LOCK) {
Address source = Address.fromExternal(context, envelope.getSource());
Recipient recipient = Recipient.from(context, source, false);
if (!isActiveNumber(recipient)) {
DatabaseFactory.getRecipientDatabase(context).setRegistered(recipient, RecipientDatabase.RegisteredState.REGISTERED);
ApplicationContext.getInstance(context).getJobManager().add(new DirectoryRefreshJob(context, recipient, false));
}
if (!isActiveNumber(recipient)) {
DatabaseFactory.getRecipientDatabase(context).setRegistered(recipient, RecipientDatabase.RegisteredState.REGISTERED);
ApplicationContext.getInstance(context).getJobManager().add(new DirectoryRefreshJob(context, recipient, false));
}
if (envelope.isReceipt()) {
handleReceipt(envelope);
} else if (envelope.isPreKeySignalMessage() || envelope.isSignalMessage()) {
handleMessage(envelope);
} else {
Log.w(TAG, "Received envelope of unknown type: " + envelope.getType());
if (envelope.isReceipt()) {
handleReceipt(envelope);
} else if (envelope.isPreKeySignalMessage() || envelope.isSignalMessage()) {
handleMessage(envelope);
} else {
Log.w(TAG, "Received envelope of unknown type: " + envelope.getType());
}
}
}
private void handleMessage(SignalServiceEnvelope envelope) {
long messageId = DatabaseFactory.getPushDatabase(context).insert(envelope);
ApplicationContext.getInstance(context)
.getJobManager()
.add(new PushDecryptJob(context, messageId));
new PushDecryptJob(context).processMessage(envelope);
}
private void handleReceipt(SignalServiceEnvelope envelope) {
@@ -56,6 +56,4 @@ public abstract class PushReceivedJob extends ContextJob {
private boolean isActiveNumber(@NonNull Recipient recipient) {
return recipient.resolve().getRegistered() == RecipientDatabase.RegisteredState.REGISTERED;
}
}